Update of /cvsroot/audacity/lib-src/portmidi/pm_mac
In directory 
23jxhf1.ch3.sourceforge.com:/tmp/cvs-serv11472/lib-src/portmidi/pm_mac

Added Files:
        finddefault.c Makefile.osx pmmac.c pmmac.h pmmacosxcm.c 
        pmmacosxcm.h readbinaryplist.c readbinaryplist.h 
        README_MAC.txt 
Log Message:
Added the 'portmidi' library (used for MIDI playback) to the repository.
Edited AudioIO.cpp to allow for MIDI playback (all changes are wrapped within 
EXPERIMENTAL_MIDI_OUT).
Edited MidiIOprefs.cpp to allow changing MIDI devices via the MIDI preferences 
panel.
Other various MIDI-related bug fixes in portmidi, portsmf, and libscorealign.

--- NEW FILE: README_MAC.txt ---
README_MAC.txt for PortMidi
Roger Dannenberg
17 jan 2007

To build PortMidi for Mac OS X:

==== USING MAKE ====

go back up to the portmidi
directory and type 

make -f pm_mac/Makefile.osx

(You can also copy pm_mac/Makefile.osx to Makfile in the 
portmidi directory and just type "make".)

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.

If you do not compile with PM_CHECK_ERRORS, you should 
check for errors yourself.

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

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.

[pm_mac.xcodeproj courtesy of Leigh Smith]

CHANGELOG

17-Jan-2007 Roger B. Dannenberg
    Explicit instructions for Xcode
15-Jan-2007 Roger B. Dannenberg
    Changed instructions because of changes to Makefile.osx
07-Oct-2006 Roger B. Dannenberg
    Added directions for xcodebuild
29-aug-2006 Roger B. Dannenberg
    Updated this documentation.
 

--- NEW FILE: pmmac.h ---
/* pmmac.h */

extern PmDeviceID pm_default_input_device_id;
extern PmDeviceID pm_default_output_device_id;
--- NEW FILE: readbinaryplist.h ---
/* readbinaryplist.h -- header to read preference files

   Roger B. Dannenberg, Jun 2008
*/

#ifndef TRUE
    #define TRUE 1
    #define FALSE 0
#endif

#define MAX_KEY_SIZE 256

enum
{
    // Object tags (high nybble)
    kTAG_SIMPLE = 0x00,        // Null, true, false, filler, or invalid
    kTAG_INT = 0x10,
    kTAG_REAL = 0x20,
    kTAG_DATE = 0x30,
    kTAG_DATA = 0x40,
    kTAG_ASCIISTRING = 0x50,
    kTAG_UNICODESTRING = 0x60,
    kTAG_UID = 0x80,
    kTAG_ARRAY = 0xA0,
    kTAG_DICTIONARY = 0xD0,
    
    // "simple" object values
    kVALUE_NULL = 0x00,
    kVALUE_FALSE = 0x08,
    kVALUE_TRUE = 0x09,
    kVALUE_FILLER = 0x0F,
    
    kVALUE_FULLDATETAG = 0x33        // Dates are tagged with a whole byte.
};


typedef struct {
    uint8_t *data;
    size_t len;
} pldata_node, *pldata_ptr;


typedef struct {
    struct value_struct **array;
    uint64_t length;
} array_node, *array_ptr;


// a dict_node is a list of <key, value> pairs
typedef struct dict_struct {
    struct value_struct *key;
    struct value_struct *value;
    struct dict_struct *next;
} dict_node, *dict_ptr;


// an value_node is a value with a tag telling the type
typedef struct value_struct {
    int tag;
    union {
        int64_t integer;
        uint64_t uinteger;
        double real;
        char *string;
        pldata_ptr data;
        array_ptr array;
        struct dict_struct *dict;
    };
} value_node, *value_ptr;


value_ptr bplist_read_file(char *filename);
value_ptr bplist_read_user_pref(char *filename);
value_ptr bplist_read_system_pref(char *filename);
void bplist_free_data();

/*************** functions for accessing values ****************/

char *value_get_asciistring(value_ptr v);
value_ptr value_dict_lookup_using_string(value_ptr v, char *key);
value_ptr value_dict_lookup_using_path(value_ptr v, char *path);

/*************** functions for debugging ***************/

void plist_print(value_ptr v);


--- NEW FILE: pmmac.c ---
/* pmmac.c -- PortMidi os-dependent code */

/* This file only needs to implement:
pm_init(), which calls various routines to register the 
available midi devices,
Pm_GetDefaultInputDeviceID(), and
Pm_GetDefaultOutputDeviceID().
It is seperate from pmmacosxcm because we might want to register
non-CoreMIDI devices.
*/

#include "stdlib.h"
#include "portmidi.h"
#include "pmutil.h"
#include "pminternal.h"
#include "pmmacosxcm.h"

PmDeviceID pm_default_input_device_id = -1;
PmDeviceID pm_default_output_device_id = -1;

void pm_init()
{
    PmError err = pm_macosxcm_init();
    // this is set when we return to Pm_Initialize, but we need it
    // now in order to (successfully) call Pm_CountDevices()
    pm_initialized = TRUE;
    if (!err) {
        pm_default_input_device_id = find_default_device(
                "/PortMidi/PM_RECOMMENDED_INPUT_DEVICE", TRUE, 
                pm_default_input_device_id);
        pm_default_output_device_id = find_default_device(
                "/PortMidi/PM_RECOMMENDED_OUTPUT_DEVICE", FALSE, 
                pm_default_output_device_id);
    }
}


