diff options
author | Wohlstand <admin@wohlnet.ru> | 2019-09-23 17:57:11 +0300 |
---|---|---|
committer | Wohlstand <admin@wohlnet.ru> | 2019-09-23 17:57:11 +0300 |
commit | 7e01feebae9bcf2e37b7d560a0308fb0a28aa89d (patch) | |
tree | e96afcb4114f341d8dbee3580778d17a03b07c3d /utils | |
parent | a17c7f12953e89c311736181807e988f570a5ac5 (diff) | |
parent | 4f121a22cc72ef8e092d234a2fe78aeb4369d0da (diff) | |
download | libADLMIDI-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.txt | 2 | ||||
-rw-r--r-- | utils/adlmidi-2/midiplay.cc | 5 | ||||
-rw-r--r-- | utils/dumpbank/CMakeLists.txt | 2 | ||||
-rw-r--r-- | utils/dumpmiles/CMakeLists.txt | 2 | ||||
-rw-r--r-- | utils/gen_adldata/CMakeLists.txt | 2 | ||||
-rw-r--r-- | utils/midiplay/CMakeLists.txt | 33 | ||||
-rw-r--r-- | utils/midiplay/adlmidiplay.cpp | 177 | ||||
-rw-r--r-- | utils/midiplay/audio.h | 64 | ||||
-rw-r--r-- | utils/midiplay/audio_sdl.c | 149 | ||||
-rw-r--r-- | utils/midiplay/audio_winmm.c | 256 |
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); +} |