1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-19 09:01:15 +02:00

Fixed MIDI playback on Linux. Still need configure files for libscorealign and portmidi. (I tweaked makefiles by hand to build and test with EXPERIMENTAL_MIDI_OUT and EXPERIMENTAL_SCOREALIGN.) Also, I updated portmidi files, including some bug fixes from the portmidi project.

This commit is contained in:
rbdannenberg
2010-10-01 17:59:18 +00:00
parent 576dfe6653
commit 6fa857c3ae
39 changed files with 375 additions and 5373 deletions

View File

@@ -5,7 +5,7 @@
* and subsequent work by Andrew Zeldis and Zico Kolter
* and Roger B. Dannenberg
*
* $Id: pmmacosxcm.c,v 1.1 2009-06-24 20:37:25 gswillia Exp $
* $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $
*/
/* Notes:
@@ -38,17 +38,34 @@
#include <CoreServices/CoreServices.h>
#include <CoreMIDI/MIDIServices.h>
#include <CoreAudio/HostTime.h>
#include <unistd.h>
#define PACKET_BUFFER_SIZE 1024
/* maximum overall data rate (OS X limit is 15000 bytes/second) */
#define MAX_BYTES_PER_S 14000
/* this is very strange: if I put in a reasonable
number here, e.g. 128, which would allow sysex data
to be sent 128 bytes at a time, then I lose sysex
data in my loopback test. With a buffer size of 4,
we put at most 4 bytes in a packet (but maybe many
packets in a packetList), and everything works fine.
/* Apple reports that packets are dropped when the MIDI bytes/sec
exceeds 15000. This is computed by "tracking the number of MIDI
bytes scheduled into 1-second buckets over the last six seconds
and averaging these counts."
This is apparently based on timestamps, not on real time, so
we have to avoid constructing packets that schedule high speed
output even if the actual writes are delayed (which was my first
solution).
The LIMIT_RATE symbol, if defined, enables code to modify
timestamps as follows:
After each packet is formed, the next allowable timestamp is
computed as this_packet_time + this_packet_len * delay_per_byte
This is the minimum timestamp allowed in the next packet.
Note that this distorts accurate timestamps somewhat.
*/
#define SYSEX_BUFFER_SIZE 4
#define LIMIT_RATE 1
#define SYSEX_BUFFER_SIZE 128
#define VERBOSE_ON 1
#define VERBOSE if (VERBOSE_ON)
@@ -75,7 +92,7 @@ extern pm_fns_node pm_macosx_out_dictionary;
typedef struct midi_macosxcm_struct {
PmTimestamp sync_time; /* when did we last determine delta? */
UInt64 delta; /* difference between stream time and real time in ns */
UInt64 last_time; /* last output time */
UInt64 last_time; /* last output time in host units*/
int first_message; /* tells midi_write to sychronize timestamps */
int sysex_mode; /* middle of sending sysex */
uint32_t sysex_word; /* accumulate data when receiving sysex */
@@ -90,6 +107,11 @@ typedef struct midi_macosxcm_struct {
/* allow for running status (is running status possible here? -rbd): -cpr */
unsigned char last_command;
int32_t last_msg_length;
/* limit midi data rate (a CoreMidi requirement): */
UInt64 min_next_time; /* when can the next send take place? */
int byte_count; /* how many bytes in the next packet list? */
Float64 us_per_host_tick; /* host clock frequency, units of min_next_time */
UInt64 host_ticks_per_byte; /* host clock units per byte at maximum rate */
} midi_macosxcm_node, *midi_macosxcm_type;
/* private function declarations */
@@ -405,7 +427,11 @@ midi_out_open(PmInternal *midi, void *driverInfo)
m->packet = NULL;
m->last_command = 0;
m->last_msg_length = 0;
m->min_next_time = 0;
m->byte_count = 0;
m->us_per_host_tick = 1000000.0 / AudioGetHostClockFrequency();
m->host_ticks_per_byte = (UInt64) (1000000.0 /
(m->us_per_host_tick * MAX_BYTES_PER_S));
return pmNoError;
}
@@ -451,8 +477,17 @@ midi_write_flush(PmInternal *midi, PmTimestamp timestamp)
assert(endpoint);
if (m->packet != NULL) {
/* out of space, send the buffer and start refilling it */
/* before we can send, maybe delay to limit data rate. OS X allows
* 15KB/s. */
UInt64 now = AudioGetCurrentHostTime();
if (now < m->min_next_time) {
usleep((useconds_t)
((m->min_next_time - now) * m->us_per_host_tick));
}
macHostError = MIDISend(portOut, endpoint, m->packetList);
m->packet = NULL; /* indicate no data in packetList now */
m->min_next_time = now + m->byte_count * m->host_ticks_per_byte;
m->byte_count = 0;
if (macHostError != noErr) goto send_packet_error;
}
return pmNoError;
@@ -475,9 +510,11 @@ send_packet(PmInternal *midi, Byte *message, unsigned int messageLength,
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
assert(m);
/* printf("add %d to packet %p len %d\n", message[0], m->packet, messageLength); */
m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
m->packet, timestamp, messageLength,
message);
m->byte_count += messageLength;
if (m->packet == NULL) {
/* out of space, send the buffer and start refilling it */
/* make midi->packet non-null to fool midi_write_flush into sending */
@@ -522,9 +559,6 @@ midi_write_short(PmInternal *midi, PmEvent *event)
/* if latency == 0, midi->now is not valid. We will just set it to zero */
if (midi->latency == 0) when = 0;
when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
/* make sure we don't go backward in time */
if (when_ns < m->last_time) when_ns = m->last_time;
m->last_time = when_ns;
timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
message[0] = Pm_MessageStatus(what);
@@ -532,6 +566,15 @@ midi_write_short(PmInternal *midi, PmEvent *event)
message[2] = Pm_MessageData2(what);
messageLength = midi_length(what);
/* make sure we go foreward in time */
if (timestamp < m->min_next_time) timestamp = m->min_next_time;
#ifdef LIMIT_RATE
if (timestamp < m->last_time)
timestamp = m->last_time;
m->last_time = timestamp + messageLength * m->host_ticks_per_byte;
#endif
/* Add this message to the packet list */
return send_packet(midi, message, messageLength, timestamp);
}
@@ -569,8 +612,16 @@ midi_end_sysex(PmInternal *midi, PmTimestamp when)
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
assert(m);
/* make sure we don't go backward in time */
if (m->sysex_timestamp < m->last_time) m->sysex_timestamp = m->last_time;
/* make sure we go foreward in time */
if (m->sysex_timestamp < m->min_next_time)
m->sysex_timestamp = m->min_next_time;
#ifdef LIMIT_RATE
if (m->sysex_timestamp < m->last_time)
m->sysex_timestamp = m->last_time;
m->last_time = m->sysex_timestamp + m->sysex_byte_count *
m->host_ticks_per_byte;
#endif
/* now send what's in the buffer */
err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count,