void pm_term(void)
{
    pm_macosxcm_term();
}


PmDeviceID Pm_GetDefaultInputDeviceID()
{
    Pm_Initialize();
    return pm_default_input_device_id;
}

PmDeviceID Pm_GetDefaultOutputDeviceID() {
    Pm_Initialize();
    return pm_default_output_device_id;
}

void *pm_alloc(size_t s) { return malloc(s); }

void pm_free(void *ptr) { free(ptr); }



--- NEW FILE: Makefile.osx ---
# MAKEFILE FOR PORTMIDI AND PORTTIME

VFLAGS = -g


# For debugging, define PM_CHECK_ERRORS
PMFLAGS = -DPM_CHECK_ERRORS
# Otherwise do not define PM_CHECK_ERRORS
# PMFLAGS = 
# Dynamic Lib is built without PM_CHECK_ERRORS
PMDLFLAGS = 
# Path for installation of dynamic libraries
PF = /usr/local
# Architecture for static libs
ARCH = -arch ppc -arch i386

pmlib = pm_mac/libportmidi.a

CC = gcc $(VFLAGS) $(PMFLAGS) $(ARCH) -g -Ipm_common -Iporttime

MACLIB = -framework CoreMIDI -framework CoreFoundation -framework CoreAudio \
         -framework CoreServices

PMSRC =  pm_mac/pmmac.c pm_common/pmutil.c pm_common/portmidi.c \
   pm_mac/pmmacosxcm.c pm_mac/readbinaryplist.c pm_mac/finddefault.c

PTSRC = porttime/porttime.c porttime/ptmacosx_mach.c

pmobjects = pm_common/pmutil.o pm_mac/pmmac.o  \
        pm_common/portmidi.o pm_mac/pmmacosxcm.o \
        pm_mac/finddefault.o pm_mac/readbinaryplist.o \
        porttime/porttime.o porttime/ptmacosx_mach.o 

current: all

all: $(pmlib) $(ptlib) pm_test/test pm_test/sysex pm_test/midithread \
        pm_test/latency pm_test/midithru pm_test/qtest pm_test/mm
        echo $$'\n\
**** For instructions: make -f pm_mac\Makefile.osx help ****\n'

help:
        echo $$'\n\n\
This is help for portmidi/pm_mac/Makefile.osx\n\n\
Flags for static library and apps are $(PMFLAGS)\n\
Flags for dynamic library are $(PMDLFLAGS)\n\
Installation path for dylib is $(PF)\n\
You might want to change these.\n\n\
To build a static library and test applications,\n        \
make -f pm_mac/Makefile.osx\n\
To build and install dynamic library, note you must use\n\
"cleanest" to remove .o and dylib, especially if this\n\
library uses different compiler flags:\n        \
make -f pm_mac/Makefile.osx cleanest\n        \
make -f pm_mac/Makefile.osx dylib\n\
To install dynamic library to /usr/local,\n        \
sudo make -f pm_mac/Makefile.osx install\n\
To install universal dynamic library with xcode,\n        \
make -f pm_mac/Makefile.osx install-with-xcode\n\
To make dynamic library for Java,\n        \
make -f pm_mac/Makefile.osx cleanest\n        \
make -f pm_mac/Makefile.osx jni\n\
To make PmDefaults Java application,\n        \
make -f pm_mac/Makefile.osx pmdefaults\n\n'

$(pmlib): pm_mac/Makefile.osx $(pmobjects)
        rm -rf $(pmlib)
        ar -crs $(pmlib) $(pmobjects)

pm_mac/pmmacosxcm.o: pm_mac/Makefile.osx pm_mac/pmmacosxcm.c pm_mac/pmmacosxcm.h
        $(CC) -c pm_mac/pmmacosxcm.c -o pm_mac/pmmacosxcm.o

pm_mac/readbinaryplist.o: pm_mac/Makefile.osx pm_mac/readbinaryplist.c 
pm_mac/readbinaryplist.h
        $(CC) -c -I/Developer/Headers/FlatCarbon \
        pm_mac/readbinaryplist.c -o pm_mac/readbinaryplist.o

pm_mac/finddefault.o: pm_mac/Makefile.osx pm_mac/finddefault.c 
pm_mac/pmmacosxcm.h
        $(CC) -c pm_mac/finddefault.c -o pm_mac/finddefault.o

pm_test/test: pm_mac/Makefile.osx pm_test/test.o $(pmlib)
        $(CC) pm_test/test.o -o pm_test/test $(pmlib) $(MACLIB)

pm_test/sysex: pm_mac/Makefile.osx pm_test/sysex.o $(pmlib)
        $(CC) pm_test/sysex.o -o pm_test/sysex $(pmlib) $(MACLIB)

pm_test/midithread: pm_mac/Makefile.osx pm_test/midithread.o $(pmlib)
        $(CC) pm_test/midithread.o -o pm_test/midithread \
        $(pmlib) $(MACLIB)

pm_test/latency: pm_mac/Makefile.osx $(pmlib) pm_test/latency.o 
        $(CC) pm_test/latency.o -o pm_test/latency $(pmlib) \
        $(MACLIB) -lpthread -lm

pm_test/midithru: pm_mac/Makefile.osx $(pmlib) pm_test/midithru.o 
        $(CC) pm_test/midithru.o -o pm_test/midithru $(pmlib) \
        $(MACLIB) -lpthread -lm

