Subversion Repositories eduke32

Compare Revisions

Ignore whitespace Rev 8758 → Rev 8759

/source/audiolib/src/driver_winmm.cpp
37,19 → 37,6
#define inline _inline
#endif
 
enum
{
WinMMErr_Error = -1,
WinMMErr_Ok = 0,
WinMMErr_Uninitialised,
WinMMErr_NotifyWindow,
WinMMErr_MIDIStreamOpen,
WinMMErr_MIDIStreamRestart,
WinMMErr_MIDICreateEvent,
WinMMErr_MIDIPlayThread,
WinMMErr_MIDICreateMutex
};
 
UINT WinMM_DeviceID = MIDI_MAPPER;
 
static int ErrorCode = WinMMErr_Ok;
67,10 → 54,11
static BOOL midiStreamRunning;
static int midiLastDivision;
 
#define THREAD_QUEUE_INTERVAL 10 // 1/10 sec
#define MIDI_BUFFER_SPACE (12 * 128u) // 128 note-on events
#define MME_THREAD_QUEUE_INTERVAL 10 // 1/10 sec
#define MME_MIDI_BUFFER_SPACE (12 * 128u) // 128 note-on events
 
typedef struct MidiBuffer {
typedef struct MidiBuffer
{
struct MidiBuffer *next;
struct MidiBuffer *prev;
 
82,25 → 70,20
static MidiBuffer spareMidiBuffers;
static MidiBuffer *currentMidiBuffer;
 
int WinMMDrv_GetError(void) { return ErrorCode; }
 
int WinMMDrv_GetError(void)
{
return ErrorCode;
}
 
const char *WinMMDrv_ErrorString(int ErrorNumber)
{
switch (ErrorNumber)
{
case WinMMErr_Error: return WinMMDrv_ErrorString(ErrorCode);
case WinMMErr_Ok: return "WinMM ok.";
case WinMMErr_Uninitialised: return "WinMM uninitialized.";
case WinMMErr_Ok: return "MME ok.";
case WinMMErr_MIDIStreamOpen: return "MIDI error: failed opening stream.";
case WinMMErr_MIDIStreamRestart: return "MIDI error: failed starting stream.";
case WinMMErr_MIDICreateEvent: return "MIDI error: failed creating play thread quit event.";
case WinMMErr_MIDIPlayThread: return "MIDI error: failed creating play thread.";
case WinMMErr_MIDICreateMutex: return "MIDI error: failed creating play mutex.";
default: return "Unknown WinMM error code.";
default: return "Unknown MME error code.";
}
}
 
143,51 → 126,47
MV_Printf(" err %d (%s)\n", (int)rv, errtxt);
}
 
static void midi_dispose_buffer(MidiBuffer * node, const char * caller)
static void midi_dispose_buffer(MidiBuffer *node, const char *caller)
{
MMRESULT rv;
if (node->prepared) {
rv = midiOutUnprepareHeader( (HMIDIOUT) midiStream, &node->hdr, sizeof(MIDIHDR) );
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM %s/midi_dispose_buffer midiOutUnprepareHeader", caller);
}
if (node->prepared)
{
auto rv = midiOutUnprepareHeader((HMIDIOUT)midiStream, &node->hdr, sizeof(MIDIHDR));
if (rv != MMSYSERR_NOERROR)
midi_error(rv, "MME %s/midi_dispose_buffer midiOutUnprepareHeader", caller);
node->prepared = FALSE;
}
 
if (midiThread) {
if (midiThread)
{
// remove the node from the activeMidiBuffers list
LL_Remove( node, next, prev );
LL_Remove(node, next, prev);
 
// when playing, we keep the buffers
LL_Add( (MidiBuffer*) &spareMidiBuffers, node, next, prev );
//MV_Printf("WinMM %s/midi_dispose_buffer recycling buffer %p\n", caller, node);
} else {
LL_Add((MidiBuffer *)&spareMidiBuffers, node, next, prev);
//MV_Printf("MME %s/midi_dispose_buffer recycling buffer %p\n", caller, node);
}
else
{
// when not, we throw them away
Xfree(node);
//MV_Printf("WinMM %s/midi_dispose_buffer freeing buffer %p\n", caller, node);
//MV_Printf("MME %s/midi_dispose_buffer freeing buffer %p\n", caller, node);
}
}
 
static void midi_gc_buffers(void)
{
MidiBuffer *node, *next;
 
for ( node = activeMidiBuffers.next; node != &activeMidiBuffers; node = next ) {
next = node->next;
 
if (node->hdr.dwFlags & MHDR_DONE) {
for (auto node = activeMidiBuffers.next, next = node->next; node != &activeMidiBuffers; node = next, next = node->next)
{
if (node->hdr.dwFlags & MHDR_DONE)
midi_dispose_buffer(node, "midi_gc_buffers");
}
}
}
 
static void midi_free_buffers(void)
{
MidiBuffer *node, *next;
 
//MV_Printf("waiting for active buffers to return\n");
while (!LL_ListEmpty(&activeMidiBuffers, next, prev)) {
while (!LL_ListEmpty(&activeMidiBuffers, next, prev))
{
// wait for Windows to finish with all the buffers queued
midi_gc_buffers();
//MV_Printf("waiting...\n");
195,49 → 174,48
}
//MV_Printf("waiting over\n");
 
for ( node = spareMidiBuffers.next; node != &spareMidiBuffers; node = next ) {
next = node->next;
LL_Remove( node, next, prev );
for (auto node = spareMidiBuffers.next, next = node->next; node != &spareMidiBuffers; node = next, next = node->next)
{
LL_Remove(node, next, prev);
Xfree(node);
//MV_Printf("WinMM midi_free_buffers freeing buffer %p\n", node);
//MV_Printf("MME midi_free_buffers freeing buffer %p\n", node);
}
assert(currentMidiBuffer == 0);
 
Bassert(currentMidiBuffer == 0);
}
 
static void midi_flush_current_buffer(void)
{
MMRESULT rv;
MIDIEVENT * evt;
BOOL needsPrepare = FALSE;
 
if (!currentMidiBuffer) {
if (!currentMidiBuffer)
return;
}
 
evt = (MIDIEVENT *) currentMidiBuffer->hdr.lpData;
auto evt = (MIDIEVENT *)currentMidiBuffer->hdr.lpData;
 
if (!midiThread) {
if (!midiThread)
{
// immediate messages don't use a MIDIEVENT header so strip it off and
// make some adjustments
 
currentMidiBuffer->hdr.dwBufferLength = currentMidiBuffer->hdr.dwBytesRecorded - 12;
currentMidiBuffer->hdr.dwBufferLength = currentMidiBuffer->hdr.dwBytesRecorded - 12;
currentMidiBuffer->hdr.dwBytesRecorded = 0;
currentMidiBuffer->hdr.lpData = (LPSTR) &evt->dwParms[0];
if (currentMidiBuffer->hdr.dwBufferLength > 0) {
currentMidiBuffer->hdr.lpData = (LPSTR)&evt->dwParms[0];
 
if (currentMidiBuffer->hdr.dwBufferLength > 0)
needsPrepare = TRUE;
}
} else {
}
else
needsPrepare = TRUE;
}
if (needsPrepare) {
 
if (needsPrepare)
{
// playing a file, or sending a sysex when not playing means
// we need to prepare the buffer
rv = midiOutPrepareHeader( (HMIDIOUT) midiStream, &currentMidiBuffer->hdr, sizeof(MIDIHDR) );
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM midi_flush_current_buffer midiOutPrepareHeader");
auto rv = midiOutPrepareHeader((HMIDIOUT)midiStream, &currentMidiBuffer->hdr, sizeof(MIDIHDR));
if (rv != MMSYSERR_NOERROR)
{
midi_error(rv, "MME midi_flush_current_buffer midiOutPrepareHeader");
return;
}
 
244,63 → 222,68
currentMidiBuffer->prepared = TRUE;
}
 
if (midiThread) {
if (midiThread)
{
// midi file playing, so send events to the stream
 
LL_Add( (MidiBuffer*) &activeMidiBuffers, currentMidiBuffer, next, prev );
LL_Add((MidiBuffer *)&activeMidiBuffers, currentMidiBuffer, next, prev);
 
rv = midiStreamOut(midiStream, &currentMidiBuffer->hdr, sizeof(MIDIHDR));
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM midi_flush_current_buffer midiStreamOut");
auto rv = midiStreamOut(midiStream, &currentMidiBuffer->hdr, sizeof(MIDIHDR));
if (rv != MMSYSERR_NOERROR)
{
midi_error(rv, "MME midi_flush_current_buffer midiStreamOut");
midi_dispose_buffer(currentMidiBuffer, "midi_flush_current_buffer");
return;
}
 
//MV_Printf("WinMM midi_flush_current_buffer queued buffer %p\n", currentMidiBuffer);
} else {
//MV_Printf("MME midi_flush_current_buffer queued buffer %p\n", currentMidiBuffer);
}
else
{
// midi file not playing, so send immediately
if (currentMidiBuffer->hdr.dwBufferLength > 0) {
rv = midiOutLongMsg( (HMIDIOUT) midiStream, &currentMidiBuffer->hdr, sizeof(MIDIHDR) );
if (rv == MMSYSERR_NOERROR) {
 
if (currentMidiBuffer->hdr.dwBufferLength > 0)
{
auto rv = midiOutLongMsg((HMIDIOUT)midiStream, &currentMidiBuffer->hdr, sizeof(MIDIHDR));
if (rv == MMSYSERR_NOERROR)
{
// busy-wait for Windows to be done with it
while (!(currentMidiBuffer->hdr.dwFlags & MHDR_DONE)) ;
//MV_Printf("WinMM midi_flush_current_buffer sent immediate long\n");
} else {
midi_error(rv, "WinMM midi_flush_current_buffer midiOutLongMsg");
while (!(currentMidiBuffer->hdr.dwFlags & MHDR_DONE));
 
//MV_Printf("MME midi_flush_current_buffer sent immediate long\n");
}
} else {
rv = midiOutShortMsg( (HMIDIOUT) midiStream, evt->dwEvent );
if (rv == MMSYSERR_NOERROR) {
//MV_Printf("WinMM midi_flush_current_buffer sent immediate short\n");
} else {
midi_error(rv, "WinMM midi_flush_current_buffer midiOutShortMsg");
}
else
midi_error(rv, "MME midi_flush_current_buffer midiOutLongMsg");
}
else
{
auto rv = midiOutShortMsg((HMIDIOUT)midiStream, evt->dwEvent);
if (rv != MMSYSERR_NOERROR)
midi_error(rv, "MME midi_flush_current_buffer midiOutShortMsg");
}
 
midi_dispose_buffer(currentMidiBuffer, "midi_flush_current_buffer");
}
 
currentMidiBuffer = 0;
}
 
static void midi_setup_event(int length, unsigned char ** data)
static void midi_setup_event(int length, unsigned char **data)
{
MIDIEVENT * evt;
auto evt = (MIDIEVENT *)((intptr_t)currentMidiBuffer->hdr.lpData + currentMidiBuffer->hdr.dwBytesRecorded);
 
evt = (MIDIEVENT *) ((intptr_t) currentMidiBuffer->hdr.lpData +
currentMidiBuffer->hdr.dwBytesRecorded);
 
evt->dwDeltaTime = midiThread ? (midiThreadTimer - midiLastEventTime) : 0;
evt->dwStreamID = 0;
evt->dwStreamID = 0;
 
if (length <= 3) {
if (length <= 3)
{
evt->dwEvent = (DWORD)MEVT_SHORTMSG << 24;
*data = (unsigned char *) &evt->dwEvent;
} else {
*data = (unsigned char *)&evt->dwEvent;
}
else
{
evt->dwEvent = ((DWORD)MEVT_LONGMSG << 24) | (length & 0x00ffffff);
*data = (unsigned char *) &evt->dwParms[0];
*data = (unsigned char *)&evt->dwParms[0];
}
}
 
311,207 → 294,221
Returns a pointer to starting writing at in 'data'.
*/
static BOOL midi_get_buffer(int length, unsigned char ** data)
static BOOL midi_get_buffer(int length, unsigned char **data)
{
uint32_t datalen;
MidiBuffer * node;
uint32_t datalen;
 
// determine the space to alloc.
// the size of a MIDIEVENT is 3*sizeof(DWORD) = 12.
// short messages need only that amount of space.
// long messages need additional space equal to the length of
// the message, padded to 4 bytes
if (length <= 3) {
 
if (length <= 3)
datalen = 12;
} else {
else
{
datalen = 12 + length;
if ((datalen & 3) > 0) {
if ((datalen & 3) > 0)
datalen += 4 - (datalen & 3);
}
}
if (!midiThread) {
assert(currentMidiBuffer == 0);
}
if (currentMidiBuffer && (currentMidiBuffer->hdr.dwBufferLength -
currentMidiBuffer->hdr.dwBytesRecorded) >= datalen) {
 
if (!midiThread)
Bassert(currentMidiBuffer == 0);
 
if (currentMidiBuffer && (currentMidiBuffer->hdr.dwBufferLength - currentMidiBuffer->hdr.dwBytesRecorded) >= datalen)
{
// there was enough space in the current buffer, so hand that back
midi_setup_event(length, data);
 
currentMidiBuffer->hdr.dwBytesRecorded += datalen;
 
return TRUE;
}
if (currentMidiBuffer) {
 
if (currentMidiBuffer)
{
// not enough space in the current buffer to accommodate the
// new data, so flush it to the stream
midi_flush_current_buffer();
currentMidiBuffer = 0;
}
 
// check if there's a spare buffer big enough to hold the message
if (midiThread) {
for ( node = spareMidiBuffers.next; node != &spareMidiBuffers; node = node->next ) {
if (node->hdr.dwBufferLength >= datalen) {
if (midiThread)
{
for (auto node = spareMidiBuffers.next; node != &spareMidiBuffers; node = node->next)
{
if (node->hdr.dwBufferLength >= datalen)
{
// yes!
LL_Remove( node, next, prev );
LL_Remove(node, next, prev);
 
node->hdr.dwBytesRecorded = 0;
memset(node->hdr.lpData, 0, node->hdr.dwBufferLength);
Bmemset(node->hdr.lpData, 0, node->hdr.dwBufferLength);
 
currentMidiBuffer = node;
//MV_Printf("WinMM midi_get_buffer fetched buffer %p\n", node);
 
//MV_Printf("MME midi_get_buffer fetched buffer %p\n", node);
break;
}
}
}
if (!currentMidiBuffer) {
// there were no spare buffers, or none were big enough, so
// allocate a new one
int size;
if (midiThread) {
// playing a file, so allocate a buffer for more than
// one event
size = max(MIDI_BUFFER_SPACE, datalen);
} else {
// not playing a file, so allocate just a buffer for
// the event we'll be sending immediately
size = datalen;
}
node = (MidiBuffer *) Xmalloc( sizeof(MidiBuffer) + size );
if (node == 0) {
return FALSE;
}
 
memset(node, 0, sizeof(MidiBuffer) + datalen);
node->hdr.dwUser = (DWORD_PTR) node;
node->hdr.lpData = (LPSTR) ((intptr_t)node + sizeof(MidiBuffer));
node->hdr.dwBufferLength = size;
if (!currentMidiBuffer)
{
// there were no spare buffers, or none were big enough, so allocate a new one
int const size = midiThread ? max(MME_MIDI_BUFFER_SPACE, datalen) : datalen;
auto node = (MidiBuffer *)Xmalloc(sizeof(MidiBuffer) + size);
 
Bmemset(node, 0, sizeof(MidiBuffer) + datalen);
 
node->hdr.dwUser = (DWORD_PTR)node;
node->hdr.lpData = (LPSTR)((intptr_t)node + sizeof(MidiBuffer));
 
node->hdr.dwBufferLength = size;
node->hdr.dwBytesRecorded = 0;
 
currentMidiBuffer = node;
//MV_Printf("WinMM midi_get_buffer allocated buffer %p\n", node);
 
//MV_Printf("MME midi_get_buffer allocated buffer %p\n", node);
}
 
midi_setup_event(length, data);
 
currentMidiBuffer->hdr.dwBytesRecorded += datalen;
 
return TRUE;
}
 
static inline void midi_sequence_event(void)
{
if (!midiThread) {
if (!midiThread)
{
// a midi event being sent out of playback (streaming) mode
midi_flush_current_buffer();
return;
}
//MV_Printf("WinMM midi_sequence_event buffered\n");
 
// update the delta time counter
//MV_Printf("MME midi_sequence_event buffered\n");
 
midiLastEventTime = midiThreadTimer;
}
 
static void Func_NoteOff( int channel, int key, int velocity )
static void MME_NoteOff(int channel, int key, int velocity)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(3, &data)) {
if (midi_get_buffer(3, &data))
{
data[0] = WINMM_NOTE_OFF | channel;
data[1] = key;
data[2] = velocity;
midi_sequence_event();
} else MV_Printf("WinMM Func_NoteOff error\n");
}
else
MV_Printf("MME_NoteOff error\n");
}
 
static void Func_NoteOn( int channel, int key, int velocity )
static void MME_NoteOn(int channel, int key, int velocity)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(3, &data)) {
if (midi_get_buffer(3, &data))
{
data[0] = WINMM_NOTE_ON | channel;
data[1] = key;
data[2] = velocity;
midi_sequence_event();
} else MV_Printf("WinMM Func_NoteOn error\n");
}
else
MV_Printf("MME_NoteOn error\n");
}
 
static void Func_PolyAftertouch( int channel, int key, int pressure )
static void MME_PolyAftertouch(int channel, int key, int pressure)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(3, &data)) {
if (midi_get_buffer(3, &data))
{
data[0] = WINMM_POLY_AFTER_TCH | channel;
data[1] = key;
data[2] = pressure;
midi_sequence_event();
} else MV_Printf("WinMM Func_PolyAftertouch error\n");
}
else
MV_Printf("MME_PolyAftertouch error\n");
}
 
static void Func_ControlChange( int channel, int number, int value )
static void MME_ControlChange(int channel, int number, int value)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(3, &data)) {
if (midi_get_buffer(3, &data))
{
data[0] = WINMM_CONTROL_CHANGE | channel;
data[1] = number;
data[2] = value;
midi_sequence_event();
} else MV_Printf("WinMM Func_ControlChange error\n");
}
else
MV_Printf("MME_ControlChange error\n");
}
 
static void Func_ProgramChange( int channel, int program )
static void MME_ProgramChange(int channel, int program)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(2, &data)) {
if (midi_get_buffer(2, &data))
{
data[0] = WINMM_PROGRAM_CHANGE | channel;
data[1] = program;
midi_sequence_event();
} else MV_Printf("WinMM Func_ProgramChange error\n");
}
else
MV_Printf("MME_ProgramChange error\n");
}
 
static void Func_ChannelAftertouch( int channel, int pressure )
static void MME_ChannelAftertouch(int channel, int pressure)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(2, &data)) {
if (midi_get_buffer(2, &data))
{
data[0] = WINMM_AFTER_TOUCH | channel;
data[1] = pressure;
midi_sequence_event();
} else MV_Printf("WinMM Func_ChannelAftertouch error\n");
}
else
MV_Printf("MME_ChannelAftertouch error\n");
}
 
static void Func_PitchBend( int channel, int lsb, int msb )
static void MME_PitchBend(int channel, int lsb, int msb)
{
unsigned char * data;
unsigned char *data;
 
if (midi_get_buffer(3, &data)) {
if (midi_get_buffer(3, &data))
{
data[0] = WINMM_PITCH_BEND | channel;
data[1] = lsb;
data[2] = msb;
midi_sequence_event();
} else MV_Printf("WinMM Func_PitchBend error\n");
}
else
MV_Printf("MME_PitchBend error\n");
}
 
static void Func_SysEx( const unsigned char * data, int length )
static void MME_SysEx(const unsigned char *data, int length)
{
unsigned char * wdata;
if (midi_get_buffer(length, &wdata)) {
memcpy(wdata, data, length);
unsigned char *wdata;
 
if (midi_get_buffer(length, &wdata))
{
Bmemcpy(wdata, data, length);
midi_sequence_event();
} else MV_Printf("WinMM Func_SysEx error\n");
}
else
MV_Printf("MME_SysEx error\n");
}
 
void WinMMDrv_MIDI_PrintDevices(void)
532,47 → 529,48
 
int WinMMDrv_MIDI_Init(midifuncs * funcs)
{
MMRESULT rv;
 
if (midiInstalled) {
if (midiInstalled)
WinMMDrv_MIDI_Shutdown();
}
 
memset(funcs, 0, sizeof(midifuncs));
Bmemset(funcs, 0, sizeof(midifuncs));
 
LL_Reset( (MidiBuffer*) &activeMidiBuffers, next, prev );
LL_Reset( (MidiBuffer*) &spareMidiBuffers, next, prev );
LL_Reset((MidiBuffer *)&activeMidiBuffers, next, prev);
LL_Reset((MidiBuffer *)&spareMidiBuffers, next, prev);
 
midiMutex = CreateMutex(0, FALSE, 0);
if (!midiMutex) {
if ((midiMutex = CreateMutex(0, FALSE, 0)) == 0)
{
ErrorCode = WinMMErr_MIDICreateMutex;
return WinMMErr_Error;
}
 
MIDIOUTCAPS midicaps;
 
if (WinMM_DeviceID > midiOutGetNumDevs() || midiOutGetDevCaps(WinMM_DeviceID, &midicaps, sizeof(MIDIOUTCAPS)))
WinMM_DeviceID = MIDI_MAPPER;
if (!midiOutGetDevCaps(WinMM_DeviceID, &midicaps, sizeof(MIDIOUTCAPS)))
MV_Printf(": [%d] %s", WinMM_DeviceID, midicaps.szPname);
WinMM_DeviceID = MIDI_MAPPER;
 
rv = midiStreamOpen(&midiStream, &WinMM_DeviceID, 1, (DWORD_PTR) 0, (DWORD_PTR) 0, CALLBACK_NULL);
if (rv != MMSYSERR_NOERROR) {
if (!midiOutGetDevCaps(WinMM_DeviceID, &midicaps, sizeof(MIDIOUTCAPS)))
MV_Printf(": [%d] %s", WinMM_DeviceID, midicaps.szPname);
 
auto rv = midiStreamOpen(&midiStream, &WinMM_DeviceID, 1, (DWORD_PTR)0, (DWORD_PTR)0, CALLBACK_NULL);
 
if (rv != MMSYSERR_NOERROR)
{
CloseHandle(midiMutex);
midiMutex = 0;
 
midi_error(rv, "WinMM MIDI_Init midiStreamOpen");
midi_error(rv, "MME MIDI_Init midiStreamOpen");
ErrorCode = WinMMErr_MIDIStreamOpen;
return WinMMErr_Error;
}
funcs->NoteOff = Func_NoteOff;
funcs->NoteOn = Func_NoteOn;
funcs->PolyAftertouch = Func_PolyAftertouch;
funcs->ControlChange = Func_ControlChange;
funcs->ProgramChange = Func_ProgramChange;
funcs->ChannelAftertouch = Func_ChannelAftertouch;
funcs->PitchBend = Func_PitchBend;
funcs->SysEx = Func_SysEx;
funcs->NoteOff = MME_NoteOff;
funcs->NoteOn = MME_NoteOn;
funcs->PolyAftertouch = MME_PolyAftertouch;
funcs->ControlChange = MME_ControlChange;
funcs->ProgramChange = MME_ProgramChange;
funcs->ChannelAftertouch = MME_ChannelAftertouch;
funcs->PitchBend = MME_PitchBend;
funcs->SysEx = MME_SysEx;
 
midiInstalled = TRUE;
581,27 → 579,23
 
void WinMMDrv_MIDI_Shutdown(void)
{
MMRESULT rv;
 
if (!midiInstalled) {
if (!midiInstalled)
return;
}
 
WinMMDrv_MIDI_HaltPlayback();
 
if (midiStream) {
rv = midiStreamClose(midiStream);
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM MIDI_Shutdown midiStreamClose");
}
if (midiStream)
{
auto rv = midiStreamClose(midiStream);
if (rv != MMSYSERR_NOERROR)
midi_error(rv, "MME MIDI_Shutdown midiStreamClose");
}
 
if (midiMutex) {
if (midiMutex)
CloseHandle(midiMutex);
}
 
midiStream = 0;
midiMutex = 0;
midiMutex = 0;
 
midiInstalled = FALSE;
}
608,14 → 602,12
 
static DWORD midi_get_tick(void)
{
MMRESULT rv;
MMTIME mmtime;
MMTIME mmtime = { TIME_TICKS, 0 };
 
mmtime.wType = TIME_TICKS;
 
rv = midiStreamPosition(midiStream, &mmtime, sizeof(MMTIME));
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM midi_get_tick midiStreamPosition");
auto rv = midiStreamPosition(midiStream, &mmtime, sizeof(MMTIME));
if (rv != MMSYSERR_NOERROR)
{
midi_error(rv, "MME midi_get_tick midiStreamPosition");
return 0;
}
 
626,14 → 618,8
{
UNREFERENCED_PARAMETER(lpParameter);
 
DWORD waitret;
DWORD sequenceTime;
DWORD sleepAmount = 100 / THREAD_QUEUE_INTERVAL;
// MV_Printf("WinMM midiDataThread: started\n");
 
midiThreadTimer = midi_get_tick();
midiLastEventTime = midiThreadTimer;
midiThreadTimer = midi_get_tick();
midiLastEventTime = midiThreadTimer;
midiThreadQueueTimer = midiThreadTimer + midiThreadQueueTicks;
 
WinMMDrv_MIDI_Lock();
646,17 → 632,22
midi_flush_current_buffer();
WinMMDrv_MIDI_Unlock();
 
do {
waitret = WaitForSingleObject(midiThreadQuitEvent, sleepAmount);
if (waitret == WAIT_OBJECT_0) {
// MV_Printf("WinMM midiDataThread: exiting\n");
DWORD sleepAmount = 100 / MME_THREAD_QUEUE_INTERVAL;
 
do
{
auto waitret = WaitForSingleObject(midiThreadQuitEvent, sleepAmount);
 
if (waitret == WAIT_OBJECT_0)
break;
} else if (waitret == WAIT_TIMEOUT) {
else if (waitret == WAIT_TIMEOUT)
{
// queue a tick
sequenceTime = midi_get_tick();
auto sequenceTime = midi_get_tick();
 
sleepAmount = 100 / THREAD_QUEUE_INTERVAL;
if ((midiThreadTimer - sequenceTime) > midiThreadQueueTicks) {
sleepAmount = 100 / MME_THREAD_QUEUE_INTERVAL;
if ((midiThreadTimer - sequenceTime) > midiThreadQueueTicks)
{
// we're running ahead, so sleep for half the usual
// amount and try again
sleepAmount /= 2;
674,10 → 665,9
}
midi_flush_current_buffer();
WinMMDrv_MIDI_Unlock();
 
} else {
MV_Printf("WinMM midiDataThread: wfmo err %d\n", (int) waitret);
}
else
MV_Printf("MME midiDataThread: wfmo err %d\n", (int)waitret);
} while (1);
 
return 0;
685,21 → 675,21
 
int WinMMDrv_MIDI_StartPlayback(void)
{
MMRESULT rv;
 
WinMMDrv_MIDI_HaltPlayback();
 
midiThreadService = WinMMDrv_MIDI_Service;
 
midiThreadQuitEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!midiThreadQuitEvent) {
if (!midiThreadQuitEvent)
{
ErrorCode = WinMMErr_MIDICreateEvent;
return WinMMErr_Error;
}
 
if (!midiStreamRunning) {
rv = midiStreamRestart(midiStream);
if (rv != MMSYSERR_NOERROR) {
if (!midiStreamRunning)
{
auto rv = midiStreamRestart(midiStream);
if (rv != MMSYSERR_NOERROR)
{
midi_error(rv, "MIDI_StartPlayback midiStreamRestart");
WinMMDrv_MIDI_HaltPlayback();
ErrorCode = WinMMErr_MIDIStreamRestart;
710,7 → 700,8
}
 
midiThread = CreateThread(nullptr, 0, midiDataThread, 0, 0, 0);
if (!midiThread) {
if (!midiThread)
{
WinMMDrv_MIDI_HaltPlayback();
ErrorCode = WinMMErr_MIDIPlayThread;
return WinMMErr_Error;
723,90 → 714,76
 
void WinMMDrv_MIDI_HaltPlayback(void)
{
MMRESULT rv;
if (midiThread) {
if (midiThread)
{
SetEvent(midiThreadQuitEvent);
 
WaitForSingleObject(midiThread, INFINITE);
// MV_Printf("WinMM MIDI_HaltPlayback synched\n");
// MV_Printf("MME MIDI_HaltPlayback synched\n");
 
CloseHandle(midiThread);
}
 
if (midiThreadQuitEvent) {
if (midiThreadQuitEvent)
CloseHandle(midiThreadQuitEvent);
}
if (midiStreamRunning) {
 
if (midiStreamRunning)
{
// MV_Printf("stopping stream\n");
rv = midiStreamStop(midiStream);
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM MIDI_HaltPlayback midiStreamStop");
}
auto rv = midiStreamStop(midiStream);
if (rv != MMSYSERR_NOERROR)
midi_error(rv, "MME MIDI_HaltPlayback midiStreamStop");
// MV_Printf("stream stopped\n");
 
midiStreamRunning = FALSE;
}
 
midi_free_buffers();
midiThread = 0;
 
midiThread = 0;
midiThreadQuitEvent = 0;
}
 
void WinMMDrv_MIDI_SetTempo(int tempo, int division)
{
MMRESULT rv;
MIDIPROPTEMPO propTempo;
MIDIPROPTIMEDIV propTimediv;
BOOL running = midiStreamRunning;
BOOL const running = midiStreamRunning;
 
//MV_Printf("MIDI_SetTempo %d/%d\n", tempo, division);
MIDIPROPTEMPO propTempo = { sizeof(MIDIPROPTEMPO), (DWORD)(60000000l / tempo) };
MIDIPROPTIMEDIV propTimediv = { sizeof(MIDIPROPTIMEDIV), (DWORD)division };
 
propTempo.cbStruct = sizeof(MIDIPROPTEMPO);
propTempo.dwTempo = 60000000l / tempo;
propTimediv.cbStruct = sizeof(MIDIPROPTIMEDIV);
propTimediv.dwTimeDiv = division;
 
if (midiLastDivision != division) {
if (midiLastDivision != division)
{
// changing the division means halting the stream
WinMMDrv_MIDI_HaltPlayback();
 
rv = midiStreamProperty(midiStream, (LPBYTE) &propTimediv, MIDIPROP_SET | MIDIPROP_TIMEDIV);
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM MIDI_SetTempo midiStreamProperty timediv");
}
auto rv = midiStreamProperty(midiStream, (LPBYTE)&propTimediv, MIDIPROP_SET | MIDIPROP_TIMEDIV);
if (rv != MMSYSERR_NOERROR)
midi_error(rv, "MME MIDI_SetTempo midiStreamProperty timediv");
}
 
rv = midiStreamProperty(midiStream, (LPBYTE) &propTempo, MIDIPROP_SET | MIDIPROP_TEMPO);
if (rv != MMSYSERR_NOERROR) {
midi_error(rv, "WinMM MIDI_SetTempo midiStreamProperty tempo");
}
auto rv = midiStreamProperty(midiStream, (LPBYTE)&propTempo, MIDIPROP_SET | MIDIPROP_TEMPO);
if (rv != MMSYSERR_NOERROR)
midi_error(rv, "MME MIDI_SetTempo midiStreamProperty tempo");
 
if (midiLastDivision != division) {
if (running && WinMMDrv_MIDI_StartPlayback() != WinMMErr_Ok) {
if (midiLastDivision != division)
{
if (running && WinMMDrv_MIDI_StartPlayback() != WinMMErr_Ok)
return;
}
 
midiLastDivision = division;
}
 
midiThreadQueueTicks = (int) ceil( ( ( (double) tempo * (double) division ) / 60.0 ) /
(double) THREAD_QUEUE_INTERVAL );
if (midiThreadQueueTicks <= 0) {
midiThreadQueueTicks = (int)ceil((((double)tempo * (double)division) / 60.0) / (double)MME_THREAD_QUEUE_INTERVAL);
if (midiThreadQueueTicks <= 0)
midiThreadQueueTicks = 1;
}
}
 
void WinMMDrv_MIDI_Lock(void)
{
DWORD err;
 
err = WaitForSingleObject(midiMutex, INFINITE);
if (err != WAIT_OBJECT_0) {
MV_Printf("WinMM midiMutex lock: wfso %d\n", (int) err);
}
DWORD err = WaitForSingleObject(midiMutex, INFINITE);
if (err != WAIT_OBJECT_0)
MV_Printf("MME midiMutex lock: wfso %d\n", (int) err);
}
 
void WinMMDrv_MIDI_Unlock(void) { ReleaseMutex(midiMutex); }
/source/audiolib/src/driver_winmm.h
29,6 → 29,17
#define WINMM_AFTER_TOUCH 0xD0
#define WINMM_PITCH_BEND 0xE0
 
enum
{
WinMMErr_Error = -1,
WinMMErr_Ok = 0,
WinMMErr_MIDIStreamOpen,
WinMMErr_MIDIStreamRestart,
WinMMErr_MIDICreateEvent,
WinMMErr_MIDIPlayThread,
WinMMErr_MIDICreateMutex
};
 
extern UINT WinMM_DeviceID;
 
int WinMMDrv_GetError(void);
/source/audiolib/src/fx_man.cpp
111,9 → 111,9
{ "mus_al_additivemode", "enable/disable alternate additive AdLib timbre mode", (void*) &AL_AdditiveMode, CVAR_BOOL, 0, 1 },
{ "mus_al_postamp", "controls post-synthesization OPL3 volume amplification", (void*) &AL_PostAmp, CVAR_INT, 0, 3 },
{ "mus_al_stereo", "enable/disable OPL3 stereo mode", (void*) &AL_Stereo, CVAR_BOOL | CVAR_FUNCPTR, 0, 1 },
{ "mus_sf2_bank", "SF2 bank file path", (void*) SF2_BankFile, CVAR_STRING, 0, sizeof(SF2_BankFile) - 1 },
{ "mus_sf2_bank", "SoundFont 2 (.sf2) bank filename", (void*) SF2_BankFile, CVAR_STRING, 0, sizeof(SF2_BankFile) - 1 },
#ifdef _WIN32
{ "mus_winmm_device", "select Windows MME MIDI device", (void*) &WinMM_DeviceID, CVAR_INT | CVAR_FUNCPTR, -1, WinMMDrv_MIDI_GetNumDevices()-1 },
{ "mus_mme_device", "select Windows MME MIDI output device", (void*) &WinMM_DeviceID, CVAR_INT | CVAR_FUNCPTR, -1, WinMMDrv_MIDI_GetNumDevices()-1 },
#endif
#ifdef HAVE_XMP
{ "mus_xmp_interpolation", "XMP output interpolation: 0: none 1: linear 2: spline", (void*) &MV_XMPInterpolation, CVAR_INT | CVAR_FUNCPTR, 0, 2 },