aboutsummaryrefslogtreecommitdiff
path: root/utils/midiplay/audio_winmm.c
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/midiplay/audio_winmm.c
parent22248f90dd83413e91c8d4b66e652ef1a7954b60 (diff)
downloadlibADLMIDI-7ff1f8b9522d132c9248a68cc5df15e841523ea6.tar.gz
libADLMIDI-7ff1f8b9522d132c9248a68cc5df15e841523ea6.tar.bz2
libADLMIDI-7ff1f8b9522d132c9248a68cc5df15e841523ea6.zip
MidiPlay: Move Audio Output into separate module
Diffstat (limited to 'utils/midiplay/audio_winmm.c')
-rw-r--r--utils/midiplay/audio_winmm.c256
1 files changed, 256 insertions, 0 deletions
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);
+}