Browse Source

Added MIDI queues, FFADO objects, etc. - see 'http://trac.jackaudio.org/ticket/187' for more details

tags/1.9.8
Devin Anderson 14 years ago
parent
commit
4bd1bef24c
41 changed files with 3082 additions and 1060 deletions
  1. +94
    -0
      common/JackMidiAsyncQueue.cpp
  2. +92
    -0
      common/JackMidiAsyncQueue.h
  3. +85
    -0
      common/JackMidiAsyncWaitQueue.cpp
  4. +97
    -0
      common/JackMidiAsyncWaitQueue.h
  5. +57
    -0
      common/JackMidiBufferReadQueue.cpp
  6. +60
    -0
      common/JackMidiBufferReadQueue.h
  7. +57
    -0
      common/JackMidiBufferWriteQueue.cpp
  8. +62
    -0
      common/JackMidiBufferWriteQueue.h
  9. +300
    -0
      common/JackMidiRawInputWriteQueue.cpp
  10. +170
    -0
      common/JackMidiRawInputWriteQueue.h
  11. +228
    -0
      common/JackMidiRawOutputWriteQueue.cpp
  12. +147
    -0
      common/JackMidiRawOutputWriteQueue.h
  13. +27
    -0
      common/JackMidiReadQueue.cpp
  14. +55
    -0
      common/JackMidiReadQueue.h
  15. +27
    -0
      common/JackMidiReceiveQueue.cpp
  16. +42
    -0
      common/JackMidiReceiveQueue.h
  17. +34
    -0
      common/JackMidiSendQueue.cpp
  18. +52
    -0
      common/JackMidiSendQueue.h
  19. +120
    -0
      common/JackMidiUtil.cpp
  20. +102
    -0
      common/JackMidiUtil.h
  21. +27
    -0
      common/JackMidiWriteQueue.cpp
  22. +79
    -0
      common/JackMidiWriteQueue.h
  23. +0
    -287
      common/JackPhysicalMidiInput.cpp
  24. +0
    -146
      common/JackPhysicalMidiInput.h
  25. +0
    -320
      common/JackPhysicalMidiOutput.cpp
  26. +0
    -118
      common/JackPhysicalMidiOutput.h
  27. +15
    -2
      common/wscript
  28. +588
    -0
      example-clients/midi_latency_test.c
  29. +1
    -0
      example-clients/wscript
  30. +12
    -26
      linux/firewire/JackFFADODriver.cpp
  31. +0
    -59
      linux/firewire/JackFFADOMidiInput.cpp
  32. +94
    -0
      linux/firewire/JackFFADOMidiInputPort.cpp
  33. +51
    -0
      linux/firewire/JackFFADOMidiInputPort.h
  34. +0
    -60
      linux/firewire/JackFFADOMidiOutput.cpp
  35. +98
    -0
      linux/firewire/JackFFADOMidiOutputPort.cpp
  36. +53
    -0
      linux/firewire/JackFFADOMidiOutputPort.h
  37. +55
    -0
      linux/firewire/JackFFADOMidiReceiveQueue.cpp
  38. +16
    -18
      linux/firewire/JackFFADOMidiReceiveQueue.h
  39. +64
    -0
      linux/firewire/JackFFADOMidiSendQueue.cpp
  40. +17
    -20
      linux/firewire/JackFFADOMidiSendQueue.h
  41. +4
    -4
      linux/wscript

+ 94
- 0
common/JackMidiAsyncQueue.cpp View File

@@ -0,0 +1,94 @@
/*
Copyright (C) 2010 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 <new>

#include "JackMidiAsyncQueue.h"

using Jack::JackMidiAsyncQueue;

JackMidiAsyncQueue::JackMidiAsyncQueue(size_t max_bytes, size_t max_messages)
{
data_buffer = new jack_midi_data_t[max_bytes];
byte_ring = jack_ringbuffer_create(max_bytes + 1);
if (byte_ring) {
info_ring = jack_ringbuffer_create((max_messages * INFO_SIZE) + 1);
if (info_ring) {
jack_ringbuffer_mlock(byte_ring);
jack_ringbuffer_mlock(info_ring);
advance_space = 0;
this->max_bytes = max_bytes;
return;
}
jack_ringbuffer_free(byte_ring);
}
delete data_buffer;
throw std::bad_alloc();
}

JackMidiAsyncQueue::~JackMidiAsyncQueue()
{
jack_ringbuffer_free(byte_ring);
jack_ringbuffer_free(info_ring);
delete[] data_buffer;
}

jack_midi_event_t *
JackMidiAsyncQueue::DequeueEvent()
{
jack_midi_event_t *event = 0;
if (jack_ringbuffer_read_space(info_ring) >= INFO_SIZE) {
if (advance_space) {
jack_ringbuffer_read_advance(byte_ring, advance_space);
}
event = &dequeue_event;
jack_ringbuffer_read(info_ring, (char *) &(event->time),
sizeof(jack_nframes_t));
size_t size;
jack_ringbuffer_read(info_ring, (char *) &size, sizeof(size_t));
event->size = size;
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(byte_ring, vector);
size_t size1 = vector[0].len;
if (size1 >= size) {
event->buffer = (jack_midi_data_t *) vector[0].buf;
} else {
event->buffer = data_buffer;
memcpy(data_buffer, vector[0].buf, size1);
memcpy(data_buffer + size1, vector[1].buf, size - size1);
}
advance_space = size;
}
return event;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiAsyncQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
if (! ((jack_ringbuffer_write_space(info_ring) >= INFO_SIZE) &&
(jack_ringbuffer_write_space(byte_ring) >= size))) {
return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL;
}
jack_ringbuffer_write(byte_ring, (const char *) buffer, size);
jack_ringbuffer_write(info_ring, (const char *) (&time),
sizeof(jack_nframes_t));
jack_ringbuffer_write(info_ring, (const char *) (&size), sizeof(size_t));
return OK;
}

+ 92
- 0
common/JackMidiAsyncQueue.h View File

@@ -0,0 +1,92 @@
/*
Copyright (C) 2010 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 __JackMidiAsyncQueue__
#define __JackMidiAsyncQueue__

#include "JackMidiPort.h"
#include "JackMidiReadQueue.h"
#include "JackMidiWriteQueue.h"
#include "ringbuffer.h"

namespace Jack {

/**
* This is a MIDI message queue designed to allow two threads to pass MIDI
* messages between two threads (though it can also be used to buffer
* events internally). This is especially useful if the MIDI API
* you're attempting to interface with doesn't provide the ability to
* schedule MIDI events ahead of time and/or has blocking send/receive
* calls, as it allows a separate thread to handle input/output while the
* JACK process thread copies events from a `JackMidiBufferReadQueue` to
* this queue, or from this queue to a `JackMidiBufferWriteQueue`.
*/

class SERVER_EXPORT JackMidiAsyncQueue:
public JackMidiReadQueue, public JackMidiWriteQueue {

private:

static const size_t INFO_SIZE =
sizeof(jack_nframes_t) + sizeof(size_t);

size_t advance_space;
jack_ringbuffer_t *byte_ring;
jack_midi_data_t *data_buffer;
jack_midi_event_t dequeue_event;
jack_ringbuffer_t *info_ring;
size_t max_bytes;

public:

using JackMidiWriteQueue::EnqueueEvent;

/**
* Creates a new asynchronous MIDI message queue. The queue can store
* up to `max_messages` MIDI messages and up to `max_bytes` of MIDI
* data before it starts rejecting messages.
*/

JackMidiAsyncQueue(size_t max_bytes=4096, size_t max_messages=1024);

virtual ~JackMidiAsyncQueue();

/**
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI
* events available. This method may be overridden.
*/

virtual jack_midi_event_t *
DequeueEvent();

/**
* Enqueues the MIDI event specified by the arguments. The return
* value indiciates whether or not the event was successfully enqueued.
* This method may be overridden.
*/

virtual EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

};

}

#endif

+ 85
- 0
common/JackMidiAsyncWaitQueue.cpp View File

@@ -0,0 +1,85 @@
/*
Copyright (C) 2010 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 <new>

#include "JackMidiAsyncWaitQueue.h"
#include "JackMidiUtil.h"
#include "JackTime.h"

using Jack::JackMidiAsyncWaitQueue;

JackMidiAsyncWaitQueue::JackMidiAsyncWaitQueue(size_t max_bytes,
size_t max_messages):
JackMidiAsyncQueue(max_bytes, max_messages)
{
if (semaphore.Allocate("JackMidiAsyncWaitQueue", "midi-thread", 0)) {
throw std::bad_alloc();
}
}

JackMidiAsyncWaitQueue::~JackMidiAsyncWaitQueue()
{
semaphore.Destroy();
}

jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent()
{
return DequeueEvent((long) 0);
}

jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent(jack_nframes_t frame)
{

// XXX: I worry about timer resolution on Solaris and Windows. When the
// resolution for the `JackSynchro` object is milliseconds, the worst-case
// scenario for processor objects is that the wait time becomes less than a
// millisecond, and the processor object continually calls this method,
// expecting to wait a certain amount of microseconds, and ends up not
// waiting at all each time, essentially busy-waiting until the current
// frame is reached. Perhaps there should be a #define that indicates the
// wait time resolution for `JackSynchro` objects so that we can wait a
// little longer if necessary.

jack_time_t frame_time = GetTimeFromFrames(frame);
jack_time_t current_time = GetMicroSeconds();
return DequeueEvent((frame_time < current_time) ? 0 :
(long) (frame_time - current_time));
}

jack_midi_event_t *
JackMidiAsyncWaitQueue::DequeueEvent(long usec)
{
return ((usec < 0) ? semaphore.Wait() : semaphore.TimedWait(usec)) ?
JackMidiAsyncQueue::DequeueEvent() : 0;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiAsyncWaitQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
EnqueueResult result = JackMidiAsyncQueue::EnqueueEvent(time, size,
buffer);
if (result == OK) {
semaphore.Signal();
}
return result;
}

+ 97
- 0
common/JackMidiAsyncWaitQueue.h View File

@@ -0,0 +1,97 @@
/*
Copyright (C) 2010 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 __JackMidiAsyncWaitQueue__
#define __JackMidiAsyncWaitQueue__

#include "JackMidiAsyncQueue.h"

namespace Jack {

/**
* This is an asynchronous wait queue that allows a thread to wait for a
* message, either indefinitely or for a specified time. This is one
* example of a way that the `JackMidiAsyncQueue` class can be extended so
* that process threads can interact with non-process threads to send MIDI
* events.
*
* XXX: As of right now, this code hasn't been tested. Also, note the
* warning in the JackMidiAsyncWaitQueue.cpp about semaphore wait
* resolution.
*/

