⛏️ index : haiku.git

/*
 * Copyright 2005-2006, Haiku.
 * 
 * Copyright (c) 2002-2004 Matthijs Hollemans
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *		Matthijs Hollemans
 */

#include <stdlib.h>

#include "debug.h"
#include <MidiConsumer.h>
#include <MidiProducer.h>
#include <MidiRoster.h>
#include "protocol.h"


BMidiLocalProducer::BMidiLocalProducer(const char* name)
	: BMidiProducer(name)
{
	TRACE(("BMidiLocalProducer::BMidiLocalProducer"))

	fIsLocal = true;
	fRefCount = 1;

	BMidiRoster::MidiRoster()->CreateLocal(this);
}


BMidiLocalProducer::~BMidiLocalProducer()
{
	TRACE(("BMidiLocalProducer::~BMidiLocalProducer"))

	BMidiRoster::MidiRoster()->DeleteLocal(this);
}


void
BMidiLocalProducer::Connected(BMidiConsumer* cons)
{
	ASSERT(cons != NULL)
	TRACE(("Connected() %" B_PRId32 " to %" B_PRId32 "", ID(), cons->ID()))

	// Do nothing.
}


void
BMidiLocalProducer::Disconnected(BMidiConsumer* cons)
{
	ASSERT(cons != NULL)
	TRACE(("Disconnected() %" B_PRId32 " from %" B_PRId32 "", ID(), cons->ID()))

	// Do nothing.
}


void
BMidiLocalProducer::SprayData(void* data, size_t length,
	bool atomic, bigtime_t time) const
{
	SprayEvent(data, length, atomic, time);
}


void
BMidiLocalProducer::SprayNoteOff(uchar channel, uchar note,
	uchar velocity, bigtime_t time) const
{
	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");
	}
}


void
BMidiLocalProducer::SprayNoteOn(uchar channel, uchar note,
	uchar velocity, bigtime_t time) const
{
	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");
	}
}


void
BMidiLocalProducer::SprayKeyPressure(uchar channel, uchar note,
	uchar pressure, bigtime_t time) const
{
	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");
	}
}


void
BMidiLocalProducer::SprayControlChange(uchar channel,
	uchar controlNumber, uchar controlValue, bigtime_t time) const
{
	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");
	}
}


void
BMidiLocalProducer::SprayProgramChange(uchar channel,
	uchar programNumber, bigtime_t time) const
{
	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");
	}
}


void
BMidiLocalProducer::SprayChannelPressure(uchar channel,
	uchar pressure, bigtime_t time) const
{
	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");
	}
}


void
BMidiLocalProducer::SprayPitchBend(uchar channel,
	uchar lsb, uchar msb, bigtime_t time) const
{
	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 length, bigtime_t time) const
{
	SprayEvent(data, length, true, time, true);
}


void
BMidiLocalProducer::SpraySystemCommon(uchar status, uchar data1,
	uchar data2, bigtime_t time) const
{
	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");
			len = 0;
	}

	SprayEvent(&data, len, true, time);
}


void
BMidiLocalProducer::SpraySystemRealTime(uchar status,
	bigtime_t time) const
{
	if (status >= B_TIMING_CLOCK)
		SprayEvent(&status, 1, true, time);
	else
		debugger("invalid real time status");
}


void
BMidiLocalProducer::SprayTempoChange(int32 beatsPerMinute,
	bigtime_t time) const
{
	int32 tempo = 60000000 / beatsPerMinute;

	uchar data[6];
	data[0] = 0xFF;
	data[1] = 0x51;
	data[2] = 0x03;
	data[3] = tempo >> 16;
	data[4] = tempo >> 8;
	data[5] = tempo;

	SprayEvent(&data, 6, true, time);
}

//------------------------------------------------------------------------------

void BMidiLocalProducer::_Reserved1() { }
void BMidiLocalProducer::_Reserved2() { }
void BMidiLocalProducer::_Reserved3() { }
void BMidiLocalProducer::_Reserved4() { }
void BMidiLocalProducer::_Reserved5() { }
void BMidiLocalProducer::_Reserved6() { }
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) {
				// add 0xF0 and 0xF7 markers
				buf_size += 2;
			}

			uint8* buffer = (uint8*)malloc(buf_size);
			if (buffer != NULL) {
				*((uint32*)    (buffer +  0)) = fId;
				*((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->fId;

					#ifdef DEBUG
					printf("*** spraying: ");
					for (uint32 t = 0; t < buf_size; ++t)
					{
						printf("%02X, ", buffer[t]);
					}
					printf("\n");
					#endif

					write_port(cons->fPort, 0, buffer, buf_size);
				}				

				free(buffer);
			}
		}

		UnlockProducer();
	}
}