1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-10 16:43:33 +02:00

Extensive changes to improve NoteTrack display and (some) editing, NoteTrack playback via MIDI, and Midi-to-Audio alignment.

This commit is contained in:
rbdannenberg
2010-09-18 21:02:36 +00:00
parent f6327602e8
commit a1f0e5ed5b
96 changed files with 5679 additions and 3566 deletions

View File

@@ -1,46 +1,147 @@
README_MAC.txt for PortMidi
Roger Dannenberg
17 jan 2007
20 nov 2009
To build PortMidi for Mac OS X:
To build PortMidi for Mac OS X, you must install Xcode, and
if you want to build from the command line, you should install
CMake.
==== USING MAKE ====
CMake can build either Makefiles or Xcode projects, or you
can use the pre-built Xcode project. These approaches are
described in separate sections below.
go back up to the portmidi
directory and type
==== CLEANING UP ====
You can remove previously built apps, object code, and libraries by
running "cd pm_mac; sh cleanslate.sh"
==== USING CMAKE (AND COMMAND LINE TOOLS) ====
Start in the portmedia/portmidi directory.
make -f pm_mac/Makefile.osx
(You can also copy pm_mac/Makefile.osx to Makfile in the
portmidi directory and just type "make".)
(Begin note: make will invoke cmake to build a Makefile and then make to
build portmidi. This extra level allows you to correctly build
both Release and Debug versions. Release is the default, so to get
the Debug version, use:
The Makefile.osx will build all test programs and the portmidi
library. You may want to modify the Makefile.osx to remove the
PM_CHECK_ERRORS definition. For experimental software,
especially programs running from the command line, we
recommend using PM_CHECK_ERRORS -- it will terminate your
program and print a helpful message if any PortMidi
function returns an error code.
make -f pm_mac/Makefile.osx configuration=Debug
)
If you do not compile with PM_CHECK_ERRORS, you should
check for errors yourself.
Release version executables and libraries are now in
portmedia/portmidi/Release
The make file will also build an OS X Universal (ppc and i386)
dynamic link library using xcode. For instructions about this
and other options, type
Debug version executables and libraries are created in
portmedia/portmidi/Debug
The Debug versions are compiled with PM_CHECK_ERRORS which
prints an error message and aborts when an error code is returned
by PortMidi functions. This is useful for small command line
applications. Otherwise, you should check and handle error returns
in your program.
You can install portmidi as follows:
cd Release; sudo make install
This will install /usr/local/include/{portmidi.h, porttime.h}
and /usr/local/lib/{libportmidi.dylib, libportmidi_s.a, libpmjni.dylib}
You should now make the pmdefaults.app:
make -f pm_mac/Makefile.osx pmdefaults
NOTES: xcode is likely to crash after building pmdefaults, but
pmdefaults should be OK (it will be in Release)
Once you get started, you can run make directly in the
Debug or Release directories
make -f pm_mac/Makefile.osx help
==== USING XCODE ====
Open portmidi/pm_mac/pm_mac.xcode with Xcode and
build what you need: if you are just exploring, start with
the lib+test suite.
(1) Open portmidi/portmidi.xcodeproj with Xcode and
build what you need. The simplest thing is to build the
ALL_BUILD target. The default will be to build the Debug
version, but you may want to change this to Release.
The Debug version is compiled with PM_CHECK_ERRORS, and the
Release version is not. PM_CHECK_ERRORS will print an error
message and exit your program if any error is returned from
a call into PortMidi.
CMake (currently) also creates MinSizRel and RelWithDebInfo
versions, but only because I cannot figure out how to disable
them.
You will probably want the application PmDefaults, which sets
default MIDI In and Out devices for PortMidi. You may also
want to build a Java application using PortMidi. Since I have
not figured out how to use CMake to make an OS X Java application,
use pm_mac/pm_mac.xcodeproj as follows:
(2) open pm_mac/pm_mac.xcodeproj
(3) pm_java/pmjni/portmidi_JportmidiApi.h is needed
by libpmjni.jnilib, the Java native interface library. Since
portmidi_JportmidiApi.h is included with PortMidi, you can skip
to step 4, but if you really want to rebuild everything from
scratch, build the JPortMidiHeaders project.
(4) If you did not build libpmjni.dylib using portmidi.xcodeproj,
do it now. (It depends on portmidi_JportmidiApi.h, and the
PmDefaults project depends on libpmjni.dylib.)
(5) Returning to pm_mac.xcodeproj, build the PmDefaults program.
(6) If you wish, copy pm_mac/build/Deployment/PmDefaults.app to
your applications folder.
(7) If you want to install libportmidi.dylib, first make it with
Xcode, then
sudo make -f pm_mac/Makefile.osx install
This command will install /usr/local/include/{porttime.h, portmidi.h}
and /usr/local/lib/libportmidi.dylib
Note that the "install" function of xcode creates portmidi/Release
and does not install the library to /usr/local/lib, so please use
the command line installer.
==== USING CMAKE TO BUILD Xcode PROJECT ====
(1) Install CMake if you do not have it already.
(2) Open portmedia/portmidi/CMakeLists.txt with CMake
(3) Use Configure and Generate buttons
(4) This creates portmedia/portmidi/portmidi.xcodeproj.
(5) Follow the directions above using Xcode to compile
PortMidi.
Notes:
(1) You will also use pm_mac/pm_mac.xcodeproj, which
is not generated by CMake.
(2) The portmidi.xcodeproj created by CMake will have absolute
paths and depend on CMake, so it will not be very portable to other
machines or even directories. You can cd to pm_mac and run
clean_up_project.sh to convert pm_mac.xcodeproj to use relative
paths and to remove the scripts that run CMake. You'll first have
to modify pm_mac/clean_up_project.awk to contain the particular
absolute path or your portmidi project. Also, this is a pretty simple
and probably fragile hack to make a stand-alone xcode project. I
don't recommend it. Instead, either use CMake all the time, or use
the portmidi.xcodeproj you get with the distribution.
[pm_mac.xcodeproj courtesy of Leigh Smith]
CHANGELOG
20-Nov-2009 Roger B. Dannenberg
Added some install instructions
26-Sep-2009 Roger B. Dannenberg
More changes for using CMake, Makefiles, XCode
20-Sep-2009 Roger B. Dannenberg
Modifications for using CMake
14-Sep-2009 Roger B. Dannenberg
Modifications for using CMake
17-Jan-2007 Roger B. Dannenberg
Explicit instructions for Xcode
15-Jan-2007 Roger B. Dannenberg

