diff options
author | Wohlstand <admin@wohlnet.ru> | 2019-09-23 17:55:22 +0300 |
---|---|---|
committer | Wohlstand <admin@wohlnet.ru> | 2019-09-23 17:55:22 +0300 |
commit | 7ff1f8b9522d132c9248a68cc5df15e841523ea6 (patch) | |
tree | 8274dc1ea2a8f289e4ac607d0e1dfc056ceef2b8 /utils | |
parent | 22248f90dd83413e91c8d4b66e652ef1a7954b60 (diff) | |
download | libADLMIDI-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.cc | 5 | ||||
-rw-r--r-- | utils/midiplay/CMakeLists.txt | 32 | ||||
-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 | ||||
-rwxr-xr-x | utils/midiplay/wave_writer.c | 214 | ||||
-rwxr-xr-x | utils/midiplay/wave_writer.h | 20 |
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 |