aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJP Cimalando <jpcima@users.noreply.github.com>2018-04-10 12:55:03 +0200
committerJP Cimalando <jpcima@users.noreply.github.com>2018-04-11 18:14:34 +0200
commit0bd662b2d7fc11c0b4c02372f97cf6062940c570 (patch)
tree378dd9714b6496f4f3a28424a468098c63cdf7d3
parent47e2f73719ab55075d2d479daea3e9269370d421 (diff)
downloadlibADLMIDI-0bd662b2d7fc11c0b4c02372f97cf6062940c570.tar.gz
libADLMIDI-0bd662b2d7fc11c0b4c02372f97cf6062940c570.tar.bz2
libADLMIDI-0bd662b2d7fc11c0b4c02372f97cf6062940c570.zip
support for 32 bit mixing, and multiple sample formats
-rw-r--r--include/adlmidi.h24
-rw-r--r--src/adlmidi.cpp126
-rw-r--r--src/adlmidi_private.hpp21
-rw-r--r--src/chips/dosbox_opl3.cpp28
-rw-r--r--src/chips/dosbox_opl3.h2
-rw-r--r--src/chips/nuked_opl3.cpp24
-rw-r--r--src/chips/nuked_opl3.h2
-rw-r--r--src/chips/nuked_opl3_v174.cpp24
-rw-r--r--src/chips/nuked_opl3_v174.h2
-rw-r--r--src/chips/opl_chip_base.h2
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;
};