View File

@@ -5,6 +5,8 @@
#include <stdlib.h>
#include <string.h>
#include "portmidi.h"
#include "pmutil.h"
#include "pminternal.h"
#include "pmmacosxcm.h"
#include "readbinaryplist.h"

File diff suppressed because it is too large Load Diff

View File

@@ -57,21 +57,29 @@
#define MIDI_EOX 0xf7
#define MIDI_STATUS_MASK 0x80
static MIDIClientRef client = NULL; /* Client handle to the MIDI server */
static MIDIPortRef portIn = NULL; /* Input port handle */
static MIDIPortRef portOut = NULL; /* Output port handle */
// "Ref"s are pointers on 32-bit machines and ints on 64 bit machines
// NULL_REF is our representation of either 0 or NULL
#ifdef __LP64__
#define NULL_REF 0
#else
#define NULL_REF NULL
#endif
static MIDIClientRef client = NULL_REF; /* Client handle to the MIDI server */
static MIDIPortRef portIn = NULL_REF; /* Input port handle */
static MIDIPortRef portOut = NULL_REF; /* Output port handle */
extern pm_fns_node pm_macosx_in_dictionary;
extern pm_fns_node pm_macosx_out_dictionary;
typedef struct midi_macosxcm_struct {
unsigned long sync_time; /* when did we last determine delta? */
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 */
int first_message; /* tells midi_write to sychronize timestamps */
int sysex_mode; /* middle of sending sysex */
unsigned long sysex_word; /* accumulate data when receiving sysex */
unsigned int sysex_byte_count; /* count how many received */
uint32_t sysex_word; /* accumulate data when receiving sysex */
uint32_t sysex_byte_count; /* count how many received */
char error[PM_HOST_ERROR_MSG_LEN];
char callback_error[PM_HOST_ERROR_MSG_LEN];
Byte packetBuffer[PACKET_BUFFER_SIZE];
@@ -81,7 +89,7 @@ typedef struct midi_macosxcm_struct {
MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */
/* allow for running status (is running status possible here? -rbd): -cpr */
unsigned char last_command;
long last_msg_length;
int32_t last_msg_length;
} midi_macosxcm_node, *midi_macosxcm_type;
/* private function declarations */
@@ -92,7 +100,7 @@ char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint);
static int
midi_length(long msg)
midi_length(int32_t msg)
{
int status, high, low;
static int high_lengths[] = {
@@ -235,7 +243,7 @@ readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
PmEvent event;
MIDIPacket *packet;
unsigned int packetIndex;
unsigned long now;
uint32_t now;
unsigned int status;
#ifdef CM_DEBUG
@@ -260,9 +268,9 @@ readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
packet->length); */
for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) {
/* Set the timestamp and dispatch this message */
event.timestamp =
event.timestamp = (PmTimestamp) /* explicit conversion */ (
(AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) /
(UInt64) 1000000;
(UInt64) 1000000);
status = packet->data[0];
/* process packet as sysex data if it begins with MIDI_SYSEX, or
MIDI_EOX or non-status byte with no running status */
@@ -303,9 +311,8 @@ midi_in_open(PmInternal *midi, void *driverInfo)
/* time_get does not take a parameter, so coerce */
midi->time_proc = (PmTimeProcPtr) Pt_Time;
}
endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
if (endpoint == NULL) {
endpoint = (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
if (endpoint == NULL_REF) {
return pmInvalidDeviceId;
}
@@ -333,7 +340,7 @@ midi_in_open(PmInternal *midi, void *driverInfo)
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDIPortConnectSource() in midi_in_open()",
macHostError);
(long) macHostError);
midi->descriptor = NULL;
pm_free(m);
return pmHostError;
@@ -353,8 +360,8 @@ midi_in_close(PmInternal *midi)
if (!m) return pmBadPtr;
endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
if (endpoint == NULL) {
endpoint = (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
if (endpoint == NULL_REF) {
pm_hosterror = pmBadPtr;
}
@@ -364,7 +371,7 @@ midi_in_close(PmInternal *midi)
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDIPortDisconnectSource() in midi_in_close()",
macHostError);
(long) macHostError);
err = pmHostError;
}
@@ -421,12 +428,12 @@ midi_abort(PmInternal *midi)
PmError err = pmNoError;
OSStatus macHostError;
MIDIEndpointRef endpoint =
(MIDIEndpointRef) descriptors[midi->device_id].descriptor;
(MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
macHostError = MIDIFlushOutput(endpoint);
if (macHostError != noErr) {
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDIFlushOutput()", macHostError);
"Host error %ld: MIDIFlushOutput()", (long) macHostError);
err = pmHostError;
}
return err;
@@ -439,7 +446,7 @@ midi_write_flush(PmInternal *midi, PmTimestamp timestamp)
OSStatus macHostError;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
MIDIEndpointRef endpoint =
(MIDIEndpointRef) descriptors[midi->device_id].descriptor;
(MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
assert(m);
assert(endpoint);
if (m->packet != NULL) {
@@ -454,7 +461,7 @@ send_packet_error:
pm_hosterror = macHostError;
sprintf(pm_hosterror_text,
"Host error %ld: MIDISend() in midi_write()",
macHostError);
(long) macHostError);
return pmHostError;
}
@@ -476,7 +483,12 @@ send_packet(PmInternal *midi, Byte *message, unsigned int messageLength,
/* out of space, send the buffer and start refilling it */
/* make midi->packet non-null to fool midi_write_flush into sending */
m->packet = (MIDIPacket *) 4;
if ((err = midi_write_flush(midi, timestamp)) != pmNoError) return err;
/* timestamp is 0 because midi_write_flush ignores timestamp since
* timestamps are already in packets. The timestamp parameter is here
* because other API's need it. midi_write_flush can be called
* from system-independent code that must be cross-API.
*/
if ((err = midi_write_flush(midi, 0)) != pmNoError) return err;
m->packet = MIDIPacketListInit(m->packetList);
assert(m->packet); /* if this fails, it's a programming error */
m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
@@ -491,8 +503,8 @@ send_packet(PmInternal *midi, Byte *message, unsigned int messageLength,
static PmError
midi_write_short(PmInternal *midi, PmEvent *event)
{
long when = event->timestamp;
long what = event->message;
PmTimestamp when = event->timestamp;
PmMessage what = event->message;
MIDITimeStamp timestamp;
UInt64 when_ns;
midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
@@ -659,9 +671,9 @@ CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
CFRelease(str);
}
MIDIEntityRef entity = NULL;
MIDIEntityRef entity = NULL_REF;
MIDIEndpointGetEntity(endpoint, &entity);
if (entity == NULL)
if (entity == NULL_REF)
// probably virtual
return result;
@@ -675,9 +687,9 @@ CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
}
}
// now consider the device's name
MIDIDeviceRef device = NULL;
MIDIDeviceRef device = NULL_REF;
MIDIEntityGetDevice(entity, &device);
if (device == NULL)
if (device == NULL_REF)
return result;
str = NULL;
@@ -722,17 +734,17 @@ static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
CFStringRef str;
OSStatus err;
int i;
long i;
// Does the endpoint have connections?
CFDataRef connections = NULL;
int nConnected = 0;
long nConnected = 0;
bool anyStrings = false;
err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections);
if (connections != NULL) {
// It has connections, follow them
// Concatenate the names of all connected devices
nConnected = CFDataGetLength(connections) / sizeof(MIDIUniqueID);
nConnected = CFDataGetLength(connections) / (int32_t) sizeof(MIDIUniqueID);
if (nConnected) {
const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
for (i = 0; i < nConnected; ++i, ++pid) {
@@ -901,7 +913,7 @@ PmError pm_macosxcm_init(void)
/* Iterate over the MIDI input devices */
for (i = 0; i < numInputs; i++) {
endpoint = MIDIGetSource(i);
if (endpoint == NULL) {
if (endpoint == NULL_REF) {
continue;
}
@@ -911,13 +923,13 @@ PmError pm_macosxcm_init(void)
/* Register this device with PortMidi */
pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
TRUE, (void*)endpoint, &pm_macosx_in_dictionary);
TRUE, (void *) (long) endpoint, &pm_macosx_in_dictionary);
}
/* Iterate over the MIDI output devices */
for (i = 0; i < numOutputs; i++) {
endpoint = MIDIGetDestination(i);
if (endpoint == NULL) {
if (endpoint == NULL_REF) {
continue;
}
@@ -927,20 +939,22 @@ PmError pm_macosxcm_init(void)
/* Register this device with PortMidi */
pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
FALSE, (void*)endpoint, &pm_macosx_out_dictionary);
FALSE, (void *) (long) endpoint,
&pm_macosx_out_dictionary);
}
return pmNoError;
error_return:
pm_hosterror = macHostError;
sprintf(pm_hosterror_text, "Host error %ld: %s\n", macHostError, error_text);
sprintf(pm_hosterror_text, "Host error %ld: %s\n", (long) macHostError,
error_text);
pm_macosxcm_term(); /* clear out any opened ports */
return pmHostError;
}
void pm_macosxcm_term(void)
{
if (client != NULL) MIDIClientDispose(client);
if (portIn != NULL) MIDIPortDispose(portIn);
if (portOut != NULL) MIDIPortDispose(portOut);
if (client != NULL_REF) MIDIClientDispose(client);
if (portIn != NULL_REF) MIDIPortDispose(portIn);
if (portOut != NULL_REF) MIDIPortDispose(portOut);
}

