* Copyright 2006, Haiku.
*
* Copyright (c) 2002-2003 Matthijs Hollemans
* Distributed under the terms of the MIT License.
*
* Authors:
* Matthijs Hollemans
*/
#include <stdlib.h>
#include "debug.h"
#include <MidiConsumer.h>
#include <MidiRoster.h>
#include "protocol.h"
int32
_midi_event_thread(void* data)
{
return ((BMidiLocalConsumer*) data)->EventThread();
}
BMidiLocalConsumer::BMidiLocalConsumer(const char* name)
: BMidiConsumer(name)
{
TRACE(("BMidiLocalConsumer::BMidiLocalConsumer"))
fIsLocal = true;
fRefCount = 1;
fTimeout = (bigtime_t) -1;
fTimeoutData = NULL;
fPort = create_port(1, "MidiEventPort");
fThread = spawn_thread(
_midi_event_thread, "MidiEventThread", B_REAL_TIME_PRIORITY, this);
resume_thread(fThread);
BMidiRoster::MidiRoster()->CreateLocal(this);
}
BMidiLocalConsumer::~BMidiLocalConsumer()
{
TRACE(("BMidiLocalConsumer::~BMidiLocalConsumer"))
BMidiRoster::MidiRoster()->DeleteLocal(this);
delete_port(fPort);
status_t result;
wait_for_thread(fThread, &result);
}
void
BMidiLocalConsumer::SetLatency(bigtime_t latency_)
{
if (latency_ < 0) {
WARN("SetLatency() does not accept negative values");
return;
} else if (!IsValid()) {
return;
} else if (fLatency != latency_) {
BMessage msg;
msg.AddInt64("midi:latency", latency_);
if (SendChangeRequest(&msg) == B_OK) {
if (LockLooper()) {
fLatency = latency_;
UnlockLooper();
}
}
}
}
int32
BMidiLocalConsumer::GetProducerID()
{
return fCurrentProducer;
}
void
BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data)
{
fTimeout = when;
fTimeoutData = data;
}
void
BMidiLocalConsumer::Timeout(void* data)
{
}
void
BMidiLocalConsumer::Data(uchar* data, size_t length, bool atomic, bigtime_t time)
{
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);
} else {
SystemExclusive(data + 1, length - 1, 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:
{
if (length == 1) {
SystemRealTime(data[0], time);
}
break;
}
case B_SYSTEM_RESET:
{
if (length == 1) {
SystemRealTime(data[0], time);
} else if ((length == 6) && (data[1] == 0x51)
&& (data[2] == 0x03)) {
int32 tempo =
(data[3] << 16) | (data[4] << 8) | data[5];
TempoChange(60000000/tempo, time);
}
}
}
break;
}
}
}
}
void
BMidiLocalConsumer::NoteOff(uchar channel, uchar note, uchar velocity, bigtime_t time)
{
}
void
BMidiLocalConsumer::NoteOn(uchar channel, uchar note, uchar velocity, bigtime_t time)
{
}
void
BMidiLocalConsumer::KeyPressure(uchar channel, uchar note, uchar pressure, bigtime_t time)
{
}
void
BMidiLocalConsumer::ControlChange(uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time)
{
}
void
BMidiLocalConsumer::ProgramChange(uchar channel, uchar programNumber, bigtime_t time)
{
}
void BMidiLocalConsumer::ChannelPressure(uchar channel, uchar pressure, bigtime_t time)
{
}
void
BMidiLocalConsumer::PitchBend(uchar channel, uchar lsb, uchar msb, bigtime_t time)
{
}
void
BMidiLocalConsumer::SystemExclusive(
void* data, size_t length, bigtime_t time)
{
}
void
BMidiLocalConsumer::SystemCommon(
uchar statusByte, uchar data1, uchar data2, bigtime_t time)
{
}
void
BMidiLocalConsumer::SystemRealTime(uchar statusByte, bigtime_t time)
{
}
void
BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time)
{
}
void
BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time)
{
}
void BMidiLocalConsumer::_Reserved1() { }
void BMidiLocalConsumer::_Reserved2() { }
void BMidiLocalConsumer::_Reserved3() { }
void BMidiLocalConsumer::_Reserved4() { }
void BMidiLocalConsumer::_Reserved5() { }
void BMidiLocalConsumer::_Reserved6() { }
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 (fTimeout == (bigtime_t) -1) {
msg_size = port_buffer_size(fPort);
} else {
msg_size = port_buffer_size_etc(fPort, B_ABSOLUTE_TIMEOUT, fTimeout);
if (msg_size == B_TIMED_OUT) {
Timeout(fTimeoutData);
fTimeout = (bigtime_t) -1;
fTimeoutData = NULL;
continue;
}
}
if (msg_size < 0)
break;
if (msg_size > buf_size) {
uint8* tmp_buffer = (uint8*) realloc(buffer, msg_size);
if (tmp_buffer == NULL)
break;
buffer = tmp_buffer;
buf_size = msg_size;
}
read_port(fPort, &msg_code, buffer, msg_size);
if (msg_size > 20) {
#ifdef DEBUG
printf("*** received: ");
for (int32 t = 0; t < msg_size; ++t) {
printf("%02X, ", ((uint8*) buffer)[t]);
}
printf("\n");
#endif
fCurrentProducer = *((uint32*) (buffer + 0));
int32 targetId = *((uint32*) (buffer + 4));
bigtime_t time = *((bigtime_t*) (buffer + 8));
bool atomic = *((bool*) (buffer + 16));
if (targetId == fId) {
Data((uchar*) (buffer + 20), msg_size - 20, atomic, time);
}
}
}
free(buffer);
return 0;
}