pm_test/mm: pm_mac/Makefile.osx pm_test/mm.o $(pmlib)
        $(CC) pm_test/mm.o -o pm_test/mm $(pmlib) $(MACLIB)

porttime/ptlinux.o: pm_mac/Makefile.osx porttime/ptlinux.c
        $(CC) -c porttime/ptlinux.c -o porttime/ptlinux.o

pm_test/qtest: pm_mac/Makefile.osx pm_test/qtest.o $(pmlib)
        $(CC) pm_test/qtest.o -o pm_test/qtest $(pmlib) $(MACLIB)

clean:
        rm -f *.o *~ core* */*.o */*~ */core* pm_test/*/pm_dll.dll 
        rm -f *.opt *.ncb *.plg pm_win/Debug/pm_dll.lib 
pm_win/Release/pm_dll.lib
        rm -f pm_test/*.opt pm_test/*.ncb
        rm -f pm_java/pmjni/*.o pm_java/pmjni/*~ pm_java/*.h

cleaner: clean
        rm -rf pm_mac/build
        rm -f *.dylib
        rm -f pm_mac/*.dylib
        rm -f pm_java/pmjni/*.jnilib

cleanest: cleaner
        rm -f $(pmlib) porttime/libporttime.dylib pm_mac/libportmidi.dylib
        rm -f pm_test/test pm_test/sysex pm_test/midithread
        rm -f pm_test/latency pm_test/midithru pm_test/qtest pm_test/mm
        rm -f pm_java/*/*.class
        rm -f pm_java/pmjni/jportmidi_JPortMidiApi_PortMidiStream.h

backup: cleanest
        cd ..; zip -r portmidi.zip portmidi

dylib: pm_mac/libportmidi.dylib

pm_mac/libportmidi.dylib: $(PMSRC) $(PTSRC)
        cc -single_module -g $(PMDLFLAGS) -Iporttime -Ipm_common $(ARCH) \
          -dynamiclib $(PMSRC) $(PTSRC) -o pm_mac/libportmidi.dylib \
          -flat_namespace -undefined suppress \
          -framework CoreMIDI -framework CoreFoundation \
          -framework CoreAudio -framework CoreServices

install: porttime/porttime.h pm_common/portmidi.h \
           pm_mac/libportmidi.dylib
        install porttime/porttime.h  $(PF)/include/
        install pm_common/portmidi.h $(PF)/include
        install pm_mac/libportmidi.dylib $(PF)/lib/

# note - this uses xcode to build and install portmidi universal binaries
install-with-xcode:
        sudo xcodebuild -project pm_mac/pm_mac.xcodeproj \
                -configuration Deployment install DSTROOT=/

##### build java interface and application ######

jni: pm_java/pmjni/libpmjni.jnilib

pm_java/pmjni/libpmjni.jnilib: $(PMSRC) pm_java/jportmidi/JPortMidiApi.class 
pm_java/pmjni/jportmidi_JPortMidiApi.h pm_java/pmjni/pmjni.c
        cc -single_module -g -Iporttime -Ipm_common \
          -Ipm_java/pmjni -I/System/Library/Frameworks/JavaVM.framework/Headers 
\
          -I/Developer/Headers/FlatCarbon \
          $(ARCH) -dynamiclib $(PMSRC) $(PTSRC) pm_java/pmjni/pmjni.c \
          -o pm_java/pmjni/libpmjni.jnilib \
          -flat_namespace -undefined suppress \
          -framework CoreMIDI -framework CoreFoundation -framework CoreAudio \
          -framework JavaVM

pm_java/pmjni/jportmidi_JPortMidiApi.h: pm_java/jportmidi/JPortMidiApi.class
        cd pm_java; javah jportmidi.JPortMidiApi
        mv pm_java/jportmidi_JportMidiApi.h pm_java/pmjni

JAVASRC = pmdefaults/PmDefaultsFrame.java \
            pmdefaults/PmDefaults.java \
            jportmidi/JPortMidiApi.java jportmidi/JPortMidi.java \
            jportmidi/JPortMidiException.java

# this compiles ALL of the java code
pm_java/jportmidi/JPortMidiApi.class: $(JAVASRC:%=pm_java/%)
        cd pm_java; javac $(JAVASRC)

pmdefaults: pm_java/pmjni/libpmjni.jnilib pm_java/jportmidi/JPortMidiApi.class
        echo "pmdefaults java application is made"

###### test plist reader #######
PLHDR = pm_mac/readbinaryplist.h
PLSRC = pm_mac/plisttest.c pm_mac/readbinaryplist.c
pm_mac/plisttest: $(PLHDR) $(PLSRC)
        cc -g -Ipm_mac \
            -I/Developer/Headers/FlatCarbon \
            -I/System/Library/Frameworks/CoreFoundation.framework/Headers \
            -I/System/Library/Frameworks/CoreServices.framework/Headers \
            $(PLSRC) -o pm_mac/plisttest \
            -framework CoreFoundation -framework CoreServices


--- NEW FILE: pmmacosxcm.c ---
/*
 * Platform interface to the MacOS X CoreMIDI framework
 * 
 * Jon Parise <jparise at cmu.edu>
 * 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 $
 */
 