class SERVER_EXPORT JackMidiAsyncWaitQueue: public JackMidiAsyncQueue {

private:

JackSynchro semaphore;

public:

/**
* Creates a new asynchronous MIDI wait message queue. The queue can
* store up to `max_messages` MIDI messages and up to `max_bytes` of
* MIDI data before it starts rejecting messages.
*/

JackMidiAsyncWaitQueue(size_t max_bytes=4096,
size_t max_messages=1024);

~JackMidiAsyncWaitQueue();

/**
* Dequeues and returns a MIDI event. Returns '0' if there are no MIDI
* events available right now.
*/

jack_midi_event_t *
DequeueEvent();

/**
* Waits a specified time for a MIDI event to be available, or
* indefinitely if the time is negative. Returns the MIDI event, or
* '0' if time runs out and no MIDI event is available.
*/

jack_midi_event_t *
DequeueEvent(long usecs);

/**
* Waits until the specified frame for a MIDI event to be available.
* Returns the MIDI event, or '0' if time runs out and no MIDI event is
* available.
*/

jack_midi_event_t *
DequeueEvent(jack_nframes_t frame);

/**
* Enqueues the MIDI event specified by the arguments. The return
* value indiciates whether or not the event was successfully enqueued.
*/

EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

};

}

#endif

+ 57
- 0
common/JackMidiBufferReadQueue.cpp View File

@@ -0,0 +1,57 @@
/*
Copyright (C) 2010 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 "JackMidiBufferReadQueue.h"
#include "JackMidiUtil.h"

using Jack::JackMidiBufferReadQueue;

JackMidiBufferReadQueue::JackMidiBufferReadQueue()
{
event_count = 0;
index = 0;
}

jack_midi_event_t *
JackMidiBufferReadQueue::DequeueEvent()
{
jack_midi_event_t *e = 0;
if (index < event_count) {
JackMidiEvent *event = &(buffer->events[index]);
midi_event.buffer = event->GetData(buffer);
midi_event.size = event->size;
midi_event.time = last_frame_time + event->time;
e = &midi_event;
index++;
}
return e;
}

void
JackMidiBufferReadQueue::ResetMidiBuffer(JackMidiBuffer *buffer)
{
index = 0;
if (buffer) {
this->buffer = buffer;
event_count = buffer->event_count;
last_frame_time = GetLastFrame();
} else {
event_count = 0;
}
}

+ 60
- 0
common/JackMidiBufferReadQueue.h View File

@@ -0,0 +1,60 @@
/*
Copyright (C) 2010 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 __JackMidiBufferReadQueue__
#define __JackMidiBufferReadQueue__

#include "JackMidiReadQueue.h"

namespace Jack {

/**
* Wrapper class to present a JackMidiBuffer in a read queue interface.
*/

class SERVER_EXPORT JackMidiBufferReadQueue: public JackMidiReadQueue {

private:

JackMidiBuffer *buffer;
jack_nframes_t event_count;
jack_nframes_t index;
jack_nframes_t last_frame_time;
jack_midi_event_t midi_event;

public:

JackMidiBufferReadQueue();

jack_midi_event_t *
DequeueEvent();

/**
* This method must be called each period to reset the MIDI buffer for
* processing.
*/

void
ResetMidiBuffer(JackMidiBuffer *buffer);

};

}

#endif

+ 57
- 0
common/JackMidiBufferWriteQueue.cpp View File

@@ -0,0 +1,57 @@
/*
Copyright (C) 2010 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 "JackMidiBufferWriteQueue.h"
#include "JackMidiUtil.h"

using Jack::JackMidiBufferWriteQueue;

JackMidiBufferWriteQueue::JackMidiBufferWriteQueue()
{
// Empty
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiBufferWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *data)
{
if (time >= next_frame_time) {
return EVENT_EARLY;
}
if (time < last_frame_time) {
time = last_frame_time;
}
jack_midi_data_t *dst = buffer->ReserveEvent(time - last_frame_time, size);
if (! dst) {
return size > max_bytes ? BUFFER_TOO_SMALL : BUFFER_FULL;
}
memcpy(dst, data, size);
return OK;
}

void
JackMidiBufferWriteQueue::ResetMidiBuffer(JackMidiBuffer *buffer,
jack_nframes_t frames)
{
this->buffer = buffer;
buffer->Reset(frames);
last_frame_time = GetLastFrame();
max_bytes = buffer->MaxEventSize();
next_frame_time = last_frame_time + frames;
}

+ 62
- 0
common/JackMidiBufferWriteQueue.h View File

@@ -0,0 +1,62 @@
/*
Copyright (C) 2010 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 __JackMidiBufferWriteQueue__
#define __JackMidiBufferWriteQueue__

#include "JackMidiWriteQueue.h"

namespace Jack {

/**
* Wrapper class to present a JackMidiBuffer in a write queue interface.
*/

class SERVER_EXPORT JackMidiBufferWriteQueue: public JackMidiWriteQueue {

private:

JackMidiBuffer *buffer;
jack_nframes_t last_frame_time;
size_t max_bytes;
jack_nframes_t next_frame_time;

public:

using JackMidiWriteQueue::EnqueueEvent;

JackMidiBufferWriteQueue();

EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

/**
* This method must be called each period to reset the MIDI buffer for
* processing.
*/

void
ResetMidiBuffer(JackMidiBuffer *buffer, jack_nframes_t frames);

};

}

#endif

+ 300
- 0
common/JackMidiRawInputWriteQueue.cpp View File

@@ -0,0 +1,300 @@
/*
Copyright (C) 2010 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 <memory>
#include <new>

#include "JackMidiRawInputWriteQueue.h"

using Jack::JackMidiRawInputWriteQueue;

JackMidiRawInputWriteQueue::
JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
size_t max_packet_data, size_t max_packets)
{
packet_queue = new JackMidiAsyncQueue(max_packet_data, max_packets);
std::auto_ptr<JackMidiAsyncQueue> packet_queue_ptr(packet_queue);
input_ring = jack_ringbuffer_create(max_packet_data + 1);
if (! input_ring) {
throw std::bad_alloc();
}
jack_ringbuffer_mlock(input_ring);
Clear();
expected_bytes = 0;
event_pending = false;
packet = 0;
status_byte = 0;
this->write_queue = write_queue;
packet_queue_ptr.release();
}

JackMidiRawInputWriteQueue::~JackMidiRawInputWriteQueue()
{
jack_ringbuffer_free(input_ring);
delete packet_queue;
}

void
JackMidiRawInputWriteQueue::Clear()
{
jack_ringbuffer_reset(input_ring);
total_bytes = 0;
unbuffered_bytes = 0;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiRawInputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
return packet_queue->EnqueueEvent(time, size, buffer);
}

void
JackMidiRawInputWriteQueue::HandleBufferFailure(size_t unbuffered_bytes,
size_t total_bytes)
{
jack_error("JackMidiRawInputWriteQueue::HandleBufferFailure - %d MIDI "
"byte(s) of a %d byte message could not be buffered. The "
"message has been dropped.", unbuffered_bytes, total_bytes);
}

void
JackMidiRawInputWriteQueue::HandleEventLoss(jack_midi_event_t *event)
{
jack_error("JackMidiRawInputWriteQueue::HandleEventLoss - A %d byte MIDI "
"event scheduled for frame '%d' could not be processed because "
"the write queue cannot accomodate an event of that size. The "
"event has been discarded.", event->size, event->time);
}

void
JackMidiRawInputWriteQueue::HandleIncompleteMessage(size_t total_bytes)
{
jack_error("JackMidiRawInputWriteQueue::HandleIncompleteMessage - "
"Discarding %d MIDI byte(s) of an incomplete message. The "
"MIDI cable may have been unplugged.", total_bytes);
}

void
JackMidiRawInputWriteQueue::HandleInvalidStatusByte(jack_midi_data_t byte)
{
jack_error("JackMidiRawInputWriteQueue::HandleInvalidStatusByte - "
"Dropping invalid MIDI status byte '%x'.", (unsigned int) byte);
}

void
JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd(size_t total_bytes)
{
jack_error("JackMidiRawInputWriteQueue::HandleUnexpectedSysexEnd - "
"Received a sysex end byte without first receiving a sysex "
"start byte. Discarding %d MIDI byte(s). The cable may have "
"been unplugged.", total_bytes);
}

bool
JackMidiRawInputWriteQueue::PrepareBufferedEvent(jack_nframes_t time)
{
bool result = ! unbuffered_bytes;
if (! result) {
HandleBufferFailure(unbuffered_bytes, total_bytes);
} else {
size_t size = jack_ringbuffer_read_space(input_ring);
jack_ringbuffer_data_t vector[2];
jack_ringbuffer_get_read_vector(input_ring, vector);
// We don't worry about the second part of the vector, as we reset the
// ringbuffer after each parsed message.
PrepareEvent(time, size, (jack_midi_data_t *) vector[0].buf);
}
Clear();
if (status_byte >= 0xf0) {
expected_bytes = 0;
status_byte = 0;
}
return result;
}

bool
JackMidiRawInputWriteQueue::PrepareByteEvent(jack_nframes_t time,
jack_midi_data_t byte)
{
event_byte = byte;
PrepareEvent(time, 1, &event_byte);
return true;
}

void
JackMidiRawInputWriteQueue::PrepareEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
event.buffer = buffer;
event.size = size;
event.time = time;
event_pending = true;
}

jack_nframes_t
JackMidiRawInputWriteQueue::Process(jack_nframes_t boundary_frame)
{
if (event_pending) {
if (! WriteEvent(boundary_frame)) {
return event.time;
}
}
if (! packet) {
packet = packet_queue->DequeueEvent();
}
for (; packet; packet = packet_queue->DequeueEvent()) {
for (; packet->size; (packet->buffer)++, (packet->size)--) {
if (ProcessByte(packet->time, *(packet->buffer))) {
if (! WriteEvent(boundary_frame)) {
(packet->buffer)++;
(packet->size)--;
return event.time;
}
}
}
}
return 0;
}

bool
JackMidiRawInputWriteQueue::ProcessByte(jack_nframes_t time,
jack_midi_data_t byte)
{
if (byte >= 0xf8) {
// Realtime
if (byte == 0xfd) {
HandleInvalidStatusByte(byte);
return false;
}
return PrepareByteEvent(time, byte);
}
if (byte == 0xf7) {
// Sysex end
if (status_byte == 0xf0) {
RecordByte(byte);
return PrepareBufferedEvent(time);
}
HandleUnexpectedSysexEnd(total_bytes);
Clear();
expected_bytes = 0;
status_byte = 0;
return false;
}
if (byte >= 0x80) {
// Non-realtime status byte
if (total_bytes) {
HandleIncompleteMessage(total_bytes);
Clear();
}
status_byte = byte;
switch (byte & 0xf0) {
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
// Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
expected_bytes = 3;
break;
case 0xc0:
case 0xd0:
// Program Change, Channel Pressure
expected_bytes = 2;
break;
case 0xf0:
switch (byte) {
case 0xf0:
// Sysex
expected_bytes = 0;
break;
case 0xf1:
case 0xf3:
// MTC Quarter Frame, Song Select
expected_bytes = 2;
break;
case 0xf2:
// Song Position
expected_bytes = 3;
break;
case 0xf4:
case 0xf5:
// Undefined
HandleInvalidStatusByte(byte);
expected_bytes = 0;
status_byte = 0;
return false;
case 0xf6:
// Tune Request
bool result = PrepareByteEvent(time, byte);
if (result) {
expected_bytes = 0;
status_byte = 0;
}
return result;
}
}
RecordByte(byte);
return false;
}
// Data byte
if (! status_byte) {
// Data bytes without a status will be discarded.
total_bytes++;
unbuffered_bytes++;
return false;
}
if (! total_bytes) {
// Apply running status.
RecordByte(status_byte);
}
RecordByte(byte);
return (total_bytes == expected_bytes) ? PrepareBufferedEvent(time) :
false;
}

void
JackMidiRawInputWriteQueue::RecordByte(jack_midi_data_t byte)
{
if (jack_ringbuffer_write(input_ring, (const char *) &byte, 1) != 1) {
unbuffered_bytes++;
}
total_bytes++;
}

bool
JackMidiRawInputWriteQueue::WriteEvent(jack_nframes_t boundary_frame)
{
if (event.time < boundary_frame) {
switch (write_queue->EnqueueEvent(&event)) {
case BUFFER_TOO_SMALL:
HandleEventLoss(&event);
// Fallthrough on purpose
case OK:
event_pending = false;
return true;
default:
// This is here to stop compilers from warning us about not
// handling enumeration values.
;
}
}
return false;
}

+ 170
- 0
common/JackMidiRawInputWriteQueue.h View File

@@ -0,0 +1,170 @@
/*
Copyright (C) 2010 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 __JackMidiRawInputWriteQueue__
#define __JackMidiRawInputWriteQueue__

#include "JackMidiAsyncQueue.h"
#include "JackMidiWriteQueue.h"
#include "ringbuffer.h"

namespace Jack {

/**
* This queue enqueues raw, unparsed MIDI packets, and outputs complete
* MIDI messages to a write queue.
*
* Use this queue if the MIDI API you're interfacing with gives you raw
* MIDI bytes that must be parsed.
*/

class SERVER_EXPORT JackMidiRawInputWriteQueue: public JackMidiWriteQueue {

private:

jack_midi_event_t event;
jack_midi_data_t event_byte;
bool event_pending;
size_t expected_bytes;
jack_ringbuffer_t *input_ring;
jack_midi_event_t *packet;
JackMidiAsyncQueue *packet_queue;
jack_midi_data_t status_byte;
size_t total_bytes;
size_t unbuffered_bytes;
JackMidiWriteQueue *write_queue;

void
Clear();

bool
PrepareBufferedEvent(jack_nframes_t time);

bool
PrepareByteEvent(jack_nframes_t time, jack_midi_data_t byte);

void
PrepareEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

bool
ProcessByte(jack_nframes_t time, jack_midi_data_t byte);

void
RecordByte(jack_midi_data_t byte);

bool
WriteEvent(jack_nframes_t boundary_frame);

protected:

/**
* Override this method to specify what happens when there isn't enough
* room in the ringbuffer to contain a parsed event. The default
* method outputs an error message.
*/

virtual void
HandleBufferFailure(size_t unbuffered_bytes, size_t total_bytes);

/**
* Override this method to specify what happens when a parsed event
* can't be written to the write queue because the event's size exceeds
* the total possible space in the write queue. The default method
* outputs an error message.
*/

virtual void
HandleEventLoss(jack_midi_event_t *event);

/**
* Override this method to specify what happens when an incomplete MIDI
* message is parsed. The default method outputs an error message.
*/

virtual void
HandleIncompleteMessage(size_t total_bytes);

/**
* Override this method to specify what happens when an invalid MIDI
* status byte is parsed. The default method outputs an error message.
*/

virtual void
HandleInvalidStatusByte(jack_midi_data_t byte);

/**
* Override this method to specify what happens when a sysex end byte
* is parsed without first parsing a sysex begin byte. The default
* method outputs an error message.
*/

virtual void
HandleUnexpectedSysexEnd(size_t total_bytes);

public:

using JackMidiWriteQueue::EnqueueEvent;

/**
* Called to create a new raw input write queue. The `write_queue`
* argument is the queue to write parsed messages to. The optional
* `max_packets` argument specifies the number of packets that can be
* enqueued in the internal queue. The optional `max_packet_data`
* argument specifies the total number of MIDI bytes that can be put in
* the internal queue, AND the maximum size for an event that can be
* written to the write queue.
*/

JackMidiRawInputWriteQueue(JackMidiWriteQueue *write_queue,
size_t max_packet_data=4096,
size_t max_packets=1024);

~JackMidiRawInputWriteQueue();

EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

/**
* The `Process()` method should be called each time the
* `EnqueueEvent()` method returns `OK`. The `Process()` method will
* return the next frame at which an event should be sent. The return
* value from `Process()` depends upon the result of writing bytes to
* the write queue:
*
* -If the return value is '0', then all *complete* events have been
* sent successfully to the write queue. Don't call `Process()` again
* until another event has been enqueued.
*
* -If the return value is a non-zero value, then it specifies the
* frame that a pending event is scheduled to sent at. If the frame is
* in the future, then `Process()` should be called again at that time;
* otherwise, `Process()` should be called as soon as the write queue
* will accept events again.
*/

jack_nframes_t
Process(jack_nframes_t boundary_frame);

};

}

#endif

+ 228
- 0
common/JackMidiRawOutputWriteQueue.cpp View File

@@ -0,0 +1,228 @@
/*
Copyright (C) 2010 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 <memory>
#include <new>

#include "JackError.h"
#include "JackMidiRawOutputWriteQueue.h"
#include "JackMidiUtil.h"

using Jack::JackMidiRawOutputWriteQueue;

#define STILL_TIME(c, b) ((! (b)) || ((c) < (b)))

JackMidiRawOutputWriteQueue::
JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue, size_t non_rt_size,
size_t max_non_rt_messages, size_t max_rt_messages)
{
non_rt_queue = new JackMidiAsyncQueue(non_rt_size, max_non_rt_messages);
std::auto_ptr<JackMidiAsyncQueue> non_rt_ptr(non_rt_queue);
rt_queue = new JackMidiAsyncQueue(max_rt_messages, max_rt_messages);
std::auto_ptr<JackMidiAsyncQueue> rt_ptr(rt_queue);
non_rt_event = 0;
rt_event = 0;
running_status = 0;
this->send_queue = send_queue;
rt_ptr.release();
non_rt_ptr.release();
}

JackMidiRawOutputWriteQueue::~JackMidiRawOutputWriteQueue()
{
delete non_rt_queue;
delete rt_queue;
}

bool
JackMidiRawOutputWriteQueue::DequeueNonRealtimeEvent()
{
non_rt_event = non_rt_queue->DequeueEvent();
bool result = non_rt_event != 0;
if (result) {
non_rt_event_time = non_rt_event->time;
running_status = ApplyRunningStatus(non_rt_event, running_status);
}
return result;
}

bool
JackMidiRawOutputWriteQueue::DequeueRealtimeEvent()
{
rt_event = rt_queue->DequeueEvent();
bool result = rt_event != 0;
if (result) {
rt_event_time = rt_event->time;
}
return result;
}

Jack::JackMidiWriteQueue::EnqueueResult
JackMidiRawOutputWriteQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
JackMidiAsyncQueue *queue = (size == 1) && (*buffer >= 0xf8) ? rt_queue :
non_rt_queue;
EnqueueResult result = queue->EnqueueEvent(time, size, buffer);
if (result == OK) {
last_enqueued_message_time = time;
}
return result;
}

void
JackMidiRawOutputWriteQueue::HandleWriteQueueBug(jack_nframes_t time,
jack_midi_data_t byte)
{
jack_error("JackMidiRawOutputWriteQueue::HandleWriteQueueBug - **BUG** "
"The write queue told us that it couldn't enqueue a 1-byte "
"MIDI event scheduled for frame '%d'. This is probably a bug "
"in the write queue implementation.", time);
}

jack_nframes_t
JackMidiRawOutputWriteQueue::Process(jack_nframes_t boundary_frame)
{
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
while (STILL_TIME(current_frame, boundary_frame)) {
if (! non_rt_event) {
DequeueNonRealtimeEvent();
}
if (! rt_event) {
DequeueRealtimeEvent();
}
if (! (non_rt_event || rt_event)) {
return 0;
}
if (! WriteRealtimeEvents(boundary_frame)) {
break;
}
jack_nframes_t non_rt_boundary =
rt_event && STILL_TIME(rt_event_time, boundary_frame) ?
rt_event_time : boundary_frame;
if (! WriteNonRealtimeEvents(non_rt_boundary)) {
break;
}
current_frame = send_queue->GetNextScheduleFrame();
}

// If we get here, that means there is some sort of message available, and
// that either we can't currently write to the write queue or we have
// reached the boundary frame. Return the earliest time that a message is
// scheduled to be sent.

return ! non_rt_event ? rt_event_time :
non_rt_event->size > 1 ? current_frame :
! rt_event ? non_rt_event_time :
non_rt_event_time < rt_event_time ? non_rt_event_time : rt_event_time;
}

bool
JackMidiRawOutputWriteQueue::SendByte(jack_nframes_t time,
jack_midi_data_t byte)
{
switch (send_queue->EnqueueEvent(time, 1, &byte)) {
case BUFFER_TOO_SMALL:
HandleWriteQueueBug(time, byte);
case OK:
return true;
default:
// This is here to stop compilers from warning us about not handling
// enumeration values.
;
}
return false;
}

bool
JackMidiRawOutputWriteQueue::
WriteNonRealtimeEvents(jack_nframes_t boundary_frame)
{
if (! non_rt_event) {
if (! DequeueNonRealtimeEvent()) {
return true;
}
}
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
do {

// Send out as much of the non-realtime buffer as we can, save for one
// byte which we will send out when the message is supposed to arrive.

for (; non_rt_event->size > 1;
(non_rt_event->size)--, (non_rt_event->buffer)++) {
if (! STILL_TIME(current_frame, boundary_frame)) {
return true;
}
if (! SendByte(current_frame, *(non_rt_event->buffer))) {
return false;
}
current_frame = send_queue->GetNextScheduleFrame();
}
if (! (STILL_TIME(current_frame, boundary_frame) &&
STILL_TIME(non_rt_event_time, boundary_frame))) {
return true;
}

// There's still time. Try to send the byte.

if (! SendByte(non_rt_event_time, *(non_rt_event->buffer))) {
return false;
}
current_frame = send_queue->GetNextScheduleFrame();
if (! DequeueNonRealtimeEvent()) {
break;
}
} while (STILL_TIME(current_frame, boundary_frame));
return true;
}

bool
JackMidiRawOutputWriteQueue::WriteRealtimeEvents(jack_nframes_t boundary_frame)
{
jack_nframes_t current_frame = send_queue->GetNextScheduleFrame();
if (! rt_event) {
if (! DequeueRealtimeEvent()) {
return true;
}
}
for (;;) {
if (! STILL_TIME(current_frame, boundary_frame)) {
return false;
}

// If:
// -there's still time before we need to send the realtime event
// -there's a non-realtime event available for sending
// -non-realtime data can be scheduled before this event

if ((rt_event_time > current_frame) && non_rt_event &&
((non_rt_event->size > 1) ||
(non_rt_event_time < rt_event_time))) {
return true;
}
if (! SendByte(rt_event_time, *(rt_event->buffer))) {
return false;
}
current_frame = send_queue->GetNextScheduleFrame();
if (! DequeueRealtimeEvent()) {
return true;
}
}
}

+ 147
- 0
common/JackMidiRawOutputWriteQueue.h View File

@@ -0,0 +1,147 @@
/*
Copyright (C) 2010 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 __JackMidiRawOutputWriteQueue__
#define __JackMidiRawOutputWriteQueue__

#include "JackMidiAsyncQueue.h"
#include "JackMidiSendQueue.h"

namespace Jack {

/**
* This queue enqueues valid MIDI events and modifies them for raw output
* to a write queue. It has a number of advantages over straight MIDI
* event copying:
*
* -Running status: Status bytes can be omitted when the status byte of the
* current MIDI message is the same as the status byte of the last sent
* MIDI message.
*
* -Realtime messages: Realtime messages are given priority over
* non-realtime messages. Realtime bytes are interspersed with
* non-realtime bytes so that realtime messages can be sent as close as
* possible to the time they're scheduled for sending.
*
* -Time optimization: Bytes in non-realtime messages are sent out early
* when possible, with the last byte of the message being sent out as close
* to the specified event time as possible.
*
* Use this queue if the MIDI API you're interfacing with allows you to
* send raw MIDI bytes.
*/

