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 14 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
Torben Hohn
Paul Davis
Peter L Jones
Peter L Jones
Devin Anderson
---------------------------
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>
* 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',
'JackArgParser.cpp',
'JackDummyDriver.cpp',
'JackPhysicalMidiInput.cpp',
'JackPhysicalMidiOutput.cpp',
]

if bld.env['IS_LINUX']:


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

@@ -2,6 +2,7 @@
Copyright (C) 2001 Paul Davis
Copyright (C) 2004 Grame
Copyright (C) 2007 Pieter Palmers
Copyright (C) 2009 Devin Anderson

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
@@ -35,6 +36,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#include <string.h>

#include "JackFFADODriver.h"
#include "JackFFADOMidiInput.h"
#include "JackFFADOMidiOutput.h"
#include "JackEngineControl.h"
#include "JackClientControl.h"
#include "JackPort.h"
@@ -91,29 +94,14 @@ JackFFADODriver::ffado_driver_read (ffado_driver_t * driver, jack_nframes_t nfra
/* process the midi data */
for (chn = 0; chn < driver->capture_nchannels; chn++) {
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_playback_stream_onoff(driver->dev, chn, 1);
} 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;
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));
buf = (jack_default_audio_sample_t *) fGraphManager->GetBuffer(fPlaybackPortList[chn], nframes);
ffado_streaming_set_playback_stream_buffer(driver->dev, chn, (char *)(midi_buffer));

/* if the returned buffer is invalid, continue */
if (!buf) {
ffado_streaming_playback_stream_onoff(driver->dev, chn, 0);
continue;
}
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
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);
}

// 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
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)) {
printError(" cannot enable port %s", buf);
}
// setup midi packer
midi_pack_reset(&driver->playback_channels[chn].midi_pack);
// 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));

port = fGraphManager->GetPort(port_index);
@@ -658,12 +588,16 @@ int JackFFADODriver::Detach()
for (chn = 0; chn < driver->capture_nchannels; chn++) {
if (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);

for (chn = 0; chn < driver->playback_nchannels; chn++) {
if (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);



+ 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
*
* Copyright (C) 2005-2007 Pieter Palmers
* Copyright (C) 2009 Devin Anderson
*
* adapted for JackMP by Pieter Palmers
*
@@ -51,9 +52,7 @@
#include <types.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
#define DEBUG_LEVEL_BUFFERS (1<<0)
@@ -143,21 +142,16 @@ struct _ffado_jack_settings
typedef struct _ffado_capture_channel
{
ffado_streaming_stream_type stream_type;
midi_unpack_t midi_unpack;
uint32_t *midi_buffer;
void *midi_input;
}
ffado_capture_channel_t;

#define MIDI_OVERFLOW_BUFFER_SIZE 4
typedef struct _ffado_playback_channel
{
ffado_streaming_stream_type stream_type;
midi_pack_t midi_pack;
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;



+ 8
- 1
linux/wscript View File

@@ -55,6 +55,13 @@ def build(bld):
'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:
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")

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



Loading…
Cancel
Save