/* * Copyright 2003-2009, Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Matthijs Hollemans * Christian Packmann * Jerome Leveque * Philippe Houdoin * Pete Goodeve */ #include "PortDrivers.h" #include #include #include #include #include MidiPortConsumer::MidiPortConsumer(int fd, const char* name) : BMidiLocalConsumer(name), fFileDescriptor(fd) { } void MidiPortConsumer::Data(uchar* data, size_t length, bool atomic, bigtime_t time) { snooze_until(time - Latency(), B_SYSTEM_TIMEBASE); if (write(fFileDescriptor, data, length) == -1) { perror("Error sending data to driver"); } } // #pragma mark - MidiPortProducer::MidiPortProducer(int fd, const char *name) : BMidiLocalProducer(name), fFileDescriptor(fd), fKeepRunning(true) { BString tmp = name; tmp << " reader"; fReaderThread = spawn_thread( _ReaderThread, tmp.String(), B_URGENT_PRIORITY, this); resume_thread(fReaderThread); } MidiPortProducer::~MidiPortProducer() { fKeepRunning = false; status_t dummy; wait_for_thread(fReaderThread, &dummy); } int32 MidiPortProducer::_ReaderThread(void* data) { return ((MidiPortProducer*) data)->GetData(); } int32 MidiPortProducer::GetData() { uint8 msgBuf[3]; uint8* sysexBuf = NULL; uint8* msgPtr = NULL; size_t msgSize = 0; size_t needed = 0; uint8 runningStatus = 0; bool haveSysEx = false; size_t sysexAlloc = 0; size_t sysexSize = 0; uint8 next = 0; while (fKeepRunning) { if (read(fFileDescriptor, &next, 1) != 1) { if (errno == B_CANCELED) fKeepRunning = false; else perror("Error reading data from driver"); break; } bigtime_t timestamp = system_time(); if (haveSysEx) { // System Exclusive mode if (next < 0x80) { // System Exclusive data byte sysexBuf[sysexSize++] = next; if (sysexSize == sysexAlloc) { sysexAlloc *= 2; sysexBuf = (uint8*) realloc(sysexBuf, sysexAlloc); } continue; } else if ((next & 0xF8) == 0xF8) { // System Realtime interleaved in System Exclusive sequence SpraySystemRealTime(next, timestamp); continue; } else { // Whatever byte, this one ends the running SysEx sequence SpraySystemExclusive(sysexBuf, sysexSize, timestamp); haveSysEx = false; if (next == B_SYS_EX_END) { // swallow SysEx end byte continue; } // any other byte, while ending the SysEx sequence, // should be handled, not dropped } } if ((next & 0xF8) == 0xF8) { // System Realtime SpraySystemRealTime(next, timestamp); } else if ((next & 0xF0) == 0xF0) { // System Common runningStatus = 0; msgBuf[0] = next; msgPtr = msgBuf + 1; switch (next) { case B_SYS_EX_START: sysexAlloc = 4096; sysexBuf = (uint8*) malloc(sysexAlloc); sysexSize = 0; haveSysEx = true; break; case B_SONG_POSITION: needed = 2; msgSize = 3; break; case B_MIDI_TIME_CODE: case B_SONG_SELECT: case B_CABLE_MESSAGE: needed = 1; msgSize = 2; break; case B_SYS_EX_END: // Unpaired with B_SYS_EX_START, but pass it anyway... case B_TUNE_REQUEST: SpraySystemCommon(next, 0, 0, timestamp); break; } } else if ((next & 0x80) == 0x80) { // Voice message runningStatus = next; msgBuf[0] = next; msgPtr = msgBuf + 1; switch (next & 0xF0) { case B_NOTE_OFF: case B_NOTE_ON: case B_KEY_PRESSURE: case B_CONTROL_CHANGE: case B_PITCH_BEND: needed = 2; msgSize = 3; break; case B_PROGRAM_CHANGE: case B_CHANNEL_PRESSURE: needed = 1; msgSize = 2; break; } } else if (needed > 0) { // Data bytes to complete message *msgPtr++ = next; if (--needed == 0) { switch (msgBuf[0] & 0xF0) { case B_NOTE_OFF: SprayNoteOff(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], timestamp); break; case B_NOTE_ON: SprayNoteOn(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], timestamp); break; case B_KEY_PRESSURE: SprayKeyPressure(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], timestamp); break; case B_CONTROL_CHANGE: SprayControlChange(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], timestamp); break; case B_PROGRAM_CHANGE: SprayProgramChange(msgBuf[0] & 0x0F, msgBuf[1], timestamp); break; case B_CHANNEL_PRESSURE: SprayChannelPressure(msgBuf[0] & 0x0F, msgBuf[1], timestamp); break; case B_PITCH_BEND: SprayPitchBend(msgBuf[0] & 0x0F, msgBuf[1], msgBuf[2], timestamp); break; } switch (msgBuf[0]) { case B_SONG_POSITION: SpraySystemCommon(msgBuf[0], msgBuf[1], msgBuf[2], timestamp); break; case B_MIDI_TIME_CODE: case B_SONG_SELECT: case B_CABLE_MESSAGE: SpraySystemCommon(msgBuf[0], msgBuf[1], 0, timestamp); break; } } } else if (runningStatus != 0) { // Repeated voice command msgBuf[0] = runningStatus; msgBuf[1] = next; msgPtr = msgBuf + 2; needed = msgSize - 2; } } // while fKeepRunning if (haveSysEx) free(sysexBuf); return fKeepRunning ? errno : B_OK; }