aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2019-09-23 17:55:22 +0300
committerWohlstand <admin@wohlnet.ru>2019-09-23 17:55:22 +0300
commit7ff1f8b9522d132c9248a68cc5df15e841523ea6 (patch)
tree8274dc1ea2a8f289e4ac607d0e1dfc056ceef2b8 /utils
parent22248f90dd83413e91c8d4b66e652ef1a7954b60 (diff)
downloadlibADLMIDI-7ff1f8b9522d132c9248a68cc5df15e841523ea6.tar.gz
libADLMIDI-7ff1f8b9522d132c9248a68cc5df15e841523ea6.tar.bz2
libADLMIDI-7ff1f8b9522d132c9248a68cc5df15e841523ea6.zip
MidiPlay: Move Audio Output into separate module
Diffstat (limited to 'utils')
-rw-r--r--utils/adlmidi-2/midiplay.cc5
-rw-r--r--utils/midiplay/CMakeLists.txt32
-rw-r--r--utils/midiplay/adlmidiplay.cpp177
-rw-r--r--utils/midiplay/audio.h64
-rw-r--r--utils/midiplay/audio_sdl.c149
-rw-r--r--utils/midiplay/audio_winmm.c256
-rwxr-xr-xutils/midiplay/wave_writer.c214
-rwxr-xr-xutils/midiplay/wave_writer.h20
8 files changed, 747 insertions, 170 deletions
diff --git a/utils/adlmidi-2/midiplay.cc b/utils/adlmidi-2/midiplay.cc
index f51c20d..3cb35a0 100644
--- a/utils/adlmidi-2/midiplay.cc
+++ b/utils/adlmidi-2/midiplay.cc
@@ -1499,8 +1499,13 @@ static int ParseCommandLine(char *cmdline, char **argv)
}
extern int main(int argc, char **argv);
+
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
{
+ (void)hInst;
+ (void)hPrev;
+ (void)szCmdLine;
+ (void)sw;
//extern int main(int, char **);
char *cmdline = GetCommandLine();
int argc = ParseCommandLine(cmdline, NULL);
diff --git a/utils/midiplay/CMakeLists.txt b/utils/midiplay/CMakeLists.txt
index c60450f..98f888a 100644
--- a/utils/midiplay/CMakeLists.txt
+++ b/utils/midiplay/CMakeLists.txt
@@ -1,15 +1,39 @@
-add_executable(adlmidiplay
+
+if(NOT ADLMIDI_DOS AND NOT MIDIPLAY_WAVE_ONLY)
+ if(NOT WIN32)
+ libADLMIDI_find_SDL2()
+ set(USE_SDL2_AUDIO 1)
+ else()
+ endif()
+endif()
+
+set(ADLMIDI_PLAY_SRC
adlmidiplay.cpp
wave_writer.c
)
-set_nopie(adlmidiplay)
+if(USE_SDL2_AUDIO)
+ list(APPEND ADLMIDI_PLAY_SRC
+ audio_sdl.c
+ )
+endif()
-if(NOT ADLMIDI_DOS AND NOT MIDIPLAY_WAVE_ONLY)
- libADLMIDI_find_SDL2()
+if(WIN32 AND NOT USE_SDL2_AUDIO)
+ list(APPEND ADLMIDI_PLAY_SRC
+ audio_winmm.c
+ )
+endif()
+
+add_executable(adlmidiplay ${ADLMIDI_PLAY_SRC})
+
+if(USE_SDL2_AUDIO)
target_link_libraries(adlmidiplay PRIVATE ADLMIDI_SDL2)
+elseif(WIN32)
+ target_link_libraries(adlmidiplay PRIVATE winmm)
endif()
+set_nopie(adlmidiplay)
+
if(MIDIPLAY_WAVE_ONLY)
target_compile_definitions(adlmidiplay PUBLIC OUTPUT_WAVE_ONLY)
message("Demo tool will only output WAVE file, no playing support.")
diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp
index aa96b5e..ce88c5f 100644
--- a/utils/midiplay/adlmidiplay.cpp
+++ b/utils/midiplay/adlmidiplay.cpp
@@ -1,3 +1,27 @@
+/*
+ * ADLMIDI Player is a free MIDI player based on a libADLMIDI,
+ * a Software MIDI synthesizer library with OPL3 emulation
+ *
+ * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
+ * ADLMIDI Library API: Copyright (c) 2015-2019 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
+ * http://iki.fi/bisqwit/source/adlmidi.html
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
#include <string>
#include <cstdio>
#include <cctype>
@@ -9,6 +33,7 @@
#include <vector>
#include <algorithm>
#include <signal.h>
+#include <stdint.h>
#if defined(__WATCOMC__)
#include <stdio.h> // snprintf is here!
@@ -78,29 +103,28 @@ void mch_delay(int32_t msec)
#ifndef HARDWARE_OPL3
#ifndef OUTPUT_WAVE_ONLY
-#define SDL_MAIN_HANDLED
-#include <SDL2/SDL.h>
+#include "audio.h"
#endif
#include "wave_writer.h"
-#ifndef OUTPUT_WAVE_ONLY
+# ifndef OUTPUT_WAVE_ONLY
class MutexType
{
- SDL_mutex *mut;
+ void *mut;
public:
- MutexType() : mut(SDL_CreateMutex()) { }
+ MutexType() : mut(audio_mutex_create()) { }
~MutexType()
{
- SDL_DestroyMutex(mut);
+ audio_mutex_destroy(mut);
}
void Lock()
{
- SDL_mutexP(mut);
+ audio_mutex_lock(mut);
}
void Unlock()
{
- SDL_mutexV(mut);
+ audio_mutex_unlock(mut);
}
};
@@ -109,50 +133,41 @@ static AudioBuff g_audioBuffer;
static MutexType g_audioBuffer_lock;
static ADLMIDI_AudioFormat g_audioFormat;
-static void SDL_AudioCallbackX(void *, Uint8 *stream, int len)
+static void SDL_AudioCallbackX(void *, uint8_t *stream, int len)
{
- SDL_LockAudio();
+ audio_lock();
//short *target = (short *) stream;
g_audioBuffer_lock.Lock();
- unsigned ate = len; // number of bytes
+ unsigned ate = static_cast<unsigned>(len); // number of bytes
if(ate > g_audioBuffer.size())
ate = (unsigned)g_audioBuffer.size();
for(unsigned a = 0; a < ate; ++a)
stream[a] = g_audioBuffer[a];
g_audioBuffer.erase(g_audioBuffer.begin(), g_audioBuffer.begin() + ate);
g_audioBuffer_lock.Unlock();
- SDL_UnlockAudio();
+ audio_unlock();
}
+# endif//OUTPUT_WAVE_ONLY
-static const char *SDLAudioToStr(int format)
+const char* audio_format_to_str(int format, int is_msb)
{
switch(format)
{
- case AUDIO_S8:
+ case ADLMIDI_SampleType_S8:
return "S8";
- case AUDIO_U8:
+ case ADLMIDI_SampleType_U8:
return "U8";
- case AUDIO_S16:
- return "S16";
- case AUDIO_S16MSB:
- return "S16MSB";
- case AUDIO_U16:
- return "U16";
- case AUDIO_U16MSB:
- return "U16MSB";
- case AUDIO_S32:
- return "S32";
- case AUDIO_S32MSB:
- return "S32MSB";
- case AUDIO_F32:
- return "F32";
- case AUDIO_F32MSB:
- return "F32MSB";
- default:
- return "UNK";
+ case ADLMIDI_SampleType_S16:
+ return is_msb ? "S16MSB" : "S16";
+ case ADLMIDI_SampleType_U16:
+ return is_msb ? "U16MSB" : "U16";
+ case ADLMIDI_SampleType_S32:
+ return is_msb ? "S32MSB" : "S32";
+ case ADLMIDI_SampleType_F32:
+ return is_msb ? "F32MSB" : "F32";
}
+ return "UNK";
}
-#endif//OUTPUT_WAVE_ONLY
#endif //HARDWARE_OPL3
@@ -310,14 +325,14 @@ int main(int argc, char **argv)
// The lag between visual content and audio content equals
// the sum of these two buffers.
#ifndef OUTPUT_WAVE_ONLY
- SDL_AudioSpec spec;
- SDL_AudioSpec obtained;
+ AudioOutputSpec spec;
+ AudioOutputSpec obtained;
spec.freq = (int)sampleRate;
- spec.format = AUDIO_S16SYS;
+ spec.format = ADLMIDI_SampleType_S16;
+ spec.is_msb = 0;
spec.channels = 2;
- spec.samples = Uint16((double)spec.freq * AudioBufferLength);
- spec.callback = SDL_AudioCallbackX;
+ spec.samples = uint16_t((double)spec.freq * AudioBufferLength);
#endif //OUTPUT_WAVE_ONLY
#endif //HARDWARE_OPL3
@@ -353,8 +368,8 @@ int main(int argc, char **argv)
#if !defined(HARDWARE_OPL3) && !defined(OUTPUT_WAVE_ONLY)
g_audioFormat.type = ADLMIDI_SampleType_S16;
- g_audioFormat.containerSize = sizeof(Sint16);
- g_audioFormat.sampleOffset = sizeof(Sint16) * 2;
+ g_audioFormat.containerSize = sizeof(int16_t);
+ g_audioFormat.sampleOffset = sizeof(int16_t) * 2;
#endif
while(argc > 2)
@@ -371,22 +386,22 @@ int main(int argc, char **argv)
{
//Current Wave output implementation allows only SINT16 output
g_audioFormat.type = ADLMIDI_SampleType_S16;
- g_audioFormat.containerSize = sizeof(Sint16);
- g_audioFormat.sampleOffset = sizeof(Sint16) * 2;
+ g_audioFormat.containerSize = sizeof(int16_t);
+ g_audioFormat.sampleOffset = sizeof(int16_t) * 2;
recordWave = true;//Record library output into WAV file
}
else if(!std::strcmp("-s8", argv[2]) && !recordWave)
- spec.format = AUDIO_S8;
+ spec.format = ADLMIDI_SampleType_S8;
else if(!std::strcmp("-u8", argv[2]) && !recordWave)
- spec.format = AUDIO_U8;
+ spec.format = ADLMIDI_SampleType_U8;
else if(!std::strcmp("-s16", argv[2]) && !recordWave)
- spec.format = AUDIO_S16;
+ spec.format = ADLMIDI_SampleType_S16;
else if(!std::strcmp("-u16", argv[2]) && !recordWave)
- spec.format = AUDIO_U16;
+ spec.format = ADLMIDI_SampleType_U16;
else if(!std::strcmp("-s32", argv[2]) && !recordWave)
- spec.format = AUDIO_S32;
+ spec.format = ADLMIDI_SampleType_S32;
else if(!std::strcmp("-f32", argv[2]) && !recordWave)
- spec.format = AUDIO_F32;
+ spec.format = ADLMIDI_SampleType_F32;
#endif
else if(!std::strcmp("-t", argv[2]))
@@ -489,46 +504,47 @@ int main(int argc, char **argv)
if(!recordWave)
{
// Set up SDL
- if(SDL_OpenAudio(&spec, &obtained) < 0)
+ if(audio_init(&spec, &obtained, SDL_AudioCallbackX) < 0)
{
- std::fprintf(stderr, "\nERROR: Couldn't open audio: %s\n\n", SDL_GetError());
- //return 1;
+ std::fprintf(stderr, "\nERROR: Couldn't open audio: %s\n\n", audio_get_error());
+ adl_close(myDevice);
+ return 1;
}
if(spec.samples != obtained.samples)
{
std::fprintf(stderr, " - Audio wanted (format=%s,samples=%u,rate=%u,channels=%u);\n"
" - Audio obtained (format=%s,samples=%u,rate=%u,channels=%u)\n",
- SDLAudioToStr(spec.format), spec.samples, spec.freq, spec.channels,
- SDLAudioToStr(obtained.format), obtained.samples, obtained.freq, obtained.channels);
+ audio_format_to_str(spec.format, spec.is_msb), spec.samples, spec.freq, spec.channels,
+ audio_format_to_str(obtained.format, obtained.is_msb), obtained.samples, obtained.freq, obtained.channels);
}
switch(obtained.format)
{
- case AUDIO_S8:
+ case ADLMIDI_SampleType_S8:
g_audioFormat.type = ADLMIDI_SampleType_S8;
- g_audioFormat.containerSize = sizeof(Sint8);
- g_audioFormat.sampleOffset = sizeof(Sint8) * 2;
+ g_audioFormat.containerSize = sizeof(int8_t);
+ g_audioFormat.sampleOffset = sizeof(int8_t) * 2;
break;
- case AUDIO_U8:
+ case ADLMIDI_SampleType_U8:
g_audioFormat.type = ADLMIDI_SampleType_U8;
- g_audioFormat.containerSize = sizeof(Uint8);
- g_audioFormat.sampleOffset = sizeof(Uint8) * 2;
+ g_audioFormat.containerSize = sizeof(uint8_t);
+ g_audioFormat.sampleOffset = sizeof(uint8_t) * 2;
break;
- case AUDIO_S16:
+ case ADLMIDI_SampleType_S16:
g_audioFormat.type = ADLMIDI_SampleType_S16;
- g_audioFormat.containerSize = sizeof(Sint16);
- g_audioFormat.sampleOffset = sizeof(Sint16) * 2;
+ g_audioFormat.containerSize = sizeof(int16_t);
+ g_audioFormat.sampleOffset = sizeof(int16_t) * 2;
break;
- case AUDIO_U16:
+ case ADLMIDI_SampleType_U16:
g_audioFormat.type = ADLMIDI_SampleType_U16;
- g_audioFormat.containerSize = sizeof(Uint16);
- g_audioFormat.sampleOffset = sizeof(Uint16) * 2;
+ g_audioFormat.containerSize = sizeof(uint16_t);
+ g_audioFormat.sampleOffset = sizeof(uint16_t) * 2;
break;
- case AUDIO_S32:
+ case ADLMIDI_SampleType_S32:
g_audioFormat.type = ADLMIDI_SampleType_S32;
- g_audioFormat.containerSize = sizeof(Sint32);
- g_audioFormat.sampleOffset = sizeof(Sint32) * 2;
+ g_audioFormat.containerSize = sizeof(int32_t);
+ g_audioFormat.sampleOffset = sizeof(int32_t) * 2;
break;
- case AUDIO_F32:
+ case ADLMIDI_SampleType_F32:
g_audioFormat.type = ADLMIDI_SampleType_F32;
g_audioFormat.containerSize = sizeof(float);
g_audioFormat.sampleOffset = sizeof(float) * 2;
@@ -546,6 +562,7 @@ int main(int argc, char **argv)
if(adl_setBank(myDevice, bankno) != 0)
{
printError(adl_errorInfo(myDevice));
+ adl_close(myDevice);
return 1;
}
std::fprintf(stdout, " - Use embedded bank #%d [%s]\n", bankno, adl_getBankNames()[bankno]);
@@ -562,6 +579,7 @@ int main(int argc, char **argv)
std::fprintf(stdout, "FAILED!\n");
flushout(stdout);
printError(adl_errorInfo(myDevice));
+ adl_close(myDevice);
return 1;
}
std::fprintf(stdout, "OK!\n");
@@ -588,12 +606,14 @@ int main(int argc, char **argv)
if(adl_getBank(myDevice, &id[i], ADLMIDI_Bank_Create, &bank) < 0)
{
printError(adl_errorInfo(myDevice));
+ adl_close(myDevice);
return 1;
}
if(adl_loadEmbeddedBank(myDevice, &bank, banks[i]) < 0)
{
printError(adl_errorInfo(myDevice));
+ adl_close(myDevice);
return 1;
}
}
@@ -610,6 +630,7 @@ int main(int argc, char **argv)
if(adl_setNumChips(myDevice, numOfChips) != 0)
{
printError(adl_errorInfo(myDevice));
+ adl_close(myDevice);
return 1;
}
#else
@@ -623,6 +644,7 @@ int main(int argc, char **argv)
if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0)
{
printError(adl_errorInfo(myDevice));
+ adl_close(myDevice);
return 1;
}
}
@@ -724,7 +746,7 @@ int main(int argc, char **argv)
flushout(stdout);
# ifndef HARDWARE_OPL3
- SDL_PauseAudio(0);
+ audio_start();
# endif
# ifdef DEBUG_SEEKING_TEST
@@ -734,10 +756,10 @@ int main(int argc, char **argv)
# endif
# ifndef HARDWARE_OPL3
- Uint8 buff[16384];
+ uint8_t buff[16384];
# endif
char posHMS[25];
- uint64_t milliseconds_prev = -1;
+ uint64_t milliseconds_prev = ~0u;
while(!stop)
{
# ifndef HARDWARE_OPL3
@@ -780,10 +802,10 @@ int main(int argc, char **argv)
g_audioBuffer[pos + p] = buff[p];
g_audioBuffer_lock.Unlock();
- const SDL_AudioSpec &spec = obtained;
- while(g_audioBuffer.size() > static_cast<size_t>(spec.samples + (spec.freq * g_audioFormat.sampleOffset) * OurHeadRoomLength))
+ const AudioOutputSpec &spec = obtained;
+ while(!stop && (g_audioBuffer.size() > static_cast<size_t>(spec.samples + (spec.freq * g_audioFormat.sampleOffset) * OurHeadRoomLength)))
{
- SDL_Delay(1);
+ audio_delay(1);
}
# ifdef DEBUG_SEEKING_TEST
@@ -826,7 +848,8 @@ int main(int argc, char **argv)
}
std::fprintf(stdout, " \n\n");
# ifndef HARDWARE_OPL3
- SDL_CloseAudio();
+ audio_stop();
+ audio_close();
# endif
}
#endif //OUTPUT_WAVE_ONLY
diff --git a/utils/midiplay/audio.h b/utils/midiplay/audio.h
new file mode 100644
index 0000000..ee770ed
--- /dev/null
+++ b/utils/midiplay/audio.h
@@ -0,0 +1,64 @@
+/*
+ * ADLMIDI Player is a free MIDI player based on a libADLMIDI,
+ * a Software MIDI synthesizer library with OPL3 emulation
+ *
+ * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
+ * ADLMIDI Library API: Copyright (c) 2015-2019 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
+ * http://iki.fi/bisqwit/source/adlmidi.html
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef void (*AudioOutputCallback)(void *, unsigned char *stream, int len);
+
+struct AudioOutputSpec
+{
+ unsigned int freq;
+ unsigned short format;
+ unsigned short is_msb;
+ unsigned short samples;
+ unsigned char channels;
+};
+
+extern int audio_init(struct AudioOutputSpec *in_spec, struct AudioOutputSpec *out_obtained, AudioOutputCallback callback);
+
+extern void audio_close();
+
+extern const char* audio_get_error();
+
+extern void audio_start();
+
+extern void audio_stop();
+
+extern void audio_lock();
+
+extern void audio_unlock();
+
+extern void audio_delay(unsigned int ms);
+
+extern void* audio_mutex_create();
+extern void audio_mutex_destroy(void*m);
+extern void audio_mutex_lock(void*m);
+extern void audio_mutex_unlock(void*m);
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/utils/midiplay/audio_sdl.c b/utils/midiplay/audio_sdl.c
new file mode 100644
index 0000000..456b32b
--- /dev/null
+++ b/utils/midiplay/audio_sdl.c
@@ -0,0 +1,149 @@
+/*
+ * ADLMIDI Player is a free MIDI player based on a libADLMIDI,
+ * a Software MIDI synthesizer library with OPL3 emulation
+ *
+ * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
+ * ADLMIDI Library API: Copyright (c) 2015-2019 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
+ * http://iki.fi/bisqwit/source/adlmidi.html
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <adlmidi.h>
+#include "audio.h"
+#define SDL_MAIN_HANDLED
+#include <SDL2/SDL.h>
+
+int audio_init(struct AudioOutputSpec *in_spec, struct AudioOutputSpec *out_obtained, AudioOutputCallback callback)
+{
+ SDL_AudioSpec spec;
+ SDL_AudioSpec obtained;
+ int ret;
+
+ spec.format = AUDIO_S16SYS;
+ spec.freq = (int)in_spec->freq;
+ spec.samples = in_spec->samples;
+ spec.channels = in_spec->channels;
+ spec.callback = callback;
+
+ switch(in_spec->format)
+ {
+ case ADLMIDI_SampleType_S8:
+ spec.format = AUDIO_S8; break;
+ case ADLMIDI_SampleType_U8:
+ spec.format = AUDIO_U8; break;
+ case ADLMIDI_SampleType_S16:
+ spec.format = in_spec->is_msb ? AUDIO_S16MSB : AUDIO_S16; break;
+ case ADLMIDI_SampleType_U16:
+ spec.format = in_spec->is_msb ? AUDIO_U16MSB : AUDIO_U16; break;
+ case ADLMIDI_SampleType_S32:
+ spec.format = in_spec->is_msb ? AUDIO_S32MSB : AUDIO_S32; break;
+ case ADLMIDI_SampleType_F32:
+ spec.format = in_spec->is_msb ? AUDIO_F32MSB : AUDIO_F32; break;
+ }
+
+ ret = SDL_OpenAudio(&spec, &obtained);
+
+ out_obtained->channels = obtained.channels;
+ out_obtained->freq = (unsigned int)obtained.freq;
+ out_obtained->samples = obtained.samples;
+ out_obtained->format = in_spec->format;
+ out_obtained->is_msb = 0;
+
+ switch(obtained.format)
+ {
+ case AUDIO_S8:
+ out_obtained->format = ADLMIDI_SampleType_S8; break;
+ case AUDIO_U8:
+ out_obtained->format = ADLMIDI_SampleType_U8; break;
+ case AUDIO_S16MSB:
+ out_obtained->is_msb = 1;/* fallthrough */
+ case AUDIO_S16:
+ out_obtained->format = ADLMIDI_SampleType_S16; break;
+ case AUDIO_U16MSB:
+ out_obtained->is_msb = 1;/* fallthrough */
+ case AUDIO_U16:
+ out_obtained->format = ADLMIDI_SampleType_U16; break;
+ case AUDIO_S32MSB:
+ out_obtained->is_msb = 1;/* fallthrough */
+ case AUDIO_S32:
+ out_obtained->format = ADLMIDI_SampleType_S32; break;
+ case AUDIO_F32MSB:
+ out_obtained->is_msb = 1;/* fallthrough */
+ case AUDIO_F32:
+ out_obtained->format = ADLMIDI_SampleType_F32; break;
+ }
+
+ return ret;
+}
+
+void audio_close()
+{
+ SDL_CloseAudio();
+}
+
+const char* audio_get_error()
+{
+ return SDL_GetError();
+}
+
+void audio_start()
+{
+ SDL_PauseAudio(0);
+}
+
+void audio_stop()
+{
+ SDL_PauseAudio(1);
+}
+
+void audio_lock()
+{
+ SDL_LockAudio();
+}
+
+void audio_unlock()
+{
+ SDL_UnlockAudio();
+}
+
+void audio_delay(unsigned int ms)
+{
+ SDL_Delay(ms);
+}
+
+void* audio_mutex_create()
+{
+ return SDL_CreateMutex();
+}
+
+void audio_mutex_destroy(void*m)
+{
+ SDL_mutex *mut = (SDL_mutex *)m;
+ SDL_DestroyMutex(mut);
+}
+
+void audio_mutex_lock(void*m)
+{
+ SDL_mutex *mut = (SDL_mutex *)m;
+ SDL_mutexP(mut);
+}
+
+void audio_mutex_unlock(void*m)
+{
+ SDL_mutex *mut = (SDL_mutex *)m;
+ SDL_mutexV(mut);
+}
diff --git a/utils/midiplay/audio_winmm.c b/utils/midiplay/audio_winmm.c
new file mode 100644
index 0000000..52142bb
--- /dev/null
+++ b/utils/midiplay/audio_winmm.c
@@ -0,0 +1,256 @@
+/*
+ * ADLMIDI Player is a free MIDI player based on a libADLMIDI,
+ * a Software MIDI synthesizer library with OPL3 emulation
+ *
+ * Original ADLMIDI code: Copyright (c) 2010-2014 Joel Yliluoma <bisqwit@iki.fi>
+ * ADLMIDI Library API: Copyright (c) 2015-2019 Vitaly Novichkov <admin@wohlnet.ru>
+ *
+ * Library is based on the ADLMIDI, a MIDI player for Linux and Windows with OPL3 emulation:
+ * http://iki.fi/bisqwit/source/adlmidi.html
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "audio.h"
+
+#include <adlmidi.h>
+#include <malloc.h>
+#include <windows.h>
+#include <mmreg.h>
+#include <mmsystem.h>
+
+#include <stdio.h>
+
+#define BUFFERS_COUNT 2
+static HWAVEOUT g_waveOut;
+static WAVEHDR g_hdr[BUFFERS_COUNT];
+static int g_started = 0;
+static void *g_audioLock = NULL;
+static const char *g_lastErrorMessage = "";
+static UINT8 *g_buffer = NULL;
+static size_t g_bufferSize = 0;
+
+static HANDLE g_event;
+static HANDLE g_thread;
+
+static AudioOutputCallback g_audioCallback = NULL;
+
+DWORD WINAPI s_audioThread(PVOID pDataInput)
+{
+ DWORD ret = 0;
+ UINT i;
+ (void)pDataInput;
+
+ while(g_started)
+ {
+ WaitForSingleObject(g_event, INFINITE);
+ for(i = 0; i < BUFFERS_COUNT; i++)
+ {
+ if(g_hdr[i].dwFlags & WHDR_DONE)
+ {
+ g_audioCallback(NULL, (UINT8*)g_hdr[i].lpData, (int)g_hdr[i].dwBufferLength);
+ audio_lock();
+ waveOutWrite(g_waveOut, &g_hdr[i], sizeof(WAVEHDR));
+ audio_unlock();
+ }
+ }
+ }
+
+ waveOutReset(g_waveOut);
+
+ return ret;
+}
+
+static void CALLBACK s_waveOutProc(HWAVEOUT waveout, UINT msg, DWORD_PTR userData, DWORD_PTR p1, DWORD_PTR p2)
+{
+ (void)waveout;
+ (void)userData;
+ (void)p1;
+ (void)p2;
+
+ switch (msg)
+ {
+ case WOM_DONE:
+ SetEvent(g_event);
+ }
+}
+
+int audio_init(struct AudioOutputSpec *in_spec, struct AudioOutputSpec *out_obtained, AudioOutputCallback callback)
+{
+ WORD bits = 16;
+ WAVEFORMATEX wformat;
+ MMRESULT result;
+
+ g_waveOut = NULL;
+ g_started = 0;
+ memset(g_hdr, 0, sizeof(WAVEHDR) * BUFFERS_COUNT);
+
+ g_audioCallback = callback;
+
+ switch(in_spec->format)
+ {
+ case ADLMIDI_SampleType_S8:
+ bits = 8; break;
+ case ADLMIDI_SampleType_U8:
+ bits = 8; break;
+ case ADLMIDI_SampleType_S16:
+ bits = 16; break;
+ case ADLMIDI_SampleType_U16:
+ bits = 16; break;
+ case ADLMIDI_SampleType_S32:
+ bits = 32; break;
+ case ADLMIDI_SampleType_F32:
+ bits = 32; break;
+ }
+
+ g_bufferSize = in_spec->samples * (bits / 8) * in_spec->channels;
+ g_buffer = (UINT8 *)malloc(g_bufferSize * BUFFERS_COUNT);
+ if(!g_buffer)
+ {
+ g_bufferSize = 0;
+ g_lastErrorMessage = "Out of memory";
+ return -1;
+ }
+
+ out_obtained->channels = in_spec->channels;
+ out_obtained->format = in_spec->format;
+ out_obtained->freq = in_spec->freq;
+ out_obtained->is_msb = in_spec->is_msb;
+ out_obtained->samples = in_spec->samples;
+
+ memset(&wformat, 0, sizeof(wformat));
+ wformat.cbSize = sizeof(WAVEFORMATEX);
+ wformat.nChannels = (WORD)in_spec->channels;
+ wformat.nSamplesPerSec = (WORD)in_spec->freq;
+ wformat.wFormatTag = WAVE_FORMAT_PCM;
+ wformat.wBitsPerSample = bits;
+ wformat.nBlockAlign = wformat.nChannels * (wformat.wBitsPerSample >> 3);
+ wformat.nAvgBytesPerSec = wformat.nSamplesPerSec * wformat.nBlockAlign;
+
+ result = waveOutOpen(&g_waveOut, WAVE_MAPPER, &wformat, (DWORD_PTR)s_waveOutProc, 0, CALLBACK_FUNCTION);
+ if(result != MMSYSERR_NOERROR)
+ {
+ g_lastErrorMessage = "Could not open the audio device";
+ return -1;
+ }
+ waveOutPause(g_waveOut);
+ g_audioLock = audio_mutex_create();
+
+ return 0;
+}
+
+void audio_close()
+{
+ audio_stop();
+ audio_mutex_lock(g_audioLock);
+ if(g_waveOut)
+ waveOutClose(g_waveOut);
+ free(g_buffer);
+ g_waveOut = NULL;
+ g_audioCallback = NULL;
+ audio_mutex_unlock(g_audioLock);
+ audio_mutex_destroy(g_audioLock);
+}
+
+const char *audio_get_error()
+{
+ return g_lastErrorMessage;
+}
+
+void audio_start()
+{
+ DWORD dwThreadId;
+ size_t i = 0;
+ if(!g_audioCallback)
+ return;
+ if(g_started)
+ return;
+
+ audio_lock();
+ memset(g_buffer, 0, g_bufferSize * BUFFERS_COUNT);
+ memset(g_hdr, 0, sizeof(WAVEHDR) * BUFFERS_COUNT);
+
+ for(i = 0; i < BUFFERS_COUNT; i++)
+ {
+ g_hdr[i].dwBufferLength = (DWORD)g_bufferSize;
+ g_hdr[i].lpData = (LPSTR)(g_buffer + (g_bufferSize * i));
+ waveOutPrepareHeader(g_waveOut, &g_hdr[i], sizeof(WAVEHDR));
+ g_audioCallback(NULL, (UINT8*)g_hdr[i].lpData, (int)g_hdr[i].dwBufferLength);
+ waveOutWrite(g_waveOut, &g_hdr[i], sizeof(WAVEHDR));
+ }
+
+ waveOutRestart(g_waveOut);
+
+ audio_unlock();
+
+ g_started = 1;
+
+ g_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ g_thread = CreateThread(NULL, 0, s_audioThread, 0, 0, &dwThreadId);
+}
+
+void audio_stop()
+{
+ audio_lock();
+ g_started = 0;
+ if(g_thread)
+ {
+ SetEvent(g_event);
+ WaitForSingleObject(g_thread, INFINITE);
+ CloseHandle(g_event);
+ CloseHandle(g_thread);
+ }
+ audio_unlock();
+}
+
+void audio_lock()
+{
+ audio_mutex_lock(g_audioLock);
+}
+
+void audio_unlock()
+{
+ audio_mutex_unlock(g_audioLock);
+}
+
+void audio_delay(unsigned int ms)
+{
+ Sleep(ms);
+}
+
+void* audio_mutex_create()
+{
+ CRITICAL_SECTION *mutex = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
+ InitializeCriticalSection(mutex);
+ return mutex;
+}
+
+void audio_mutex_destroy(void *m)
+{
+ CRITICAL_SECTION *mutex = (CRITICAL_SECTION *)m;
+ DeleteCriticalSection(mutex);
+ free(mutex);
+}
+
+void audio_mutex_lock(void *m)
+{
+ CRITICAL_SECTION *mutex = (CRITICAL_SECTION *)m;
+ EnterCriticalSection(mutex);
+}
+
+void audio_mutex_unlock(void* m)
+{
+ CRITICAL_SECTION *mutex = (CRITICAL_SECTION *)m;
+ LeaveCriticalSection(mutex);
+}
diff --git a/utils/midiplay/wave_writer.c b/utils/midiplay/wave_writer.c
index e8ee10c..bdee5d7 100755
--- a/utils/midiplay/wave_writer.c
+++ b/utils/midiplay/wave_writer.c
@@ -26,145 +26,195 @@ enum { header_size = 0x2C };
typedef short sample_t;
-static unsigned char* buf;
-static FILE* file;
-static long sample_count_;
-static long sample_rate_;
-static long buf_pos;
-static int chan_count;
-
-static void exit_with_error( const char* str )
+struct Context
{
- fprintf(stderr, "WAVE Writer Error: %s\n", str );
+ unsigned char *m_buf;
+ FILE *m_file;
+ long m_sample_count;
+ long m_sample_rate;
+ long m_buf_pos;
+ int m_chan_count;
+};
+
+static struct Context *g_wwContext = NULL;
+
+static void exit_with_error(const char *str)
+{
+ fprintf(stderr, "WAVE Writer Error: %s\n", str);
fflush(stderr);
}
-int wave_open( long sample_rate, const char* filename )
+int wave_open(long sample_rate, const char *filename)
+{
+ g_wwContext = ctx_wave_open(sample_rate, filename);
+ return g_wwContext ? 0 : -1;
+}
+
+void wave_enable_stereo(void)
+{
+ ctx_wave_enable_stereo(g_wwContext);
+}
+
+void wave_write(short const *in, long remain)
+{
+ ctx_wave_write(g_wwContext, in, remain);
+}
+
+long wave_sample_count(void)
+{
+ return ctx_wave_sample_count(g_wwContext);
+}
+
+void wave_close(void)
{
- sample_count_ = 0;
- sample_rate_ = sample_rate;
- buf_pos = header_size;
- chan_count = 1;
+ ctx_wave_close(g_wwContext);
+}
+
+
+
+static void set_le32(void *p, unsigned long n)
+{
+ ((unsigned char *) p) [0] = (unsigned char) n & (0xFF);
+ ((unsigned char *) p) [1] = (unsigned char)(n >> 8) & (0xFF);
+ ((unsigned char *) p) [2] = (unsigned char)(n >> 16) & (0xFF);
+ ((unsigned char *) p) [3] = (unsigned char)(n >> 24) & (0xFF);
+}
- buf = (unsigned char*) malloc( buf_size * sizeof *buf );
- if ( !buf )
+void *ctx_wave_open(long sample_rate, const char *filename)
+{
+ struct Context *ctx = (struct Context*)malloc(sizeof(struct Context));
+ if(!ctx)
{
- exit_with_error( "Out of memory" );
- return -1;
+ exit_with_error("Out of memory");
+ return NULL;
+ }
+
+ ctx->m_sample_count = 0;
+ ctx->m_sample_rate = sample_rate;
+ ctx->m_buf_pos = header_size;
+ ctx->m_chan_count = 1;
+
+ ctx->m_buf = (unsigned char *) malloc(buf_size);
+ if(!ctx->m_buf)
+ {
+ exit_with_error("Out of memory");
+ free(ctx);
+ return NULL;
}
#if !defined(_WIN32) || defined(__WATCOMC__)
- file = fopen( filename, "wb" );
+ ctx->m_file = fopen(filename, "wb");
#else
wchar_t widePath[MAX_PATH];
int size = MultiByteToWideChar(CP_UTF8, 0, filename, strlen(filename), widePath, MAX_PATH);
widePath[size] = '\0';
- file = _wfopen( widePath, L"wb" );
+ ctx->m_file = _wfopen(widePath, L"wb");
#endif
- if (!file)
+ if(!ctx->m_file)
{
- exit_with_error( "Couldn't open WAVE file for writing" );
- return -1;
+ exit_with_error("Couldn't open WAVE file for writing");
+ free(ctx);
+ return NULL;
}
- setvbuf( file, 0, _IOFBF, 32 * 1024L );
- return 0;
+ setvbuf(ctx->m_file, 0, _IOFBF, 32 * 1024L);
+ return ctx;
}
-void wave_enable_stereo( void )
+void ctx_wave_enable_stereo(void *ctx)
{
- chan_count = 2;
+ struct Context *wWriter = (struct Context *)ctx;
+ wWriter->m_chan_count = 2;
}
-static void flush_()
+
+static void flush_(struct Context *ctx)
{
- if ( buf_pos && !fwrite( buf, (size_t)buf_pos, 1, file ) )
- exit_with_error( "Couldn't write WAVE data" );
- buf_pos = 0;
+ if(ctx->m_buf_pos && !fwrite(ctx->m_buf, (size_t)ctx->m_buf_pos, 1, ctx->m_file))
+ exit_with_error("Couldn't write WAVE data");
+ ctx->m_buf_pos = 0;
}
-void wave_write( short const* in, long remain )
+void ctx_wave_write(void *ctx, const short *in, long remain)
{
- sample_count_ += remain;
- while ( remain )
+ struct Context *wWriter = (struct Context *)ctx;
+ wWriter->m_sample_count += remain;
+ while(remain)
{
- if ( buf_pos >= buf_size )
- flush_();
-
+ if(wWriter->m_buf_pos >= buf_size)
+ flush_(wWriter);
{
- unsigned char* p = &buf [buf_pos];
- long n = (buf_size - (unsigned long)buf_pos) / sizeof (sample_t);
- if ( n > remain )
+ unsigned char *p = &wWriter->m_buf [wWriter->m_buf_pos];
+ long n = (buf_size - (unsigned long)wWriter->m_buf_pos) / sizeof(sample_t);
+ if(n > remain)
n = remain;
remain -= n;
/* convert to LSB first format */
- while ( n-- )
+ while(n--)
{
int s = *in++;
*p++ = (unsigned char) s & (0x00FF);
- *p++ = (unsigned char) (s >> 8) & (0x00FF);
+ *p++ = (unsigned char)(s >> 8) & (0x00FF);
}
- buf_pos = p - buf;
- assert( buf_pos <= buf_size );
+ wWriter->m_buf_pos = p - wWriter->m_buf;
+ assert(wWriter->m_buf_pos <= buf_size);
}
}
}
-long wave_sample_count( void )
+long ctx_wave_sample_count(void *ctx)
{
- return sample_count_;
+ struct Context *wWriter = (struct Context *)ctx;
+ return wWriter->m_sample_count;
}
-static void set_le32( void* p, unsigned long n )
+void ctx_wave_close(void *ctx)
{
- ((unsigned char*) p) [0] = (unsigned char) n & (0xFF);
- ((unsigned char*) p) [1] = (unsigned char) (n >> 8) & (0xFF);
- ((unsigned char*) p) [2] = (unsigned char) (n >> 16) & (0xFF);
- ((unsigned char*) p) [3] = (unsigned char) (n >> 24) & (0xFF);
-}
+ struct Context *wWriter = (struct Context *)ctx;
+ if(!wWriter)
+ return;
-void wave_close( void )
-{
- if ( file )
+ if(wWriter->m_file)
{
/* generate header */
unsigned char h [header_size] =
{
- 'R','I','F','F',
- 0,0,0,0, /* length of rest of file */
- 'W','A','V','E',
- 'f','m','t',' ',
- 0x10,0,0,0, /* size of fmt chunk */
- 1,0, /* uncompressed format */
- 0,0, /* channel count */
- 0,0,0,0, /* sample rate */
- 0,0,0,0, /* bytes per second */
- 0,0, /* bytes per sample frame */
- 16,0, /* bits per sample */
- 'd','a','t','a',
- 0,0,0,0, /* size of sample data */
+ 'R', 'I', 'F', 'F',
+ 0, 0, 0, 0, /* length of rest of file */
+ 'W', 'A', 'V', 'E',
+ 'f', 'm', 't', ' ',
+ 0x10, 0, 0, 0, /* size of fmt chunk */
+ 1, 0, /* uncompressed format */
+ 0, 0, /* channel count */
+ 0, 0, 0, 0, /* sample rate */
+ 0, 0, 0, 0, /* bytes per second */
+ 0, 0, /* bytes per sample frame */
+ 16, 0, /* bits per sample */
+ 'd', 'a', 't', 'a',
+ 0, 0, 0, 0, /* size of sample data */
/* ... */ /* sample data */
};
- long ds = sample_count_ * (long)sizeof (sample_t);
- int frame_size = chan_count * (long)sizeof (sample_t);
+ long ds = wWriter->m_sample_count * (long)sizeof(sample_t);
+ int frame_size = wWriter->m_chan_count * (long)sizeof(sample_t);
- set_le32( h + 0x04, header_size - 8 + ds );
- h [0x16] = (unsigned char)chan_count;
- set_le32( h + 0x18, (unsigned long)sample_rate_ );
- set_le32( h + 0x1C, (unsigned long)sample_rate_ * (unsigned long)frame_size );
+ set_le32(h + 0x04, header_size - 8 + ds);
+ h [0x16] = (unsigned char)wWriter->m_chan_count;
+ set_le32(h + 0x18, (unsigned long)wWriter->m_sample_rate);
+ set_le32(h + 0x1C, (unsigned long)wWriter->m_sample_rate * (unsigned long)frame_size);
h [0x20] = (unsigned char)frame_size;
- set_le32( h + 0x28, (unsigned long)ds );
+ set_le32(h + 0x28, (unsigned long)ds);
- flush_();
+ flush_(wWriter);
/* write header */
- fseek( file, 0, SEEK_SET );
- fwrite( h, header_size, 1, file );
- fclose( file );
- file = 0;
- free( buf );
- buf = 0;
+ fseek(wWriter->m_file, 0, SEEK_SET);
+ fwrite(h, header_size, 1, wWriter->m_file);
+ fclose(wWriter->m_file);
+ wWriter->m_file = 0;
+ free(wWriter->m_buf);
+ wWriter->m_buf = 0;
}
+ free(wWriter);
}
diff --git a/utils/midiplay/wave_writer.h b/utils/midiplay/wave_writer.h
index 6d49718..17318c3 100755
--- a/utils/midiplay/wave_writer.h
+++ b/utils/midiplay/wave_writer.h
@@ -5,17 +5,23 @@
#define WAVE_WRITER_H
#ifdef __cplusplus
- extern "C" {
+extern "C" {
#endif
-int wave_open( long sample_rate, const char* filename );
-void wave_enable_stereo( void );
-void wave_write( short const* in, long count );
-long wave_sample_count( void );
-void wave_close( void );
+int wave_open(long sample_rate, const char *filename);
+void wave_enable_stereo(void);
+void wave_write(short const *in, long count);
+long wave_sample_count(void);
+void wave_close(void);
+
+void *ctx_wave_open(long sample_rate, const char *filename);
+void ctx_wave_enable_stereo(void *ctx);
+void ctx_wave_write(void *ctx, short const *in, long count);
+long ctx_wave_sample_count(void *ctx);
+void ctx_wave_close(void *ctx);
#ifdef __cplusplus
- }
+}
#endif
#endif