/* Notes:
    since the input and output streams are represented by MIDIEndpointRef
    values and almost no other state, we store the MIDIEndpointRef on
    descriptors[midi->device_id].descriptor. The only other state we need
    is for errors: we need to know if there is an error and if so, what is
    the error text. We use a structure with two kinds of
    host error: "error" and "callback_error". That way, asynchronous callbacks
    do not interfere with other error information.
    
    OS X does not seem to have an error-code-to-text function, so we will
    just use text messages instead of error codes.
 */

#include <stdlib.h>

//#define CM_DEBUG 1

#include "portmidi.h"
#include "pmutil.h"
#include "pminternal.h"
#include "porttime.h"
#include "pmmac.h"
#include "pmmacosxcm.h"

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

#include <CoreServices/CoreServices.h>
#include <CoreMIDI/MIDIServices.h>
#include <CoreAudio/HostTime.h>

#define PACKET_BUFFER_SIZE 1024

/* 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.
 */
#define SYSEX_BUFFER_SIZE 4

#define VERBOSE_ON 1
#define VERBOSE if (VERBOSE_ON)

#define MIDI_SYSEX      0xf0
#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 */

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? */
    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 */
    char error[PM_HOST_ERROR_MSG_LEN];
    char callback_error[PM_HOST_ERROR_MSG_LEN];
    Byte packetBuffer[PACKET_BUFFER_SIZE];
    MIDIPacketList *packetList; /* a pointer to packetBuffer */
    MIDIPacket *packet;
    Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */
    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;
} midi_macosxcm_node, *midi_macosxcm_type;

/* private function declarations */
MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp);
PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp);

char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint);


static int
midi_length(long msg)
{
    int status, high, low;
    static int high_lengths[] = {
        1, 1, 1, 1, 1, 1, 1, 1,         /* 0x00 through 0x70 */
        3, 3, 3, 3, 2, 2, 3, 1          /* 0x80 through 0xf0 */
    };
    static int low_lengths[] = {
        1, 2, 3, 2, 1, 1, 1, 1,         /* 0xf0 through 0xf8 */
        1, 1, 1, 1, 1, 1, 1, 1          /* 0xf9 through 0xff */
    };

    status = msg & 0xFF;
    high = status >> 4;
    low = status & 15;

    return (high != 0xF) ? high_lengths[high] : low_lengths[low];
}

static PmTimestamp midi_synchronize(PmInternal *midi)
{
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    UInt64 pm_stream_time_2 = 
            AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
    PmTimestamp real_time;
    UInt64 pm_stream_time;
    /* if latency is zero and this is an output, there is no 
       time reference and midi_synchronize should never be called */
    assert(midi->time_proc);
    assert(!(midi->write_flag && midi->latency == 0));
    do {
         /* read real_time between two reads of stream time */
         pm_stream_time = pm_stream_time_2;
         real_time = (*midi->time_proc)(midi->time_info);
         pm_stream_time_2 = 
AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
         /* repeat if more than 0.5 ms has elapsed */
    } while (pm_stream_time_2 > pm_stream_time + 500000);
    m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000);
    m->sync_time = real_time;
    return real_time;
}


static void
process_packet(MIDIPacket *packet, PmEvent *event, 
               PmInternal *midi, midi_macosxcm_type m)
{
    /* handle a packet of MIDI messages from CoreMIDI */
    /* there may be multiple short messages in one packet (!) */
    unsigned int remaining_length = packet->length;
    unsigned char *cur_packet_data = packet->data;
    while (remaining_length > 0) {
        if (cur_packet_data[0] == MIDI_SYSEX ||
            /* are we in the middle of a sysex message? */
            (m->last_command == 0 &&
             !(cur_packet_data[0] & MIDI_STATUS_MASK))) {
            m->last_command = 0; /* no running status */
            unsigned int amt = pm_read_bytes(midi, cur_packet_data, 
                                             remaining_length, 
                                             event->timestamp);
            remaining_length -= amt;
            cur_packet_data += amt;
        } else if (cur_packet_data[0] == MIDI_EOX) {
            /* this should never happen, because pm_read_bytes should
             * get and read all EOX bytes*/
            midi->sysex_in_progress = FALSE;
            m->last_command = 0;
        } else if (cur_packet_data[0] & MIDI_STATUS_MASK) {
            /* compute the length of the next (short) msg in packet */
            unsigned int cur_message_length = midi_length(cur_packet_data[0]);
            if (cur_message_length > remaining_length) {
#ifdef DEBUG
                printf("PortMidi debug msg: not enough data");
#endif
                /* since there's no more data, we're done */
                return;
            }
            m->last_msg_length = cur_message_length;
            m->last_command = cur_packet_data[0];
            switch (cur_message_length) {
            case 1:
                event->message = Pm_Message(cur_packet_data[0], 0, 0);
                break; 
            case 2:
                event->message = Pm_Message(cur_packet_data[0], 
                                            cur_packet_data[1], 0);
                break;
            case 3:
                event->message = Pm_Message(cur_packet_data[0],
                                            cur_packet_data[1], 
                                            cur_packet_data[2]);
                break;
            default:
                /* PortMIDI internal error; should never happen */
                assert(cur_message_length == 1);
                return; /* give up on packet if continued after assert */
            }
            pm_read_short(midi, event);
            remaining_length -= m->last_msg_length;
            cur_packet_data += m->last_msg_length;
        } else if (m->last_msg_length > remaining_length + 1) {
            /* we have running status, but not enough data */
#ifdef DEBUG
            printf("PortMidi debug msg: not enough data in CoreMIDI packet");
#endif
            /* since there's no more data, we're done */
            return;
        } else { /* output message using running status */
            switch (m->last_msg_length) {
            case 1:
                event->message = Pm_Message(m->last_command, 0, 0);
                break;
            case 2:
                event->message = Pm_Message(m->last_command, 
                                            cur_packet_data[0], 0);
                break;
            case 3:
                event->message = Pm_Message(m->last_command, 
                                            cur_packet_data[0], 
                                            cur_packet_data[1]);
                break;
            default:
                /* last_msg_length is invalid -- internal PortMIDI error */
                assert(m->last_msg_length == 1);
            }
            pm_read_short(midi, event);
            remaining_length -= (m->last_msg_length - 1);
            cur_packet_data += (m->last_msg_length - 1);
        }
    }
}



