Browse Source

Devin Anderson patch for Jack FFADO driver issues with lost MIDI bytes between periods (and more).

git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@3833 0c269be4-1314-0410-8aa9-9f06e86f4224
tags/v1.9.5
sletz 15 years ago
parent
commit
23a88af253
13 changed files with 1148 additions and 104 deletions
  1. +6
    -1
      ChangeLog
  2. +287
    -0
      common/JackPhysicalMidiInput.cpp
  3. +146
    -0
      common/JackPhysicalMidiInput.h
  4. +321
    -0
      common/JackPhysicalMidiOutput.cpp
  5. +118
    -0
      common/JackPhysicalMidiOutput.h
  6. +2
    -0
      common/wscript
  7. +26
    -92
      linux/firewire/JackFFADODriver.cpp
  8. +59
    -0
      linux/firewire/JackFFADOMidiInput.cpp
  9. +54
    -0
      linux/firewire/JackFFADOMidiInput.h
  10. +60
    -0
      linux/firewire/JackFFADOMidiOutput.cpp
  11. +57
    -0
      linux/firewire/JackFFADOMidiOutput.h
  12. +4
    -10
      linux/firewire/ffado_driver.h
  13. +8
    -1
      linux/wscript

+ 6
- 1
ChangeLog View File

@@ -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.


+ 287
- 0
common/JackPhysicalMidiInput.cpp View File

@@ -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);
}
}

}

+ 146
- 0
common/JackPhysicalMidiInput.h View File

@@ -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

+ 321
- 0
common/JackPhysicalMidiOutput.cpp View File

@@ -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;
}

}

+ 118
- 0
common/JackPhysicalMidiOutput.h View File

@@ -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

+ 2
- 0
common/wscript View File

@@ -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']:


+ 26
- 92
linux/firewire/JackFFADODriver.cpp View File

@@ -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);




+ 59
- 0
linux/firewire/JackFFADOMidiInput.cpp View File

@@ -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;
}

}

+ 54
- 0
linux/firewire/JackFFADOMidiInput.h View File

@@ -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

+ 60
- 0
linux/firewire/JackFFADOMidiOutput.cpp View File

@@ -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;
}

}

+ 57
- 0
linux/firewire/JackFFADOMidiOutput.h View File

@@ -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

+ 4
- 10
linux/firewire/ffado_driver.h View File

@@ -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;




+ 8
- 1
linux/wscript View File

@@ -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')




Loading…
Cancel
Save