From e4fa49727a197ec01134aef0244639f1ecdf0d4a Mon Sep 17 00:00:00 2001 From: mahlzeit Date: Sun, 9 Mar 2003 16:10:30 +0000 Subject: [PATCH] Milestone 2 is complete. You can now send and receive MIDI events. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2874 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/os/midi2/MidiConsumer.h | 20 +-- headers/os/midi2/MidiProducer.h | 6 +- src/kits/midi2/MidiEndpoint.cpp | 7 +- src/kits/midi2/MidiLocalConsumer.cpp | 250 +++++++++++++++++++++++++-- src/kits/midi2/MidiLocalProducer.cpp | 213 +++++++++++++++++++++-- 5 files changed, 445 insertions(+), 51 deletions(-) diff --git a/headers/os/midi2/MidiConsumer.h b/headers/os/midi2/MidiConsumer.h index d07695ed41..c4e0ab39a7 100644 --- a/headers/os/midi2/MidiConsumer.h +++ b/headers/os/midi2/MidiConsumer.h @@ -4,8 +4,6 @@ #include -class BMidiDispatcher; - class BMidiConsumer : public BMidiEndpoint { public: @@ -75,7 +73,7 @@ public: uchar channel, uchar lsb, uchar msb, bigtime_t time); virtual void SystemExclusive( - void* data, size_t dataLength, bigtime_t time); + void* data, size_t length, bigtime_t time); virtual void SystemCommon( uchar status, uchar data1, uchar data2, bigtime_t time); @@ -92,8 +90,8 @@ protected: private: - friend class BMidiDispatcher; friend class BMidiRoster; + friend int32 _midi_event_thread(void*); virtual void _Reserved1(); virtual void _Reserved2(); @@ -103,12 +101,14 @@ private: virtual void _Reserved6(); virtual void _Reserved7(); virtual void _Reserved8(); - - BMidiDispatcher* fDispatcher; - bigtime_t fTimeout; - void* fTimeoutData; - - int32 fCurrentProducer; + + int32 EventThread(); + + bigtime_t timeout; + void* timeoutData; + int32 currentProducer; + thread_id thread; + uint32 _reserved[1]; }; diff --git a/headers/os/midi2/MidiProducer.h b/headers/os/midi2/MidiProducer.h index 4c86e362a1..b367393bbd 100644 --- a/headers/os/midi2/MidiProducer.h +++ b/headers/os/midi2/MidiProducer.h @@ -89,7 +89,7 @@ public: uchar channel, uchar lsb, uchar msb, bigtime_t time = 0) const; void SpraySystemExclusive( - void* data, size_t dataLength, bigtime_t time = 0) const; + void* data, size_t length, bigtime_t time = 0) const; void SpraySystemCommon( uchar status, uchar data1, uchar data2, bigtime_t time = 0) const; @@ -106,7 +106,9 @@ protected: private: - //void SprayEvent(BMidiEvent* event, size_t length) const; + void SprayEvent( + const void* data, size_t length, bool atomic, bigtime_t time, + bool sysex = false) const; virtual void _Reserved1(); virtual void _Reserved2(); diff --git a/src/kits/midi2/MidiEndpoint.cpp b/src/kits/midi2/MidiEndpoint.cpp index 8be5e9c0d7..4b4bf70647 100644 --- a/src/kits/midi2/MidiEndpoint.cpp +++ b/src/kits/midi2/MidiEndpoint.cpp @@ -14,10 +14,9 @@ const char* BMidiEndpoint::Name() const { const char* str = NULL; - // It seems reasonable to assume that the pointer - // returned by BString::String() can change when the - // string is modified, e.g. to allocate more space. - // That's why we need to lock here too. + // It seems reasonable to assume that the pointer returned by + // BString::String() can change when the string is modified, + // e.g. to allocate more space. That's why we lock here too. if (LockLooper()) { diff --git a/src/kits/midi2/MidiLocalConsumer.cpp b/src/kits/midi2/MidiLocalConsumer.cpp index e1f1bd033a..4a4284a2cb 100644 --- a/src/kits/midi2/MidiLocalConsumer.cpp +++ b/src/kits/midi2/MidiLocalConsumer.cpp @@ -2,6 +2,8 @@ * Copyright (c) 2002-2003 Matthijs Hollemans */ +#include + #include "debug.h" #include "MidiConsumer.h" #include "MidiRoster.h" @@ -9,6 +11,13 @@ //------------------------------------------------------------------------------ +int32 _midi_event_thread(void* data) +{ + return ((BMidiLocalConsumer*) data)->EventThread(); +} + +//------------------------------------------------------------------------------ + BMidiLocalConsumer::BMidiLocalConsumer(const char* name) : BMidiConsumer(name) { @@ -16,6 +25,13 @@ BMidiLocalConsumer::BMidiLocalConsumer(const char* name) isLocal = true; refCount = 1; + timeout = (bigtime_t) -1; + timeoutData = NULL; + + port = create_port(1, "MidiEventPort"); + thread = spawn_thread( + _midi_event_thread, "MidiEventThread", B_REAL_TIME_PRIORITY, this); + resume_thread(thread); BMidiRoster::MidiRoster()->CreateLocal(this); } @@ -27,6 +43,11 @@ BMidiLocalConsumer::~BMidiLocalConsumer() TRACE(("BMidiLocalConsumer::~BMidiLocalConsumer")) BMidiRoster::MidiRoster()->DeleteLocal(this); + + delete_port(port); + + status_t result; + wait_for_thread(thread, &result); } //------------------------------------------------------------------------------ @@ -62,22 +83,22 @@ void BMidiLocalConsumer::SetLatency(bigtime_t latency_) int32 BMidiLocalConsumer::GetProducerID(void) { - UNIMPLEMENTED - return 0; + return currentProducer; } //------------------------------------------------------------------------------ void BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data) { - UNIMPLEMENTED + timeout = when; + timeoutData = data; } //------------------------------------------------------------------------------ void BMidiLocalConsumer::Timeout(void* data) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -85,7 +106,134 @@ void BMidiLocalConsumer::Timeout(void* data) void BMidiLocalConsumer::Data( uchar* data, size_t length, bool atomic, bigtime_t time) { - UNIMPLEMENTED + if (atomic) + { + switch (data[0] & 0xF0) + { + case B_NOTE_OFF: + { + if (length == 3) + { + NoteOff(data[0] & 0x0F, data[1], data[2], time); + } + break; + } + + case B_NOTE_ON: + { + if (length == 3) + { + NoteOn(data[0] & 0x0F, data[1], data[2], time); + } + break; + } + + case B_KEY_PRESSURE: + { + if (length == 3) + { + KeyPressure(data[0] & 0x0F, data[1], data[2], time); + } + break; + } + + case B_CONTROL_CHANGE: + { + if (length == 3) + { + ControlChange(data[0] & 0x0F, data[1], data[2], time); + } + break; + } + + case B_PROGRAM_CHANGE: + { + if (length == 2) + { + ProgramChange(data[0] & 0x0F, data[1], time); + } + break; + } + + case B_CHANNEL_PRESSURE: + { + if (length == 2) + { + ChannelPressure(data[0] & 0x0F, data[1], time); + } + break; + } + + case B_PITCH_BEND: + { + if (length == 3) + { + PitchBend(data[0] & 0x0F, data[1], data[2], time); + } + break; + } + + case 0xF0: + { + switch (data[0]) + { + case B_SYS_EX_START: + { + if (data[length - 1] == B_SYS_EX_END) + { + SystemExclusive(data + 1, length - 2, time); + } + break; + } + + case B_TUNE_REQUEST: + case B_SYS_EX_END: + { + if (length == 1) + { + SystemCommon(data[0], 0, 0, time); + } + break; + } + + case B_CABLE_MESSAGE: + case B_MIDI_TIME_CODE: + case B_SONG_SELECT: + { + if (length == 2) + { + SystemCommon(data[0], data[1], 0, time); + } + break; + } + + case B_SONG_POSITION: + { + if (length == 3) + { + SystemCommon(data[0], data[1], data[2], time); + } + break; + } + + case B_TIMING_CLOCK: + case B_START: + case B_CONTINUE: + case B_STOP: + case B_ACTIVE_SENSING: + case B_SYSTEM_RESET: + { + if (length == 1) + { + SystemRealTime(data[0], time); + } + break; + } + } + break; + } + } + } } //------------------------------------------------------------------------------ @@ -93,7 +241,7 @@ void BMidiLocalConsumer::Data( void BMidiLocalConsumer::NoteOff( uchar channel, uchar note, uchar velocity, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -101,7 +249,7 @@ void BMidiLocalConsumer::NoteOff( void BMidiLocalConsumer::NoteOn( uchar channel, uchar note, uchar velocity, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -109,7 +257,7 @@ void BMidiLocalConsumer::NoteOn( void BMidiLocalConsumer::KeyPressure( uchar channel, uchar note, uchar pressure, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -117,7 +265,7 @@ void BMidiLocalConsumer::KeyPressure( void BMidiLocalConsumer::ControlChange( uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -125,7 +273,7 @@ void BMidiLocalConsumer::ControlChange( void BMidiLocalConsumer::ProgramChange( uchar channel, uchar programNumber, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -133,7 +281,7 @@ void BMidiLocalConsumer::ProgramChange( void BMidiLocalConsumer::ChannelPressure( uchar channel, uchar pressure, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -141,15 +289,15 @@ void BMidiLocalConsumer::ChannelPressure( void BMidiLocalConsumer::PitchBend( uchar channel, uchar lsb, uchar msb, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ void BMidiLocalConsumer::SystemExclusive( - void* data, size_t dataLength, bigtime_t time) + void* data, size_t length, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -157,28 +305,28 @@ void BMidiLocalConsumer::SystemExclusive( void BMidiLocalConsumer::SystemCommon( uchar statusByte, uchar data1, uchar data2, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ void BMidiLocalConsumer::SystemRealTime(uchar statusByte, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ -void BMidiLocalConsumer::TempoChange(int32 bpm, bigtime_t time) +void BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ void BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time) { - UNIMPLEMENTED + // Do nothing. } //------------------------------------------------------------------------------ @@ -193,3 +341,67 @@ void BMidiLocalConsumer::_Reserved7() { } void BMidiLocalConsumer::_Reserved8() { } //------------------------------------------------------------------------------ + +int32 BMidiLocalConsumer::EventThread() +{ + int32 msg_code; + ssize_t msg_size; + ssize_t buf_size = 100; + uint8* buffer = (uint8*) malloc(buf_size); + + while (true) + { + if (timeout == (bigtime_t) -1) + { + msg_size = port_buffer_size(port); + } + else // have timeout + { + msg_size = port_buffer_size_etc(port, B_ABSOLUTE_TIMEOUT, timeout); + if (msg_size == B_TIMED_OUT) + { + Timeout(timeoutData); + timeout = (bigtime_t) -1; + timeoutData = NULL; + continue; + } + } + + if (msg_size < 0) { break; } // error reading port + + if (msg_size > buf_size) + { + buffer = (uint8*) realloc(buffer, msg_size); + buf_size = msg_size; + } + + read_port(port, &msg_code, buffer, msg_size); + + if (msg_size > 20) // minimum valid size + { + #ifdef DEBUG + printf("*** received: "); + for (int32 t = 0; t < msg_size; ++t) + { + printf("%02X, ", ((uint8*) buffer)[t]); + } + printf("\n"); + #endif + + currentProducer = *((uint32*) (buffer + 0)); + int32 targetId = *((uint32*) (buffer + 4)); + bigtime_t time = *((bigtime_t*) (buffer + 8)); + bool atomic = *((bool*) (buffer + 16)); + + if (targetId == id) // only if we are the destination + { + Data((uchar*) (buffer + 20), msg_size - 20, atomic, time); + } + } + } + + free(buffer); + return 0; +} + +//------------------------------------------------------------------------------ diff --git a/src/kits/midi2/MidiLocalProducer.cpp b/src/kits/midi2/MidiLocalProducer.cpp index 767e472c64..0a5a3525bf 100644 --- a/src/kits/midi2/MidiLocalProducer.cpp +++ b/src/kits/midi2/MidiLocalProducer.cpp @@ -2,6 +2,8 @@ * Copyright (c) 2002-2003 Matthijs Hollemans */ +#include + #include "debug.h" #include "MidiConsumer.h" #include "MidiProducer.h" @@ -55,7 +57,7 @@ void BMidiLocalProducer::Disconnected(BMidiConsumer* cons) void BMidiLocalProducer::SprayData( void* data, size_t length, bool atomic, bigtime_t time) const { - UNIMPLEMENTED + SprayEvent(data, length, atomic, time); } //------------------------------------------------------------------------------ @@ -63,7 +65,19 @@ void BMidiLocalProducer::SprayData( void BMidiLocalProducer::SprayNoteOff( uchar channel, uchar note, uchar velocity, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[3]; + data[0] = B_NOTE_OFF + channel; + data[1] = note; + data[2] = velocity; + + SprayEvent(&data, 3, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ @@ -71,7 +85,19 @@ void BMidiLocalProducer::SprayNoteOff( void BMidiLocalProducer::SprayNoteOn( uchar channel, uchar note, uchar velocity, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[3]; + data[0] = B_NOTE_ON + channel; + data[1] = note; + data[2] = velocity; + + SprayEvent(&data, 3, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ @@ -79,7 +105,19 @@ void BMidiLocalProducer::SprayNoteOn( void BMidiLocalProducer::SprayKeyPressure( uchar channel, uchar note, uchar pressure, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[3]; + data[0] = B_KEY_PRESSURE + channel; + data[1] = note; + data[2] = pressure; + + SprayEvent(&data, 3, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ @@ -88,7 +126,19 @@ void BMidiLocalProducer::SprayControlChange( uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[3]; + data[0] = B_CONTROL_CHANGE + channel; + data[1] = controlNumber; + data[2] = controlValue; + + SprayEvent(&data, 3, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ @@ -96,7 +146,18 @@ void BMidiLocalProducer::SprayControlChange( void BMidiLocalProducer::SprayProgramChange( uchar channel, uchar programNumber, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[2]; + data[0] = B_PROGRAM_CHANGE + channel; + data[1] = programNumber; + + SprayEvent(&data, 2, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ @@ -104,7 +165,18 @@ void BMidiLocalProducer::SprayProgramChange( void BMidiLocalProducer::SprayChannelPressure( uchar channel, uchar pressure, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[2]; + data[0] = B_CHANNEL_PRESSURE + channel; + data[1] = pressure; + + SprayEvent(&data, 2, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ @@ -112,39 +184,82 @@ void BMidiLocalProducer::SprayChannelPressure( void BMidiLocalProducer::SprayPitchBend( uchar channel, uchar lsb, uchar msb, bigtime_t time) const { - UNIMPLEMENTED + if (channel < 16) + { + uchar data[3]; + data[0] = B_PITCH_BEND + channel; + data[1] = lsb; + data[2] = msb; + + SprayEvent(&data, 3, true, time); + } + else + { + debugger("invalid MIDI channel"); + } } //------------------------------------------------------------------------------ void BMidiLocalProducer::SpraySystemExclusive( - void* data, size_t dataLength, bigtime_t time) const + void* data, size_t length, bigtime_t time) const { - UNIMPLEMENTED + SprayEvent(data, length, true, time, true); } //------------------------------------------------------------------------------ void BMidiLocalProducer::SpraySystemCommon( - uchar statusByte, uchar data1, uchar data2, bigtime_t time) const + uchar status, uchar data1, uchar data2, bigtime_t time) const { - UNIMPLEMENTED + size_t len; + uchar data[3]; + data[0] = status; + data[1] = data1; + data[2] = data2; + + switch (status) + { + case B_TUNE_REQUEST: + case B_SYS_EX_END: + len = 1; break; + + case B_CABLE_MESSAGE: + case B_MIDI_TIME_CODE: + case B_SONG_SELECT: + len = 2; break; + + case B_SONG_POSITION: + len = 3; break; + + default: + debugger("invalid system common status"); + } + + SprayEvent(&data, len, true, time); } //------------------------------------------------------------------------------ void BMidiLocalProducer::SpraySystemRealTime( - uchar statusByte, bigtime_t time) const + uchar status, bigtime_t time) const { - UNIMPLEMENTED + if (status >= B_TIMING_CLOCK) + { + SprayEvent(&status, 1, true, time); + } + else + { + debugger("invalid real time status"); + } } //------------------------------------------------------------------------------ void BMidiLocalProducer::SprayTempoChange( - int32 bpm, bigtime_t time) const + int32 beatsPerMinute, bigtime_t time) const { - UNIMPLEMENTED + // This method does nothing, just like the BeOS R5 Midi Kit. } //------------------------------------------------------------------------------ @@ -159,3 +274,69 @@ void BMidiLocalProducer::_Reserved7() { } void BMidiLocalProducer::_Reserved8() { } //------------------------------------------------------------------------------ + +void BMidiLocalProducer::SprayEvent( + const void* data, size_t length, bool atomic, bigtime_t time, + bool sysex) const +{ + if (LockProducer()) + { + if (CountConsumers() > 0) + { + // We don't just send the MIDI event data to all connected + // consumers, we also send a header. The header contains our + // ID (4 bytes), the consumer's ID (4 bytes), the performance + // time (8 bytes), whether the data is atomic (1 byte), and + // padding (3 bytes). The MIDI event data follows the header. + + size_t buf_size = 20 + length; + if (sysex) { buf_size += 2; } // add 0xF0 and 0xF7 markers + + uint8* buffer = (uint8*) malloc(buf_size); + if (buffer != NULL) + { + *((uint32*) (buffer + 0)) = id; + *((bigtime_t*) (buffer + 8)) = time; + *((uint32*) (buffer + 16)) = 0; + *((bool*) (buffer + 16)) = atomic; + + if (sysex) + { + *((uint8*) (buffer + 20)) = B_SYS_EX_START; + if (data != NULL) + { + memcpy(buffer + 21, data, length); + } + *((uint8*) (buffer + buf_size - 1)) = B_SYS_EX_END; + } + else if (data != NULL) + { + memcpy(buffer + 20, data, length); + } + + for (int32 t = 0; t < CountConsumers(); ++t) + { + BMidiConsumer* cons = ConsumerAt(t); + *((uint32*) (buffer + 4)) = cons->id; + + #ifdef DEBUG + printf("*** spraying: "); + for (int32 t = 0; t < buf_size; ++t) + { + printf("%02X, ", buffer[t]); + } + printf("\n"); + #endif + + write_port(cons->port, 0, buffer, buf_size); + } + + free(buffer); + } + } + + UnlockProducer(); + } +} + +//------------------------------------------------------------------------------