class SERVER_EXPORT JackMidiRawOutputWriteQueue:
public JackMidiWriteQueue {

private:

jack_nframes_t last_enqueued_message_time;
jack_midi_event_t *non_rt_event;
jack_nframes_t non_rt_event_time;
JackMidiAsyncQueue *non_rt_queue;
jack_midi_event_t *rt_event;
jack_nframes_t rt_event_time;
JackMidiAsyncQueue *rt_queue;
jack_midi_data_t running_status;
JackMidiSendQueue *send_queue;

bool
DequeueNonRealtimeEvent();

bool
DequeueRealtimeEvent();

bool
SendByte(jack_nframes_t time, jack_midi_data_t byte);

bool
WriteNonRealtimeEvents(jack_nframes_t boundary_frame);

bool
WriteRealtimeEvents(jack_nframes_t boundary_frame);

protected:

/**
* Override this method to specify what happens when the write queue
* says that a 1-byte event is too large for its buffer. Basically,
* this should never happen.
*/

virtual void
HandleWriteQueueBug(jack_nframes_t time, jack_midi_data_t byte);

public:

using JackMidiWriteQueue::EnqueueEvent;

/**
* Called to create a new raw write queue. The `send_queue` argument
* is the queue to write raw bytes to. The optional `max_rt_messages`
* argument specifies the number of messages that can be enqueued in
* the internal realtime queue. The optional `max_non_rt_messages`
* argument specifies the number of messages that can be enqueued in
* the internal non-realtime queue. The optional `non_rt_size`
* argument specifies the total number of MIDI bytes that can be put in
* the non-realtime queue.
*/

JackMidiRawOutputWriteQueue(JackMidiSendQueue *send_queue,
size_t non_rt_size=4096,
size_t max_non_rt_messages=1024,
size_t max_rt_messages=128);

~JackMidiRawOutputWriteQueue();

EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);

/**
* The `Process()` method should be called each time the
* `EnqueueEvent()` method returns 'OK'. The `Process()` method will
* return the next frame at which an event should be sent. The return
* value from `Process()` depends upon the result of writing bytes to
* the write queue:
*
* -If the return value is '0', then all events that have been enqueued
* in this queue have been sent successfully to the write queue. Don't
* call `Process()` again until another event has been enqueued.
*
* -If the return value is an earlier frame or the current frame, it
* means that the write queue returned 'BUFFER_FULL', 'ERROR', or
* 'EVENT_EARLY' when this queue attempted to send the next byte, and
* that the byte should have already been sent, or is scheduled to be
* sent *now*. `Process()` should be called again when the write queue
* can enqueue events again successfully. How to determine when this
* will happen is left up to the caller.
*
* -If the return value is in the future, then `Process()` should be
* called again at that time, or after another event is enqueued.
*/

jack_nframes_t
Process(jack_nframes_t boundary_frame);

};

}

#endif

+ 27
- 0
common/JackMidiReadQueue.cpp View File

@@ -0,0 +1,27 @@
/*
Copyright (C) 2010 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 "JackMidiReadQueue.h"

using Jack::JackMidiReadQueue;

JackMidiReadQueue::~JackMidiReadQueue()
{
// Empty
}

+ 55
- 0
common/JackMidiReadQueue.h View File

@@ -0,0 +1,55 @@
/*
Copyright (C) 2010 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 __JackMidiReadQueue__
#define __JackMidiReadQueue__

#include "JackMidiPort.h"

namespace Jack {

/**
* Interface for objects that MIDI events can be read from.
*/

class SERVER_EXPORT JackMidiReadQueue {

public:

virtual
~JackMidiReadQueue();

/**
* Dequeues an event from the queue. Returns the event, or 0 if no
* events are available for reading.
*
* An event dequeued from the read queue is guaranteed to be valid up
* until another event is dequeued, at which all bets are off. Make
* sure that you handle each event you dequeue before dequeueing the
* next event.
*/

virtual jack_midi_event_t *
DequeueEvent() = 0;

};

}

#endif

+ 27
- 0
common/JackMidiReceiveQueue.cpp View File

@@ -0,0 +1,27 @@
/*
Copyright (C) 2010 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 "JackMidiReceiveQueue.h"

using Jack::JackMidiReceiveQueue;

JackMidiReceiveQueue::~JackMidiReceiveQueue()
{
// Empty
}

+ 42
- 0
common/JackMidiReceiveQueue.h View File

@@ -0,0 +1,42 @@
/*
Copyright (C) 2010 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 __JackMidiReceiveQueue__
#define __JackMidiReceiveQueue__

#include "JackMidiReadQueue.h"

namespace Jack {

/**
* Implemented by MIDI input connections.
*/

class SERVER_EXPORT JackMidiReceiveQueue: public JackMidiReadQueue {

public:

virtual
~JackMidiReceiveQueue();

};

}

#endif

+ 34
- 0
common/JackMidiSendQueue.cpp View File

@@ -0,0 +1,34 @@
/*
Copyright (C) 2010 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 "JackMidiSendQueue.h"
#include "JackMidiUtil.h"

using Jack::JackMidiSendQueue;

JackMidiSendQueue::~JackMidiSendQueue()
{
// Empty
}

jack_nframes_t
JackMidiSendQueue::GetNextScheduleFrame()
{
return GetCurrentFrame();
}

+ 52
- 0
common/JackMidiSendQueue.h View File

@@ -0,0 +1,52 @@
/*
Copyright (C) 2010 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 __JackMidiSendQueue__
#define __JackMidiSendQueue__

#include "JackMidiWriteQueue.h"

namespace Jack {

/**
* Implemented by MIDI output connections.
*/

class SERVER_EXPORT JackMidiSendQueue: public JackMidiWriteQueue {

public:

using JackMidiWriteQueue::EnqueueEvent;

virtual
~JackMidiSendQueue();

/**
* Returns the next frame that a MIDI message can be sent at. The
* default method returns the current frame.
*/

virtual jack_nframes_t
GetNextScheduleFrame();

};

}

#endif

+ 120
- 0
common/JackMidiUtil.cpp View File

