aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorWohlstand <admin@wohlnet.ru>2019-09-23 17:57:11 +0300
committerWohlstand <admin@wohlnet.ru>2019-09-23 17:57:11 +0300
commit7e01feebae9bcf2e37b7d560a0308fb0a28aa89d (patch)
treee96afcb4114f341d8dbee3580778d17a03b07c3d /utils
parenta17c7f12953e89c311736181807e988f570a5ac5 (diff)
parent4f121a22cc72ef8e092d234a2fe78aeb4369d0da (diff)
downloadlibADLMIDI-7e01feebae9bcf2e37b7d560a0308fb0a28aa89d.tar.gz
libADLMIDI-7e01feebae9bcf2e37b7d560a0308fb0a28aa89d.tar.bz2
libADLMIDI-7e01feebae9bcf2e37b7d560a0308fb0a28aa89d.zip
MidiPlay: Move Audio Output into separate module
Diffstat (limited to 'utils')
-rw-r--r--utils/adlmidi-2/CMakeLists.txt2
-rw-r--r--utils/adlmidi-2/midiplay.cc5
-rw-r--r--utils/dumpbank/CMakeLists.txt2
-rw-r--r--utils/dumpmiles/CMakeLists.txt2
-rw-r--r--utils/gen_adldata/CMakeLists.txt2
-rw-r--r--utils/midiplay/CMakeLists.txt33
-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
10 files changed, 612 insertions, 80 deletions
diff --git a/utils/adlmidi-2/CMakeLists.txt b/utils/adlmidi-2/CMakeLists.txt
index 8390a95..02351fa 100644
--- a/utils/adlmidi-2/CMakeLists.txt
+++ b/utils/adlmidi-2/CMakeLists.txt
@@ -29,6 +29,8 @@ if(NOT WIN32 AND NOT ADLMIDI_DOS)
target_link_libraries(adlmidi2 PRIVATE ADLMIDI_SDL2)
endif()
+set_nopie(adlmidi2)
+
if(ADLMIDI2_HAS_PUZZLE_GAME)
target_compile_definitions(adlmidi2 PUBLIC SUPPORT_PUZZLE_GAME)
endif()
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/dumpbank/CMakeLists.txt b/utils/dumpbank/CMakeLists.txt
index 1b84efe..571cdef 100644
--- a/utils/dumpbank/CMakeLists.txt
+++ b/utils/dumpbank/CMakeLists.txt
@@ -1,5 +1,7 @@
add_executable(adldumpbank dumpbank.cpp)
+set_nopie(adldumpbank)
+
install(TARGETS adldumpbank
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
diff --git a/utils/dumpmiles/CMakeLists.txt b/utils/dumpmiles/CMakeLists.txt
index 0faab06..07f0a47 100644
--- a/utils/dumpmiles/CMakeLists.txt
+++ b/utils/dumpmiles/CMakeLists.txt
@@ -1,5 +1,7 @@
add_executable(adldumpmiles dumpmiles.cpp)
+set_nopie(adldumpmiles)
+
install(TARGETS adldumpmiles
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
diff --git a/utils/gen_adldata/CMakeLists.txt b/utils/gen_adldata/CMakeLists.txt
index 5b7a18e..7939d5b 100644
--- a/utils/gen_adldata/CMakeLists.txt
+++ b/utils/gen_adldata/CMakeLists.txt
@@ -67,6 +67,8 @@ else()
target_link_libraries(gen_adldata PRIVATE pthread m)
endif()
+set_nopie(gen_adldata)
+
if(WITH_GENADLDATA_DEEPDEBUG)
add_dependencies(gen_adldata gen_adldata_deepdebug_prepare)
endif()
diff --git a/utils/midiplay/CMakeLists.txt b/utils/midiplay/CMakeLists.txt
index 59f77fe..98f888a 100644
--- a/utils/midiplay/CMakeLists.txt
+++ b/utils/midiplay/CMakeLists.txt
@@ -1,13 +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
)
-if(NOT ADLMIDI_DOS AND NOT MIDIPLAY_WAVE_ONLY)
- libADLMIDI_find_SDL2()
+if(USE_SDL2_AUDIO)
+ list(APPEND ADLMIDI_PLAY_SRC
+ audio_sdl.c
+ )
+endif()
+
+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.")
@@ -41,3 +67,4 @@ endif()
install(TARGETS adlmidiplay
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}")
+
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);
+}