View File

@@ -3,6 +3,10 @@
readbinaryplist.c -- Roger B. Dannenberg, Jun 2008
Based on ReadBinaryPList.m by Jens Ayton, 2007
Note that this code is intended to read preference files and has an upper
bound on file size (currently 100MB) and assumes in some places that 32 bit
offsets are sufficient.
Here are his comments:
Reader for binary property list files (version 00).
@@ -74,13 +78,25 @@ memory requested or calls longjmp, so callers don't have to check.
#include <stdio.h>
#include <sys/stat.h>
#include "readbinaryplist.h"
#include <Carbon/Carbon.h>
#define BPLIST_LOG_VERBOSE 1
#include "Folders.h"
#define NO 0
#define YES 1
#define BOOL int
#define MAXPATHLEN 256
/* there are 2 levels of error logging/printing:
* BPLIST_LOG and BPLIST_LOG_VERBOSE
* either or both can be set to non-zero to turn on
* If BPLIST_LOG_VERBOSE is true, then BPLIST_LOG
* is also true.
*
* In the code, logging is done by calling either
* bplist_log() or bplist_log_verbose(), which take
* parameters like printf but might be a no-op.
*/
/* #define BPLIST_LOG_VERBOSE 1 */
#if BPLIST_LOG_VERBOSE
@@ -245,7 +261,7 @@ void value_set_uid(value_ptr v, uint64_t uid)
v->tag = kTAG_UID; v->uinteger = uid;
}
// value->data points to a pldata that points to the actual bytes
// v->data points to a pldata that points to the actual bytes
// the bytes are copied, so caller must free byte source (*data)
void value_set_data(value_ptr v, const uint8_t *data, size_t len) {
v->tag = kTAG_DATA;
@@ -324,18 +340,26 @@ value_ptr bplist_read_file(char *filename)
value_ptr value;
int rslt = stat(filename, &stbuf);
if (rslt) {
perror("in stat: ");
#if BPLIST_LOG
perror("in stat");
#endif
bplist_log("Could not stat %s, error %d\n", filename, rslt);
return NULL;
}
pldata.len = stbuf.st_size;
// if file is >100MB, assume it is not a preferences file and give up
if (stbuf.st_size > 100000000) {
bplist_log("Large file %s encountered (%llu bytes) -- not read\n",
filename, stbuf.st_size);
return NULL;
}
pldata.len = (size_t) stbuf.st_size;
// note: this is supposed to be malloc, not allocate. It is separate
// from the graph structure, large, and easy to free right after
// parsing.
pldata.data = (uint8_t *) malloc(pldata.len);
if (!pldata.data) {
bplist_log("Could not allocate %d bytes for %s\n",
(long) pldata.len, filename);
bplist_log("Could not allocate %lu bytes for %s\n",
(unsigned long) pldata.len, filename);
return NULL;
}
file = fopen(filename, "rb");
@@ -664,7 +688,8 @@ static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset)
}
if (size == sizeof (float)) {
uint32_t i = read_sized_int(bplist, offset + 1, size);
// cast is ok because we know size is 4 bytes
uint32_t i = (uint32_t) read_sized_int(bplist, offset + 1, size);
// Note that this handles byte swapping.
value_set_real(value, *(float *)&i);
return value;
@@ -755,7 +780,8 @@ static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset)
return NULL;
value = value_create();
value_set_data(value, bplist->data_bytes + offset, size);
// cast is ok because we only allow files up to 100MB:
value_set_data(value, bplist->data_bytes + (size_t) offset, (size_t) size);
return value;
}
@@ -772,7 +798,9 @@ static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset)
return NULL;
value = value_create();
value_set_ascii_string(value, bplist->data_bytes + offset, size);
// cast is ok because we only allow 100MB files
value_set_ascii_string(value, bplist->data_bytes + (size_t) offset,
(size_t) size);
return value;
}
@@ -789,7 +817,9 @@ static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset)
return NULL;
value = value_create();
value_set_unicode_string(value, bplist->data_bytes + offset, size);
// cast is ok because we only allow 100MB files
value_set_unicode_string(value, bplist->data_bytes + (size_t) offset,
(size_t) size);
return value;
}
@@ -814,15 +844,20 @@ static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset)
return NULL;
}
assert(NO); // original code suggests using a string for a key
// assert(NO); // original code suggests using a string for a key
// but our dictionaries all use big ints for keys, so I don't know
// what to do here
// In practice, I believe this code is never executed by PortMidi.
// I changed it to do something and not raise compiler warnings, but
// not sure what the code should do.
value = value_create();
value_set_uid(value, uid);
// return [NSDictionary dictionaryWithObject:
// [NSNumber numberWithUnsignedLongLong:value]
// forKey:"CF$UID"];
return value;
}
@@ -861,11 +896,12 @@ static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset)
assert(value);
if (count == 0) {
value_set_array(value, array, count);
// count must be size_t or smaller because max file size is 100MB
value_set_array(value, array, (size_t) count);
return value;
}
array = allocate(sizeof(value_ptr) * count);
array = allocate(sizeof(value_ptr) * (size_t) count);
for (i = 0; i != count; ++i) {
bplist_log_verbose("[%u]\n", i);
@@ -879,8 +915,8 @@ static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset)
break;
}
}
if (ok) {
value_set_array(value, array, count);
if (ok) { // count is smaller than size_t max because of 100MB file limit
value_set_array(value, array, (size_t) count);
}
return value;

View File

@@ -3,6 +3,8 @@
Roger B. Dannenberg, Jun 2008
*/
#include <stdint.h> /* for uint8_t ... */
#ifndef TRUE
#define TRUE 1
#define FALSE 0
@@ -34,13 +36,13 @@ enum
};
typedef struct {
typedef struct pldata_struct {
uint8_t *data;
size_t len;
} pldata_node, *pldata_ptr;
typedef struct {
typedef struct array_struct {
struct value_struct **array;
uint64_t length;
} array_node, *array_ptr;