diff options
author | JP Cimalando <jpcima@users.noreply.github.com> | 2018-04-10 12:55:03 +0200 |
---|---|---|
committer | JP Cimalando <jpcima@users.noreply.github.com> | 2018-04-11 18:14:34 +0200 |
commit | 0bd662b2d7fc11c0b4c02372f97cf6062940c570 (patch) | |
tree | 378dd9714b6496f4f3a28424a468098c63cdf7d3 | |
parent | 47e2f73719ab55075d2d479daea3e9269370d421 (diff) | |
download | libADLMIDI-0bd662b2d7fc11c0b4c02372f97cf6062940c570.tar.gz libADLMIDI-0bd662b2d7fc11c0b4c02372f97cf6062940c570.tar.bz2 libADLMIDI-0bd662b2d7fc11c0b4c02372f97cf6062940c570.zip |
support for 32 bit mixing, and multiple sample formats
-rw-r--r-- | include/adlmidi.h | 24 | ||||
-rw-r--r-- | src/adlmidi.cpp | 126 | ||||
-rw-r--r-- | src/adlmidi_private.hpp | 21 | ||||
-rw-r--r-- | src/chips/dosbox_opl3.cpp | 28 | ||||
-rw-r--r-- | src/chips/dosbox_opl3.h | 2 | ||||
-rw-r--r-- | src/chips/nuked_opl3.cpp | 24 | ||||
-rw-r--r-- | src/chips/nuked_opl3.h | 2 | ||||
-rw-r--r-- | src/chips/nuked_opl3_v174.cpp | 24 | ||||
-rw-r--r-- | src/chips/nuked_opl3_v174.h | 2 | ||||
-rw-r--r-- | src/chips/opl_chip_base.h | 2 |
10 files changed, 237 insertions, 18 deletions
diff --git a/include/adlmidi.h b/include/adlmidi.h index b21df2c..8ba8939 100644 --- a/include/adlmidi.h +++ b/include/adlmidi.h @@ -65,6 +65,22 @@ enum ADLMIDI_VolumeModels ADLMIDI_VolumeModel_9X }; +enum ADLMIDI_SampleType +{ + ADLMIDI_SampleType_S16 = 0, /* signed PCM 16-bit */ + ADLMIDI_SampleType_S8, /* signed PCM 8-bit */ + ADLMIDI_SampleType_F32, /* float 32-bit */ + ADLMIDI_SampleType_F64, /* float 64-bit */ + ADLMIDI_SampleType_Count, +}; + +struct ADLMIDI_AudioFormat +{ + enum ADLMIDI_SampleType type; /* type of sample */ + unsigned containerSize; /* size in bytes of the storage type */ + unsigned sampleOffset; /* distance in bytes between consecutive samples */ +}; + struct ADL_MIDIPlayer { void *adl_midiPlayer; @@ -237,8 +253,14 @@ extern struct Adl_MarkerEntry adl_metaMarker(struct ADL_MIDIPlayer *device, size /*Take a sample buffer and iterate MIDI timers */ extern int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short out[]); +/*Take a sample buffer and iterate MIDI timers */ +extern int adl_playFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 left[], ADL_UInt8 right[], const struct ADLMIDI_AudioFormat *format); + +/*Generate audio output from chip emulators without iteration of MIDI timers.*/ +extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short out[]); + /*Generate audio output from chip emulators without iteration of MIDI timers.*/ -extern int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out); +extern int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, ADL_UInt8 left[], ADL_UInt8 right[], const struct ADLMIDI_AudioFormat *format); /** * @brief Periodic tick handler. diff --git a/src/adlmidi.cpp b/src/adlmidi.cpp index 7e4d457..39bb5b6 100644 --- a/src/adlmidi.cpp +++ b/src/adlmidi.cpp @@ -37,6 +37,13 @@ static ADL_Version adl_version = { ADLMIDI_VERSION_PATCHLEVEL }; +static const ADLMIDI_AudioFormat adl_DefaultAudioFormat = +{ + ADLMIDI_SampleType_S16, + sizeof(short), + 2 * sizeof(short), +}; + /*---------------------------EXPORTS---------------------------*/ ADLMIDI_EXPORT struct ADL_MIDIPlayer *adl_init(long sample_rate) @@ -608,30 +615,107 @@ ADLMIDI_EXPORT void adl_setDebugMessageHook(struct ADL_MIDIPlayer *device, ADL_D } +template <class Dst> +static void CopySamplesRaw(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + i * sampleOffset) = src[2 * i]; + *(Dst *)(dstRight + i * sampleOffset) = src[2 * i + 1]; + } +} -inline static void SendStereoAudio(int &samples_requested, - ssize_t &in_size, - short *_in, - ssize_t out_pos, - short *_out) +template <class Dst, class Ret> +static void CopySamplesTransformed(ADL_UInt8 *dstLeft, ADL_UInt8 *dstRight, const int32_t *src, + size_t frameCount, unsigned sampleOffset, + Ret(&transform)(int32_t)) +{ + for(size_t i = 0; i < frameCount; ++i) { + *(Dst *)(dstLeft + i * sampleOffset) = transform(src[2 * i]); + *(Dst *)(dstRight + i * sampleOffset) = transform(src[2 * i + 1]); + } +} + +static int SendStereoAudio(int samples_requested, + ssize_t in_size, + int32_t *_in, + ssize_t out_pos, + ADL_UInt8 *left, + ADL_UInt8 *right, + const ADLMIDI_AudioFormat *format) { if(!in_size) - return; - size_t offset = static_cast<size_t>(out_pos); + return 0; + size_t outputOffset = static_cast<size_t>(out_pos); size_t inSamples = static_cast<size_t>(in_size * 2); - size_t maxSamples = static_cast<size_t>(samples_requested) - offset; + size_t maxSamples = static_cast<size_t>(samples_requested) - outputOffset; size_t toCopy = std::min(maxSamples, inSamples); - std::memcpy(_out + out_pos, _in, toCopy * sizeof(short)); + + ADLMIDI_SampleType sampleType = format->type; + const unsigned containerSize = format->containerSize; + const unsigned sampleOffset = format->sampleOffset; + + switch(sampleType) { + case ADLMIDI_SampleType_S8: + switch(containerSize) { + case sizeof(int8_t): + CopySamplesTransformed<int8_t>(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS8); + break; + case sizeof(int16_t): + CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS8); + break; + case sizeof(int32_t): + CopySamplesTransformed<int32_t>(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS8); + break; + default: + return -1; + } + break; + case ADLMIDI_SampleType_S16: + switch(containerSize) { + case sizeof(int16_t): + CopySamplesTransformed<int16_t>(left, right, _in, toCopy / 2, sampleOffset, adl_cvtS16); + break; + case sizeof(int32_t): + CopySamplesRaw<int32_t>(left, right, _in, toCopy / 2, sampleOffset); + break; + default: + return -1; + } + break; + case ADLMIDI_SampleType_F32: + if(containerSize != sizeof(float)) + return -1; + CopySamplesTransformed<float>(left, right, _in, toCopy / 2, sampleOffset, adl_cvtReal<float>); + break; + case ADLMIDI_SampleType_F64: + if(containerSize != sizeof(double)) + return -1; + CopySamplesTransformed<double>(left, right, _in, toCopy / 2, sampleOffset, adl_cvtReal<double>); + break; + default: + return -1; + } + + return 0; } -ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) +ADLMIDI_EXPORT int adl_play(struct ADL_MIDIPlayer *device, int sampleCount, short *out) +{ + return adl_playFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_playFormat(ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 out_left[], ADL_UInt8 out_right[], + const ADLMIDI_AudioFormat *format) { #ifndef ADLMIDI_DISABLE_MIDI_SEQUENCER #ifdef ADLMIDI_HW_OPL (void)device; (void)sampleCount; (void)out; + (void)format; return 0; #else sampleCount -= sampleCount % 2; //Avoid even sample requests @@ -685,8 +769,8 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int16_t *out_buf = player->outBuf; - std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t)); + int32_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0])); unsigned int chips = player->opl.NumCards; if(chips == 1) { @@ -699,7 +783,8 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) player->opl.cardsOP2[card]->generateAndMix(out_buf, (size_t)in_generatedStereo); } /* Process it */ - SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out); + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; @@ -726,10 +811,18 @@ ADLMIDI_EXPORT int adl_play(ADL_MIDIPlayer *device, int sampleCount, short *out) ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, short *out) { + return adl_generateFormat(device, sampleCount, (ADL_UInt8 *)out, (ADL_UInt8 *)(out + 1), &adl_DefaultAudioFormat); +} + +ADLMIDI_EXPORT int adl_generateFormat(struct ADL_MIDIPlayer *device, int sampleCount, + ADL_UInt8 out_left[], ADL_UInt8 out_right[], + const ADLMIDI_AudioFormat *format) +{ #ifdef ADLMIDI_HW_OPL (void)device; (void)sampleCount; (void)out; + (void)format; return 0; #else sampleCount -= sampleCount % 2; //Avoid even sample requests @@ -766,8 +859,8 @@ ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, ssize_t in_generatedPhys = in_generatedStereo * 2; //! Unsigned total sample count //fill buffer with zeros - int16_t *out_buf = player->outBuf; - std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(int16_t)); + int32_t *out_buf = player->outBuf; + std::memset(out_buf, 0, static_cast<size_t>(in_generatedPhys) * sizeof(out_buf[0])); unsigned int chips = player->opl.NumCards; if(chips == 1) player->opl.cardsOP2[0]->generate(out_buf, (size_t)in_generatedStereo); @@ -778,7 +871,8 @@ ADLMIDI_EXPORT int adl_generate(struct ADL_MIDIPlayer *device, int sampleCount, player->opl.cardsOP2[card]->generateAndMix(out_buf, (size_t)in_generatedStereo); } /* Process it */ - SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out); + if(SendStereoAudio(sampleCount, in_generatedStereo, out_buf, gotten_len, out_left, out_right, format) == -1) + return 0; left -= (int)in_generatedPhys; gotten_len += (in_generatedPhys) /* - setup.stored_samples*/; diff --git a/src/adlmidi_private.hpp b/src/adlmidi_private.hpp index caaf4f9..63cadb3 100644 --- a/src/adlmidi_private.hpp +++ b/src/adlmidi_private.hpp @@ -132,6 +132,25 @@ typedef int32_t ssize_t; extern std::string ADLMIDI_ErrorString; /* + Sample conversions to various formats +*/ +template <class Real> +inline Real adl_cvtReal(int32_t x) +{ + return x * ((Real)1 / INT16_MAX); +} +inline int32_t adl_cvtS16(int32_t x) +{ + x = (x < INT16_MIN) ? INT16_MIN : x; + x = (x > INT16_MAX) ? INT16_MAX : x; + return x; +} +inline int32_t adl_cvtS8(int32_t x) +{ + return adl_cvtS16(x) / 256; +} + +/* Smart pointer for C heaps, created with malloc() call. FAQ: Why not std::shared_ptr? Because of Android NDK now doesn't supports it */ @@ -909,7 +928,7 @@ public: #endif OPL3 opl; - int16_t outBuf[1024]; + int32_t outBuf[1024]; Setup m_setup; diff --git a/src/chips/dosbox_opl3.cpp b/src/chips/dosbox_opl3.cpp index b696343..57b1412 100644 --- a/src/chips/dosbox_opl3.cpp +++ b/src/chips/dosbox_opl3.cpp @@ -78,6 +78,34 @@ int DosBoxOPL3::generateAndMix(int16_t *output, size_t frames) return (int)frames; } +int DosBoxOPL3::generate(int32_t *output, size_t frames) +{ + DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip); + ssize_t left = (ssize_t)frames; + while(left > 0) + { + ssize_t frames_i = left; + chip_r->GenerateArr(output, &frames_i); + output += (frames_i * 2); + left -= frames_i; + } + return (int)frames; +} + +int DosBoxOPL3::generateAndMix(int32_t *output, size_t frames) +{ + DBOPL::Handler *chip_r = reinterpret_cast<DBOPL::Handler*>(m_chip); + ssize_t left = (ssize_t)frames; + while(left > 0) + { + ssize_t frames_i = left; + chip_r->GenerateArrMix(output, &frames_i); + output += (frames_i * 2); + left -= frames_i; + } + return (int)frames; +} + const char *DosBoxOPL3::emulatorName() { return "DosBox 0.74 OPL3"; diff --git a/src/chips/dosbox_opl3.h b/src/chips/dosbox_opl3.h index 46c20fa..05682ae 100644 --- a/src/chips/dosbox_opl3.h +++ b/src/chips/dosbox_opl3.h @@ -17,6 +17,8 @@ public: virtual void writeReg(uint16_t addr, uint8_t data) override; virtual int generate(int16_t *output, size_t frames) override; virtual int generateAndMix(int16_t *output, size_t frames) override; + virtual int generate(int32_t *output, size_t frames) override; + virtual int generateAndMix(int32_t *output, size_t frames) override; virtual const char *emulatorName() override; }; diff --git a/src/chips/nuked_opl3.cpp b/src/chips/nuked_opl3.cpp index 0898907..16cc73b 100644 --- a/src/chips/nuked_opl3.cpp +++ b/src/chips/nuked_opl3.cpp @@ -61,6 +61,30 @@ int NukedOPL3::generateAndMix(int16_t *output, size_t frames) return (int)frames; } +int NukedOPL3::generate(int32_t *output, size_t frames) +{ + opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip); + for(size_t i = 0; i < frames; ++i) { + int16_t frame[2]; + OPL3_GenerateResampled(chip_r, frame); + output[2 * i] = frame[0]; + output[2 * i + 1] = frame[1]; + } + return (int)frames; +} + +int NukedOPL3::generateAndMix(int32_t *output, size_t frames) +{ + opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip); + for(size_t i = 0; i < frames; ++i) { + int16_t frame[2]; + OPL3_GenerateResampled(chip_r, frame); + output[2 * i] += frame[0]; + output[2 * i + 1] += frame[1]; + } + return (int)frames; +} + const char *NukedOPL3::emulatorName() { return "Nuked OPL3 (v 1.8)"; diff --git a/src/chips/nuked_opl3.h b/src/chips/nuked_opl3.h index db665fe..144fb23 100644 --- a/src/chips/nuked_opl3.h +++ b/src/chips/nuked_opl3.h @@ -17,6 +17,8 @@ public: virtual void writeReg(uint16_t addr, uint8_t data) override; virtual int generate(int16_t *output, size_t frames) override; virtual int generateAndMix(int16_t *output, size_t frames) override; + virtual int generate(int32_t *output, size_t frames) override; + virtual int generateAndMix(int32_t *output, size_t frames) override; virtual const char *emulatorName() override; }; diff --git a/src/chips/nuked_opl3_v174.cpp b/src/chips/nuked_opl3_v174.cpp index 7188e83..59f7953 100644 --- a/src/chips/nuked_opl3_v174.cpp +++ b/src/chips/nuked_opl3_v174.cpp @@ -61,6 +61,30 @@ int NukedOPL3v174::generateAndMix(int16_t *output, size_t frames) return (int)frames; } +int NukedOPL3v174::generate(int32_t *output, size_t frames) +{ + opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip); + for(size_t i = 0; i < frames; ++i) { + int16_t frame[2]; + OPL3v17_GenerateResampled(chip_r, frame); + output[2 * i] = frame[0]; + output[2 * i + 1] = frame[1]; + } + return (int)frames; +} + +int NukedOPL3v174::generateAndMix(int32_t *output, size_t frames) +{ + opl3_chip *chip_r = reinterpret_cast<opl3_chip*>(m_chip); + for(size_t i = 0; i < frames; ++i) { + int16_t frame[2]; + OPL3v17_GenerateResampled(chip_r, frame); + output[2 * i] += frame[0]; + output[2 * i + 1] += frame[1]; + } + return (int)frames; +} + const char *NukedOPL3v174::emulatorName() { return "Nuked OPL3 (v 1.7.4)"; diff --git a/src/chips/nuked_opl3_v174.h b/src/chips/nuked_opl3_v174.h index cf23187..b9a0466 100644 --- a/src/chips/nuked_opl3_v174.h +++ b/src/chips/nuked_opl3_v174.h @@ -17,6 +17,8 @@ public: virtual void writeReg(uint16_t addr, uint8_t data) override; virtual int generate(int16_t *output, size_t frames) override; virtual int generateAndMix(int16_t *output, size_t frames) override; + virtual int generate(int32_t *output, size_t frames) override; + virtual int generateAndMix(int32_t *output, size_t frames) override; virtual const char *emulatorName() override; }; diff --git a/src/chips/opl_chip_base.h b/src/chips/opl_chip_base.h index aa6934b..1aaddf8 100644 --- a/src/chips/opl_chip_base.h +++ b/src/chips/opl_chip_base.h @@ -19,6 +19,8 @@ public: virtual void writeReg(uint16_t addr, uint8_t data) = 0; virtual int generate(int16_t *output, size_t frames) = 0; virtual int generateAndMix(int16_t *output, size_t frames) = 0; + virtual int generate(int32_t *output, size_t frames) = 0; + virtual int generateAndMix(int32_t *output, size_t frames) = 0; virtual const char* emulatorName() = 0; }; |