@@ -0,0 +1,120 @@
/*
Copyright (C) 2010 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 "JackEngineControl.h"
#include "JackFrameTimer.h"
#include "JackGlobals.h"
#include "JackMidiUtil.h"
#include "JackTime.h"

jack_midi_data_t
Jack::ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer,
jack_midi_data_t running_status)
{

// Stolen and modified from alsa/midi_pack.h

jack_midi_data_t status = **buffer;
if ((status >= 0x80) && (status < 0xf0)) {
if (status == running_status) {
(*buffer)++;
(*size)--;
} else {
running_status = status;
}
} else if (status < 0xf8) {
running_status = 0;
}
return running_status;
}

jack_midi_data_t
Jack::ApplyRunningStatus(jack_midi_event_t *event,
jack_midi_data_t running_status)
{
return ApplyRunningStatus(&(event->size), &(event->buffer),
running_status);
}

jack_nframes_t
Jack::GetCurrentFrame()
{
JackEngineControl *control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Time2Frames(GetMicroSeconds(), control->fBufferSize);
}

jack_nframes_t
Jack::GetFramesFromTime(jack_time_t time)
{
JackEngineControl* control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Time2Frames(time, control->fBufferSize);
}

jack_nframes_t
Jack::GetLastFrame()
{
return GetEngineControl()->fFrameTimer.ReadCurrentState()->CurFrame();
}

int
Jack::GetMessageLength(jack_midi_data_t status_byte)
{
switch (status_byte & 0xf0) {
case 0x80:
case 0x90:
case 0xa0:
case 0xb0:
case 0xe0:
return 3;
case 0xc0:
case 0xd0:
return 2;
case 0xf0:
switch (status_byte) {
case 0xf0:
return 0;
case 0xf1:
case 0xf3:
return 2;
case 0xf2:
return 3;
case 0xf4:
case 0xf5:
case 0xf7:
case 0xfd:
break;
default:
return 1;
}
}
return -1;
}

jack_time_t
Jack::GetTimeFromFrames(jack_nframes_t frames)
{
JackEngineControl* control = GetEngineControl();
JackTimer timer;
control->ReadFrameTime(&timer);
return timer.Frames2Time(frames, control->fBufferSize);
}

+ 102
- 0
common/JackMidiUtil.h View File

@@ -0,0 +1,102 @@
/*
Copyright (C) 2010 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 __JackMidiUtil__
#define __JackMidiUtil__

#include "JackMidiPort.h"

namespace Jack {

/**
* Use this function to optimize MIDI output by omitting unnecessary status
* bytes. This can't be used with all MIDI APIs, so before using this
* function, make sure that your MIDI API doesn't require complete MIDI
* messages to be sent.
*
* To start using this function, call this method with pointers to the
* `size` and `buffer` arguments of the MIDI message you want to send, and
* set the `running_status` argument to '0'. For each subsequent MIDI
* message, call this method with pointers to its `size` and `buffer`
* arguments, and set the `running_status` argument to the return value of
* the previous call to this function.
*
* Note: This function will alter the `size` and `buffer` of your MIDI
* message for each message that can be optimized.
*/

SERVER_EXPORT jack_midi_data_t
ApplyRunningStatus(size_t *size, jack_midi_data_t **buffer,
jack_midi_data_t running_status=0);

/**
* A wrapper function for the above `ApplyRunningStatus` function.
*/

SERVER_EXPORT jack_midi_data_t
ApplyRunningStatus(jack_midi_event_t *event,
jack_midi_data_t running_status);

/**
* Gets the estimated current time in frames. This function has the same
* functionality as the JACK client API function `jack_frame_time`.
*/

SERVER_EXPORT jack_nframes_t
GetCurrentFrame();

/**
* Gets the estimated frame that will be occurring at the given time. This
* function has the same functionality as the JACK client API function
* `jack_time_to_frames`.
*/

SERVER_EXPORT jack_nframes_t
GetFramesFromTime(jack_time_t time);

/**
* Gets the precise time at the start of the current process cycle. This
* function has the same functionality as the JACK client API function
* `jack_last_frame_time`.
*/

SERVER_EXPORT jack_nframes_t
GetLastFrame();

/**
* Returns the expected message length for the status byte. Returns 0 if
* the status byte is a system exclusive status byte, or -1 if the status
* byte is invalid.
*/

SERVER_EXPORT int
GetMessageLength(jack_midi_data_t status_byte);

/**
* Gets the estimated time at which the given frame will occur. This
* function has the same functionality as the JACK client API function
* `jack_frames_to_time`.
*/

SERVER_EXPORT jack_time_t
GetTimeFromFrames(jack_nframes_t frames);

};

#endif

+ 27
- 0
common/JackMidiWriteQueue.cpp View File

@@ -0,0 +1,27 @@
/*
Copyright (C) 2010 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 "JackMidiWriteQueue.h"

using Jack::JackMidiWriteQueue;

JackMidiWriteQueue::~JackMidiWriteQueue()
{
// Empty
}

+ 79
- 0
common/JackMidiWriteQueue.h View File

@@ -0,0 +1,79 @@
/*
Copyright (C) 2010 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 __JackMidiWriteQueue__
#define __JackMidiWriteQueue__

#include "JackMidiPort.h"

namespace Jack {

/**
* Interface for classes that act as write queues for MIDI messages. Write
* queues are used by processors to transfer data to the next processor.
*/

class SERVER_EXPORT JackMidiWriteQueue {

public:

enum EnqueueResult {
BUFFER_FULL,
BUFFER_TOO_SMALL,
EVENT_EARLY,
ERROR,
OK
};

virtual ~JackMidiWriteQueue();

/**
* Enqueues a data packet in the write queue of `size` bytes contained
* in `buffer` that will be sent the absolute time specified by `time`.
* This method should not block unless 1.) this write queue represents
* the actual outbound MIDI connection, 2.) the MIDI event is being
* sent *now*, meaning that `time` is less than or equal to *now*, and
* 3.) the method is *not* being called in the process thread. The
* method should return `OK` if the event was enqueued, `BUFFER_FULL`
* if the write queue isn't able to accept the event right now,
* `BUFFER_TOO_SMALL` if this write queue will never be able to accept
* the event because the event is too large, `EVENT_EARLY` if this
* queue cannot schedule events ahead of time, and `ERROR` if an error
* occurs that cannot be specified by another return code.
*/

virtual EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer) = 0;

/**
* A wrapper method for the `EnqueueEvent` method above.
*/

inline EnqueueResult
EnqueueEvent(jack_midi_event_t *event)
{
return EnqueueEvent(event->time, event->size, event->buffer);
}

};

}

#endif

+ 0
- 287
common/JackPhysicalMidiInput.cpp View File

@@ -1,287 +0,0 @@
/*
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
- 146
common/JackPhysicalMidiInput.h View File

@@ -1,146 +0,0 @@
/*
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);
virtual ~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
- 320
common/JackPhysicalMidiOutput.cpp View File

@@ -1,320 +0,0 @@
/*
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 < 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
- 118
common/JackPhysicalMidiOutput.h View File

@@ -1,118 +0,0 @@
/*
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);
virtual ~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

+ 15
- 2
common/wscript View File

@@ -130,8 +130,21 @@ def build(bld):
'JackNetTool.cpp', 'JackNetTool.cpp',
'JackNetInterface.cpp', 'JackNetInterface.cpp',
'JackArgParser.cpp', 'JackArgParser.cpp',
'JackPhysicalMidiInput.cpp',
'JackPhysicalMidiOutput.cpp',

#'JackPhysicalMidiInput.cpp',
#'JackPhysicalMidiOutput.cpp',

'JackMidiAsyncQueue.cpp',
'JackMidiAsyncWaitQueue.cpp',
'JackMidiBufferReadQueue.cpp',
'JackMidiBufferWriteQueue.cpp',
'JackMidiRawInputWriteQueue.cpp',
'JackMidiRawOutputWriteQueue.cpp',
'JackMidiReadQueue.cpp',
'JackMidiReceiveQueue.cpp',
'JackMidiSendQueue.cpp',
'JackMidiUtil.cpp',
'JackMidiWriteQueue.cpp'
] ]


if bld.env['IS_LINUX']: if bld.env['IS_LINUX']:


+ 588
- 0
example-clients/midi_latency_test.c View File

@@ -0,0 +1,588 @@
/*
Copyright (C) 2010 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.

*/

/*
* This program is used to measure MIDI latency and jitter. It writes MIDI
* messages to one port and calculates how long it takes before it reads the
* same MIDI message over another port. It was written to calculate the
* latency and jitter of hardware and JACK hardware drivers, but might have
* other practical applications.
*
* The latency results of the program include the latency introduced by the
* JACK system. Because JACK has sample accurate MIDI, the same latency
* imposed on audio is also imposed on MIDI going through the system. Make
* sure you take this into account before complaining to me or (*especially*)
* other JACK developers about reported MIDI latency.
*
* The jitter results are a little more interesting. The program attempts to
* calculate 'average jitter' and 'peak jitter', as defined here:
*
* http://openmuse.org/transport/fidelity.html
*
* It also outputs a jitter plot, which gives you a more specific idea about
* the MIDI jitter for the ports you're testing. This is useful for catching
* extreme jitter values, and for analyzing the amount of truth in the
* technical specifications for your MIDI interface(s). :)
*
* This program is loosely based on 'alsa-midi-latency-test' in the ALSA test
* suite.
*
* To port this program to non-POSIX platforms, you'll have to include
* implementations for mutexes, semaphores, and command-line argument handling.
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <getopt.h>
#include <pthread.h>
#include <semaphore.h>

#include <jack/jack.h>
#include <jack/midiport.h>

#define ABS(x) (((x) >= 0) ? (x) : (-(x)))

const char *ERROR_UNEXPECTED = "in port received unexpected MIDI message";
const char *ERROR_UNEXPECTED_EXTRA = "received more than one MIDI message";
const char *ERROR_RESERVE = "could not reserve MIDI event on port buffer";
const char *ERROR_TIMEOUT = "timed out while waiting for MIDI message";
const char *SOURCE_EVENT_RESERVE = "jack_midi_event_reserve";
const char *SOURCE_PROCESS = "handle_process";
const char *SOURCE_TRYLOCK = "pthread_mutex_trylock";
const char *SOURCE_UNLOCK = "pthread_mutex_unlock";

jack_client_t *client;
const char *error_message;
const char *error_source;
jack_nframes_t highest_latency;
jack_time_t highest_latency_time;
jack_latency_range_t in_latency_range;
jack_port_t *in_port;
jack_nframes_t last_activity;
jack_time_t last_activity_time;
jack_time_t *latency_time_values;
jack_nframes_t *latency_values;
jack_nframes_t lowest_latency;
jack_time_t lowest_latency_time;
jack_midi_data_t *message_1;
jack_midi_data_t *message_2;
size_t messages_processed;
size_t message_size;
jack_latency_range_t out_latency_range;
jack_port_t *out_port;
int process_state;
char *program_name;
jack_port_t *remote_in_port;
jack_port_t *remote_out_port;
size_t samples;
sem_t semaphore;
pthread_mutex_t start_mutex;
int timeout;
jack_nframes_t total_latency;
jack_time_t total_latency_time;
size_t unexpected_messages;

static void
output_error(const char *source, const char *message);

static void
output_usage();

static void
set_process_error(const char *source, const char *message);

static void
die(char *source, char *error_message)
{
output_error(source, error_message);
output_usage();
exit(EXIT_FAILURE);
}

static void
handle_info(const char *message)
{
/* Suppress info */
}

