Commit 92fbb441 authored by Huw Davies's avatar Huw Davies Committed by Alexandre Julliard

winealsa: Add helpers to handle regular and sysex events.

parent d40956b7
......@@ -170,114 +170,115 @@ static int midiCloseSeq(void)
return 0;
}
static void handle_sysex_event(struct midi_src *src, BYTE *data, UINT len)
{
UINT pos = 0, copy_len, current_time = NtGetTickCount() - src->startTime;
MIDIHDR *hdr;
in_buffer_lock();
while (len)
{
hdr = src->lpQueueHdr;
if (!hdr) break;
copy_len = min(len, hdr->dwBufferLength - hdr->dwBytesRecorded);
memcpy(hdr->lpData + hdr->dwBytesRecorded, data + pos, copy_len);
hdr->dwBytesRecorded += copy_len;
len -= copy_len;
pos += copy_len;
if ((hdr->dwBytesRecorded == hdr->dwBufferLength) ||
(*(BYTE*)(hdr->lpData + hdr->dwBytesRecorded - 1) == 0xF7))
{ /* buffer full or end of sysex message */
src->lpQueueHdr = hdr->lpNext;
hdr->dwFlags &= ~MHDR_INQUEUE;
hdr->dwFlags |= MHDR_DONE;
MIDI_NotifyClient(src - MidiInDev, MIM_LONGDATA, (DWORD_PTR)hdr, current_time);
}
}
in_buffer_unlock();
}
static void handle_regular_event(struct midi_src *src, snd_seq_event_t *ev)
{
UINT data = 0, value, current_time = NtGetTickCount() - src->startTime;
switch (ev->type)
{
case SND_SEQ_EVENT_NOTEOFF:
data = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
break;
case SND_SEQ_EVENT_NOTEON:
data = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
break;
case SND_SEQ_EVENT_KEYPRESS:
data = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
break;
case SND_SEQ_EVENT_CONTROLLER:
data = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
break;
case SND_SEQ_EVENT_PITCHBEND:
value = ev->data.control.value + 0x2000;
data = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
break;
case SND_SEQ_EVENT_PGMCHANGE:
data = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
break;
case SND_SEQ_EVENT_CHANPRESS:
data = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
break;
case SND_SEQ_EVENT_CLOCK:
data = 0xF8;
break;
case SND_SEQ_EVENT_START:
data = 0xFA;
break;
case SND_SEQ_EVENT_CONTINUE:
data = 0xFB;
break;
case SND_SEQ_EVENT_STOP:
data = 0xFC;
break;
case SND_SEQ_EVENT_SONGPOS:
data = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
break;
case SND_SEQ_EVENT_SONGSEL:
data = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
break;
case SND_SEQ_EVENT_RESET:
data = 0xFF;
break;
case SND_SEQ_EVENT_QFRAME:
data = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
break;
case SND_SEQ_EVENT_SENSING:
/* Noting to do */
break;
}
if (data != 0)
{
MIDI_NotifyClient(src - MidiInDev, MIM_DATA, data, current_time);
}
}
static void handle_midi_event(snd_seq_event_t *ev)
{
WORD wDevID;
struct midi_src *src;
/* Find the target device */
for (wDevID = 0; wDevID < MIDM_NumDevs; wDevID++)
if ( (ev->source.client == MidiInDev[wDevID].addr.client) && (ev->source.port == MidiInDev[wDevID].addr.port) )
break;
if ((wDevID == MIDM_NumDevs) || (MidiInDev[wDevID].state != 1))
FIXME("Unexpected event received, type = %x from %d:%d\n", ev->type, ev->source.client, ev->source.port);
else {
DWORD dwTime, toSend = 0;
int value = 0;
/* FIXME: Should use ev->time instead for better accuracy */
dwTime = GetTickCount() - MidiInDev[wDevID].startTime;
TRACE("Event received, type = %x, device = %d\n", ev->type, wDevID);
switch(ev->type)
{
case SND_SEQ_EVENT_NOTEOFF:
toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_OFF | ev->data.control.channel;
break;
case SND_SEQ_EVENT_NOTEON:
toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_ON | ev->data.control.channel;
break;
case SND_SEQ_EVENT_KEYPRESS:
toSend = (ev->data.note.velocity << 16) | (ev->data.note.note << 8) | MIDI_CMD_NOTE_PRESSURE | ev->data.control.channel;
for (src = MidiInDev; src < MidiInDev + MIDM_NumDevs; src++)
if ((ev->source.client == src->addr.client) && (ev->source.port == src->addr.port))
break;
case SND_SEQ_EVENT_CONTROLLER:
toSend = (ev->data.control.value << 16) | (ev->data.control.param << 8) | MIDI_CMD_CONTROL | ev->data.control.channel;
break;
case SND_SEQ_EVENT_PITCHBEND:
value = ev->data.control.value + 0x2000;
toSend = (((value >> 7) & 0x7f) << 16) | ((value & 0x7f) << 8) | MIDI_CMD_BENDER | ev->data.control.channel;
break;
case SND_SEQ_EVENT_PGMCHANGE:
toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_PGM_CHANGE | ev->data.control.channel;
break;
case SND_SEQ_EVENT_CHANPRESS:
toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_CHANNEL_PRESSURE | ev->data.control.channel;
break;
case SND_SEQ_EVENT_CLOCK:
toSend = 0xF8;
break;
case SND_SEQ_EVENT_START:
toSend = 0xFA;
break;
case SND_SEQ_EVENT_CONTINUE:
toSend = 0xFB;
break;
case SND_SEQ_EVENT_STOP:
toSend = 0xFC;
break;
case SND_SEQ_EVENT_SONGPOS:
toSend = (((ev->data.control.value >> 7) & 0x7f) << 16) | ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_POS;
break;
case SND_SEQ_EVENT_SONGSEL:
toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_SONG_SELECT;
break;
case SND_SEQ_EVENT_RESET:
toSend = 0xFF;
break;
case SND_SEQ_EVENT_QFRAME:
toSend = ((ev->data.control.value & 0x7f) << 8) | MIDI_CMD_COMMON_MTC_QUARTER;
break;
case SND_SEQ_EVENT_SYSEX:
{
int pos = 0;
int len = ev->data.ext.len;
LPBYTE ptr = ev->data.ext.ptr;
LPMIDIHDR lpMidiHdr;
in_buffer_lock();
while (len) {
if ((lpMidiHdr = MidiInDev[wDevID].lpQueueHdr) != NULL) {
int copylen = min(len, lpMidiHdr->dwBufferLength - lpMidiHdr->dwBytesRecorded);
memcpy(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded, ptr + pos, copylen);
lpMidiHdr->dwBytesRecorded += copylen;
len -= copylen;
pos += copylen;
/* We check if we reach the end of buffer or the end of sysex before notifying
* to handle the case where ALSA split the sysex into several events */
if ((lpMidiHdr->dwBytesRecorded == lpMidiHdr->dwBufferLength) ||
(*(BYTE*)(lpMidiHdr->lpData + lpMidiHdr->dwBytesRecorded - 1) == 0xF7)) {
MidiInDev[wDevID].lpQueueHdr = lpMidiHdr->lpNext;
lpMidiHdr->dwFlags &= ~MHDR_INQUEUE;
lpMidiHdr->dwFlags |= MHDR_DONE;
MIDI_NotifyClient(wDevID, MIM_LONGDATA, (DWORD_PTR)lpMidiHdr, dwTime);
}
} else {
FIXME("Sysex data received but no buffer to store it!\n");
break;
}
}
in_buffer_unlock();
}
break;
case SND_SEQ_EVENT_SENSING:
/* Noting to do */
break;
default:
FIXME("Unhandled event received, type = %x\n", ev->type);
break;
}
if (toSend != 0) {
TRACE("Received event %08x from %d:%d\n", toSend, ev->source.client, ev->source.port);
MIDI_NotifyClient(wDevID, MIM_DATA, toSend, dwTime);
}
}
if ((src == MidiInDev + MIDM_NumDevs) || (src->state != 1))
return;
if (ev->type == SND_SEQ_EVENT_SYSEX)
handle_sysex_event(src, ev->data.ext.ptr, ev->data.ext.len);
else
handle_regular_event(src, ev);
}
static DWORD WINAPI midRecThread(void *arg)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment