git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@3833 0c269be4-1314-0410-8aa9-9f06e86f4224tags/v1.9.5
@@ -20,12 +20,17 @@ Florian Faber | |||||
Michael Voigt | Michael Voigt | ||||
Torben Hohn | Torben Hohn | ||||
Paul Davis | Paul Davis | ||||
Peter L Jones | |||||
Peter L Jones | |||||
Devin Anderson | |||||
--------------------------- | --------------------------- | ||||
Jackdmp changes log | Jackdmp changes log | ||||
--------------------------- | --------------------------- | ||||
2009-11-30 Stephane Letz <letz@grame.fr> | |||||
* Devin Anderson patch for Jack FFADO driver issues with lost MIDI bytes between periods (and more). | |||||
2009-11-29 Stephane Letz <letz@grame.fr> | 2009-11-29 Stephane Letz <letz@grame.fr> | ||||
* More robust sample rate change handling code in JackCoreAudioDriver. | * More robust sample rate change handling code in JackCoreAudioDriver. | ||||
@@ -0,0 +1,287 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#include <cassert> | |||||
#include <cstring> | |||||
#include <new> | |||||
#include "JackError.h" | |||||
#include "JackPhysicalMidiInput.h" | |||||
namespace Jack { | |||||
JackPhysicalMidiInput::JackPhysicalMidiInput(size_t buffer_size) | |||||
{ | |||||
size_t datum_size = sizeof(jack_midi_data_t); | |||||
assert(buffer_size > 0); | |||||
input_ring = jack_ringbuffer_create((buffer_size + 1) * datum_size); | |||||
if (! input_ring) { | |||||
throw std::bad_alloc(); | |||||
} | |||||
jack_ringbuffer_mlock(input_ring); | |||||
Clear(); | |||||
expected_data_bytes = 0; | |||||
status_byte = 0; | |||||
} | |||||
JackPhysicalMidiInput::~JackPhysicalMidiInput() | |||||
{ | |||||
jack_ringbuffer_free(input_ring); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::Clear() | |||||
{ | |||||
jack_ringbuffer_reset(input_ring); | |||||
buffered_bytes = 0; | |||||
unbuffered_bytes = 0; | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::HandleBufferFailure(size_t unbuffered_bytes, | |||||
size_t total_bytes) | |||||
{ | |||||
jack_error("%d MIDI byte(s) of a %d byte message could not be buffered - " | |||||
"message dropped", unbuffered_bytes, total_bytes); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::HandleIncompleteMessage(size_t bytes) | |||||
{ | |||||
jack_error("Discarding %d MIDI byte(s) - incomplete message (cable " | |||||
"unplugged?)", bytes); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::HandleInvalidStatusByte(jack_midi_data_t status) | |||||
{ | |||||
jack_error("Dropping invalid MIDI status byte '%x'", | |||||
(unsigned int) status); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::HandleUnexpectedSysexEnd(size_t bytes) | |||||
{ | |||||
jack_error("Discarding %d MIDI byte(s) - received sysex end without sysex " | |||||
"start (cable unplugged?)", bytes); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::HandleWriteFailure(size_t bytes) | |||||
{ | |||||
jack_error("Failed to write a %d byte MIDI message to the port buffer", | |||||
bytes); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::Process(jack_nframes_t frames) | |||||
{ | |||||
assert(port_buffer); | |||||
port_buffer->Reset(frames); | |||||
jack_nframes_t current_frame = 0; | |||||
size_t datum_size = sizeof(jack_midi_data_t); | |||||
for (;;) { | |||||
jack_midi_data_t datum; | |||||
current_frame = Receive(&datum, current_frame, frames); | |||||
if (current_frame >= frames) { | |||||
break; | |||||
} | |||||
jack_log("JackPhysicalMidiInput::Process (%d) - Received '%x' byte", | |||||
current_frame, (unsigned int) datum); | |||||
if (datum >= 0xf8) { | |||||
// Realtime | |||||
if (datum == 0xfd) { | |||||
HandleInvalidStatusByte(datum); | |||||
} else { | |||||
jack_log("JackPhysicalMidiInput::Process - Writing realtime " | |||||
"event."); | |||||
WriteByteEvent(current_frame, datum); | |||||
} | |||||
continue; | |||||
} | |||||
if (datum == 0xf7) { | |||||
// Sysex end | |||||
if (status_byte != 0xf0) { | |||||
HandleUnexpectedSysexEnd(buffered_bytes + unbuffered_bytes); | |||||
Clear(); | |||||
expected_data_bytes = 0; | |||||
status_byte = 0; | |||||
} else { | |||||
jack_log("JackPhysicalMidiInput::Process - Writing sysex " | |||||
"event."); | |||||
WriteBufferedSysexEvent(current_frame); | |||||
} | |||||
continue; | |||||
} | |||||
if (datum >= 0x80) { | |||||
// We're handling a non-realtime status byte | |||||
jack_log("JackPhysicalMidiInput::Process - Handling non-realtime " | |||||
"status byte."); | |||||
if (buffered_bytes || unbuffered_bytes) { | |||||
HandleIncompleteMessage(buffered_bytes + unbuffered_bytes + 1); | |||||
Clear(); | |||||
} | |||||
status_byte = datum; | |||||
switch (datum & 0xf0) { | |||||
case 0x80: | |||||
case 0x90: | |||||
case 0xa0: | |||||
case 0xb0: | |||||
case 0xe0: | |||||
// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel | |||||
expected_data_bytes = 2; | |||||
break; | |||||
case 0xc0: | |||||
case 0xd0: | |||||
// Program Change, Channel Pressure | |||||
expected_data_bytes = 1; | |||||
break; | |||||
case 0xf0: | |||||
switch (datum) { | |||||
case 0xf0: | |||||
// Sysex message | |||||
expected_data_bytes = 0; | |||||
break; | |||||
case 0xf1: | |||||
case 0xf3: | |||||
// MTC Quarter frame, Song Select | |||||
expected_data_bytes = 1; | |||||
break; | |||||
case 0xf2: | |||||
// Song Position | |||||
expected_data_bytes = 2; | |||||
break; | |||||
case 0xf4: | |||||
case 0xf5: | |||||
// Undefined | |||||
HandleInvalidStatusByte(datum); | |||||
expected_data_bytes = 0; | |||||
status_byte = 0; | |||||
break; | |||||
case 0xf6: | |||||
// Tune Request | |||||
WriteByteEvent(current_frame, datum); | |||||
expected_data_bytes = 0; | |||||
status_byte = 0; | |||||
} | |||||
break; | |||||
} | |||||
continue; | |||||
} | |||||
// We're handling a data byte | |||||
jack_log("JackPhysicalMidiInput::Process - Buffering data byte."); | |||||
if (jack_ringbuffer_write(input_ring, (const char *) &datum, | |||||
datum_size) == datum_size) { | |||||
buffered_bytes++; | |||||
} else { | |||||
unbuffered_bytes++; | |||||
} | |||||
unsigned long total_bytes = buffered_bytes + unbuffered_bytes; | |||||
assert((! expected_data_bytes) || | |||||
(total_bytes <= expected_data_bytes)); | |||||
if (total_bytes == expected_data_bytes) { | |||||
if (! unbuffered_bytes) { | |||||
jack_log("JackPhysicalMidiInput::Process - Writing buffered " | |||||
"event."); | |||||
WriteBufferedEvent(current_frame); | |||||
} else { | |||||
HandleBufferFailure(unbuffered_bytes, total_bytes); | |||||
Clear(); | |||||
} | |||||
if (status_byte >= 0xf0) { | |||||
expected_data_bytes = 0; | |||||
status_byte = 0; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::WriteBufferedEvent(jack_nframes_t frame) | |||||
{ | |||||
assert(port_buffer && port_buffer->IsValid()); | |||||
size_t space = jack_ringbuffer_read_space(input_ring); | |||||
jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 1); | |||||
if (event) { | |||||
jack_ringbuffer_data_t vector[2]; | |||||
jack_ringbuffer_get_read_vector(input_ring, vector); | |||||
event[0] = status_byte; | |||||
size_t data_length_1 = vector[0].len; | |||||
memcpy(event + 1, vector[0].buf, data_length_1); | |||||
size_t data_length_2 = vector[1].len; | |||||
if (data_length_2) { | |||||
memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); | |||||
} | |||||
} else { | |||||
HandleWriteFailure(space + 1); | |||||
} | |||||
Clear(); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::WriteBufferedSysexEvent(jack_nframes_t frame) | |||||
{ | |||||
assert(port_buffer && port_buffer->IsValid()); | |||||
size_t space = jack_ringbuffer_read_space(input_ring); | |||||
jack_midi_data_t *event = port_buffer->ReserveEvent(frame, space + 2); | |||||
if (event) { | |||||
jack_ringbuffer_data_t vector[2]; | |||||
jack_ringbuffer_get_read_vector(input_ring, vector); | |||||
event[0] = status_byte; | |||||
size_t data_length_1 = vector[0].len; | |||||
memcpy(event + 1, vector[0].buf, data_length_1); | |||||
size_t data_length_2 = vector[1].len; | |||||
if (data_length_2) { | |||||
memcpy(event + data_length_1 + 1, vector[1].buf, data_length_2); | |||||
} | |||||
event[data_length_1 + data_length_2 + 1] = 0xf7; | |||||
} else { | |||||
HandleWriteFailure(space + 2); | |||||
} | |||||
Clear(); | |||||
} | |||||
void | |||||
JackPhysicalMidiInput::WriteByteEvent(jack_nframes_t frame, | |||||
jack_midi_data_t datum) | |||||
{ | |||||
assert(port_buffer && port_buffer->IsValid()); | |||||
jack_midi_data_t *event = port_buffer->ReserveEvent(frame, 1); | |||||
if (event) { | |||||
event[0] = datum; | |||||
} else { | |||||
HandleWriteFailure(1); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,146 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#ifndef __JackPhysicalMidiInput__ | |||||
#define __JackPhysicalMidiInput__ | |||||
#include "JackMidiPort.h" | |||||
#include "ringbuffer.h" | |||||
namespace Jack { | |||||
class JackPhysicalMidiInput { | |||||
private: | |||||
size_t buffered_bytes; | |||||
size_t expected_data_bytes; | |||||
jack_ringbuffer_t *input_ring; | |||||
JackMidiBuffer *port_buffer; | |||||
jack_midi_data_t status_byte; | |||||
size_t unbuffered_bytes; | |||||
void | |||||
Clear(); | |||||
void | |||||
WriteBufferedEvent(jack_nframes_t); | |||||
void | |||||
WriteBufferedSysexEvent(jack_nframes_t); | |||||
void | |||||
WriteByteEvent(jack_nframes_t, jack_midi_data_t); | |||||
protected: | |||||
/** | |||||
* Override to specify how to react when 1 or more bytes of a MIDI | |||||
* message are lost because there wasn't enough room in the input | |||||
* buffer. The first argument is the amount of bytes that couldn't be | |||||
* buffered, and the second argument is the total amount of bytes in | |||||
* the MIDI message. The default implementation calls 'jack_error' | |||||
* with a basic error message. | |||||
*/ | |||||
virtual void | |||||
HandleBufferFailure(size_t, size_t); | |||||
/** | |||||
* Override to specify how to react when a new status byte is received | |||||
* before all of the data bytes in a message are received. The | |||||
* argument is the number of bytes being discarded. The default | |||||
* implementation calls 'jack_error' with a basic error message. | |||||
*/ | |||||
virtual void | |||||
HandleIncompleteMessage(size_t); | |||||
/** | |||||
* Override to specify how to react when an invalid status byte (0xf4, | |||||
* 0xf5, 0xfd) is received. The argument contains the invalid status | |||||
* byte. The default implementation calls 'jack_error' with a basic | |||||
* error message. | |||||
*/ | |||||
virtual void | |||||
HandleInvalidStatusByte(jack_midi_data_t); | |||||
/** | |||||
* Override to specify how to react when a sysex end byte (0xf7) is | |||||
* received without first receiving a sysex start byte (0xf0). The | |||||
* argument contains the amount of bytes that will be discarded. The | |||||
* default implementation calls 'jack_error' with a basic error | |||||
* message. | |||||
*/ | |||||
virtual void | |||||
HandleUnexpectedSysexEnd(size_t); | |||||
/** | |||||
* Override to specify how to react when a MIDI message can not be | |||||
* written to the port buffer. The argument specifies the length of | |||||
* the MIDI message. The default implementation calls 'jack_error' | |||||
* with a basic error message. | |||||
*/ | |||||
virtual void | |||||
HandleWriteFailure(size_t); | |||||
/** | |||||
* This method *must* be overridden to handle receiving MIDI bytes. | |||||
* The first argument is a pointer to the memory location at which the | |||||
* MIDI byte should be stored. The second argument is the last frame | |||||
* at which a MIDI byte was received, except at the beginning of the | |||||
* period when the value is 0. The third argument is the total number | |||||
* of frames in the period. The return value is the frame at which the | |||||
* MIDI byte is received at, or the value of the third argument is no | |||||
* more MIDI bytes can be received in this period. | |||||
*/ | |||||
virtual jack_nframes_t | |||||
Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t) = 0; | |||||
public: | |||||
JackPhysicalMidiInput(size_t buffer_size=1024); | |||||
~JackPhysicalMidiInput(); | |||||
/** | |||||
* Called to process MIDI data during a period. | |||||
*/ | |||||
void | |||||
Process(jack_nframes_t); | |||||
/** | |||||
* Set the MIDI buffer that will receive incoming messages. | |||||
*/ | |||||
inline void | |||||
SetPortBuffer(JackMidiBuffer *port_buffer) | |||||
{ | |||||
this->port_buffer = port_buffer; | |||||
} | |||||
}; | |||||
} | |||||
#endif |
@@ -0,0 +1,321 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#include <cassert> | |||||
#include "JackError.h" | |||||
#include "JackPhysicalMidiOutput.h" | |||||
namespace Jack { | |||||
JackPhysicalMidiOutput::JackPhysicalMidiOutput(size_t non_rt_buffer_size, | |||||
size_t rt_buffer_size) | |||||
{ | |||||
size_t datum_size = sizeof(jack_midi_data_t); | |||||
assert(non_rt_buffer_size > 0); | |||||
assert(rt_buffer_size > 0); | |||||
output_ring = jack_ringbuffer_create((non_rt_buffer_size + 1) * | |||||
datum_size); | |||||
if (! output_ring) { | |||||
throw std::bad_alloc(); | |||||
} | |||||
rt_output_ring = jack_ringbuffer_create((rt_buffer_size + 1) * | |||||
datum_size); | |||||
if (! rt_output_ring) { | |||||
jack_ringbuffer_free(output_ring); | |||||
throw std::bad_alloc(); | |||||
} | |||||
jack_ringbuffer_mlock(output_ring); | |||||
jack_ringbuffer_mlock(rt_output_ring); | |||||
running_status = 0; | |||||
} | |||||
JackPhysicalMidiOutput::~JackPhysicalMidiOutput() | |||||
{ | |||||
jack_ringbuffer_free(output_ring); | |||||
jack_ringbuffer_free(rt_output_ring); | |||||
} | |||||
jack_nframes_t | |||||
JackPhysicalMidiOutput::Advance(jack_nframes_t frame) | |||||
{ | |||||
return frame; | |||||
} | |||||
inline jack_midi_data_t | |||||
JackPhysicalMidiOutput::ApplyRunningStatus(jack_midi_data_t **buffer, | |||||
size_t *size) | |||||
{ | |||||
// Stolen and modified from alsa/midi_pack.h | |||||
jack_midi_data_t status = (*buffer)[0]; | |||||
if ((status >= 0x80) && (status < 0xf0)) { | |||||
if (status == running_status) { | |||||
(*buffer)++; | |||||
(*size)--; | |||||
} else { | |||||
running_status = status; | |||||
} | |||||
} else if (status < 0xf8) { | |||||
running_status = 0; | |||||
} | |||||
return status; | |||||
} | |||||
void | |||||
JackPhysicalMidiOutput::HandleEventLoss(JackMidiEvent *event) | |||||
{ | |||||
jack_error("%d byte MIDI event lost", event->size); | |||||
} | |||||
void | |||||
JackPhysicalMidiOutput::Process(jack_nframes_t frames) | |||||
{ | |||||
assert(port_buffer); | |||||
jack_nframes_t current_frame = Advance(0); | |||||
jack_nframes_t current_midi_event = 0; | |||||
jack_midi_data_t datum; | |||||
size_t datum_size = sizeof(jack_midi_data_t); | |||||
JackMidiEvent *midi_event; | |||||
jack_midi_data_t *midi_event_buffer; | |||||
size_t midi_event_size; | |||||
jack_nframes_t midi_events = port_buffer->event_count; | |||||
// First, send any realtime MIDI data that's left from last cycle. | |||||
if ((current_frame < frames) && | |||||
jack_ringbuffer_read_space(rt_output_ring)) { | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sending buffered " | |||||
"realtime data from last period.", current_frame); | |||||
current_frame = SendBufferedData(rt_output_ring, current_frame, | |||||
frames); | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", current_frame); | |||||
} | |||||
// Iterate through the events in this cycle. | |||||
for (; (current_midi_event < midi_events) && (current_frame < frames); | |||||
current_midi_event++) { | |||||
// Once we're inside this loop, we know that the realtime buffer | |||||
// is empty. As long as we don't find a realtime message, we can | |||||
// concentrate on sending non-realtime data. | |||||
midi_event = &(port_buffer->events[current_midi_event]); | |||||
jack_nframes_t midi_event_time = midi_event->time; | |||||
midi_event_buffer = midi_event->GetData(port_buffer); | |||||
midi_event_size = midi_event->size; | |||||
datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); | |||||
if (current_frame < midi_event_time) { | |||||
// We have time before this event is scheduled to be sent. | |||||
// Send data in the non-realtime buffer. | |||||
if (jack_ringbuffer_read_space(output_ring)) { | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " | |||||
"buffered non-realtime data from last period.", | |||||
current_frame); | |||||
current_frame = SendBufferedData(output_ring, current_frame, | |||||
midi_event_time); | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sent", | |||||
current_frame); | |||||
} | |||||
if (current_frame < midi_event_time) { | |||||
// We _still_ have time before this event is scheduled to | |||||
// be sent. Let's send as much of this event as we can | |||||
// (save for one byte, which will need to be sent at or | |||||
// after its scheduled time). First though, we need to | |||||
// make sure that we can buffer this data if we need to. | |||||
// Otherwise, we might start sending a message that we | |||||
// can't finish. | |||||
if (midi_event_size > 1) { | |||||
if (jack_ringbuffer_write_space(output_ring) < | |||||
((midi_event_size - 1) * datum_size)) { | |||||
HandleEventLoss(midi_event); | |||||
continue; | |||||
} | |||||
// Send as much of the event as possible (save for one | |||||
// byte). | |||||
do { | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - " | |||||
"Sending unbuffered event byte early.", | |||||
current_frame); | |||||
current_frame = Send(current_frame, | |||||
*midi_event_buffer); | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - " | |||||
"Sent.", current_frame); | |||||
midi_event_buffer++; | |||||
midi_event_size--; | |||||
if (current_frame >= midi_event_time) { | |||||
// The event we're processing must be a | |||||
// non-realtime event. It has more than one | |||||
// byte. | |||||
goto buffer_non_realtime_data; | |||||
} | |||||
} while (midi_event_size > 1); | |||||
} | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Advancing to " | |||||
">= %d", current_frame, midi_event_time); | |||||
current_frame = Advance(midi_event_time); | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Advanced.", | |||||
current_frame); | |||||
} | |||||
} | |||||
// If the event is realtime, then we'll send the event now. | |||||
// Otherwise, we attempt to put the rest of the event bytes in the | |||||
// non-realtime buffer. | |||||
if (datum >= 0xf8) { | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sending " | |||||
"unbuffered realtime event.", current_frame); | |||||
current_frame = Send(current_frame, datum); | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", | |||||
current_frame); | |||||
} else if (jack_ringbuffer_write_space(output_ring) >= | |||||
(midi_event_size * datum_size)) { | |||||
buffer_non_realtime_data: | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Buffering %d " | |||||
"byte(s) of non-realtime data.", current_frame, | |||||
midi_event_size); | |||||
jack_ringbuffer_write(output_ring, | |||||
(const char *) midi_event_buffer, | |||||
midi_event_size); | |||||
} else { | |||||
HandleEventLoss(midi_event); | |||||
} | |||||
} | |||||
if (current_frame < frames) { | |||||
// If we have time left to send data, then we know that all of the | |||||
// data in the realtime buffer has been sent, and that all of the | |||||
// non-realtime messages have either been sent, or buffered. We | |||||
// use whatever time is left to send data in the non-realtime | |||||
// buffer. | |||||
if (jack_ringbuffer_read_space(output_ring)) { | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - All events " | |||||
"processed. Sending buffered non-realtime data.", | |||||
current_frame); | |||||
current_frame = SendBufferedData(output_ring, current_frame, | |||||
frames); | |||||
jack_log("JackPhysicalMidiOutput::Process (%d) - Sent.", | |||||
current_frame); | |||||
} | |||||
} else { | |||||
// Since we have no time left, we need to put all remaining midi | |||||
// events in their appropriate buffers, and send them next period. | |||||
for (current_midi_event++; current_midi_event < midi_events; | |||||
current_midi_event++) { | |||||
midi_event = &(port_buffer->events[current_midi_event]); | |||||
midi_event_buffer = midi_event->GetData(port_buffer); | |||||
midi_event_size = midi_event->size; | |||||
datum = ApplyRunningStatus(&midi_event_buffer, &midi_event_size); | |||||
if (datum >= 0xf8) { | |||||
// Realtime. | |||||
if (jack_ringbuffer_write_space(rt_output_ring) >= | |||||
datum_size) { | |||||
jack_log("JackPhysicalMidiOutput::Process - Buffering " | |||||
"realtime event for next period."); | |||||
jack_ringbuffer_write(rt_output_ring, | |||||
(const char *) &datum, datum_size); | |||||
continue; | |||||
} | |||||
} else { | |||||
// Non-realtime. | |||||
if (jack_ringbuffer_write_space(output_ring) >= | |||||
(midi_event_size * datum_size)) { | |||||
jack_log("JackPhysicalMidiOutput::Process - Buffering " | |||||
"non-realtime event for next period."); | |||||
jack_ringbuffer_write(output_ring, | |||||
(const char *) midi_event_buffer, | |||||
midi_event_size * datum_size); | |||||
continue; | |||||
} | |||||
} | |||||
HandleEventLoss(midi_event); | |||||
} | |||||
} | |||||
} | |||||
jack_nframes_t | |||||
JackPhysicalMidiOutput::SendBufferedData(jack_ringbuffer_t *buffer, | |||||
jack_nframes_t current_frame, | |||||
jack_nframes_t boundary) | |||||
{ | |||||
assert(buffer); | |||||
assert(current_frame < boundary); | |||||
size_t datum_size = sizeof(jack_midi_data_t); | |||||
size_t data_length = jack_ringbuffer_read_space(buffer) / datum_size; | |||||
for (size_t i = 0; i < data_length; i++) { | |||||
jack_midi_data_t datum; | |||||
jack_ringbuffer_read(buffer, (char *) &datum, datum_size); | |||||
current_frame = Send(current_frame, datum); | |||||
if (current_frame >= boundary) { | |||||
break; | |||||
} | |||||
} | |||||
return current_frame; | |||||
} | |||||
} |
@@ -0,0 +1,118 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#ifndef __JackPhysicalMidiOutput__ | |||||
#define __JackPhysicalMidiOutput__ | |||||
#include "JackMidiPort.h" | |||||
#include "ringbuffer.h" | |||||
namespace Jack { | |||||
class JackPhysicalMidiOutput { | |||||
private: | |||||
jack_midi_data_t | |||||
ApplyRunningStatus(jack_midi_data_t **, size_t *); | |||||
jack_ringbuffer_t *output_ring; | |||||
JackMidiBuffer *port_buffer; | |||||
jack_ringbuffer_t *rt_output_ring; | |||||
jack_midi_data_t running_status; | |||||
protected: | |||||
/** | |||||
* Override to specify the next frame at which a midi byte can be sent. | |||||
* The returned frame must be greater than or equal to the frame | |||||
* argument. The default returns the frame passed to it. | |||||
*/ | |||||
virtual jack_nframes_t | |||||
Advance(jack_nframes_t); | |||||
/** | |||||
* Override to customize how to react when a MIDI event can't be | |||||
* buffered and can't be sent immediately. The default calls | |||||
* 'jack_error' and specifies the number of bytes lost. | |||||
*/ | |||||
virtual void | |||||
HandleEventLoss(JackMidiEvent *); | |||||
/** | |||||
* This method *must* be overridden to specify what happens when a MIDI | |||||
* byte is sent at the specfied frame. The frame argument specifies | |||||
* the frame at which the MIDI byte should be sent, and the second | |||||
* argument specifies the byte itself. The return value is the next | |||||
* frame at which a MIDI byte can be sent, and must be greater than or | |||||
* equal to the frame argument. | |||||
*/ | |||||
virtual jack_nframes_t | |||||
Send(jack_nframes_t, jack_midi_data_t) = 0; | |||||
/** | |||||
* Override to optimize behavior when sending MIDI data that's in the | |||||
* ringbuffer. The first frame argument is the current frame, and the | |||||
* second frame argument is the boundary frame. The function returns | |||||
* the next frame at which MIDI data can be sent, regardless of whether | |||||
* or not the boundary is reached. The default implementation calls | |||||
* 'Send' with each byte in the ringbuffer until either the ringbuffer | |||||
* is empty, or a frame beyond the boundary frame is returned by | |||||
* 'Send'. | |||||
*/ | |||||
virtual jack_nframes_t | |||||
SendBufferedData(jack_ringbuffer_t *, jack_nframes_t, jack_nframes_t); | |||||
public: | |||||
/** | |||||
* The non-realtime buffer size and the realtime buffer size are both | |||||
* optional arguments. | |||||
*/ | |||||
JackPhysicalMidiOutput(size_t non_rt_buffer_size=1024, | |||||
size_t rt_buffer_size=64); | |||||
~JackPhysicalMidiOutput(); | |||||
/** | |||||
* Called to process MIDI data during a period. | |||||
*/ | |||||
void | |||||
Process(jack_nframes_t); | |||||
/** | |||||
* Set the MIDI buffer that will contain the outgoing MIDI messages. | |||||
*/ | |||||
inline void | |||||
SetPortBuffer(JackMidiBuffer *port_buffer) | |||||
{ | |||||
this->port_buffer = port_buffer; | |||||
} | |||||
}; | |||||
} | |||||
#endif |
@@ -131,6 +131,8 @@ def build(bld): | |||||
'JackNetInterface.cpp', | 'JackNetInterface.cpp', | ||||
'JackArgParser.cpp', | 'JackArgParser.cpp', | ||||
'JackDummyDriver.cpp', | 'JackDummyDriver.cpp', | ||||
'JackPhysicalMidiInput.cpp', | |||||
'JackPhysicalMidiOutput.cpp', | |||||
] | ] | ||||
if bld.env['IS_LINUX']: | if bld.env['IS_LINUX']: | ||||
@@ -2,6 +2,7 @@ | |||||
Copyright (C) 2001 Paul Davis | Copyright (C) 2001 Paul Davis | ||||
Copyright (C) 2004 Grame | Copyright (C) 2004 Grame | ||||
Copyright (C) 2007 Pieter Palmers | Copyright (C) 2007 Pieter Palmers | ||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | This program is free software; you can redistribute it and/or modify | ||||
it under the terms of the GNU General Public License as published by | it under the terms of the GNU General Public License as published by | ||||
@@ -35,6 +36,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
#include <string.h> | #include <string.h> | ||||
#include "JackFFADODriver.h" | #include "JackFFADODriver.h" | ||||
#include "JackFFADOMidiInput.h" | |||||
#include "JackFFADOMidiOutput.h" | |||||
#include "JackEngineControl.h" | #include "JackEngineControl.h" | ||||
#include "JackClientControl.h" | #include "JackClientControl.h" | ||||
#include "JackPort.h" | #include "JackPort.h" | ||||
@@ -91,29 +94,14 @@ JackFFADODriver::ffado_driver_read (ffado_driver_t * driver, jack_nframes_t nfra | |||||
/* process the midi data */ | /* process the midi data */ | ||||
for (chn = 0; chn < driver->capture_nchannels; chn++) { | for (chn = 0; chn < driver->capture_nchannels; chn++) { | ||||
if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { | if (driver->capture_channels[chn].stream_type == ffado_stream_type_midi) { | ||||
jack_nframes_t i; | |||||
int done; | |||||
uint32_t *midi_buffer = driver->capture_channels[chn].midi_buffer; | |||||
midi_unpack_t *midi_unpack = &driver->capture_channels[chn].midi_unpack; | |||||
buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fCapturePortList[chn], nframes); | |||||
jack_midi_clear_buffer(buf); | |||||
/* if the returned buffer is invalid, discard the midi data */ | |||||
if (!buf) continue; | |||||
/* else unpack | |||||
note that libffado guarantees that midi bytes are on 8-byte aligned indexes | |||||
*/ | |||||
for (i = 0; i < nframes; i += 8) { | |||||
if (midi_buffer[i] & 0xFF000000) { | |||||
done = midi_unpack_buf(midi_unpack, (unsigned char *)(midi_buffer + i), 1, buf, i); | |||||
if (done != 1) { | |||||
printError("buffer overflow in channel %d\n", chn); | |||||
break; | |||||
} | |||||
printMessage("MIDI IN: %08X (i=%d)", midi_buffer[i], i); | |||||
} | |||||
JackFFADOMidiInput *midi_input = (JackFFADOMidiInput *) driver->capture_channels[chn].midi_input; | |||||
JackMidiBuffer *buffer = (JackMidiBuffer *) fGraphManager->GetBuffer(fCapturePortList[chn], nframes); | |||||
if (! buffer) { | |||||
continue; | |||||
} | } | ||||
midi_input->SetInputBuffer(driver->capture_channels[chn].midi_buffer); | |||||
midi_input->SetPortBuffer(buffer); | |||||
midi_input->Process(nframes); | |||||
} | } | ||||
} | } | ||||
@@ -146,80 +134,20 @@ JackFFADODriver::ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nfr | |||||
ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buf)); | ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(buf)); | ||||
ffado_streaming_playback_stream_onoff(driver->dev, chn, 1); | ffado_streaming_playback_stream_onoff(driver->dev, chn, 1); | ||||
} else if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { | } else if (driver->playback_channels[chn].stream_type == ffado_stream_type_midi) { | ||||
jack_nframes_t nevents; | |||||
jack_nframes_t i; | |||||
midi_pack_t *midi_pack = &driver->playback_channels[chn].midi_pack; | |||||
uint32_t *midi_buffer = driver->playback_channels[chn].midi_buffer; | uint32_t *midi_buffer = driver->playback_channels[chn].midi_buffer; | ||||
buf = (jack_default_audio_sample_t*)fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); | |||||
jack_nframes_t min_next_pos = 0; | |||||
memset(midi_buffer, 0, nframes * sizeof(uint32_t)); | memset(midi_buffer, 0, nframes * sizeof(uint32_t)); | ||||
buf = (jack_default_audio_sample_t *) fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes); | |||||
ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(midi_buffer)); | ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(midi_buffer)); | ||||
/* if the returned buffer is invalid, continue */ | /* if the returned buffer is invalid, continue */ | ||||
if (!buf) { | if (!buf) { | ||||
ffado_streaming_playback_stream_onoff(driver->dev, chn, 0); | ffado_streaming_playback_stream_onoff(driver->dev, chn, 0); | ||||
continue; | continue; | ||||
} | } | ||||
ffado_streaming_playback_stream_onoff(driver->dev, chn, 1); | ffado_streaming_playback_stream_onoff(driver->dev, chn, 1); | ||||
// check if we still have to process bytes from the previous period | |||||
if (driver->playback_channels[chn].nb_overflow_bytes) { | |||||
printMessage("have to process %d bytes from previous period", driver->playback_channels[chn].nb_overflow_bytes); | |||||
} | |||||
for (i = 0; i < driver->playback_channels[chn].nb_overflow_bytes; ++i) { | |||||
midi_buffer[min_next_pos] = 0x01000000 | (driver->playback_channels[chn].overflow_buffer[i] & 0xFF); | |||||
min_next_pos += 8; | |||||
} | |||||
driver->playback_channels[chn].nb_overflow_bytes = 0; | |||||
// process the events in this period | |||||
nevents = jack_midi_get_event_count(buf); | |||||
//if (nevents) | |||||
// printMessage("MIDI: %d events in ch %d", nevents, chn); | |||||
for (i = 0; i < nevents; ++i) { | |||||
size_t j; | |||||
jack_midi_event_t event; | |||||
jack_midi_event_get(&event, buf, i); | |||||
midi_pack_event(midi_pack, &event); | |||||
// floor the initial position to be a multiple of 8 | |||||
jack_nframes_t pos = event.time & 0xFFFFFFF8; | |||||
for (j = 0; j < event.size; j++) { | |||||
// make sure we don't overwrite a previous byte | |||||
while (pos < min_next_pos && pos < nframes) { | |||||
pos += 8; | |||||
printMessage("have to correct pos to %d", pos); | |||||
} | |||||
if (pos >= nframes) { | |||||
unsigned int f; | |||||
printMessage("midi message crosses period boundary"); | |||||
driver->playback_channels[chn].nb_overflow_bytes = event.size - j; | |||||
if (driver->playback_channels[chn].nb_overflow_bytes > MIDI_OVERFLOW_BUFFER_SIZE) { | |||||
printError("too much midi bytes cross period boundary"); | |||||
driver->playback_channels[chn].nb_overflow_bytes = MIDI_OVERFLOW_BUFFER_SIZE; | |||||
} | |||||
// save the bytes that still have to be transmitted in the next period | |||||
for (f = 0; f < driver->playback_channels[chn].nb_overflow_bytes; f++) { | |||||
driver->playback_channels[chn].overflow_buffer[f] = event.buffer[j+f]; | |||||
} | |||||
// exit since we can't transmit anything anymore. | |||||
// the rate should be controlled | |||||
if (i < nevents - 1) { | |||||
printError("%d midi events lost due to period crossing", nevents - i - 1); | |||||
} | |||||
break; | |||||
} else { | |||||
midi_buffer[pos] = 0x01000000 | (event.buffer[j] & 0xFF); | |||||
pos += 8; | |||||
min_next_pos = pos; | |||||
} | |||||
} | |||||
//printMessage("MIDI: sent %d-byte event at %ld", (int)event.size, (long)event.time); | |||||
} | |||||
JackFFADOMidiOutput *midi_output = (JackFFADOMidiOutput *) driver->playback_channels[chn].midi_output; | |||||
midi_output->SetPortBuffer((JackMidiBuffer *) buf); | |||||
midi_output->SetOutputBuffer(midi_buffer); | |||||
midi_output->Process(nframes); | |||||
} else { // always have a valid buffer | } else { // always have a valid buffer | ||||
ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(driver->nullbuffer)); | ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(driver->nullbuffer)); | ||||
@@ -541,9 +469,7 @@ int JackFFADODriver::Attach() | |||||
printError(" cannot enable port %s", buf); | printError(" cannot enable port %s", buf); | ||||
} | } | ||||
// setup midi unpacker | |||||
midi_unpack_init(&driver->capture_channels[chn].midi_unpack); | |||||
midi_unpack_reset(&driver->capture_channels[chn].midi_unpack); | |||||
driver->capture_channels[chn].midi_input = new JackFFADOMidiInput(); | |||||
// setup the midi buffer | // setup the midi buffer | ||||
driver->capture_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); | driver->capture_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); | ||||
@@ -616,9 +542,13 @@ int JackFFADODriver::Attach() | |||||
if (ffado_streaming_playback_stream_onoff(driver->dev, chn, 0)) { | if (ffado_streaming_playback_stream_onoff(driver->dev, chn, 0)) { | ||||
printError(" cannot enable port %s", buf); | printError(" cannot enable port %s", buf); | ||||
} | } | ||||
// setup midi packer | |||||
midi_pack_reset(&driver->playback_channels[chn].midi_pack); | |||||
// setup the midi buffer | // setup the midi buffer | ||||
// This constructor optionally accepts arguments for the | |||||
// non-realtime buffer size and the realtime buffer size. Ideally, | |||||
// these would become command-line options for the FFADO driver. | |||||
driver->playback_channels[chn].midi_output = new JackFFADOMidiOutput(); | |||||
driver->playback_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); | driver->playback_channels[chn].midi_buffer = (uint32_t *)calloc(driver->period_size, sizeof(uint32_t)); | ||||
port = fGraphManager->GetPort(port_index); | port = fGraphManager->GetPort(port_index); | ||||
@@ -658,12 +588,16 @@ int JackFFADODriver::Detach() | |||||
for (chn = 0; chn < driver->capture_nchannels; chn++) { | for (chn = 0; chn < driver->capture_nchannels; chn++) { | ||||
if (driver->capture_channels[chn].midi_buffer) | if (driver->capture_channels[chn].midi_buffer) | ||||
free(driver->capture_channels[chn].midi_buffer); | free(driver->capture_channels[chn].midi_buffer); | ||||
if (driver->capture_channels[chn].midi_input) | |||||
delete ((JackFFADOMidiInput *) (driver->capture_channels[chn].midi_input)); | |||||
} | } | ||||
free(driver->capture_channels); | free(driver->capture_channels); | ||||
for (chn = 0; chn < driver->playback_nchannels; chn++) { | for (chn = 0; chn < driver->playback_nchannels; chn++) { | ||||
if (driver->playback_channels[chn].midi_buffer) | if (driver->playback_channels[chn].midi_buffer) | ||||
free(driver->playback_channels[chn].midi_buffer); | free(driver->playback_channels[chn].midi_buffer); | ||||
if (driver->playback_channels[chn].midi_output) | |||||
delete ((JackFFADOMidiOutput *) (driver->playback_channels[chn].midi_output)); | |||||
} | } | ||||
free(driver->playback_channels); | free(driver->playback_channels); | ||||
@@ -0,0 +1,59 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#include <cassert> | |||||
#include "JackFFADOMidiInput.h" | |||||
namespace Jack { | |||||
JackFFADOMidiInput::JackFFADOMidiInput(size_t buffer_size): | |||||
JackPhysicalMidiInput(buffer_size) | |||||
{ | |||||
new_period = true; | |||||
} | |||||
JackFFADOMidiInput::~JackFFADOMidiInput() | |||||
{ | |||||
// Empty | |||||
} | |||||
jack_nframes_t | |||||
JackFFADOMidiInput::Receive(jack_midi_data_t *datum, | |||||
jack_nframes_t current_frame, | |||||
jack_nframes_t total_frames) | |||||
{ | |||||
assert(input_buffer); | |||||
if (! new_period) { | |||||
current_frame += 8; | |||||
} else { | |||||
new_period = false; | |||||
} | |||||
for (; current_frame < total_frames; current_frame += 8) { | |||||
uint32_t data = input_buffer[current_frame]; | |||||
if (data & 0xff000000) { | |||||
*datum = (jack_midi_data_t) (data & 0xff); | |||||
return current_frame; | |||||
} | |||||
} | |||||
new_period = true; | |||||
return total_frames; | |||||
} | |||||
} |
@@ -0,0 +1,54 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#ifndef __JackFFADOMidiInput__ | |||||
#define __JackFFADOMidiInput__ | |||||
#include "JackPhysicalMidiInput.h" | |||||
namespace Jack { | |||||
class JackFFADOMidiInput: public JackPhysicalMidiInput { | |||||
private: | |||||
uint32_t *input_buffer; | |||||
bool new_period; | |||||
protected: | |||||
jack_nframes_t | |||||
Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t); | |||||
public: | |||||
JackFFADOMidiInput(size_t buffer_size=1024); | |||||
~JackFFADOMidiInput(); | |||||
inline void | |||||
SetInputBuffer(uint32_t *input_buffer) | |||||
{ | |||||
this->input_buffer = input_buffer; | |||||
} | |||||
}; | |||||
} | |||||
#endif |
@@ -0,0 +1,60 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#include <cassert> | |||||
#include "JackError.h" | |||||
#include "JackFFADOMidiOutput.h" | |||||
namespace Jack { | |||||
JackFFADOMidiOutput::JackFFADOMidiOutput(size_t non_rt_buffer_size, | |||||
size_t rt_buffer_size): | |||||
JackPhysicalMidiOutput(non_rt_buffer_size, rt_buffer_size) | |||||
{ | |||||
// Empty | |||||
} | |||||
JackFFADOMidiOutput::~JackFFADOMidiOutput() | |||||
{ | |||||
// Empty | |||||
} | |||||
jack_nframes_t | |||||
JackFFADOMidiOutput::Advance(jack_nframes_t current_frame) | |||||
{ | |||||
if (current_frame % 8) { | |||||
current_frame = (current_frame & (~ ((jack_nframes_t) 7))) + 8; | |||||
} | |||||
return current_frame; | |||||
} | |||||
jack_nframes_t | |||||
JackFFADOMidiOutput::Send(jack_nframes_t current_frame, jack_midi_data_t datum) | |||||
{ | |||||
assert(output_buffer); | |||||
jack_log("JackFFADOMidiOutput::Send (%d) - Sending '%x' byte.", | |||||
current_frame, (unsigned int) datum); | |||||
output_buffer[current_frame] = 0x01000000 | ((uint32_t) datum); | |||||
return current_frame + 8; | |||||
} | |||||
} |
@@ -0,0 +1,57 @@ | |||||
/* | |||||
Copyright (C) 2009 Devin Anderson | |||||
This program is free software; you can redistribute it and/or modify | |||||
it under the terms of the GNU Lesser General Public License as published by | |||||
the Free Software Foundation; either version 2.1 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU Lesser General Public License for more details. | |||||
You should have received a copy of the GNU Lesser General Public License | |||||
along with this program; if not, write to the Free Software | |||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |||||
*/ | |||||
#ifndef __JackFFADOMidiOutput__ | |||||
#define __JackFFADOMidiOutput__ | |||||
#include "JackPhysicalMidiOutput.h" | |||||
namespace Jack { | |||||
class JackFFADOMidiOutput: public JackPhysicalMidiOutput { | |||||
private: | |||||
uint32_t *output_buffer; | |||||
protected: | |||||
jack_nframes_t | |||||
Advance(jack_nframes_t); | |||||
jack_nframes_t | |||||
Send(jack_nframes_t, jack_midi_data_t); | |||||
public: | |||||
JackFFADOMidiOutput(size_t non_rt_buffer_size=1024, | |||||
size_t rt_buffer_size=64); | |||||
~JackFFADOMidiOutput(); | |||||
inline void | |||||
SetOutputBuffer(uint32_t *output_buffer) | |||||
{ | |||||
this->output_buffer = output_buffer; | |||||
} | |||||
}; | |||||
} | |||||
#endif |
@@ -7,6 +7,7 @@ | |||||
* http://www.jackaudio.org | * http://www.jackaudio.org | ||||
* | * | ||||
* Copyright (C) 2005-2007 Pieter Palmers | * Copyright (C) 2005-2007 Pieter Palmers | ||||
* Copyright (C) 2009 Devin Anderson | |||||
* | * | ||||
* adapted for JackMP by Pieter Palmers | * adapted for JackMP by Pieter Palmers | ||||
* | * | ||||
@@ -51,9 +52,7 @@ | |||||
#include <types.h> | #include <types.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <jack/midiport.h> | |||||
#include "../alsa/midi_pack.h" | |||||
#include "../alsa/midi_unpack.h" | |||||
//#include <jack/midiport.h> | |||||
// debug print control flags | // debug print control flags | ||||
#define DEBUG_LEVEL_BUFFERS (1<<0) | #define DEBUG_LEVEL_BUFFERS (1<<0) | ||||
@@ -143,21 +142,16 @@ struct _ffado_jack_settings | |||||
typedef struct _ffado_capture_channel | typedef struct _ffado_capture_channel | ||||
{ | { | ||||
ffado_streaming_stream_type stream_type; | ffado_streaming_stream_type stream_type; | ||||
midi_unpack_t midi_unpack; | |||||
uint32_t *midi_buffer; | uint32_t *midi_buffer; | ||||
void *midi_input; | |||||
} | } | ||||
ffado_capture_channel_t; | ffado_capture_channel_t; | ||||
#define MIDI_OVERFLOW_BUFFER_SIZE 4 | |||||
typedef struct _ffado_playback_channel | typedef struct _ffado_playback_channel | ||||
{ | { | ||||
ffado_streaming_stream_type stream_type; | ffado_streaming_stream_type stream_type; | ||||
midi_pack_t midi_pack; | |||||
uint32_t *midi_buffer; | uint32_t *midi_buffer; | ||||
// to hold the midi bytes that couldn't be transferred | |||||
// during the previous period | |||||
char overflow_buffer[MIDI_OVERFLOW_BUFFER_SIZE]; | |||||
unsigned int nb_overflow_bytes; | |||||
void *midi_output; | |||||
} | } | ||||
ffado_playback_channel_t; | ffado_playback_channel_t; | ||||
@@ -55,6 +55,13 @@ def build(bld): | |||||
'alsa/ice1712.c' | 'alsa/ice1712.c' | ||||
] | ] | ||||
ffado_driver_src = ['firewire/JackFFADODriver.cpp', | |||||
'firewire/JackFFADOMidiInput.cpp', | |||||
'firewire/JackFFADOMidiOutput.cpp', | |||||
'../common/JackPhysicalMidiInput.cpp', | |||||
'../common/JackPhysicalMidiOutput.cpp' | |||||
] | |||||
if bld.env['BUILD_DRIVER_ALSA'] == True: | if bld.env['BUILD_DRIVER_ALSA'] == True: | ||||
create_jack_driver_obj(bld, 'alsa', alsa_driver_src, "ALSA") | create_jack_driver_obj(bld, 'alsa', alsa_driver_src, "ALSA") | ||||
@@ -62,7 +69,7 @@ def build(bld): | |||||
create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB") | create_jack_driver_obj(bld, 'freebob', 'freebob/JackFreebobDriver.cpp', "LIBFREEBOB") | ||||
if bld.env['BUILD_DRIVER_FFADO'] == True: | if bld.env['BUILD_DRIVER_FFADO'] == True: | ||||
create_jack_driver_obj(bld, 'firewire', 'firewire/JackFFADODriver.cpp', "LIBFFADO") | |||||
create_jack_driver_obj(bld, 'firewire', ffado_driver_src, "LIBFFADO") | |||||
create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') | create_jack_driver_obj(bld, 'net', '../common/JackNetDriver.cpp') | ||||