static int
handle_process(jack_nframes_t frames, void *arg)
{
jack_midi_data_t *buffer;
int code;
jack_midi_event_t event;
jack_nframes_t event_count;
jack_nframes_t event_time;
jack_nframes_t frame;
size_t i;
jack_nframes_t last_frame_time;
jack_midi_data_t *message;
void *port_buffer;
jack_time_t time;
switch (process_state) {

case 0:
/* State: initializing */
code = pthread_mutex_trylock(&start_mutex);
if (code) {
if (code != EBUSY) {
set_process_error(SOURCE_TRYLOCK, strerror(code));
}
break;
}
code = pthread_mutex_unlock(&start_mutex);
if (code) {
set_process_error(SOURCE_UNLOCK, strerror(code));
break;
}
highest_latency = 0;
lowest_latency = 0;
messages_processed = 0;
process_state = 1;
total_latency = 0;
total_latency_time = 0;
unexpected_messages = 0;
jack_port_get_latency_range(remote_in_port, JackCaptureLatency,
&in_latency_range);
jack_port_get_latency_range(remote_out_port, JackPlaybackLatency,
&out_latency_range);
goto send_message;

case 1:
/* State: processing */
jack_midi_clear_buffer(jack_port_get_buffer(out_port, frames));
port_buffer = jack_port_get_buffer(in_port, frames);
event_count = jack_midi_get_event_count(port_buffer);
last_frame_time = jack_last_frame_time(client);
for (i = 0; i < event_count; i++) {
jack_midi_event_get(&event, port_buffer, i);
message = (messages_processed % 2) ? message_2 : message_1;
if ((event.size == message_size) &&
(! memcmp(message, event.buffer,
message_size * sizeof(jack_midi_data_t)))) {
goto found_message;
}
unexpected_messages++;
}
jack_time_t microseconds =
jack_frames_to_time(client, last_frame_time) - last_activity_time;
if ((microseconds / 1000000) >= timeout) {
set_process_error(SOURCE_PROCESS, ERROR_TIMEOUT);
}
break;
found_message:
event_time = last_frame_time + event.time;
frame = event_time - last_activity;
time = jack_frames_to_time(client, event_time) - last_activity_time;
if ((! highest_latency) || (frame > highest_latency)) {
highest_latency = frame;
highest_latency_time = time;
}
if ((! lowest_latency) || (frame < lowest_latency)) {
lowest_latency = frame;
lowest_latency_time = time;
}
latency_time_values[messages_processed] = time;
latency_values[messages_processed] = frame;
total_latency += frame;
total_latency_time += time;
messages_processed++;
if (messages_processed == samples) {
process_state = 2;
sem_post(&semaphore);
break;
}
send_message:
frame = (jack_nframes_t) ((((double) rand()) / RAND_MAX) * frames);
if (frame == frames) {
frame--;
}
port_buffer = jack_port_get_buffer(out_port, frames);
jack_midi_clear_buffer(port_buffer);
buffer = jack_midi_event_reserve(port_buffer, frame, message_size);
if (buffer == NULL) {
set_process_error(SOURCE_EVENT_RESERVE, ERROR_RESERVE);
break;
}
message = (messages_processed % 2) ? message_2 : message_1;
memcpy(buffer, message, message_size * sizeof(jack_midi_data_t));
last_activity = jack_last_frame_time(client) + frame;
last_activity_time = jack_frames_to_time(client, last_activity);

case 2:
/* State: finished - do nothing */

case -1:
/* State: error - do nothing */
;

}
return 0;
}

static void
handle_shutdown(void *arg)
{
set_process_error("handle_shutdown", "The JACK server has been shutdown");
}

static void
output_error(const char *source, const char *message)
{
fprintf(stderr, "%s: %s: %s\n", program_name, source, message);
}

static void
output_usage()
{
fprintf(stderr, "Usage: %s [options] out-port-name in-port-name\n\n"
"\t-h, --help print program usage\n"
"\t-m, --message-size=size set size of MIDI messages to send\n"
"\t-s, --samples=n number of MIDI messages to send\n"
"\t-t, --timeout=seconds wait time before giving up on message\n"
"\n", program_name);
}

static unsigned long
parse_positive_number_arg(char *s, char *name)
{
char *end_ptr;
unsigned long result;
errno = 0;
result = strtoul(s, &end_ptr, 10);
if (errno) {
die(name, strerror(errno));
}
if (*s == '\0') {
die(name, "argument value cannot be empty");
}
if (*end_ptr != '\0') {
die(name, "invalid value");
}
if (! result) {
die(name, "must be a positive number");
}
return result;
}

static void
set_process_error(const char *source, const char *message)
{
error_source = source;
error_message = message;
process_state = -1;
sem_post(&semaphore);
}

int
main(int argc, char **argv)
{
int code;
size_t jitter_plot[101];
int long_index = 0;
struct option long_options[] = {
{"help", 0, NULL, 'h'},
{"message-size", 1, NULL, 'm'},
{"samples", 1, NULL, 's'},
{"timeout", 1, NULL, 't'}
};
char *option_string = "hm:s:t:";
int show_usage = 0;
error_message = NULL;
message_size = 3;
program_name = argv[0];
samples = 1024;
timeout = 5;
for (;;) {
char c = getopt_long(argc, argv, option_string, long_options,
&long_index);
switch (c) {
case 'h':
show_usage = 1;
break;
case 'm':
message_size = parse_positive_number_arg(optarg, "message-size");
break;
case 's':
samples = parse_positive_number_arg(optarg, "samples");
break;
case 't':
timeout = parse_positive_number_arg(optarg, "timeout");
break;
default:
{
char *s = "'- '";
s[2] = c;
die(s, "invalid switch");
}
case -1:
if (show_usage) {
output_usage();
exit(EXIT_SUCCESS);
}
goto parse_port_names;
case 1:
/* end of switch :) */
;
}
}
parse_port_names:
if ((argc - optind) != 2) {
output_usage();
return EXIT_FAILURE;
}
latency_values = malloc(sizeof(jack_nframes_t) * samples);
if (latency_values == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto show_error;
}
latency_time_values = malloc(sizeof(jack_time_t) * samples);
if (latency_time_values == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto free_latency_values;
}
message_1 = malloc(message_size * sizeof(jack_midi_data_t));
if (message_1 == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto free_latency_time_values;
}
message_2 = malloc(message_size * sizeof(jack_midi_data_t));
if (message_2 == NULL) {
error_message = strerror(errno);
error_source = "malloc";
goto free_message_1;
}
switch (message_size) {
case 1:
message_1[0] = 0xf6;
message_2[0] = 0xf8;
break;
case 2:
message_1[0] = 0xc0;
message_1[1] = 0x00;
message_2[0] = 0xd0;
message_2[1] = 0x7f;
break;
case 3:
message_1[0] = 0x80;
message_1[1] = 0x00;
message_1[2] = 0x00;
message_2[0] = 0x90;
message_2[1] = 0x7f;
message_2[2] = 0x7f;
break;
default:
message_1[0] = 0xf0;
memset(message_1 + 1, 0,
(message_size - 2) * sizeof(jack_midi_data_t));
message_1[message_size - 1] = 0xf7;
message_2[0] = 0xf0;
memset(message_2 + 1, 0x7f,
(message_size - 2) * sizeof(jack_midi_data_t));
message_2[message_size - 1] = 0xf7;
}
client = jack_client_open(program_name, JackNullOption, NULL);
if (client == NULL) {
error_message = "failed to open JACK client";
error_source = "jack_client_open";
goto free_message_2;
}
remote_in_port = jack_port_by_name(client, argv[optind + 1]);
if (remote_in_port == NULL) {
error_message = "invalid port name";
error_source = argv[optind + 1];
goto close_client;
}
remote_out_port = jack_port_by_name(client, argv[optind]);
if (remote_out_port == NULL) {
error_message = "invalid port name";
error_source = argv[optind];
goto close_client;
}
in_port = jack_port_register(client, "in", JACK_DEFAULT_MIDI_TYPE,
JackPortIsInput, 0);
if (in_port == NULL) {
error_message = "failed to register MIDI-in port";
error_source = "jack_port_register";
goto close_client;
}
out_port = jack_port_register(client, "out", JACK_DEFAULT_MIDI_TYPE,
JackPortIsOutput, 0);
if (out_port == NULL) {
error_message = "failed to register MIDI-out port";
error_source = "jack_port_register";
goto unregister_in_port;
}
if (jack_set_process_callback(client, handle_process, NULL)) {
error_message = "failed to set process callback";
error_source = "jack_set_process_callback";
goto unregister_out_port;
}
jack_on_shutdown(client, handle_shutdown, NULL);
jack_set_info_function(handle_info);
process_state = 0;
if (sem_init(&semaphore, 0, 0)) {
error_message = strerror(errno);
error_source = "sem_init";
goto unregister_out_port;
}
code = pthread_mutex_init(&start_mutex, NULL);
if (code) {
error_message = strerror(errno);
error_source = "pthread_mutex_init";
goto destroy_semaphore;
}
code = pthread_mutex_trylock(&start_mutex);
if (code) {
error_message = strerror(code);
error_source = "pthread_mutex_trylock";
goto destroy_mutex;
}
if (jack_activate(client)) {
error_message = "could not activate client";
error_source = "jack_activate";
goto destroy_mutex;
}
if (jack_connect(client, jack_port_name(out_port),
jack_port_name(remote_out_port))) {
error_message = "could not connect MIDI out port";
error_source = "jack_connect";
goto deactivate_client;
}
if (jack_connect(client, jack_port_name(remote_in_port),
jack_port_name(in_port))) {
error_message = "could not connect MIDI in port";
error_source = "jack_connect";
goto deactivate_client;
}
jack_port_get_latency_range(remote_in_port, JackCaptureLatency,
&in_latency_range);
jack_port_get_latency_range(remote_out_port, JackPlaybackLatency,
&out_latency_range);
code = pthread_mutex_unlock(&start_mutex);
if (code) {
error_message = strerror(code);
error_source = "pthread_mutex_unlock";
goto deactivate_client;
}
if (sem_wait(&semaphore)) {
error_message = strerror(errno);
error_source = "sem_wait";
goto deactivate_client;
}
if (process_state == 2) {
double average_latency = ((double) total_latency) / samples;
double average_latency_time = total_latency_time / samples;
double high_jitter = highest_latency - average_latency;
size_t i;
double low_jitter = average_latency - lowest_latency;
double peak_jitter;
double peak_jitter_time;
double sample_rate = (double) jack_get_sample_rate(client);
jack_nframes_t total_jitter = 0;
jack_time_t total_jitter_time = 0;
if (high_jitter > low_jitter) {
peak_jitter = high_jitter;
peak_jitter_time = highest_latency_time - average_latency_time;
} else {
peak_jitter = low_jitter;
peak_jitter_time = average_latency_time - lowest_latency_time;
}
for (i = 0; i <= 100; i++) {
jitter_plot[i] = 0;
}
for (i = 0; i < samples; i++) {
double jitter_time = ABS(average_latency_time -
((double) latency_time_values[i]));
if (jitter_time >= 10000.0) {
(jitter_plot[100])++;
} else {
(jitter_plot[(int) (jitter_time / 100.0)])++;
}
total_jitter += ABS(average_latency -
((double) latency_values[i]));
total_jitter_time += jitter_time;
}
printf("Reported out-port latency: %.2f-%.2f ms (%u-%u frames)\n"
"Reported in-port latency: %.2f-%.2f ms (%u-%u frames)\n"
"Average latency: %.2f ms (%.2f frames)\n"
"Best latency: %.2f ms (%u frames)\n"
"Worst latency: %.2f ms (%u frames)\n"
"Peak MIDI jitter: %.2f ms (%.2f frames)\n"
"Average MIDI jitter: %.2f ms (%.2f frames)\n",
(out_latency_range.min / sample_rate) * 1000.0,
(out_latency_range.max / sample_rate) * 1000.0,
out_latency_range.min, out_latency_range.max,
(in_latency_range.min / sample_rate) * 1000.0,
(in_latency_range.max / sample_rate) * 1000.0,
in_latency_range.min, in_latency_range.max,
average_latency_time / 1000.0, average_latency,
lowest_latency_time / 1000.0, lowest_latency,
highest_latency_time / 1000.0, highest_latency,
peak_jitter_time / 1000.0, peak_jitter,
(total_jitter_time / 1000.0) / samples,
((double) total_jitter) / samples);
printf("\nJitter Plot:\n");
for (i = 0; i < 100; i++) {
if (jitter_plot[i]) {
printf("%.1f - %.1f ms: %u\n", ((float) i) / 10.0,
((float) (i + 1)) / 10.0, jitter_plot[i]);
}
}
if (jitter_plot[100]) {
printf(" > 10 ms: %u\n", jitter_plot[100]);
}
if (unexpected_messages) {
printf("\nUnexpected messages received: %d\n",
unexpected_messages);
}
}
deactivate_client:
jack_deactivate(client);
destroy_mutex:
pthread_mutex_destroy(&start_mutex);
destroy_semaphore:
sem_destroy(&semaphore);
unregister_out_port:
jack_port_unregister(client, out_port);
unregister_in_port:
jack_port_unregister(client, in_port);
close_client:
jack_client_close(client);
free_message_2:
free(message_2);
free_message_1:
free(message_1);
free_latency_time_values:
free(latency_time_values);
free_latency_values:
free(latency_values);
if (error_message != NULL) {
show_error:
output_error(error_source, error_message);
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}

+ 1
- 0
example-clients/wscript View File

@@ -27,6 +27,7 @@ example_programs = {
'jack_server_control' : 'server_control.cpp', 'jack_server_control' : 'server_control.cpp',
'jack_latent_client' : 'latent_client.c', 'jack_latent_client' : 'latent_client.c',
'jack_midi_dump' : 'midi_dump.c', 'jack_midi_dump' : 'midi_dump.c',
'jack_midi_latency_test' : 'midi_latency_test.c'
} }