/* called when MIDI packets are received */
static void
readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
{
    PmInternal *midi;
    midi_macosxcm_type m;
    PmEvent event;
    MIDIPacket *packet;
    unsigned int packetIndex;
    unsigned long now;
    unsigned int status;
    
#ifdef CM_DEBUG
    printf("readProc: numPackets %d: ", newPackets->numPackets);
#endif

    /* Retrieve the context for this connection */
    midi = (PmInternal *) connRefCon;
    m = (midi_macosxcm_type) midi->descriptor;
    assert(m);
    
    /* synchronize time references every 100ms */
    now = (*midi->time_proc)(midi->time_info);
    if (m->first_message || m->sync_time + 100 /*ms*/ < now) { 
        /* time to resync */
        now = midi_synchronize(midi);
        m->first_message = FALSE;
    }
    
    packet = (MIDIPacket *) &newPackets->packet[0];
    /* printf("readproc packet status %x length %d\n", packet->data[0], 
               packet->length); */
    for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) {
        /* Set the timestamp and dispatch this message */
        event.timestamp = 
                (AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) / 
                (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 */
#ifdef CM_DEBUG
        printf(" %d", packet->length);
#endif
        if (status == MIDI_SYSEX || status == MIDI_EOX || 
            ((!(status & MIDI_STATUS_MASK)) && !m->last_command)) {
            /* previously was: !(status & MIDI_STATUS_MASK)) {
             * but this could mistake running status for sysex data
             */
            /* reset running status data -cpr */
            m->last_command = 0;
            m->last_msg_length = 0;
            /* printf("sysex packet length: %d\n", packet->length); */
            pm_read_bytes(midi, packet->data, packet->length, event.timestamp);
        } else {
            process_packet(packet, &event, midi, m);
        }
        packet = MIDIPacketNext(packet);
    }
#ifdef CM_DEBUG
    printf("\n");
#endif
}

static PmError
midi_in_open(PmInternal *midi, void *driverInfo)
{
    MIDIEndpointRef endpoint;
    midi_macosxcm_type m;
    OSStatus macHostError;
    
    /* insure that we have a time_proc for timing */
    if (midi->time_proc == NULL) {
        if (!Pt_Started()) 
            Pt_Start(1, 0, 0);
        /* 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) {
        return pmInvalidDeviceId;
    }

    m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
    midi->descriptor = m;
    if (!m) {
        return pmInsufficientMemory;
    }
    m->error[0] = 0;
    m->callback_error[0] = 0;
    m->sync_time = 0;
    m->delta = 0;
    m->last_time = 0;
    m->first_message = TRUE;
    m->sysex_mode = FALSE;
    m->sysex_word = 0;
    m->sysex_byte_count = 0;
    m->packetList = NULL;
    m->packet = NULL;
    m->last_command = 0;
    m->last_msg_length = 0;

    macHostError = MIDIPortConnectSource(portIn, endpoint, midi);
    if (macHostError != noErr) {
        pm_hosterror = macHostError;
        sprintf(pm_hosterror_text, 
                "Host error %ld: MIDIPortConnectSource() in midi_in_open()",
                macHostError);
        midi->descriptor = NULL;
        pm_free(m);
        return pmHostError;
    }
    
    return pmNoError;
}

static PmError
midi_in_close(PmInternal *midi)
{
    MIDIEndpointRef endpoint;
    OSStatus macHostError;
    PmError err = pmNoError;
    
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    
    if (!m) return pmBadPtr;

    endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
    if (endpoint == NULL) {
        pm_hosterror = pmBadPtr;
    }
    
    /* shut off the incoming messages before freeing data structures */
    macHostError = MIDIPortDisconnectSource(portIn, endpoint);
    if (macHostError != noErr) {
        pm_hosterror = macHostError;
        sprintf(pm_hosterror_text, 
                "Host error %ld: MIDIPortDisconnectSource() in midi_in_close()",
                macHostError);
        err = pmHostError;
    }
    
    midi->descriptor = NULL;
    pm_free(midi->descriptor);
    
    return err;
}


static PmError
midi_out_open(PmInternal *midi, void *driverInfo)
{
    midi_macosxcm_type m;

    m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
    midi->descriptor = m;
    if (!m) {
        return pmInsufficientMemory;
    }
    m->error[0] = 0;
    m->callback_error[0] = 0;
    m->sync_time = 0;
    m->delta = 0;
    m->last_time = 0;
    m->first_message = TRUE;
    m->sysex_mode = FALSE;
    m->sysex_word = 0;
    m->sysex_byte_count = 0;
    m->packetList = (MIDIPacketList *) m->packetBuffer;
    m->packet = NULL;
    m->last_command = 0;
    m->last_msg_length = 0;

    return pmNoError;
}


static PmError
midi_out_close(PmInternal *midi)
{
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    if (!m) return pmBadPtr;
    
    midi->descriptor = NULL;
    pm_free(midi->descriptor);
    
    return pmNoError;
}

static PmError
midi_abort(PmInternal *midi)
{
    PmError err = pmNoError;
    OSStatus macHostError;
    MIDIEndpointRef endpoint =
            (MIDIEndpointRef) descriptors[midi->device_id].descriptor;
    macHostError = MIDIFlushOutput(endpoint);
    if (macHostError != noErr) {
        pm_hosterror = macHostError;
        sprintf(pm_hosterror_text,
                "Host error %ld: MIDIFlushOutput()", macHostError);
        err = pmHostError;
    }
    return err;
}


static PmError
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;
    assert(m);
    assert(endpoint);
    if (m->packet != NULL) {
        /* out of space, send the buffer and start refilling it */
        macHostError = MIDISend(portOut, endpoint, m->packetList);
        m->packet = NULL; /* indicate no data in packetList now */
        if (macHostError != noErr) goto send_packet_error;
    }
    return pmNoError;
    
send_packet_error:
    pm_hosterror = macHostError;
    sprintf(pm_hosterror_text, 
            "Host error %ld: MIDISend() in midi_write()",
            macHostError);
    return pmHostError;

}


