static PyObject * midi_parse_track (unsigned char **track, unsigned char *track_end) { unsigned int time = 0; unsigned long track_len, track_size; PyObject *pytrack = 0; debug_print ("%s", "\n"); track_size = track_end - *track; debug_print ("%s", "\n"); if (memcmp (*track, "MTrk", 4)) return midi_error (__FUNCTION__, ": MTrk expected"); *track += 4; track_len = get_number (track, *track + 4, 4); debug_print ("track_len: %u\n", track_len); debug_print ("track_size: %u\n", track_size); debug_print ("track begin: %p\n", track); debug_print ("track end: %p\n", track + track_len); if (track_len > track_size) return midi_error (__FUNCTION__, ": track size corrupt"); pytrack = PyList_New (0); if (*track + track_len < track_end) track_end = *track + track_len; { PyObject *pytime = PyInt_FromLong (0L); unsigned char running_status = 0; while (*track < track_end) { long dt = get_variable_length_number(track, track_end); PyObject *pyev = 0; time += dt; if (dt) pytime = PyInt_FromLong (time); pyev = read_event (track, track_end, pytime, &running_status); if (pyev) PyList_Append (pytrack, pyev); } } *track = track_end; return pytrack; }
int WinMMDrv_MIDI_StartPlayback(void (*service)(void)) { MMRESULT rv; WinMMDrv_MIDI_HaltPlayback(); midiThreadService = service; midiThreadQuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!midiThreadQuitEvent) { ErrorCode = WinMMErr_MIDICreateEvent; return WinMMErr_Error; } if (!midiStreamRunning) { rv = midiStreamRestart(midiStream); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "MIDI_StartPlayback midiStreamRestart"); WinMMDrv_MIDI_HaltPlayback(); ErrorCode = WinMMErr_MIDIStreamRestart; return WinMMErr_Error; } midiStreamRunning = TRUE; } midiThread = CreateThread(NULL, 0, midiDataThread, 0, 0, 0); if (!midiThread) { WinMMDrv_MIDI_HaltPlayback(); ErrorCode = WinMMErr_MIDIPlayThread; return WinMMErr_Error; } return WinMMErr_Ok; }
void WinMMDrv_MIDI_Shutdown(void) { MMRESULT rv; if (!midiInstalled) { return; } WinMMDrv_MIDI_HaltPlayback(); if (midiStream) { rv = midiStreamClose(midiStream); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM MIDI_Shutdown midiStreamClose"); } } if (midiMutex) { CloseHandle(midiMutex); } midiStream = 0; midiMutex = 0; midiInstalled = FALSE; }
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); } node->prepared = FALSE; } if (midiThread) { // remove the node from the activeMidiBuffers list LL_Remove( node, next, prev ); // when playing, we keep the buffers LL_Add( (MidiBuffer*) &spareMidiBuffers, node, next, prev ); //fprintf(stderr, "WinMM %s/midi_dispose_buffer recycling buffer %p\n", caller, node); } else { // when not, we throw them away free(node); //fprintf(stderr, "WinMM %s/midi_dispose_buffer freeing buffer %p\n", caller, node); } }
void WinMMDrv_MIDI_HaltPlayback(void) { MMRESULT rv; if (midiThread) { SetEvent(midiThreadQuitEvent); WaitForSingleObject(midiThread, INFINITE); fprintf(stderr, "WinMM MIDI_HaltPlayback synched\n"); CloseHandle(midiThread); } if (midiThreadQuitEvent) { CloseHandle(midiThreadQuitEvent); } if (midiStreamRunning) { fprintf(stderr, "stopping stream\n"); rv = midiStreamStop(midiStream); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM MIDI_HaltPlayback midiStreamStop"); } fprintf(stderr, "stream stopped\n"); midiStreamRunning = FALSE; } midi_free_buffers(); midiThread = 0; midiThreadQuitEvent = 0; }
void WinMMDrv_MIDI_SetTempo(int tempo, int division) { MMRESULT rv; MIDIPROPTEMPO propTempo; MIDIPROPTIMEDIV propTimediv; BOOL running = midiStreamRunning; //fprintf(stderr, "MIDI_SetTempo %d/%d\n", tempo, division); propTempo.cbStruct = sizeof(MIDIPROPTEMPO); propTempo.dwTempo = 60000000l / tempo; propTimediv.cbStruct = sizeof(MIDIPROPTIMEDIV); propTimediv.dwTimeDiv = 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"); } } rv = midiStreamProperty(midiStream, (LPBYTE) &propTempo, MIDIPROP_SET | MIDIPROP_TEMPO); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM MIDI_SetTempo midiStreamProperty tempo"); } if (midiLastDivision != division) { if (running && WinMMDrv_MIDI_StartPlayback(midiThreadService) != WinMMErr_Ok) { return; } midiLastDivision = division; } midiThreadQueueTicks = (int) ceil( ( ( (double) tempo * (double) division ) / 60.0 ) / (double) THREAD_QUEUE_INTERVAL ); if (midiThreadQueueTicks <= 0) { midiThreadQueueTicks = 1; } }
static PyObject * midi_parse (unsigned char **midi,unsigned char *midi_end) { PyObject *pymidi = 0; unsigned long header_len; unsigned format, tracks; int division; int i; debug_print ("%s", "\n"); /* Header */ header_len = get_number (midi, *midi + 4, 4); if (header_len < 6) return midi_error (__FUNCTION__, ": header too short"); format = get_number (midi, *midi + 2, 2); tracks = get_number (midi, *midi + 2, 2); if (tracks > 32) return midi_error (__FUNCTION__, ": too many tracks"); division = get_number (midi, *midi + 2, 2) * 4; if (division < 0) /* return midi_error (cannot handle non-metrical time"); */ ; *midi += header_len - 6; pymidi = PyList_New (0); /* Tracks */ for (i = 0; i < tracks; i++) PyList_Append (pymidi, midi_parse_track (midi, midi_end)); pymidi = Py_BuildValue ("(OO)", Py_BuildValue ("(ii)", format, division), pymidi); return pymidi; }
static DWORD midi_get_tick(void) { MMRESULT rv; MMTIME mmtime; mmtime.wType = TIME_TICKS; rv = midiStreamPosition(midiStream, &mmtime, sizeof(MMTIME)); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM midi_get_tick midiStreamPosition"); return 0; } return mmtime.u.ticks; }
static PyObject * pymidi_parse_track (PyObject *self, PyObject *args) { unsigned char *track, *track_end; unsigned long track_size; debug_print ("%s", "\n"); if (!PyArg_ParseTuple (args, "s#", &track, &track_size)) return 0; if (track_size < 0) return midi_error (__FUNCTION__, ": negative track size"); track_end = track + track_size; return midi_parse_track (&track, track_end); }
static PyObject * pymidi_parse (PyObject *self, PyObject *args) { unsigned char *midi, *midi_end; unsigned long midi_size; debug_print ("%s", "\n"); if (!PyArg_ParseTuple (args, "s#", &midi, &midi_size)) return 0; if (memcmp (midi, "MThd", 4)) return midi_error (__FUNCTION__, ": MThd expected"); midi += 4; midi_end = midi + midi_size; return midi_parse (&midi, midi_end); }
int WinMMDrv_MIDI_Init(midifuncs * funcs) { MMRESULT rv; if (midiInstalled) { WinMMDrv_MIDI_Shutdown(); } memset(funcs, 0, sizeof(midifuncs)); LL_Reset( (MidiBuffer*) &activeMidiBuffers, next, prev ); LL_Reset( (MidiBuffer*) &spareMidiBuffers, next, prev ); midiMutex = CreateMutex(0, FALSE, 0); if (!midiMutex) { ErrorCode = WinMMErr_MIDICreateMutex; return WinMMErr_Error; } rv = midiStreamOpen(&midiStream, &midiDeviceID, 1, (DWORD_PTR) 0, (DWORD_PTR) 0, CALLBACK_NULL); if (rv != MMSYSERR_NOERROR) { CloseHandle(midiMutex); midiMutex = 0; midi_error(rv, "WinMM 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; midiInstalled = TRUE; return WinMMErr_Ok; }
static void midi_flush_current_buffer(void) { MMRESULT rv; MIDIEVENT * evt; BOOL needsPrepare = FALSE; if (!currentMidiBuffer) { return; } evt = (MIDIEVENT *) currentMidiBuffer->hdr.lpData; 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.dwBytesRecorded = 0; currentMidiBuffer->hdr.lpData = (LPSTR) &evt->dwParms[0]; if (currentMidiBuffer->hdr.dwBufferLength > 0) { needsPrepare = TRUE; } } else { needsPrepare = TRUE; } if (needsPrepare) { // playing a file, or sending a sysex when not playing means // we need to prepare the buffer rv = midiOutPrepareHeader( (HMIDIOUT) midiStream, ¤tMidiBuffer->hdr, sizeof(MIDIHDR) ); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM midi_flush_current_buffer midiOutPrepareHeader"); return; } currentMidiBuffer->prepared = TRUE; } if (midiThread) { // midi file playing, so send events to the stream LL_Add( (MidiBuffer*) &activeMidiBuffers, currentMidiBuffer, next, prev ); rv = midiStreamOut(midiStream, ¤tMidiBuffer->hdr, sizeof(MIDIHDR)); if (rv != MMSYSERR_NOERROR) { midi_error(rv, "WinMM midi_flush_current_buffer midiStreamOut"); midi_dispose_buffer(currentMidiBuffer, "midi_flush_current_buffer"); return; } //fprintf(stderr, "WinMM 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, ¤tMidiBuffer->hdr, sizeof(MIDIHDR) ); if (rv == MMSYSERR_NOERROR) { // busy-wait for Windows to be done with it while (!(currentMidiBuffer->hdr.dwFlags & MHDR_DONE)) ; //fprintf(stderr, "WinMM midi_flush_current_buffer sent immediate long\n"); } else { midi_error(rv, "WinMM midi_flush_current_buffer midiOutLongMsg"); } } else { rv = midiOutShortMsg( (HMIDIOUT) midiStream, evt->dwEvent ); if (rv == MMSYSERR_NOERROR) { //fprintf(stderr, "WinMM midi_flush_current_buffer sent immediate short\n"); } else { midi_error(rv, "WinMM midi_flush_current_buffer midiOutShortMsg"); } } midi_dispose_buffer(currentMidiBuffer, "midi_flush_current_buffer"); } currentMidiBuffer = 0; }