example_libs = { example_libs = {


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

@@ -36,8 +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 "JackFFADOMidiInputPort.h"
#include "JackFFADOMidiOutputPort.h"
#include "JackEngineControl.h" #include "JackEngineControl.h"
#include "JackClientControl.h" #include "JackClientControl.h"
#include "JackPort.h" #include "JackPort.h"
@@ -94,14 +94,9 @@ 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) {
JackFFADOMidiInput *midi_input = (JackFFADOMidiInput *) driver->capture_channels[chn].midi_input;
JackFFADOMidiInputPort *midi_input = (JackFFADOMidiInputPort *) driver->capture_channels[chn].midi_input;
JackMidiBuffer *buffer = (JackMidiBuffer *) fGraphManager->GetBuffer(fCapturePortList[chn], nframes); 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);
midi_input->Process(buffer, driver->capture_channels[chn].midi_buffer, nframes);
} }
} }


@@ -138,16 +133,9 @@ JackFFADODriver::ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nfr
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); 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 (!buf) {
ffado_streaming_playback_stream_onoff(driver->dev, chn, 0);
continue;
}
ffado_streaming_playback_stream_onoff(driver->dev, chn, 1);
JackFFADOMidiOutput *midi_output = (JackFFADOMidiOutput *) driver->playback_channels[chn].midi_output;
midi_output->SetPortBuffer((JackMidiBuffer *) buf);
midi_output->SetOutputBuffer(midi_buffer);
midi_output->Process(nframes);
ffado_streaming_playback_stream_onoff(driver->dev, chn, buf ? 1 : 0);
JackFFADOMidiOutputPort *midi_output = (JackFFADOMidiOutputPort *) driver->playback_channels[chn].midi_output;
midi_output->Process((JackMidiBuffer *) buf, midi_buffer, 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));
@@ -155,9 +143,7 @@ JackFFADODriver::ffado_driver_write (ffado_driver_t * driver, jack_nframes_t nfr
} }
} }
} }

ffado_streaming_transfer_playback_buffers(driver->dev); ffado_streaming_transfer_playback_buffers(driver->dev);

printExit(); printExit();
return 0; return 0;
} }
@@ -476,7 +462,7 @@ int JackFFADODriver::Attach()
printError(" cannot enable port %s", buf); printError(" cannot enable port %s", buf);
} }


driver->capture_channels[chn].midi_input = new JackFFADOMidiInput();
driver->capture_channels[chn].midi_input = new JackFFADOMidiInputPort();
// 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));


@@ -557,12 +543,12 @@ int JackFFADODriver::Attach()
// This constructor optionally accepts arguments for the // This constructor optionally accepts arguments for the
// non-realtime buffer size and the realtime buffer size. Ideally, // non-realtime buffer size and the realtime buffer size. Ideally,
// these would become command-line options for the FFADO driver. // these would become command-line options for the FFADO driver.
driver->playback_channels[chn].midi_output = new JackFFADOMidiOutput();
driver->playback_channels[chn].midi_output = new JackFFADOMidiOutputPort();


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);
range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + driver->playback_frame_latency;
range.min = range.max = (driver->period_size * (driver->device_options.nb_buffers - 1)) + ((fEngineControl->fSyncMode) ? 0 : fEngineControl->fBufferSize) + driver->playback_frame_latency;
port->SetLatencyRange(JackPlaybackLatency, &range); port->SetLatencyRange(JackPlaybackLatency, &range);
fPlaybackPortList[chn] = port_index; fPlaybackPortList[chn] = port_index;
jack_log("JackFFADODriver::Attach fPlaybackPortList[i] %ld ", port_index); jack_log("JackFFADODriver::Attach fPlaybackPortList[i] %ld ", port_index);
@@ -600,7 +586,7 @@ int JackFFADODriver::Detach()
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) if (driver->capture_channels[chn].midi_input)
delete ((JackFFADOMidiInput *) (driver->capture_channels[chn].midi_input));
delete ((JackFFADOMidiInputPort *) (driver->capture_channels[chn].midi_input));
} }
free(driver->capture_channels); free(driver->capture_channels);


@@ -608,7 +594,7 @@ int JackFFADODriver::Detach()
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) if (driver->playback_channels[chn].midi_output)
delete ((JackFFADOMidiOutput *) (driver->playback_channels[chn].midi_output));
delete ((JackFFADOMidiOutputPort *) (driver->playback_channels[chn].midi_output));
} }
free(driver->playback_channels); free(driver->playback_channels);




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