static PmError
send_packet(PmInternal *midi, Byte *message, unsigned int messageLength, 
            MIDITimeStamp timestamp)
{
    PmError err;
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    assert(m);
    
    /* printf("add %d to packet %lx len %d\n", message[0], m->packet, 
messageLength); */
    m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), 
                                  m->packet, timestamp, messageLength, 
                                  message);
    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 */
        m->packet = (MIDIPacket *) 4; 
        if ((err = midi_write_flush(midi, timestamp)) != 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),
                                      m->packet, timestamp, messageLength, 
                                      message);
        assert(m->packet); /* can't run out of space on first message */        
   
    }
    return pmNoError;
}    


static PmError
midi_write_short(PmInternal *midi, PmEvent *event)
{
    long when = event->timestamp;
    long what = event->message;
    MIDITimeStamp timestamp;
    UInt64 when_ns;
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    Byte message[4];
    unsigned int messageLength;

    if (m->packet == NULL) {
        m->packet = MIDIPacketListInit(m->packetList);
        /* this can never fail, right? failure would indicate something 
           unrecoverable */
        assert(m->packet);
    }
    
    /* compute timestamp */
    if (when == 0) when = midi->now;
    /* 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);
    message[1] = Pm_MessageData1(what);
    message[2] = Pm_MessageData2(what);
    messageLength = midi_length(what);
        
    /* Add this message to the packet list */
    return send_packet(midi, message, messageLength, timestamp);
}


static PmError 
midi_begin_sysex(PmInternal *midi, PmTimestamp when)
{
    UInt64 when_ns;
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    assert(m);
    m->sysex_byte_count = 0;
    
    /* compute timestamp */
    if (when == 0) when = midi->now;
    /* 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;
    m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);

    if (m->packet == NULL) {
        m->packet = MIDIPacketListInit(m->packetList);
        /* this can never fail, right? failure would indicate something 
           unrecoverable */
        assert(m->packet);
    }
    return pmNoError;
}


static PmError
midi_end_sysex(PmInternal *midi, PmTimestamp when)
{
    PmError err;
    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;
    
    /* now send what's in the buffer */
    err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count,
                      m->sysex_timestamp);
    m->sysex_byte_count = 0;
    if (err != pmNoError) {
        m->packet = NULL; /* flush everything in the packet list */
        return err;
    }
    return pmNoError;
}


static PmError
midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp)
{
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    assert(m);
    if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) {
        PmError err = midi_end_sysex(midi, timestamp);
        if (err != pmNoError) return err;
    }
    m->sysex_buffer[m->sysex_byte_count++] = byte;
    return pmNoError;
}


static PmError
midi_write_realtime(PmInternal *midi, PmEvent *event)
{
    /* to send a realtime message during a sysex message, first
       flush all pending sysex bytes into packet list */
    PmError err = midi_end_sysex(midi, 0);
    if (err != pmNoError) return err;
    /* then we can just do a normal midi_write_short */
    return midi_write_short(midi, event);
}

static unsigned int midi_has_host_error(PmInternal *midi)
{
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    return (m->callback_error[0] != 0) || (m->error[0] != 0);
}


static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len)
{
    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
    msg[0] = 0; /* initialize to empty string */
    if (m) { /* make sure there is an open device to examine */
        if (m->error[0]) {
            strncpy(msg, m->error, len);
            m->error[0] = 0; /* clear the error */
        } else if (m->callback_error[0]) {
            strncpy(msg, m->callback_error, len);
            m->callback_error[0] = 0; /* clear the error */
        }
        msg[len - 1] = 0; /* make sure string is terminated */
    }
}


MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp)
{
    UInt64 nanos;
    if (timestamp <= 0) {
        return (MIDITimeStamp)0;
    } else {
        nanos = (UInt64)timestamp * (UInt64)1000000;
        return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos);
    }
}

PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp)
{
    UInt64 nanos;
    nanos = AudioConvertHostTimeToNanos(timestamp);
    return (PmTimestamp)(nanos / (UInt64)1000000);
}


//
// Code taken from http://developer.apple.com/qa/qa2004/qa1374.html
//////////////////////////////////////
// Obtain the name of an endpoint without regard for whether it has connections.
// The result should be released by the caller.
CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
{
  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
  CFStringRef str;
  
  // begin with the endpoint's name
  str = NULL;
  MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str);
  if (str != NULL) {
    CFStringAppend(result, str);
    CFRelease(str);
  }
  
  MIDIEntityRef entity = NULL;
  MIDIEndpointGetEntity(endpoint, &entity);
  if (entity == NULL)
    // probably virtual
    return result;
  
  if (CFStringGetLength(result) == 0) {
    // endpoint name has zero length -- try the entity
    str = NULL;
    MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str);
    if (str != NULL) {
      CFStringAppend(result, str);
      CFRelease(str);
    }
  }
  // now consider the device's name
  MIDIDeviceRef device = NULL;
  MIDIEntityGetDevice(entity, &device);
  if (device == NULL)
    return result;
  
  str = NULL;
  MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str);
  if (CFStringGetLength(result) == 0) {
      CFRelease(result);
      return str;
  }
  if (str != NULL) {
    // if an external device has only one entity, throw away
    // the endpoint name and just use the device name
    if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) {
      CFRelease(result);
      return str;
    } else {
      if (CFStringGetLength(str) == 0) {
        CFRelease(str);
        return result;
      }
      // does the entity name already start with the device name?
      // (some drivers do this though they shouldn't)
      // if so, do not prepend
        if (CFStringCompareWithOptions( result, /* endpoint name */
             str /* device name */,
             CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) {
        // prepend the device name to the entity name
        if (CFStringGetLength(result) > 0)
          CFStringInsert(result, 0, CFSTR(" "));
        CFStringInsert(result, 0, str);
      }
      CFRelease(str);
    }
  }
  return result;
}


// Obtain the name of an endpoint, following connections.
// The result should be released by the caller.
static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
{
  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
  CFStringRef str;
  OSStatus err;
  int i;
  
  // Does the endpoint have connections?
  CFDataRef connections = NULL;
  int 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);
    if (nConnected) {
      const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
      for (i = 0; i < nConnected; ++i, ++pid) {
        MIDIUniqueID id = EndianS32_BtoN(*pid);
        MIDIObjectRef connObject;
        MIDIObjectType connObjectType;
        err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType);
        if (err == noErr) {
          if (connObjectType == kMIDIObjectType_ExternalSource  ||
              connObjectType == kMIDIObjectType_ExternalDestination) {
            // Connected to an external device's endpoint (10.3 and later).
            str = EndpointName((MIDIEndpointRef)(connObject), true);
          } else {
            // Connected to an external device (10.2) (or something else, 
catch-all)
            str = NULL;
            MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str);
          }
          if (str != NULL) {
            if (anyStrings)
              CFStringAppend(result, CFSTR(", "));
            else anyStrings = true;
            CFStringAppend(result, str);
            CFRelease(str);
          }
        }
      }
    }
    CFRelease(connections);
  }
  if (anyStrings)
    return result;
  
  // Here, either the endpoint had no connections, or we failed to obtain names 
for any of them.
  return EndpointName(endpoint, false);
}


char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint)
{
#ifdef OLDCODE
    MIDIEntityRef entity;
    MIDIDeviceRef device;

    CFStringRef endpointName = NULL;
    CFStringRef deviceName = NULL;
#endif
    CFStringRef fullName = NULL;
    CFStringEncoding defaultEncoding;
    char* newName;

    /* get the default string encoding */
    defaultEncoding = CFStringGetSystemEncoding();

    fullName = ConnectedEndpointName(endpoint);
    
#ifdef OLDCODE
    /* get the entity and device info */
    MIDIEndpointGetEntity(endpoint, &entity);
    MIDIEntityGetDevice(entity, &device);

    /* create the nicely formated name */
    MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName);
    MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
    if (deviceName != NULL) {
        fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"),
                                            deviceName, endpointName);
    } else {
        fullName = endpointName;
    }
#endif    
    /* copy the string into our buffer */
    newName = (char *) malloc(CFStringGetLength(fullName) + 1);
    CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1,
                       defaultEncoding);

    /* clean up */
#ifdef OLDCODE
    if (endpointName) CFRelease(endpointName);
    if (deviceName) CFRelease(deviceName);
#endif
    if (fullName) CFRelease(fullName);

    return newName;
}

 

pm_fns_node pm_macosx_in_dictionary = {
    none_write_short,
    none_sysex,
    none_sysex,
    none_write_byte,
    none_write_short,
    none_write_flush,
    none_synchronize,
    midi_in_open,
    midi_abort,
    midi_in_close,
    success_poll,
    midi_has_host_error,
    midi_get_host_error,
};

pm_fns_node pm_macosx_out_dictionary = {
    midi_write_short,
    midi_begin_sysex,
    midi_end_sysex,
    midi_write_byte,
    midi_write_realtime,
    midi_write_flush,
    midi_synchronize,
    midi_out_open,
    midi_abort,
    midi_out_close,
    success_poll,
    midi_has_host_error,
    midi_get_host_error,
};