@@ -1,59 +0,0 @@
/*
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;
}

}

+ 94
- 0
linux/firewire/JackFFADOMidiInputPort.cpp View File

@@ -0,0 +1,94 @@
/*
Copyright (C) 2010 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 <memory>

#include "JackFFADOMidiInputPort.h"
#include "JackMidiUtil.h"

using Jack::JackFFADOMidiInputPort;

JackFFADOMidiInputPort::JackFFADOMidiInputPort(size_t max_bytes)
{
event = 0;
receive_queue = new JackFFADOMidiReceiveQueue();
std::auto_ptr<JackFFADOMidiReceiveQueue> receive_queue_ptr(receive_queue);
write_queue = new JackMidiBufferWriteQueue();
std::auto_ptr<JackMidiBufferWriteQueue> write_queue_ptr(write_queue);
raw_queue = new JackMidiRawInputWriteQueue(write_queue, max_bytes,
max_bytes);
write_queue_ptr.release();
receive_queue_ptr.release();
}

JackFFADOMidiInputPort::~JackFFADOMidiInputPort()
{
delete raw_queue;
delete receive_queue;
delete write_queue;
}

void
JackFFADOMidiInputPort::Process(JackMidiBuffer *port_buffer,
uint32_t *input_buffer, jack_nframes_t frames)
{
receive_queue->ResetInputBuffer(input_buffer, frames);
write_queue->ResetMidiBuffer(port_buffer, frames);
jack_nframes_t boundary_frame = GetLastFrame() + frames;
if (! event) {
event = receive_queue->DequeueEvent();
}
for (; event; event = receive_queue->DequeueEvent()) {
switch (raw_queue->EnqueueEvent(event)) {
case JackMidiWriteQueue::BUFFER_FULL:

// Processing events early might free up some space in the raw
// input queue.

raw_queue->Process(boundary_frame);
switch (raw_queue->EnqueueEvent(event)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
// This shouldn't really happen. It indicates a bug if it
// does.
jack_error("JackFFADOMidiInputPort::Process - **BUG** "
"JackMidiRawInputWriteQueue::EnqueueEvent returned "
"`BUFFER_FULL`, and then returned "
"`BUFFER_TOO_SMALL` after a `Process()` call.");
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
return;
}
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackFFADOMidiInputPort::Process - The write queue "
"couldn't enqueue a %d-byte event. Dropping event.",
event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
// This is here to stop compliers from warning us about not
// handling enumeration values.
;
}
break;
}
raw_queue->Process(boundary_frame);
}

+ 51
- 0
linux/firewire/JackFFADOMidiInputPort.h View File

@@ -0,0 +1,51 @@
/*
Copyright (C) 2010 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 __JackFFADOMidiInputPort__
#define __JackFFADOMidiInputPort__

#include "JackFFADOMidiReceiveQueue.h"
#include "JackMidiBufferWriteQueue.h"
#include "JackMidiRawInputWriteQueue.h"

namespace Jack {

class JackFFADOMidiInputPort {

private:

jack_midi_event_t *event;
JackMidiRawInputWriteQueue *raw_queue;
JackFFADOMidiReceiveQueue *receive_queue;
JackMidiBufferWriteQueue *write_queue;

public:

JackFFADOMidiInputPort(size_t max_bytes=4096);
~JackFFADOMidiInputPort();

void
Process(JackMidiBuffer *port_buffer, uint32_t *input_buffer,
jack_nframes_t frames);

};

}

#endif

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

@@ -1,60 +0,0 @@
/*
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;
}

}

+ 98
- 0
linux/firewire/JackFFADOMidiOutputPort.cpp View File

@@ -0,0 +1,98 @@
/*
Copyright (C) 2010 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 <memory>

#include "JackFFADOMidiOutputPort.h"
#include "JackMidiUtil.h"

using Jack::JackFFADOMidiOutputPort;

JackFFADOMidiOutputPort::JackFFADOMidiOutputPort(size_t non_rt_size,
size_t max_non_rt_messages,
size_t max_rt_messages)
{
event = 0;
read_queue = new JackMidiBufferReadQueue();
std::auto_ptr<JackMidiBufferReadQueue> read_queue_ptr(read_queue);
send_queue = new JackFFADOMidiSendQueue();
std::auto_ptr<JackFFADOMidiSendQueue> send_queue_ptr(send_queue);
raw_queue = new JackMidiRawOutputWriteQueue(send_queue, max_rt_messages,
max_non_rt_messages,
non_rt_size);
send_queue_ptr.release();
read_queue_ptr.release();
}

JackFFADOMidiOutputPort::~JackFFADOMidiOutputPort()
{
delete raw_queue;
delete read_queue;
delete send_queue;
}

void
JackFFADOMidiOutputPort::Process(JackMidiBuffer *port_buffer,
uint32_t *output_buffer,
jack_nframes_t frames)
{
read_queue->ResetMidiBuffer(port_buffer);
send_queue->ResetOutputBuffer(output_buffer, frames);
jack_nframes_t boundary_frame = GetLastFrame() + frames;
if (! event) {
event = read_queue->DequeueEvent();
}
for (; event; event = read_queue->DequeueEvent()) {
switch (raw_queue->EnqueueEvent(event)) {
case JackMidiWriteQueue::BUFFER_FULL:

// Processing events early might free up some space in the raw
// output queue.

raw_queue->Process(boundary_frame);
switch (raw_queue->EnqueueEvent(event)) {
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
// This shouldn't really happen. It indicates a bug if it
// does.
jack_error("JackFFADOMidiOutputPort::Process - **BUG** "
"JackMidiRawOutputWriteQueue::EnqueueEvent "
"returned `BUFFER_FULL`, and then returned "
"`BUFFER_TOO_SMALL` after a `Process()` call.");
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
return;
}
case JackMidiWriteQueue::BUFFER_TOO_SMALL:
jack_error("JackFFADOMidiOutputPort::Process - The write queue "
"couldn't enqueue a %d-byte event. Dropping event.",
event->size);
// Fallthrough on purpose
case JackMidiWriteQueue::OK:
continue;
default:
// This is here to stop compliers from warning us about not
// handling enumeration values.
;
}
break;
}
raw_queue->Process(boundary_frame);
}

+ 53
- 0
linux/firewire/JackFFADOMidiOutputPort.h View File

@@ -0,0 +1,53 @@
/*
Copyright (C) 2010 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 __JackFFADOMidiOutputPort__
#define __JackFFADOMidiOutputPort__

#include "JackFFADOMidiSendQueue.h"
#include "JackMidiBufferReadQueue.h"
#include "JackMidiRawOutputWriteQueue.h"

namespace Jack {

class JackFFADOMidiOutputPort {

private:

jack_midi_event_t *event;
JackMidiRawOutputWriteQueue *raw_queue;
JackMidiBufferReadQueue *read_queue;
JackFFADOMidiSendQueue *send_queue;

public:

JackFFADOMidiOutputPort(size_t non_rt_size=4096,
size_t max_non_rt_messages=1024,
size_t max_rt_messages=128);
~JackFFADOMidiOutputPort();

void
Process(JackMidiBuffer *port_buffer, uint32_t *output_buffer,
jack_nframes_t frames);

};

}

#endif

+ 55
- 0
linux/firewire/JackFFADOMidiReceiveQueue.cpp View File

@@ -0,0 +1,55 @@
/*
Copyright (C) 2010 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 "JackFFADOMidiReceiveQueue.h"
#include "JackMidiUtil.h"

using Jack::JackFFADOMidiReceiveQueue;

JackFFADOMidiReceiveQueue::JackFFADOMidiReceiveQueue()
{
// Empty
}

jack_midi_event_t *
JackFFADOMidiReceiveQueue::DequeueEvent()
{
for (; index < length; index += 8) {
uint32_t data = input_buffer[index];
if (data & 0xff000000) {
byte = (jack_midi_data_t) (data & 0xff);
event.buffer = &byte;
event.size = 1;
event.time = last_frame + index;
index += 8;
return &event;
}
}
return 0;
}

void
JackFFADOMidiReceiveQueue::ResetInputBuffer(uint32_t *input_buffer,
jack_nframes_t length)
{
this->input_buffer = input_buffer;
index = 0;
last_frame = GetLastFrame();
this->length = length;
}

linux/firewire/JackFFADOMidiInput.h → linux/firewire/JackFFADOMidiReceiveQueue.h View File

@@ -1,5 +1,5 @@
/* /*
Copyright (C) 2009 Devin Anderson
Copyright (C) 2010 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 Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@@ -17,35 +17,33 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


*/ */


#ifndef __JackFFADOMidiInput__
#define __JackFFADOMidiInput__
#ifndef __JackFFADOMidiReceiveQueue__
#define __JackFFADOMidiReceiveQueue__


#include "JackPhysicalMidiInput.h"
#include "JackMidiReceiveQueue.h"


namespace Jack { namespace Jack {


class JackFFADOMidiInput: public JackPhysicalMidiInput {
class JackFFADOMidiReceiveQueue: public JackMidiReceiveQueue {


private: private:


jack_midi_data_t byte;
jack_midi_event_t event;
jack_nframes_t index;
uint32_t *input_buffer; uint32_t *input_buffer;
bool new_period;

protected:

jack_nframes_t
Receive(jack_midi_data_t *, jack_nframes_t, jack_nframes_t);
jack_nframes_t last_frame;
jack_nframes_t length;


public: public:


JackFFADOMidiInput(size_t buffer_size=1024);
~JackFFADOMidiInput();
JackFFADOMidiReceiveQueue();

jack_midi_event_t *
DequeueEvent();


inline void
SetInputBuffer(uint32_t *input_buffer)
{
this->input_buffer = input_buffer;
}
void
ResetInputBuffer(uint32_t *input_buffer, jack_nframes_t length);


}; };



+ 64
- 0
linux/firewire/JackFFADOMidiSendQueue.cpp View File

@@ -0,0 +1,64 @@
/*
Copyright (C) 2010 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 "JackFFADOMidiSendQueue.h"
#include "JackMidiUtil.h"

using Jack::JackFFADOMidiSendQueue;

JackFFADOMidiSendQueue::JackFFADOMidiSendQueue()
{
// Empty
}

Jack::JackMidiWriteQueue::EnqueueResult
JackFFADOMidiSendQueue::EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer)
{
assert(size == 1);
jack_nframes_t relative_time = (time < last_frame) ? 0 : time - last_frame;
if (index < relative_time) {
index = (relative_time % 8) ?
(relative_time & (~ ((jack_nframes_t) 7))) + 8 : relative_time;
}
if (index >= length) {
return BUFFER_FULL;
}
output_buffer[index] = 0x01000000 | ((uint32_t) *buffer);
index += 8;
return OK;
}

jack_nframes_t
JackFFADOMidiSendQueue::GetNextScheduleFrame()
{
return last_frame + index;
}

void
JackFFADOMidiSendQueue::ResetOutputBuffer(uint32_t *output_buffer,
jack_nframes_t length)
{
index = 0;
last_frame = GetLastFrame();
this->length = length;
this->output_buffer = output_buffer;
}

linux/firewire/JackFFADOMidiOutput.h → linux/firewire/JackFFADOMidiSendQueue.h View File

@@ -1,5 +1,5 @@
/* /*
Copyright (C) 2009 Devin Anderson
Copyright (C) 2010 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 Lesser General Public License as published by it under the terms of the GNU Lesser General Public License as published by
@@ -17,38 +17,35 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.


*/ */


#ifndef __JackFFADOMidiOutput__
#define __JackFFADOMidiOutput__
#ifndef __JackFFADOMidiSendQueue__
#define __JackFFADOMidiSendQueue__


#include "JackPhysicalMidiOutput.h"
#include "JackMidiSendQueue.h"


namespace Jack { namespace Jack {


class JackFFADOMidiOutput: public JackPhysicalMidiOutput {
class JackFFADOMidiSendQueue: public JackMidiSendQueue {


private: private:


jack_nframes_t index;
jack_nframes_t last_frame;
jack_nframes_t length;
uint32_t *output_buffer; uint32_t *output_buffer;


protected:

jack_nframes_t
Advance(jack_nframes_t);
public:


jack_nframes_t
Send(jack_nframes_t, jack_midi_data_t);
JackFFADOMidiSendQueue();


public:
EnqueueResult
EnqueueEvent(jack_nframes_t time, size_t size,
jack_midi_data_t *buffer);


JackFFADOMidiOutput(size_t non_rt_buffer_size=1024,
size_t rt_buffer_size=64);
~JackFFADOMidiOutput();
jack_nframes_t
GetNextScheduleFrame();


inline void
SetOutputBuffer(uint32_t *output_buffer)
{
this->output_buffer = output_buffer;
}
void
ResetOutputBuffer(uint32_t *output_buffer, jack_nframes_t length);


}; };



+ 4
- 4
linux/wscript View File

@@ -58,10 +58,10 @@ def build(bld):
] ]


ffado_driver_src = ['firewire/JackFFADODriver.cpp', ffado_driver_src = ['firewire/JackFFADODriver.cpp',
'firewire/JackFFADOMidiInput.cpp',
'firewire/JackFFADOMidiOutput.cpp',
'../common/JackPhysicalMidiInput.cpp',
'../common/JackPhysicalMidiOutput.cpp'
'firewire/JackFFADOMidiInputPort.cpp',
'firewire/JackFFADOMidiOutputPort.cpp',
'firewire/JackFFADOMidiReceiveQueue.cpp',
'firewire/JackFFADOMidiSendQueue.cpp'
] ]


if bld.env['BUILD_DRIVER_ALSA'] == True: if bld.env['BUILD_DRIVER_ALSA'] == True:


Loading…
Cancel
Save