PmError pm_macosxcm_init(void)
{
    ItemCount numInputs, numOutputs, numDevices;
    MIDIEndpointRef endpoint;
    int i;
    OSStatus macHostError;
    char *error_text;

    /* Determine the number of MIDI devices on the system */
    numDevices = MIDIGetNumberOfDevices();
    numInputs = MIDIGetNumberOfSources();
    numOutputs = MIDIGetNumberOfDestinations();

    /* Return prematurely if no devices exist on the system
       Note that this is not an error. There may be no devices.
       Pm_CountDevices() will return zero, which is correct and
       useful information
     */
    if (numDevices <= 0) {
        return pmNoError;
    }


    /* Initialize the client handle */
    macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client);
    if (macHostError != noErr) {
        error_text = "MIDIClientCreate() in pm_macosxcm_init()";
        goto error_return;
    }

    /* Create the input port */
    macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc,
                                          NULL, &portIn);
    if (macHostError != noErr) {
        error_text = "MIDIInputPortCreate() in pm_macosxcm_init()";
        goto error_return;
    }
        
    /* Create the output port */
    macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut);
    if (macHostError != noErr) {
        error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()";
        goto error_return;
    }

    /* Iterate over the MIDI input devices */
    for (i = 0; i < numInputs; i++) {
        endpoint = MIDIGetSource(i);
        if (endpoint == NULL) {
            continue;
        }

        /* set the first input we see to the default */
        if (pm_default_input_device_id == -1)
            pm_default_input_device_id = pm_descriptor_index;
        
        /* Register this device with PortMidi */
        pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
                      TRUE, (void*)endpoint, &pm_macosx_in_dictionary);
    }

    /* Iterate over the MIDI output devices */
    for (i = 0; i < numOutputs; i++) {
        endpoint = MIDIGetDestination(i);
        if (endpoint == NULL) {
            continue;
        }

        /* set the first output we see to the default */
        if (pm_default_output_device_id == -1)
            pm_default_output_device_id = pm_descriptor_index;

        /* Register this device with PortMidi */
        pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint),
                      FALSE, (void*)endpoint, &pm_macosx_out_dictionary);
    }
    return pmNoError;
    
error_return:
    pm_hosterror = macHostError;
    sprintf(pm_hosterror_text, "Host error %ld: %s\n", 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);
}

--- NEW FILE: pmmacosxcm.h ---
/* system-specific definitions */

PmError pm_macosxcm_init(void);
void pm_macosxcm_term(void);

PmDeviceID find_default_device(char *path, int input, PmDeviceID id);

--- NEW FILE: finddefault.c ---
/* finddefault.c -- find_default_device() implementation
   Roger Dannenberg, June 2008
*/

#include <stdlib.h>
#include <string.h>
#include "portmidi.h"
#include "pmmacosxcm.h"
#include "readbinaryplist.h"

/* Parse preference files, find default device, search devices --
   This parses the preference file(s) once for input and once for
   output, which is inefficient but much simpler to manage. Note
   that using the readbinaryplist.c module, you cannot keep two
   plist files (user and system) open at once (due to a simple
   memory management scheme).
*/
PmDeviceID find_default_device(char *path, int input, PmDeviceID id)
/* path -- the name of the preference we are searching for
   input -- true iff this is an input device
   id -- current default device id
   returns matching device id if found, otherwise id
*/
{
    static char *pref_file = "com.apple.java.util.prefs.plist";
    char *pref_str = NULL;
    // read device preferences
    value_ptr prefs = bplist_read_user_pref(pref_file);
    if (prefs) {
        value_ptr pref_val = value_dict_lookup_using_path(prefs, path);
        if (pref_val) {
            pref_str = value_get_asciistring(pref_val);
        }
    }
    if (!pref_str) {
        bplist_free_data(); /* look elsewhere */
        prefs = bplist_read_system_pref(pref_file);
        if (prefs) {
            value_ptr pref_val = value_dict_lookup_using_path(prefs, path);
            if (pref_val) {
                pref_str = value_get_asciistring(pref_val);
            }
        }
    }
    if (pref_str) { /* search devices for match */
        int i = pm_find_default_device(pref_str, input);
        if (i != pmNoDevice) {
            id = i;
        }
    }
    if (prefs) {
        bplist_free_data();
    }
    return id;
}

--- NEW FILE: readbinaryplist.c ---
/*

readbinaryplist.c -- Roger B. Dannenberg, Jun 2008
Based on ReadBinaryPList.m by Jens Ayton, 2007

Here are his comments:

Reader for binary property list files (version 00).

This has been found to work on all 566 binary plists in my 
~/Library/Preferences/
and /Library/Preferences/ directories. This probably does not provide full
test coverage. It has also been found to provide different data to Apple's
implementation when presented with a key-value archive. This is because Apple's
implementation produces undocumented CFKeyArchiverUID objects. My implementation
produces dictionaries instead, matching the in-file representation used in XML
and OpenStep plists. See extract_uid().

Full disclosure: in implementing this software, I read one comment and one
struct defintion in CFLite, Apple's implementation, which is under the APSL
[...1040 lines suppressed...]
        comma_needed = FALSE;
        printf("%p:[", v);
        dict = v->dict;
        while (dict) {
            if (comma_needed) printf(", ");
            printf("%p:", dict);
            plist_print(dict->key);
            printf("->");
            plist_print(dict->value);
            comma_needed = TRUE;
            dict = dict->next;
        }
        printf("]"); break;
    default:
        printf("UNKNOWN tag=%x", v->tag);
        break;
    }
}

            


------------------------------------------------------------------------------
_______________________________________________
Audacity-cvs mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/audacity-cvs

Reply via email to