Previous 199869 Revisions Next

r19990 Tuesday 1st January, 2013 at 16:11:32 UTC by R. Belmont
portmidi: Initial commit. [R. Belmont]

(nw: this isn't enabled to compile yet, this is just to make it easier to run the final tests on my Mac and my Windows laptop)
[src/lib]lib.mak
[src/lib/portmidi]finddefault.c* finddefaultlinux.c* osxsupport.h* osxsupport.m* pminternal.h* pmlinux.c* pmlinux.h* pmlinuxalsa.c* pmlinuxalsa.h* pmmac.c* pmmac.h* pmmacosxcm.c* pmmacosxcm.h* pmutil.c* pmutil.h* pmwin.c* pmwinmm.c* pmwinmm.h* portmidi.c* portmidi.h* porttime.c* porttime.h* ptlinux.c* ptmacosx_cf.c* ptmacosx_mach.c* ptwinmm.c* readbinaryplist.c* readbinaryplist.h*
[src/osd]osdcore.h
[src/osd/portmedia]pmmidi.c*
[src/osd/sdl]sdlmidi.c*
[src/osd/windows]winmidi.c*

trunk/src/osd/windows/winmidi.c
r0r19990
1#include "../portmedia/pmmidi.c"
Property changes on: trunk/src/osd/windows/winmidi.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/osd/sdl/sdlmidi.c
r0r19990
1#include "../portmedia/pmmidi.c"
Property changes on: trunk/src/osd/sdl/sdlmidi.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/osd/osdcore.h
r19989r19990
885885
886886
887887/***************************************************************************
888    MIDI I/O INTERFACES
889***************************************************************************/
890struct osd_midi_device;
891
892void osd_init_midi(void);
893void osd_shutdown_midi(void);
894void osd_list_midi_devices(void);
895osd_midi_device *osd_open_midi_input(const char *devname);
896osd_midi_device *osd_open_midi_output(const char *devname);
897void osd_close_midi_channel(osd_midi_device *dev);
898
899/***************************************************************************
888900    UNCATEGORIZED INTERFACES
889901***************************************************************************/
890902
trunk/src/osd/portmedia/pmmidi.c
r0r19990
1//============================================================
2//
3//  pmmidi.c - OSD interface for PortMidi
4//
5//  Copyright (c) 1996-2013, Nicola Salmoria and the MAME Team.
6//  Visit http://mamedev.org for licensing and usage restrictions.
7//
8//============================================================
9
10#include "emu.h"
11#include "osdcore.h"
12#include "portmidi/portmidi.h"
13
14void osd_list_midi_devices(void)
15{
16   #ifndef DISABLE_MIDI
17   int num_devs = Pm_CountDevices();
18   const PmDeviceInfo *pmInfo;
19
20   printf("\n");
21
22   if (num_devs == 0)
23   {
24      printf("No MIDI ports were found\n");
25      return;
26   }
27
28   printf("MIDI input ports:\n");
29   for (int i = 0; i < num_devs; i++)
30   {
31      pmInfo = Pm_GetDeviceInfo(i);
32
33      if (pmInfo->input)
34      {
35         printf("%s %s\n", pmInfo->name, (i == Pm_GetDefaultInputDeviceID()) ? "(default)" : "");
36      }
37   }
38
39   printf("\nMIDI output ports:\n");
40   for (int i = 0; i < num_devs; i++)
41   {
42      pmInfo = Pm_GetDeviceInfo(i);
43
44      if (pmInfo->output)
45      {
46         printf("%s %s\n", pmInfo->name, (i == Pm_GetDefaultOutputDeviceID()) ? "(default)" : "");
47      }
48   }
49   #else
50   printf("\nMIDI is not supported in this build\n");
51   #endif
52}
53
54osd_midi_device *osd_open_midi_input(const char *devname)
55{
56   return NULL;
57}
58
59osd_midi_device *osd_open_midi_output(const char *devname)
60{
61   return NULL;
62}
63
64void osd_close_midi_channel(osd_midi_device *dev)
65{
66}
67
68void osd_init_midi(void)
69{
70   #ifndef DISABLE_MIDI
71   Pm_Initialize();
72   #endif
73}
74
75void osd_shutdown_midi(void)
76{
77   #ifndef DISABLE_MIDI
78   Pm_Terminate();
79   #endif
80}
Property changes on: trunk/src/osd/portmedia/pmmidi.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/readbinaryplist.h
r0r19990
1/* readbinaryplist.h -- header to read preference files
2
3   Roger B. Dannenberg, Jun 2008
4*/
5
6#include <stdint.h> /* for uint8_t ... */
7
8#ifndef TRUE
9    #define TRUE 1
10    #define FALSE 0
11#endif
12
13#define MAX_KEY_SIZE 256
14
15enum
16{
17    // Object tags (high nybble)
18    kTAG_SIMPLE = 0x00,        // Null, true, false, filler, or invalid
19    kTAG_INT = 0x10,
20    kTAG_REAL = 0x20,
21    kTAG_DATE = 0x30,
22    kTAG_DATA = 0x40,
23    kTAG_ASCIISTRING = 0x50,
24    kTAG_UNICODESTRING = 0x60,
25    kTAG_UID = 0x80,
26    kTAG_ARRAY = 0xA0,
27    kTAG_DICTIONARY = 0xD0,
28   
29    // "simple" object values
30    kVALUE_NULL = 0x00,
31    kVALUE_FALSE = 0x08,
32    kVALUE_TRUE = 0x09,
33    kVALUE_FILLER = 0x0F,
34   
35    kVALUE_FULLDATETAG = 0x33        // Dates are tagged with a whole byte.
36};
37
38
39typedef struct pldata_struct {
40    uint8_t *data;
41    size_t len;
42} pldata_node, *pldata_ptr;
43
44
45typedef struct array_struct {
46    struct value_struct **array;
47    uint64_t length;
48} array_node, *array_ptr;
49
50
51// a dict_node is a list of <key, value> pairs
52typedef struct dict_struct {
53    struct value_struct *key;
54    struct value_struct *value;
55    struct dict_struct *next;
56} dict_node, *dict_ptr;
57
58
59// an value_node is a value with a tag telling the type
60typedef struct value_struct {
61    int tag;
62    union {
63        int64_t integer;
64        uint64_t uinteger;
65        double real;
66        char *string;
67        pldata_ptr data;
68        array_ptr array;
69        struct dict_struct *dict;
70    };
71} value_node, *value_ptr;
72
73
74value_ptr bplist_read_file(char *filename);
75value_ptr bplist_read_user_pref(char *filename);
76value_ptr bplist_read_system_pref(char *filename);
77void bplist_free_data(void);
78
79/*************** functions for accessing values ****************/
80
81char *value_get_asciistring(value_ptr v);
82value_ptr value_dict_lookup_using_string(value_ptr v, char *key);
83value_ptr value_dict_lookup_using_path(value_ptr v, char *path);
84
85/*************** functions for debugging ***************/
86
87void plist_print(value_ptr v);
88
Property changes on: trunk/src/lib/portmidi/readbinaryplist.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/pmlinux.h
r0r19990
1/* pmlinux.h */
2
3extern PmDeviceID pm_default_input_device_id;
4extern PmDeviceID pm_default_output_device_id;
5
Property changes on: trunk/src/lib/portmidi/pmlinux.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmmac.h
r0r19990
1/* pmmac.h */
2
3extern PmDeviceID pm_default_input_device_id;
4extern PmDeviceID pm_default_output_device_id;
No newline at end of file
Property changes on: trunk/src/lib/portmidi/pmmac.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/pmlinuxalsa.h
r0r19990
1/* pmlinuxalsa.h -- system-specific definitions */
2
3PmError pm_linuxalsa_init(void);
4void pm_linuxalsa_term(void);
5
6
Property changes on: trunk/src/lib/portmidi/pmlinuxalsa.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/ptlinux.c
r0r19990
1/* ptlinux.c -- portable timer implementation for linux */
2
3
4/* IMPLEMENTATION NOTES (by Mark Nelson):
5
6Unlike Windows, Linux has no system call to request a periodic callback,
7so if Pt_Start() receives a callback parameter, it must create a thread
8that wakes up periodically and calls the provided callback function.
9If running as superuser, use setpriority() to renice thread to -20. 
10One could also set the timer thread to a real-time priority (SCHED_FIFO
11and SCHED_RR), but this is dangerous for This is necessary because 
12if the callback hangs it'll never return. A more serious reason
13is that the current scheduler implementation busy-waits instead
14of sleeping when realtime threads request a sleep of <=2ms (as a way
15to get around the 10ms granularity), which means the thread would never
16let anyone else on the CPU.
17
18CHANGE LOG
19
2018-Jul-03 Roger Dannenberg -- Simplified code to set priority of timer
21            thread. Simplified implementation notes.
22
23*/
24/* stdlib, stdio, unistd, and sys/types were added because they appeared
25 * in a Gentoo patch, but I'm not sure why they are needed. -RBD
26 */
27#include <stdlib.h>
28#include <stdio.h>
29#include <unistd.h>
30#include <sys/types.h>
31#include "porttime.h"
32#include "sys/time.h"
33#include "sys/resource.h"
34#include "sys/timeb.h"
35#include "pthread.h"
36
37#define TRUE 1
38#define FALSE 0
39
40static int time_started_flag = FALSE;
41static struct timeb time_offset = {0, 0, 0, 0};
42static pthread_t pt_thread_pid;
43static int pt_thread_created = FALSE;
44
45/* note that this is static data -- we only need one copy */
46typedef struct {
47    int id;
48    int resolution;
49    PtCallback *callback;
50    void *userData;
51} pt_callback_parameters;
52
53static int pt_callback_proc_id = 0;
54
55static void *Pt_CallbackProc(void *p)
56{
57    pt_callback_parameters *parameters = (pt_callback_parameters *) p;
58    int mytime = 1;
59    /* to kill a process, just increment the pt_callback_proc_id */
60    /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id,
61           parameters->id); */
62    if (geteuid() == 0) setpriority(PRIO_PROCESS, 0, -20);
63    while (pt_callback_proc_id == parameters->id) {
64        /* wait for a multiple of resolution ms */
65        struct timeval timeout;
66        int delay = mytime++ * parameters->resolution - Pt_Time();
67        if (delay < 0) delay = 0;
68        timeout.tv_sec = 0;
69        timeout.tv_usec = delay * 1000;
70        select(0, NULL, NULL, NULL, &timeout);
71        (*(parameters->callback))(Pt_Time(), parameters->userData);
72    }
73    /* printf("Pt_CallbackProc exiting\n"); */
74    // free(parameters);
75    return NULL;
76}
77
78
79PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
80{
81    if (time_started_flag) return ptNoError;
82    ftime(&time_offset); /* need this set before process runs */
83    if (callback) {
84        int res;
85        pt_callback_parameters *parms = (pt_callback_parameters *)
86            malloc(sizeof(pt_callback_parameters));
87        if (!parms) return ptInsufficientMemory;
88        parms->id = pt_callback_proc_id;
89        parms->resolution = resolution;
90        parms->callback = callback;
91        parms->userData = userData;
92        res = pthread_create(&pt_thread_pid, NULL,
93                             Pt_CallbackProc, parms);
94        if (res != 0) return ptHostError;
95        pt_thread_created = TRUE;
96    }
97    time_started_flag = TRUE;
98    return ptNoError;
99}
100
101
102PtError Pt_Stop()
103{
104    /* printf("Pt_Stop called\n"); */
105    pt_callback_proc_id++;
106    if (pt_thread_created) {
107        pthread_join(pt_thread_pid, NULL);
108        pt_thread_created = FALSE;
109    }
110    time_started_flag = FALSE;
111    return ptNoError;
112}
113
114
115int Pt_Started()
116{
117    return time_started_flag;
118}
119
120
121PtTimestamp Pt_Time()
122{
123    long seconds, milliseconds;
124    struct timeb now;
125    ftime(&now);
126    seconds = now.time - time_offset.time;
127    milliseconds = now.millitm - time_offset.millitm;
128    return seconds * 1000 + milliseconds;
129}
130
131
132void Pt_Sleep(int32_t duration)
133{
134    usleep(duration * 1000);
135}
136
137
138
Property changes on: trunk/src/lib/portmidi/ptlinux.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/porttime.c
r0r19990
1/* porttime.c -- portable API for millisecond timer */
2
3/* There is no machine-independent implementation code to put here */
Property changes on: trunk/src/lib/portmidi/porttime.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/finddefaultlinux.c
r0r19990
1/* finddefault.c -- find_default_device() implementation
2   Roger Dannenberg, Jan 2009
3*/
4
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8#include <ctype.h>
9#include "portmidi.h"
10
11extern int pm_find_default_device(char *pattern, int is_input);
12
13#define STRING_MAX 256
14
15/* skip over spaces, return first non-space */
16void skip_spaces(FILE *inf)
17{
18    char c;
19    while (isspace(c = getc(inf))) ;
20    ungetc(c, inf);
21}
22
23/* trim leading spaces and match a string */
24int match_string(FILE *inf, char *s)
25{
26    skip_spaces(inf);
27    while (*s && *s == getc(inf)) s++;
28    return (*s == 0);
29}
30
31
32/* Parse preference files, find default device, search devices --
33 */
34PmDeviceID find_default_device(char *path, int input, PmDeviceID id)
35/* path -- the name of the preference we are searching for
36   input -- true iff this is an input device
37   id -- current default device id
38   returns matching device id if found, otherwise id
39*/
40{
41    static char *pref_2 = (char *)"/.java/.userPrefs/";
42    static char *pref_3 = (char *)"prefs.xml";
43    char *pref_1 = getenv("HOME");
44    char *full_name, *path_ptr;
45    FILE *inf;
46    int c, i;
47    if (!pref_1) goto nopref; // cannot find preference file
48    // full_name will be larger than necessary
49    full_name  = malloc(strlen(pref_1) + strlen(pref_2) + strlen(pref_3) +
50                        strlen(path) + 2);
51    strcpy(full_name, pref_1);
52    strcat(full_name, pref_2);
53    // copy all but last path segment to full_name
54    if (*path == '/') path++; // skip initial slash in path
55    path_ptr = strrchr(path, '/');
56    if (path_ptr) { // copy up to slash after full_name
57        path_ptr++;
58        int offset = strlen(full_name);
59        memcpy(full_name + offset, path, path_ptr - path);
60        full_name[offset + path_ptr - path] = 0; // end of string
61    } else {
62        path_ptr = path;
63    }
64    strcat(full_name, pref_3);
65    inf = fopen(full_name, "r");
66    if (!inf) goto nopref; // cannot open preference file
67    // We're not going to build or link in a full XML parser.
68    // Instead, find the path string and quoute. Then, look for
69    // "value", "=", quote. Then get string up to quote.
70    while ((c = getc(inf)) != EOF) {
71        char pref_str[STRING_MAX];
72        if (c != '"') continue; // scan up to quote
73        // look for quote string quote
74        if (!match_string(inf, path_ptr)) continue; // path not found
75        if (getc(inf) != '"') continue; // path not found, keep scanning
76        if (!match_string(inf, (char *)"value")) goto nopref; // value not found
77        if (!match_string(inf, (char *)"=")) goto nopref; // = not found
78        if (!match_string(inf, (char *)"\"")) goto nopref; // quote not found
79        // now read the value up to the close quote
80        for (i = 0; i < STRING_MAX; i++) {
81            if ((c = getc(inf)) == '"') break;
82            pref_str[i] = c;
83        }
84        if (i == STRING_MAX) continue; // value too long, ignore
85        pref_str[i] = 0;
86        i = pm_find_default_device(pref_str, input);
87        if (i != pmNoDevice) {
88            id = i;
89   }
90        break;
91    }
92 nopref:
93    return id;
94}
Property changes on: trunk/src/lib/portmidi/finddefaultlinux.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/ptmacosx_cf.c
r0r19990
1/* ptmacosx.c -- portable timer implementation for mac os x */
2
3#include <stdlib.h>
4#include <stdio.h>
5#include <pthread.h>
6#include <CoreFoundation/CoreFoundation.h>
7
8#import <mach/mach.h>
9#import <mach/mach_error.h>
10#import <mach/mach_time.h>
11#import <mach/clock.h>
12
13#include "porttime.h"
14
15#define THREAD_IMPORTANCE 30
16#define LONG_TIME 1000000000.0
17
18static int time_started_flag = FALSE;
19static CFAbsoluteTime startTime = 0.0;
20static CFRunLoopRef timerRunLoop;
21
22typedef struct {
23    int resolution;
24    PtCallback *callback;
25    void *userData;
26} PtThreadParams;
27
28
29void Pt_CFTimerCallback(CFRunLoopTimerRef timer, void *info)
30{
31    PtThreadParams *params = (PtThreadParams*)info;
32    (*params->callback)(Pt_Time(), params->userData);
33}
34
35static void* Pt_Thread(void *p)
36{
37    CFTimeInterval timerInterval;
38    CFRunLoopTimerContext timerContext;
39    CFRunLoopTimerRef timer;
40    PtThreadParams *params = (PtThreadParams*)p;
41    //CFTimeInterval timeout;
42
43    /* raise the thread's priority */
44    kern_return_t error;
45    thread_extended_policy_data_t extendedPolicy;
46    thread_precedence_policy_data_t precedencePolicy;
47
48    extendedPolicy.timeshare = 0;
49    error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
50                              (thread_policy_t)&extendedPolicy,
51                              THREAD_EXTENDED_POLICY_COUNT);
52    if (error != KERN_SUCCESS) {
53        mach_error("Couldn't set thread timeshare policy", error);
54    }
55
56    precedencePolicy.importance = THREAD_IMPORTANCE;
57    error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
58                              (thread_policy_t)&precedencePolicy,
59                              THREAD_PRECEDENCE_POLICY_COUNT);
60    if (error != KERN_SUCCESS) {
61        mach_error("Couldn't set thread precedence policy", error);
62    }
63
64    /* set up the timer context */
65    timerContext.version = 0;
66    timerContext.info = params;
67    timerContext.retain = NULL;
68    timerContext.release = NULL;
69    timerContext.copyDescription = NULL;
70
71    /* create a new timer */
72    timerInterval = (double)params->resolution / 1000.0;
73    timer = CFRunLoopTimerCreate(NULL, startTime+timerInterval, timerInterval,
74                                 0, 0, Pt_CFTimerCallback, &timerContext);
75
76    timerRunLoop = CFRunLoopGetCurrent();
77    CFRunLoopAddTimer(timerRunLoop, timer, CFSTR("PtTimeMode"));
78
79    /* run until we're told to stop by Pt_Stop() */
80    CFRunLoopRunInMode(CFSTR("PtTimeMode"), LONG_TIME, false);
81   
82    CFRunLoopRemoveTimer(CFRunLoopGetCurrent(), timer, CFSTR("PtTimeMode"));
83    CFRelease(timer);
84    free(params);
85
86    return NULL;
87}
88
89PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
90{
91    PtThreadParams *params = (PtThreadParams*)malloc(sizeof(PtThreadParams));
92    pthread_t pthread_id;
93
94    printf("Pt_Start() called\n");
95
96    // /* make sure we're not already playing */
97    if (time_started_flag) return ptAlreadyStarted;
98    startTime = CFAbsoluteTimeGetCurrent();
99
100    if (callback) {
101   
102        params->resolution = resolution;
103        params->callback = callback;
104        params->userData = userData;
105   
106        pthread_create(&pthread_id, NULL, Pt_Thread, params);
107    }
108
109    time_started_flag = TRUE;
110    return ptNoError;
111}
112
113
114PtError Pt_Stop()
115{
116    printf("Pt_Stop called\n");
117
118    CFRunLoopStop(timerRunLoop);
119    time_started_flag = FALSE;
120    return ptNoError;
121}
122
123
124int Pt_Started()
125{
126    return time_started_flag;
127}
128
129
130PtTimestamp Pt_Time()
131{
132    CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
133    return (PtTimestamp) ((now - startTime) * 1000.0);
134}
135
136
137void Pt_Sleep(int32_t duration)
138{
139    usleep(duration * 1000);
140}
Property changes on: trunk/src/lib/portmidi/ptmacosx_cf.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmmacosxcm.c
r0r19990
1/*
2 * Platform interface to the MacOS X CoreMIDI framework
3 *
4 * Jon Parise <jparise at cmu.edu>
5 * and subsequent work by Andrew Zeldis and Zico Kolter
6 * and Roger B. Dannenberg
7 *
8 * $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $
9 */
10 
11/* Notes:
12    since the input and output streams are represented by MIDIEndpointRef
13    values and almost no other state, we store the MIDIEndpointRef on
14    descriptors[midi->device_id].descriptor. The only other state we need
15    is for errors: we need to know if there is an error and if so, what is
16    the error text. We use a structure with two kinds of
17    host error: "error" and "callback_error". That way, asynchronous callbacks
18    do not interfere with other error information.
19   
20    OS X does not seem to have an error-code-to-text function, so we will
21    just use text messages instead of error codes.
22 */
23
24#include <stdlib.h>
25
26//#define CM_DEBUG 1
27
28#include "portmidi.h"
29#include "pmutil.h"
30#include "pminternal.h"
31#include "porttime.h"
32#include "pmmac.h"
33#include "pmmacosxcm.h"
34
35#include <stdio.h>
36#include <string.h>
37
38#include <CoreServices/CoreServices.h>
39#include <CoreMIDI/MIDIServices.h>
40#include <CoreAudio/HostTime.h>
41#include <unistd.h>
42
43#define PACKET_BUFFER_SIZE 1024
44/* maximum overall data rate (OS X limit is 15000 bytes/second) */
45#define MAX_BYTES_PER_S 14000
46
47/* Apple reports that packets are dropped when the MIDI bytes/sec
48   exceeds 15000. This is computed by "tracking the number of MIDI
49   bytes scheduled into 1-second buckets over the last six seconds
50   and averaging these counts."
51
52   This is apparently based on timestamps, not on real time, so
53   we have to avoid constructing packets that schedule high speed
54   output even if the actual writes are delayed (which was my first
55   solution).
56
57   The LIMIT_RATE symbol, if defined, enables code to modify
58   timestamps as follows:
59     After each packet is formed, the next allowable timestamp is
60     computed as this_packet_time + this_packet_len * delay_per_byte
61
62     This is the minimum timestamp allowed in the next packet.
63
64     Note that this distorts accurate timestamps somewhat.
65 */
66#define LIMIT_RATE 1
67
68#define SYSEX_BUFFER_SIZE 128
69
70#define VERBOSE_ON 1
71#define VERBOSE if (VERBOSE_ON)
72
73#define MIDI_SYSEX      0xf0
74#define MIDI_EOX        0xf7
75#define MIDI_STATUS_MASK 0x80
76
77// "Ref"s are pointers on 32-bit machines and ints on 64 bit machines
78// NULL_REF is our representation of either 0 or NULL
79#ifdef __LP64__
80#define NULL_REF 0
81#else
82#define NULL_REF NULL
83#endif
84
85static MIDIClientRef   client = NULL_REF;    /* Client handle to the MIDI server */
86static MIDIPortRef   portIn = NULL_REF;   /* Input port handle */
87static MIDIPortRef   portOut = NULL_REF;   /* Output port handle */
88
89extern pm_fns_node pm_macosx_in_dictionary;
90extern pm_fns_node pm_macosx_out_dictionary;
91
92typedef struct midi_macosxcm_struct {
93    PmTimestamp sync_time; /* when did we last determine delta? */
94    UInt64 delta;   /* difference between stream time and real time in ns */
95    UInt64 last_time;   /* last output time in host units*/
96    int first_message;  /* tells midi_write to sychronize timestamps */
97    int sysex_mode;     /* middle of sending sysex */
98    uint32_t sysex_word; /* accumulate data when receiving sysex */
99    uint32_t sysex_byte_count; /* count how many received */
100    char error[PM_HOST_ERROR_MSG_LEN];
101    char callback_error[PM_HOST_ERROR_MSG_LEN];
102    Byte packetBuffer[PACKET_BUFFER_SIZE];
103    MIDIPacketList *packetList; /* a pointer to packetBuffer */
104    MIDIPacket *packet;
105    Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */
106    MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */
107    /* allow for running status (is running status possible here? -rbd): -cpr */
108    unsigned char last_command;
109    int32_t last_msg_length;
110    /* limit midi data rate (a CoreMidi requirement): */
111    UInt64 min_next_time; /* when can the next send take place? */
112    int byte_count; /* how many bytes in the next packet list? */
113    Float64 us_per_host_tick; /* host clock frequency, units of min_next_time */
114    UInt64 host_ticks_per_byte; /* host clock units per byte at maximum rate */
115} midi_macosxcm_node, *midi_macosxcm_type;
116
117/* private function declarations */
118MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp);
119PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp);
120
121char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint);
122
123
124static int
125midi_length(int32_t msg)
126{
127    int status, high, low;
128    static int high_lengths[] = {
129        1, 1, 1, 1, 1, 1, 1, 1,         /* 0x00 through 0x70 */
130        3, 3, 3, 3, 2, 2, 3, 1          /* 0x80 through 0xf0 */
131    };
132    static int low_lengths[] = {
133        1, 2, 3, 2, 1, 1, 1, 1,         /* 0xf0 through 0xf8 */
134        1, 1, 1, 1, 1, 1, 1, 1          /* 0xf9 through 0xff */
135    };
136
137    status = msg & 0xFF;
138    high = status >> 4;
139    low = status & 15;
140
141    return (high != 0xF) ? high_lengths[high] : low_lengths[low];
142}
143
144static PmTimestamp midi_synchronize(PmInternal *midi)
145{
146    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
147    UInt64 pm_stream_time_2 =
148            AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
149    PmTimestamp real_time;
150    UInt64 pm_stream_time;
151    /* if latency is zero and this is an output, there is no
152       time reference and midi_synchronize should never be called */
153    assert(midi->time_proc);
154    assert(!(midi->write_flag && midi->latency == 0));
155    do {
156         /* read real_time between two reads of stream time */
157         pm_stream_time = pm_stream_time_2;
158         real_time = (*midi->time_proc)(midi->time_info);
159         pm_stream_time_2 = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
160         /* repeat if more than 0.5 ms has elapsed */
161    } while (pm_stream_time_2 > pm_stream_time + 500000);
162    m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000);
163    m->sync_time = real_time;
164    return real_time;
165}
166
167
168static void
169process_packet(MIDIPacket *packet, PmEvent *event,
170          PmInternal *midi, midi_macosxcm_type m)
171{
172    /* handle a packet of MIDI messages from CoreMIDI */
173    /* there may be multiple short messages in one packet (!) */
174    unsigned int remaining_length = packet->length;
175    unsigned char *cur_packet_data = packet->data;
176    while (remaining_length > 0) {
177        if (cur_packet_data[0] == MIDI_SYSEX ||
178            /* are we in the middle of a sysex message? */
179            (m->last_command == 0 &&
180             !(cur_packet_data[0] & MIDI_STATUS_MASK))) {
181            m->last_command = 0; /* no running status */
182            unsigned int amt = pm_read_bytes(midi, cur_packet_data,
183                                             remaining_length,
184                                             event->timestamp);
185            remaining_length -= amt;
186            cur_packet_data += amt;
187        } else if (cur_packet_data[0] == MIDI_EOX) {
188            /* this should never happen, because pm_read_bytes should
189             * get and read all EOX bytes*/
190            midi->sysex_in_progress = FALSE;
191            m->last_command = 0;
192        } else if (cur_packet_data[0] & MIDI_STATUS_MASK) {
193            /* compute the length of the next (short) msg in packet */
194       unsigned int cur_message_length = midi_length(cur_packet_data[0]);
195            if (cur_message_length > remaining_length) {
196#ifdef DEBUG
197                printf("PortMidi debug msg: not enough data");
198#endif
199      /* since there's no more data, we're done */
200      return;
201       }
202       m->last_msg_length = cur_message_length;
203       m->last_command = cur_packet_data[0];
204       switch (cur_message_length) {
205       case 1:
206           event->message = Pm_Message(cur_packet_data[0], 0, 0);
207      break;
208       case 2:
209           event->message = Pm_Message(cur_packet_data[0],
210                   cur_packet_data[1], 0);
211      break;
212       case 3:
213           event->message = Pm_Message(cur_packet_data[0],
214                   cur_packet_data[1],
215                   cur_packet_data[2]);
216      break;
217       default:
218                /* PortMIDI internal error; should never happen */
219                assert(cur_message_length == 1);
220           return; /* give up on packet if continued after assert */
221       }
222       pm_read_short(midi, event);
223       remaining_length -= m->last_msg_length;
224       cur_packet_data += m->last_msg_length;
225   } else if (m->last_msg_length > remaining_length + 1) {
226       /* we have running status, but not enough data */
227#ifdef DEBUG
228       printf("PortMidi debug msg: not enough data in CoreMIDI packet");
229#endif
230       /* since there's no more data, we're done */
231       return;
232   } else { /* output message using running status */
233       switch (m->last_msg_length) {
234       case 1:
235           event->message = Pm_Message(m->last_command, 0, 0);
236      break;
237       case 2:
238           event->message = Pm_Message(m->last_command,
239                   cur_packet_data[0], 0);
240      break;
241       case 3:
242           event->message = Pm_Message(m->last_command,
243                   cur_packet_data[0],
244                   cur_packet_data[1]);
245      break;
246       default:
247           /* last_msg_length is invalid -- internal PortMIDI error */
248           assert(m->last_msg_length == 1);
249       }
250       pm_read_short(midi, event);
251       remaining_length -= (m->last_msg_length - 1);
252       cur_packet_data += (m->last_msg_length - 1);
253   }
254    }
255}
256
257
258
259/* called when MIDI packets are received */
260static void
261readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon)
262{
263    PmInternal *midi;
264    midi_macosxcm_type m;
265    PmEvent event;
266    MIDIPacket *packet;
267    unsigned int packetIndex;
268    uint32_t now;
269    unsigned int status;
270   
271#ifdef CM_DEBUG
272    printf("readProc: numPackets %d: ", newPackets->numPackets);
273#endif
274
275    /* Retrieve the context for this connection */
276    midi = (PmInternal *) connRefCon;
277    m = (midi_macosxcm_type) midi->descriptor;
278    assert(m);
279   
280    /* synchronize time references every 100ms */
281    now = (*midi->time_proc)(midi->time_info);
282    if (m->first_message || m->sync_time + 100 /*ms*/ < now) {
283        /* time to resync */
284        now = midi_synchronize(midi);
285        m->first_message = FALSE;
286    }
287   
288    packet = (MIDIPacket *) &newPackets->packet[0];
289    /* printf("readproc packet status %x length %d\n", packet->data[0],
290               packet->length); */
291    for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) {
292        /* Set the timestamp and dispatch this message */
293        event.timestamp = (PmTimestamp) /* explicit conversion */ (
294                (AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) /
295                (UInt64) 1000000);
296        status = packet->data[0];
297        /* process packet as sysex data if it begins with MIDI_SYSEX, or
298           MIDI_EOX or non-status byte with no running status */
299#ifdef CM_DEBUG
300        printf(" %d", packet->length);
301#endif
302        if (status == MIDI_SYSEX || status == MIDI_EOX ||
303            ((!(status & MIDI_STATUS_MASK)) && !m->last_command)) {
304       /* previously was: !(status & MIDI_STATUS_MASK)) {
305             * but this could mistake running status for sysex data
306             */
307            /* reset running status data -cpr */
308       m->last_command = 0;
309       m->last_msg_length = 0;
310            /* printf("sysex packet length: %d\n", packet->length); */
311            pm_read_bytes(midi, packet->data, packet->length, event.timestamp);
312        } else {
313            process_packet(packet, &event, midi, m);
314   }
315        packet = MIDIPacketNext(packet);
316    }
317#ifdef CM_DEBUG
318    printf("\n");
319#endif
320}
321
322static PmError
323midi_in_open(PmInternal *midi, void *driverInfo)
324{
325    MIDIEndpointRef endpoint;
326    midi_macosxcm_type m;
327    OSStatus macHostError;
328   
329    /* insure that we have a time_proc for timing */
330    if (midi->time_proc == NULL) {
331        if (!Pt_Started())
332            Pt_Start(1, 0, 0);
333        /* time_get does not take a parameter, so coerce */
334        midi->time_proc = (PmTimeProcPtr) Pt_Time;
335    }
336    endpoint = (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
337    if (endpoint == NULL_REF) {
338        return pmInvalidDeviceId;
339    }
340
341    m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
342    midi->descriptor = m;
343    if (!m) {
344        return pmInsufficientMemory;
345    }
346    m->error[0] = 0;
347    m->callback_error[0] = 0;
348    m->sync_time = 0;
349    m->delta = 0;
350    m->last_time = 0;
351    m->first_message = TRUE;
352    m->sysex_mode = FALSE;
353    m->sysex_word = 0;
354    m->sysex_byte_count = 0;
355    m->packetList = NULL;
356    m->packet = NULL;
357    m->last_command = 0;
358    m->last_msg_length = 0;
359
360    macHostError = MIDIPortConnectSource(portIn, endpoint, midi);
361    if (macHostError != noErr) {
362        pm_hosterror = macHostError;
363        sprintf(pm_hosterror_text,
364                "Host error %ld: MIDIPortConnectSource() in midi_in_open()",
365                (long) macHostError);
366        midi->descriptor = NULL;
367        pm_free(m);
368        return pmHostError;
369    }
370   
371    return pmNoError;
372}
373
374static PmError
375midi_in_close(PmInternal *midi)
376{
377    MIDIEndpointRef endpoint;
378    OSStatus macHostError;
379    PmError err = pmNoError;
380   
381    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
382   
383    if (!m) return pmBadPtr;
384
385    endpoint = (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
386    if (endpoint == NULL_REF) {
387        pm_hosterror = pmBadPtr;
388    }
389   
390    /* shut off the incoming messages before freeing data structures */
391    macHostError = MIDIPortDisconnectSource(portIn, endpoint);
392    if (macHostError != noErr) {
393        pm_hosterror = macHostError;
394        sprintf(pm_hosterror_text,
395                "Host error %ld: MIDIPortDisconnectSource() in midi_in_close()",
396                (long) macHostError);
397        err = pmHostError;
398    }
399   
400    midi->descriptor = NULL;
401    pm_free(midi->descriptor);
402   
403    return err;
404}
405
406
407static PmError
408midi_out_open(PmInternal *midi, void *driverInfo)
409{
410    midi_macosxcm_type m;
411
412    m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */
413    midi->descriptor = m;
414    if (!m) {
415        return pmInsufficientMemory;
416    }
417    m->error[0] = 0;
418    m->callback_error[0] = 0;
419    m->sync_time = 0;
420    m->delta = 0;
421    m->last_time = 0;
422    m->first_message = TRUE;
423    m->sysex_mode = FALSE;
424    m->sysex_word = 0;
425    m->sysex_byte_count = 0;
426    m->packetList = (MIDIPacketList *) m->packetBuffer;
427    m->packet = NULL;
428    m->last_command = 0;
429    m->last_msg_length = 0;
430    m->min_next_time = 0;
431    m->byte_count = 0;
432    m->us_per_host_tick = 1000000.0 / AudioGetHostClockFrequency();
433    m->host_ticks_per_byte = (UInt64) (1000000.0 /
434                                       (m->us_per_host_tick * MAX_BYTES_PER_S));
435    return pmNoError;
436}
437
438
439static PmError
440midi_out_close(PmInternal *midi)
441{
442    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
443    if (!m) return pmBadPtr;
444   
445    midi->descriptor = NULL;
446    pm_free(midi->descriptor);
447   
448    return pmNoError;
449}
450
451static PmError
452midi_abort(PmInternal *midi)
453{
454    PmError err = pmNoError;
455    OSStatus macHostError;
456    MIDIEndpointRef endpoint =
457            (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
458    macHostError = MIDIFlushOutput(endpoint);
459    if (macHostError != noErr) {
460        pm_hosterror = macHostError;
461        sprintf(pm_hosterror_text,
462                "Host error %ld: MIDIFlushOutput()", (long) macHostError);
463        err = pmHostError;
464    }
465    return err;
466}
467
468
469static PmError
470midi_write_flush(PmInternal *midi, PmTimestamp timestamp)
471{
472    OSStatus macHostError;
473    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
474    MIDIEndpointRef endpoint =
475            (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor;
476    assert(m);
477    assert(endpoint);
478    if (m->packet != NULL) {
479        /* out of space, send the buffer and start refilling it */
480        /* before we can send, maybe delay to limit data rate. OS X allows
481         * 15KB/s. */
482        UInt64 now = AudioGetCurrentHostTime();
483        if (now < m->min_next_time) {
484            usleep((useconds_t)
485                   ((m->min_next_time - now) * m->us_per_host_tick));
486        }
487        macHostError = MIDISend(portOut, endpoint, m->packetList);
488        m->packet = NULL; /* indicate no data in packetList now */
489        m->min_next_time = now + m->byte_count * m->host_ticks_per_byte;
490        m->byte_count = 0;
491        if (macHostError != noErr) goto send_packet_error;
492    }
493    return pmNoError;
494   
495send_packet_error:
496    pm_hosterror = macHostError;
497    sprintf(pm_hosterror_text,
498            "Host error %ld: MIDISend() in midi_write()",
499            (long) macHostError);
500    return pmHostError;
501
502}
503
504
505static PmError
506send_packet(PmInternal *midi, Byte *message, unsigned int messageLength,
507            MIDITimeStamp timestamp)
508{
509    PmError err;
510    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
511    assert(m);
512   
513    /* printf("add %d to packet %p len %d\n", message[0], m->packet, messageLength); */
514    m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
515                                  m->packet, timestamp, messageLength,
516                                  message);
517    m->byte_count += messageLength;
518    if (m->packet == NULL) {
519        /* out of space, send the buffer and start refilling it */
520        /* make midi->packet non-null to fool midi_write_flush into sending */
521        m->packet = (MIDIPacket *) 4;
522        /* timestamp is 0 because midi_write_flush ignores timestamp since
523         * timestamps are already in packets. The timestamp parameter is here
524         * because other API's need it. midi_write_flush can be called
525         * from system-independent code that must be cross-API.
526         */
527        if ((err = midi_write_flush(midi, 0)) != pmNoError) return err;
528        m->packet = MIDIPacketListInit(m->packetList);
529        assert(m->packet); /* if this fails, it's a programming error */
530        m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer),
531                                      m->packet, timestamp, messageLength,
532                                      message);
533        assert(m->packet); /* can't run out of space on first message */           
534    }
535    return pmNoError;
536}   
537
538
539static PmError
540midi_write_short(PmInternal *midi, PmEvent *event)
541{
542    PmTimestamp when = event->timestamp;
543    PmMessage what = event->message;
544    MIDITimeStamp timestamp;
545    UInt64 when_ns;
546    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
547    Byte message[4];
548    unsigned int messageLength;
549
550    if (m->packet == NULL) {
551        m->packet = MIDIPacketListInit(m->packetList);
552        /* this can never fail, right? failure would indicate something
553           unrecoverable */
554        assert(m->packet);
555    }
556   
557    /* compute timestamp */
558    if (when == 0) when = midi->now;
559    /* if latency == 0, midi->now is not valid. We will just set it to zero */
560    if (midi->latency == 0) when = 0;
561    when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
562    timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
563
564    message[0] = Pm_MessageStatus(what);
565    message[1] = Pm_MessageData1(what);
566    message[2] = Pm_MessageData2(what);
567    messageLength = midi_length(what);
568       
569    /* make sure we go foreward in time */
570    if (timestamp < m->min_next_time) timestamp = m->min_next_time;
571
572    #ifdef LIMIT_RATE
573        if (timestamp < m->last_time)
574            timestamp = m->last_time;
575   m->last_time = timestamp + messageLength * m->host_ticks_per_byte;
576    #endif
577
578    /* Add this message to the packet list */
579    return send_packet(midi, message, messageLength, timestamp);
580}
581
582
583static PmError
584midi_begin_sysex(PmInternal *midi, PmTimestamp when)
585{
586    UInt64 when_ns;
587    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
588    assert(m);
589    m->sysex_byte_count = 0;
590   
591    /* compute timestamp */
592    if (when == 0) when = midi->now;
593    /* if latency == 0, midi->now is not valid. We will just set it to zero */
594    if (midi->latency == 0) when = 0;
595    when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta;
596    m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns);
597
598    if (m->packet == NULL) {
599        m->packet = MIDIPacketListInit(m->packetList);
600        /* this can never fail, right? failure would indicate something
601           unrecoverable */
602        assert(m->packet);
603    }
604    return pmNoError;
605}
606
607
608static PmError
609midi_end_sysex(PmInternal *midi, PmTimestamp when)
610{
611    PmError err;
612    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
613    assert(m);
614   
615    /* make sure we go foreward in time */
616    if (m->sysex_timestamp < m->min_next_time)
617        m->sysex_timestamp = m->min_next_time;
618
619    #ifdef LIMIT_RATE
620        if (m->sysex_timestamp < m->last_time)
621            m->sysex_timestamp = m->last_time;
622        m->last_time = m->sysex_timestamp + m->sysex_byte_count *
623                                            m->host_ticks_per_byte;
624    #endif
625   
626    /* now send what's in the buffer */
627    err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count,
628                      m->sysex_timestamp);
629    m->sysex_byte_count = 0;
630    if (err != pmNoError) {
631        m->packet = NULL; /* flush everything in the packet list */
632        return err;
633    }
634    return pmNoError;
635}
636
637
638static PmError
639midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp)
640{
641    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
642    assert(m);
643    if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) {
644        PmError err = midi_end_sysex(midi, timestamp);
645        if (err != pmNoError) return err;
646    }
647    m->sysex_buffer[m->sysex_byte_count++] = byte;
648    return pmNoError;
649}
650
651
652static PmError
653midi_write_realtime(PmInternal *midi, PmEvent *event)
654{
655    /* to send a realtime message during a sysex message, first
656       flush all pending sysex bytes into packet list */
657    PmError err = midi_end_sysex(midi, 0);
658    if (err != pmNoError) return err;
659    /* then we can just do a normal midi_write_short */
660    return midi_write_short(midi, event);
661}
662
663static unsigned int midi_has_host_error(PmInternal *midi)
664{
665    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
666    return (m->callback_error[0] != 0) || (m->error[0] != 0);
667}
668
669
670static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len)
671{
672    midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor;
673    msg[0] = 0; /* initialize to empty string */
674    if (m) { /* make sure there is an open device to examine */
675        if (m->error[0]) {
676            strncpy(msg, m->error, len);
677            m->error[0] = 0; /* clear the error */
678        } else if (m->callback_error[0]) {
679            strncpy(msg, m->callback_error, len);
680            m->callback_error[0] = 0; /* clear the error */
681        }
682        msg[len - 1] = 0; /* make sure string is terminated */
683    }
684}
685
686
687MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp)
688{
689    UInt64 nanos;
690    if (timestamp <= 0) {
691        return (MIDITimeStamp)0;
692    } else {
693        nanos = (UInt64)timestamp * (UInt64)1000000;
694        return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos);
695    }
696}
697
698PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp)
699{
700    UInt64 nanos;
701    nanos = AudioConvertHostTimeToNanos(timestamp);
702    return (PmTimestamp)(nanos / (UInt64)1000000);
703}
704
705
706//
707// Code taken from http://developer.apple.com/qa/qa2004/qa1374.html
708//////////////////////////////////////
709// Obtain the name of an endpoint without regard for whether it has connections.
710// The result should be released by the caller.
711CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal)
712{
713  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
714  CFStringRef str;
715 
716  // begin with the endpoint's name
717  str = NULL;
718  MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str);
719  if (str != NULL) {
720    CFStringAppend(result, str);
721    CFRelease(str);
722  }
723 
724  MIDIEntityRef entity = NULL_REF;
725  MIDIEndpointGetEntity(endpoint, &entity);
726  if (entity == NULL_REF)
727    // probably virtual
728    return result;
729 
730  if (CFStringGetLength(result) == 0) {
731    // endpoint name has zero length -- try the entity
732    str = NULL;
733    MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str);
734    if (str != NULL) {
735      CFStringAppend(result, str);
736      CFRelease(str);
737    }
738  }
739  // now consider the device's name
740  MIDIDeviceRef device = NULL_REF;
741  MIDIEntityGetDevice(entity, &device);
742  if (device == NULL_REF)
743    return result;
744 
745  str = NULL;
746  MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str);
747  if (CFStringGetLength(result) == 0) {
748      CFRelease(result);
749      return str;
750  }
751  if (str != NULL) {
752    // if an external device has only one entity, throw away
753    // the endpoint name and just use the device name
754    if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) {
755      CFRelease(result);
756      return str;
757    } else {
758      if (CFStringGetLength(str) == 0) {
759        CFRelease(str);
760        return result;
761      }
762      // does the entity name already start with the device name?
763      // (some drivers do this though they shouldn't)
764      // if so, do not prepend
765        if (CFStringCompareWithOptions( result, /* endpoint name */
766             str /* device name */,
767             CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) {
768        // prepend the device name to the entity name
769        if (CFStringGetLength(result) > 0)
770          CFStringInsert(result, 0, CFSTR(" "));
771        CFStringInsert(result, 0, str);
772      }
773      CFRelease(str);
774    }
775  }
776  return result;
777}
778
779
780// Obtain the name of an endpoint, following connections.
781// The result should be released by the caller.
782static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint)
783{
784  CFMutableStringRef result = CFStringCreateMutable(NULL, 0);
785  CFStringRef str;
786  OSStatus err;
787  long i;
788 
789  // Does the endpoint have connections?
790  CFDataRef connections = NULL;
791  long nConnected = 0;
792  bool anyStrings = false;
793  err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections);
794  if (connections != NULL) {
795    // It has connections, follow them
796    // Concatenate the names of all connected devices
797    nConnected = CFDataGetLength(connections) / (int32_t) sizeof(MIDIUniqueID);
798    if (nConnected) {
799      const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections));
800      for (i = 0; i < nConnected; ++i, ++pid) {
801        MIDIUniqueID id = EndianS32_BtoN(*pid);
802        MIDIObjectRef connObject;
803        MIDIObjectType connObjectType;
804        err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType);
805        if (err == noErr) {
806          if (connObjectType == kMIDIObjectType_ExternalSource  ||
807              connObjectType == kMIDIObjectType_ExternalDestination) {
808            // Connected to an external device's endpoint (10.3 and later).
809            str = EndpointName((MIDIEndpointRef)(connObject), true);
810          } else {
811            // Connected to an external device (10.2) (or something else, catch-all)
812            str = NULL;
813            MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str);
814          }
815          if (str != NULL) {
816            if (anyStrings)
817              CFStringAppend(result, CFSTR(", "));
818            else anyStrings = true;
819            CFStringAppend(result, str);
820            CFRelease(str);
821          }
822        }
823      }
824    }
825    CFRelease(connections);
826  }
827  if (anyStrings)
828    return result;
829 
830  // Here, either the endpoint had no connections, or we failed to obtain names for any of them.
831  return EndpointName(endpoint, false);
832}
833
834
835char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint)
836{
837#ifdef OLDCODE
838    MIDIEntityRef entity;
839    MIDIDeviceRef device;
840
841    CFStringRef endpointName = NULL;
842    CFStringRef deviceName = NULL;
843#endif
844    CFStringRef fullName = NULL;
845    CFStringEncoding defaultEncoding;
846    char* newName;
847
848    /* get the default string encoding */
849    defaultEncoding = CFStringGetSystemEncoding();
850
851    fullName = ConnectedEndpointName(endpoint);
852   
853#ifdef OLDCODE
854    /* get the entity and device info */
855    MIDIEndpointGetEntity(endpoint, &entity);
856    MIDIEntityGetDevice(entity, &device);
857
858    /* create the nicely formated name */
859    MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName);
860    MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName);
861    if (deviceName != NULL) {
862        fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"),
863                                            deviceName, endpointName);
864    } else {
865        fullName = endpointName;
866    }
867#endif   
868    /* copy the string into our buffer */
869    newName = (char *) malloc(CFStringGetLength(fullName) + 1);
870    CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1,
871                       defaultEncoding);
872
873    /* clean up */
874#ifdef OLDCODE
875    if (endpointName) CFRelease(endpointName);
876    if (deviceName) CFRelease(deviceName);
877#endif
878    if (fullName) CFRelease(fullName);
879
880    return newName;
881}
882
883 
884
885pm_fns_node pm_macosx_in_dictionary = {
886    none_write_short,
887    none_sysex,
888    none_sysex,
889    none_write_byte,
890    none_write_short,
891    none_write_flush,
892    none_synchronize,
893    midi_in_open,
894    midi_abort,
895    midi_in_close,
896    success_poll,
897    midi_has_host_error,
898    midi_get_host_error,
899};
900
901pm_fns_node pm_macosx_out_dictionary = {
902    midi_write_short,
903    midi_begin_sysex,
904    midi_end_sysex,
905    midi_write_byte,
906    midi_write_realtime,
907    midi_write_flush,
908    midi_synchronize,
909    midi_out_open,
910    midi_abort,
911    midi_out_close,
912    success_poll,
913    midi_has_host_error,
914    midi_get_host_error,
915};
916
917
918PmError pm_macosxcm_init(void)
919{
920    ItemCount numInputs, numOutputs, numDevices;
921    MIDIEndpointRef endpoint;
922    int i;
923    OSStatus macHostError;
924    char *error_text;
925
926    /* Determine the number of MIDI devices on the system */
927    numDevices = MIDIGetNumberOfDevices();
928    numInputs = MIDIGetNumberOfSources();
929    numOutputs = MIDIGetNumberOfDestinations();
930
931    /* Return prematurely if no devices exist on the system
932       Note that this is not an error. There may be no devices.
933       Pm_CountDevices() will return zero, which is correct and
934       useful information
935     */
936    if (numDevices <= 0) {
937        return pmNoError;
938    }
939
940
941    /* Initialize the client handle */
942    macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client);
943    if (macHostError != noErr) {
944        error_text = (char *)"MIDIClientCreate() in pm_macosxcm_init()";
945        goto error_return;
946    }
947
948    /* Create the input port */
949    macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc,
950                                          NULL, &portIn);
951    if (macHostError != noErr) {
952        error_text = (char *)"MIDIInputPortCreate() in pm_macosxcm_init()";
953        goto error_return;
954    }
955       
956    /* Create the output port */
957    macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut);
958    if (macHostError != noErr) {
959        error_text = (char *)"MIDIOutputPortCreate() in pm_macosxcm_init()";
960        goto error_return;
961    }
962
963    /* Iterate over the MIDI input devices */
964    for (i = 0; i < numInputs; i++) {
965        endpoint = MIDIGetSource(i);
966        if (endpoint == NULL_REF) {
967            continue;
968        }
969
970        /* set the first input we see to the default */
971        if (pm_default_input_device_id == -1)
972            pm_default_input_device_id = pm_descriptor_index;
973       
974        /* Register this device with PortMidi */
975        pm_add_device((char *)"CoreMIDI", cm_get_full_endpoint_name(endpoint),
976                      TRUE, (void *) (long) endpoint, &pm_macosx_in_dictionary);
977    }
978
979    /* Iterate over the MIDI output devices */
980    for (i = 0; i < numOutputs; i++) {
981        endpoint = MIDIGetDestination(i);
982        if (endpoint == NULL_REF) {
983            continue;
984        }
985
986        /* set the first output we see to the default */
987        if (pm_default_output_device_id == -1)
988            pm_default_output_device_id = pm_descriptor_index;
989
990        /* Register this device with PortMidi */
991        pm_add_device((char *)"CoreMIDI", cm_get_full_endpoint_name(endpoint),
992                      FALSE, (void *) (long) endpoint,
993                      &pm_macosx_out_dictionary);
994    }
995    return pmNoError;
996   
997error_return:
998    pm_hosterror = macHostError;
999    sprintf(pm_hosterror_text, "Host error %ld: %s\n", (long) macHostError,
1000            error_text);
1001    pm_macosxcm_term(); /* clear out any opened ports */
1002    return pmHostError;
1003}
1004
1005void pm_macosxcm_term(void)
1006{
1007    if (client != NULL_REF) MIDIClientDispose(client);
1008    if (portIn != NULL_REF) MIDIPortDispose(portIn);
1009    if (portOut != NULL_REF) MIDIPortDispose(portOut);
1010}
Property changes on: trunk/src/lib/portmidi/pmmacosxcm.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/porttime.h
r0r19990
1/* porttime.h -- portable interface to millisecond timer */
2
3/* CHANGE LOG FOR PORTTIME
4  10-Jun-03 Mark Nelson & RBD
5    boost priority of timer thread in ptlinux.c implementation
6 */
7
8/* Should there be a way to choose the source of time here? */
9
10#ifdef WIN32
11#ifndef INT32_DEFINED
12// rather than having users install a special .h file for windows,
13// just put the required definitions inline here. portmidi.h uses
14// these too, so the definitions are (unfortunately) duplicated there
15typedef int int32_t;
16typedef unsigned int uint32_t;
17#define INT32_DEFINED
18#endif
19#else
20#include <stdint.h> // needed for int32_t
21#endif
22
23#ifdef __cplusplus
24extern "C" {
25#endif
26
27#ifndef PMEXPORT
28#define PMEXPORT
29#endif
30
31typedef enum {
32    ptNoError = 0,         /* success */
33    ptHostError = -10000,  /* a system-specific error occurred */
34    ptAlreadyStarted,      /* cannot start timer because it is already started */
35    ptAlreadyStopped,      /* cannot stop timer because it is already stopped */
36    ptInsufficientMemory   /* memory could not be allocated */
37} PtError;
38
39
40typedef int32_t PtTimestamp;
41
42typedef void (PtCallback)( PtTimestamp timestamp, void *userData );
43
44/*
45    Pt_Start() starts a real-time service.
46
47    resolution is the timer resolution in ms. The time will advance every
48    resolution ms.
49
50    callback is a function pointer to be called every resolution ms.
51
52    userData is passed to callback as a parameter.
53
54    return value:
55    Upon success, returns ptNoError. See PtError for other values.
56*/
57PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData);
58
59/*
60    Pt_Stop() stops the timer.
61
62    return value:
63    Upon success, returns ptNoError. See PtError for other values.
64*/
65PMEXPORT PtError Pt_Stop(void);
66
67/*
68    Pt_Started() returns true iff the timer is running.
69*/
70PMEXPORT int Pt_Started(void);
71
72/*
73    Pt_Time() returns the current time in ms.
74*/
75PMEXPORT PtTimestamp Pt_Time(void);
76
77/*
78    Pt_Sleep() pauses, allowing other threads to run.
79
80    duration is the length of the pause in ms. The true duration
81    of the pause may be rounded to the nearest or next clock tick
82    as determined by resolution in Pt_Start().
83*/
84PMEXPORT void Pt_Sleep(int32_t duration);
85
86#ifdef __cplusplus
87}
88#endif
Property changes on: trunk/src/lib/portmidi/porttime.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmutil.c
r0r19990
1/* pmutil.c -- some helpful utilities for building midi
2               applications that use PortMidi
3 */
4#include <stdlib.h>
5#include <assert.h>
6#include <string.h>
7#include "portmidi.h"
8#include "pmutil.h"
9#include "pminternal.h"
10
11#ifdef WIN32
12#define bzero(addr, siz) memset(addr, 0, siz)
13#endif
14
15// #define QUEUE_DEBUG 1
16#ifdef QUEUE_DEBUG
17#include "stdio.h"
18#endif
19
20typedef struct {
21    long head;
22    long tail;
23    long len;
24    long overflow;
25    int32_t msg_size; /* number of int32_t in a message including extra word */
26    int32_t peek_overflow;
27    int32_t *buffer;
28    int32_t *peek;
29    int32_t peek_flag;
30} PmQueueRep;
31
32
33PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg)
34{
35    int32_t int32s_per_msg =
36            (int32_t) (((bytes_per_msg + sizeof(int32_t) - 1) &
37                       ~(sizeof(int32_t) - 1)) / sizeof(int32_t));
38    PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep));
39    if (!queue) /* memory allocation failed */
40        return NULL;
41
42    /* need extra word per message for non-zero encoding */
43    queue->len = num_msgs * (int32s_per_msg + 1);
44    queue->buffer = (int32_t *) pm_alloc(queue->len * sizeof(int32_t));
45    bzero(queue->buffer, queue->len * sizeof(int32_t));
46    if (!queue->buffer) {
47        pm_free(queue);
48        return NULL;
49    } else { /* allocate the "peek" buffer */
50        queue->peek = (int32_t *) pm_alloc(int32s_per_msg * sizeof(int32_t));
51        if (!queue->peek) {
52            /* free everything allocated so far and return */
53            pm_free(queue->buffer);
54            pm_free(queue);
55            return NULL;
56        }
57    }
58    bzero(queue->buffer, queue->len * sizeof(int32_t));
59    queue->head = 0;
60    queue->tail = 0;
61    /* msg_size is in words */
62    queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */
63    queue->overflow = FALSE;
64    queue->peek_overflow = FALSE;
65    queue->peek_flag = FALSE;
66    return queue;
67}
68
69
70PMEXPORT PmError Pm_QueueDestroy(PmQueue *q)
71{
72    PmQueueRep *queue = (PmQueueRep *) q;
73       
74    /* arg checking */
75    if (!queue || !queue->buffer || !queue->peek)
76                return pmBadPtr;
77   
78    pm_free(queue->peek);
79    pm_free(queue->buffer);
80    pm_free(queue);
81    return pmNoError;
82}
83
84
85PMEXPORT PmError Pm_Dequeue(PmQueue *q, void *msg)
86{
87    long head;
88    PmQueueRep *queue = (PmQueueRep *) q;
89    int i;
90    int32_t *msg_as_int32 = (int32_t *) msg;
91
92    /* arg checking */
93    if (!queue)
94        return pmBadPtr;
95    /* a previous peek operation encountered an overflow, but the overflow
96     * has not yet been reported to client, so do it now. No message is
97     * returned, but on the next call, we will return the peek buffer.
98     */
99    if (queue->peek_overflow) {
100        queue->peek_overflow = FALSE;
101        return pmBufferOverflow;
102    }
103    if (queue->peek_flag) {
104        memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32_t));
105        queue->peek_flag = FALSE;
106        return pmGotData;
107    }
108
109    head = queue->head;
110    /* if writer overflows, it writes queue->overflow = tail+1 so that
111     * when the reader gets to that position in the buffer, it can
112     * return the overflow condition to the reader. The problem is that
113     * at overflow, things have wrapped around, so tail == head, and the
114     * reader will detect overflow immediately instead of waiting until
115     * it reads everything in the buffer, wrapping around again to the
116     * point where tail == head. So the condition also checks that
117     * queue->buffer[head] is zero -- if so, then the buffer is now
118     * empty, and we're at the point in the msg stream where overflow
119     * occurred. It's time to signal overflow to the reader. If
120     * queue->buffer[head] is non-zero, there's a message there and we
121     * should read all the way around the buffer before signalling overflow.
122     * There is a write-order dependency here, but to fail, the overflow
123     * field would have to be written while an entire buffer full of
124     * writes are still pending. I'm assuming out-of-order writes are
125     * possible, but not that many.
126     */
127    if (queue->overflow == head + 1 && !queue->buffer[head]) {
128        queue->overflow = 0; /* non-overflow condition */
129        return pmBufferOverflow;
130    }
131
132    /* test to see if there is data in the queue -- test from back
133     * to front so if writer is simultaneously writing, we don't
134     * waste time discovering the write is not finished
135     */
136    for (i = queue->msg_size - 1; i >= 0; i--) {
137        if (!queue->buffer[head + i]) {
138            return pmNoData;
139        }
140    }
141    memcpy(msg, (char *) &queue->buffer[head + 1],
142           sizeof(int32_t) * (queue->msg_size - 1));
143    /* fix up zeros */
144    i = queue->buffer[head];
145    while (i < queue->msg_size) {
146        int32_t j;
147        i--; /* msg does not have extra word so shift down */
148        j = msg_as_int32[i];
149        msg_as_int32[i] = 0;
150        i = j;
151    }
152    /* signal that data has been removed by zeroing: */
153    bzero((char *) &queue->buffer[head], sizeof(int32_t) * queue->msg_size);
154
155    /* update head */
156    head += queue->msg_size;
157    if (head == queue->len) head = 0;
158    queue->head = head;
159    return pmGotData; /* success */
160}
161
162
163
164PMEXPORT PmError Pm_SetOverflow(PmQueue *q)
165{
166    PmQueueRep *queue = (PmQueueRep *) q;
167    long tail;
168    /* arg checking */
169    if (!queue)
170        return pmBadPtr;
171    /* no more enqueue until receiver acknowledges overflow */
172    if (queue->overflow) return pmBufferOverflow;
173    tail = queue->tail;
174    queue->overflow = tail + 1;
175    return pmBufferOverflow;
176}
177
178
179PMEXPORT PmError Pm_Enqueue(PmQueue *q, void *msg)
180{
181    PmQueueRep *queue = (PmQueueRep *) q;
182    long tail;
183    int i;
184    int32_t *src = (int32_t *) msg;
185    int32_t *ptr;
186    int32_t *dest;
187    int rslt;
188    if (!queue)
189        return pmBadPtr;
190    /* no more enqueue until receiver acknowledges overflow */
191    if (queue->overflow) return pmBufferOverflow;
192    rslt = Pm_QueueFull(q);
193    /* already checked above: if (rslt == pmBadPtr) return rslt; */
194    tail = queue->tail;
195    if (rslt) {
196        queue->overflow = tail + 1;
197        return pmBufferOverflow;
198    }
199
200    /* queue is has room for message, and overflow flag is cleared */
201    ptr = &queue->buffer[tail];
202    dest = ptr + 1;
203    for (i = 1; i < queue->msg_size; i++) {
204        int32_t j = src[i - 1];
205        if (!j) {
206            *ptr = i;
207            ptr = dest;
208        } else {
209            *dest = j;
210        }
211        dest++;
212    }
213    *ptr = i;
214    tail += queue->msg_size;
215    if (tail == queue->len) tail = 0;
216    queue->tail = tail;
217    return pmNoError;
218}
219
220
221PMEXPORT int Pm_QueueEmpty(PmQueue *q)
222{
223    PmQueueRep *queue = (PmQueueRep *) q;
224    return (!queue) ||  /* null pointer -> return "empty" */
225           (queue->buffer[queue->head] == 0 && !queue->peek_flag);
226}
227
228
229PMEXPORT int Pm_QueueFull(PmQueue *q)
230{
231    long tail;
232    int i;
233    PmQueueRep *queue = (PmQueueRep *) q;
234    /* arg checking */
235    if (!queue)
236        return pmBadPtr;
237    tail = queue->tail;
238    /* test to see if there is space in the queue */
239    for (i = 0; i < queue->msg_size; i++) {
240        if (queue->buffer[tail + i]) {
241            return TRUE;
242        }
243    }
244    return FALSE;
245}
246
247
248PMEXPORT void *Pm_QueuePeek(PmQueue *q)
249{
250    PmError rslt;
251    int32_t temp;
252    PmQueueRep *queue = (PmQueueRep *) q;
253    /* arg checking */
254    if (!queue)
255        return NULL;
256
257    if (queue->peek_flag) {
258        return queue->peek;
259    }
260    /* this is ugly: if peek_overflow is set, then Pm_Dequeue()
261     * returns immediately with pmBufferOverflow, but here, we
262     * want Pm_Dequeue() to really check for data. If data is
263     * there, we can return it
264     */
265    temp = queue->peek_overflow;
266    queue->peek_overflow = FALSE;
267    rslt = Pm_Dequeue(q, queue->peek);
268    queue->peek_overflow = temp;
269
270    if (rslt == 1) {
271        queue->peek_flag = TRUE;
272        return queue->peek;
273    } else if (rslt == pmBufferOverflow) {
274        /* when overflow is indicated, the queue is empty and the
275         * first message that was dropped by Enqueue (signalling
276         * pmBufferOverflow to its caller) would have been the next
277         * message in the queue. Pm_QueuePeek will return NULL, but
278         * remember that an overflow occurred. (see Pm_Dequeue)
279         */
280        queue->peek_overflow = TRUE;
281    }
282    return NULL;
283}
284
Property changes on: trunk/src/lib/portmidi/pmutil.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/osxsupport.h
r0r19990
1/*
2    osxsupport.h - Cocoa glue to emulated deprecated old Carbon path finder functions
3*/
4
5#ifndef _OSXSUPPORT_H_
6#define _OSXSUPPORT_H_
7
8#ifdef __cplusplus
9extern "C" {
10#endif
11
12char *FindPrefsDir(void);
13
14#ifdef __cplusplus
15}
16#endif
17
18#endif
19
Property changes on: trunk/src/lib/portmidi/osxsupport.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmmacosxcm.h
r0r19990
1/* system-specific definitions */
2
3PmError pm_macosxcm_init(void);
4void pm_macosxcm_term(void);
5
6PmDeviceID find_default_device(char *path, int input, PmDeviceID id);
Property changes on: trunk/src/lib/portmidi/pmmacosxcm.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pminternal.h
r0r19990
1/* pminternal.h -- header for interface implementations */
2
3/* this file is included by files that implement library internals */
4/* Here is a guide to implementers:
5     provide an initialization function similar to pm_winmm_init()
6     add your initialization function to pm_init()
7     Note that your init function should never require not-standard
8         libraries or fail in any way. If the interface is not available,
9         simply do not call pm_add_device. This means that non-standard
10         libraries should try to do dynamic linking at runtime using a DLL
11         and return without error if the DLL cannot be found or if there
12         is any other failure.
13     implement functions as indicated in pm_fns_type to open, read, write,
14         close, etc.
15     call pm_add_device() for each input and output device, passing it a
16         pm_fns_type structure.
17     assumptions about pm_fns_type functions are given below.
18 */
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
24extern int pm_initialized; /* see note in portmidi.c */
25
26/* these are defined in system-specific file */
27void *pm_alloc(size_t s);
28void pm_free(void *ptr);
29
30/* if an error occurs while opening or closing a midi stream, set these: */
31extern int pm_hosterror;
32extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
33 
34struct pm_internal_struct;
35
36/* these do not use PmInternal because it is not defined yet... */
37typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi,
38                                     PmEvent *buffer);
39typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi,
40                                     PmTimestamp timestamp);
41typedef PmError (*pm_end_sysex_fn)(struct pm_internal_struct *midi,
42                                   PmTimestamp timestamp);
43typedef PmError (*pm_write_byte_fn)(struct pm_internal_struct *midi,
44                                    unsigned char byte, PmTimestamp timestamp);
45typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi,
46                                        PmEvent *buffer);
47typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi,
48                                     PmTimestamp timestamp);
49typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi);
50/* pm_open_fn should clean up all memory and close the device if any part
51   of the open fails */
52typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi,
53                              void *driverInfo);
54typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi);
55/* pm_close_fn should clean up all memory and close the device if any
56   part of the close fails. */
57typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi);
58typedef PmError (*pm_poll_fn)(struct pm_internal_struct *midi);
59typedef void (*pm_host_error_fn)(struct pm_internal_struct *midi, char * msg,
60                                 unsigned int len);
61typedef unsigned int (*pm_has_host_error_fn)(struct pm_internal_struct *midi);
62
63typedef struct {
64    pm_write_short_fn write_short; /* output short MIDI msg */
65    pm_begin_sysex_fn begin_sysex; /* prepare to send a sysex message */
66    pm_end_sysex_fn end_sysex; /* marks end of sysex message */
67    pm_write_byte_fn write_byte; /* accumulate one more sysex byte */
68    pm_write_realtime_fn write_realtime; /* send real-time message within sysex */
69    pm_write_flush_fn write_flush; /* send any accumulated but unsent data */
70    pm_synchronize_fn synchronize; /* synchronize portmidi time to stream time */
71    pm_open_fn open;   /* open MIDI device */
72    pm_abort_fn abort; /* abort */
73    pm_close_fn close; /* close device */
74    pm_poll_fn poll;   /* read pending midi events into portmidi buffer */
75    pm_has_host_error_fn has_host_error; /* true when device has had host
76                                            error message */
77    pm_host_error_fn host_error; /* provide text readable host error message
78                                    for device (clears and resets) */
79} pm_fns_node, *pm_fns_type;
80
81
82/* when open fails, the dictionary gets this set of functions: */
83extern pm_fns_node pm_none_dictionary;
84
85typedef struct {
86    PmDeviceInfo pub; /* some portmidi state also saved in here (for autmatic
87                         device closing (see PmDeviceInfo struct) */
88    void *descriptor; /* ID number passed to win32 multimedia API open */
89    void *internalDescriptor; /* points to PmInternal device, allows automatic
90                                 device closing */
91    pm_fns_type dictionary;
92} descriptor_node, *descriptor_type;
93
94extern int pm_descriptor_max;
95extern descriptor_type descriptors;
96extern int pm_descriptor_index;
97
98typedef uint32_t (*time_get_proc_type)(void *time_info);
99
100typedef struct pm_internal_struct {
101    int device_id; /* which device is open (index to descriptors) */
102    short write_flag; /* MIDI_IN, or MIDI_OUT */
103   
104    PmTimeProcPtr time_proc; /* where to get the time */
105    void *time_info; /* pass this to get_time() */
106    int32_t buffer_len; /* how big is the buffer or queue? */
107    PmQueue *queue;
108
109    int32_t latency; /* time delay in ms between timestamps and actual output */
110                  /* set to zero to get immediate, simple blocking output */
111                  /* if latency is zero, timestamps will be ignored; */
112                  /* if midi input device, this field ignored */
113   
114    int sysex_in_progress; /* when sysex status is seen, this flag becomes
115        * true until EOX is seen. When true, new data is appended to the
116        * stream of outgoing bytes. When overflow occurs, sysex data is
117        * dropped (until an EOX or non-real-timei status byte is seen) so
118        * that, if the overflow condition is cleared, we don't start
119        * sending data from the middle of a sysex message. If a sysex
120        * message is filtered, sysex_in_progress is false, causing the
121        * message to be dropped. */
122    PmMessage sysex_message; /* buffer for 4 bytes of sysex data */
123    int sysex_message_count; /* how many bytes in sysex_message so far */
124
125    int32_t filters; /* flags that filter incoming message classes */
126    int32_t channel_mask; /* filter incoming messages based on channel */
127    PmTimestamp last_msg_time; /* timestamp of last message */
128    PmTimestamp sync_time; /* time of last synchronization */
129    PmTimestamp now; /* set by PmWrite to current time */
130    int first_message; /* initially true, used to run first synchronization */
131    pm_fns_type dictionary; /* implementation functions */
132    void *descriptor; /* system-dependent state */
133    /* the following are used to expedite sysex data */
134    /* on windows, in debug mode, based on some profiling, these optimizations
135     * cut the time to process sysex bytes from about 7.5 to 0.26 usec/byte,
136     * but this does not count time in the driver, so I don't know if it is
137     * important
138     */
139    unsigned char *fill_base; /* addr of ptr to sysex data */
140    uint32_t *fill_offset_ptr; /* offset of next sysex byte */
141    int32_t fill_length; /* how many sysex bytes to write */
142} PmInternal;
143
144
145/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */
146void pm_init(void);
147void pm_term(void);
148
149/* defined by portMidi, used by pmwinmm */
150PmError none_write_short(PmInternal *midi, PmEvent *buffer);
151PmError none_write_byte(PmInternal *midi, unsigned char byte,
152                        PmTimestamp timestamp);
153PmTimestamp none_synchronize(PmInternal *midi);
154
155PmError pm_fail_fn(PmInternal *midi);
156PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp);
157PmError pm_success_fn(PmInternal *midi);
158PmError pm_add_device(char *interf, char *name, int input, void *descriptor,
159                      pm_fns_type dictionary);
160uint32_t pm_read_bytes(PmInternal *midi, const unsigned char *data, int len,
161                           PmTimestamp timestamp);
162void pm_read_short(PmInternal *midi, PmEvent *event);
163
164#define none_write_flush pm_fail_timestamp_fn
165#define none_sysex pm_fail_timestamp_fn
166#define none_poll pm_fail_fn
167#define success_poll pm_success_fn
168
169#define MIDI_REALTIME_MASK 0xf8
170#define is_real_time(msg) \
171    ((Pm_MessageStatus(msg) & MIDI_REALTIME_MASK) == MIDI_REALTIME_MASK)
172
173int pm_find_default_device(char *pattern, int is_input);
174
175#ifdef __cplusplus
176}
177#endif
178
Property changes on: trunk/src/lib/portmidi/pminternal.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmutil.h
r0r19990
1/* pmutil.h -- some helpful utilities for building midi
2               applications that use PortMidi
3 */
4
5#ifdef __cplusplus
6extern "C" {
7#endif /* __cplusplus */
8
9typedef void PmQueue;
10
11/*
12    A single-reader, single-writer queue is created by
13    Pm_QueueCreate(), which takes the number of messages and
14    the message size as parameters. The queue only accepts
15    fixed sized messages. Returns NULL if memory cannot be allocated.
16
17    This queue implementation uses the "light pipe" algorithm which
18    operates correctly even with multi-processors and out-of-order
19    memory writes. (see Alexander Dokumentov, "Lock-free Interprocess
20    Communication," Dr. Dobbs Portal, http://www.ddj.com/,
21    articleID=189401457, June 15, 2006. This algorithm requires
22    that messages be translated to a form where no words contain
23    zeros. Each word becomes its own "data valid" tag. Because of
24    this translation, we cannot return a pointer to data still in
25    the queue when the "peek" method is called. Instead, a buffer
26    is preallocated so that data can be copied there. Pm_QueuePeek()
27    dequeues a message into this buffer and returns a pointer to
28    it. A subsequent Pm_Dequeue() will copy from this buffer.
29
30    This implementation does not try to keep reader/writer data in
31    separate cache lines or prevent thrashing on cache lines.
32    However, this algorithm differs by doing inserts/removals in
33    units of messages rather than units of machine words. Some
34    performance improvement might be obtained by not clearing data
35    immediately after a read, but instead by waiting for the end
36    of the cache line, especially if messages are smaller than
37    cache lines. See the Dokumentov article for explanation.
38
39    The algorithm is extended to handle "overflow" reporting. To report
40    an overflow, the sender writes the current tail position to a field.
41    The receiver must acknowlege receipt by zeroing the field. The sender
42    will not send more until the field is zeroed.
43   
44    Pm_QueueDestroy() destroys the queue and frees its storage.
45 */
46
47PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg);
48PMEXPORT PmError Pm_QueueDestroy(PmQueue *queue);
49
50/*
51    Pm_Dequeue() removes one item from the queue, copying it into msg.
52    Returns 1 if successful, and 0 if the queue is empty.
53    Returns pmBufferOverflow if what would have been the next thing
54    in the queue was dropped due to overflow. (So when overflow occurs,
55    the receiver can receive a queue full of messages before getting the
56    overflow report. This protocol ensures that the reader will be
57    notified when data is lost due to overflow.
58 */
59PMEXPORT PmError Pm_Dequeue(PmQueue *queue, void *msg);
60
61
62/*
63    Pm_Enqueue() inserts one item into the queue, copying it from msg.
64    Returns pmNoError if successful and pmBufferOverflow if the queue was
65    already full. If pmBufferOverflow is returned, the overflow flag is set.
66 */
67PMEXPORT PmError Pm_Enqueue(PmQueue *queue, void *msg);
68
69
70/*
71    Pm_QueueFull() returns non-zero if the queue is full
72    Pm_QueueEmpty() returns non-zero if the queue is empty
73
74    Either condition may change immediately because a parallel
75    enqueue or dequeue operation could be in progress. Furthermore,
76    Pm_QueueEmpty() is optimistic: it may say false, when due to
77    out-of-order writes, the full message has not arrived. Therefore,
78    Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns
79    false. On the other hand, Pm_QueueFull() is pessimistic: if it
80    returns false, then Pm_Enqueue() is guaranteed to succeed.
81
82    Error conditions: Pm_QueueFull() returns pmBadPtr if queue is NULL.
83    Pm_QueueEmpty() returns FALSE if queue is NULL.
84 */
85PMEXPORT int Pm_QueueFull(PmQueue *queue);
86PMEXPORT int Pm_QueueEmpty(PmQueue *queue);
87
88
89/*
90    Pm_QueuePeek() returns a pointer to the item at the head of the queue,
91    or NULL if the queue is empty. The item is not removed from the queue.
92    Pm_QueuePeek() will not indicate when an overflow occurs. If you want
93    to get and check pmBufferOverflow messages, use the return value of
94    Pm_QueuePeek() *only* as an indication that you should call
95    Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would
96    return pmBufferOverflow, Pm_QueuePeek() will return NULL but internally
97    clear the pmBufferOverflow flag, enabling Pm_Enqueue() to resume
98    enqueuing messages. A subsequent call to Pm_QueuePeek()
99    will return a pointer to the first message *after* the overflow.
100    Using this as an indication to call Pm_Dequeue(), the first call
101    to Pm_Dequeue() will return pmBufferOverflow. The second call will
102    return success, copying the same message pointed to by the previous
103    Pm_QueuePeek().
104
105    When to use Pm_QueuePeek(): (1) when you need to look at the message
106    data to decide who should be called to receive it. (2) when you need
107    to know a message is ready but cannot accept the message.
108
109    Note that Pm_QueuePeek() is not a fast check, so if possible, you
110    might as well just call Pm_Dequeue() and accept the data if it is there.
111 */
112PMEXPORT void *Pm_QueuePeek(PmQueue *queue);
113
114/*
115    Pm_SetOverflow() allows the writer (enqueuer) to signal an overflow
116    condition to the reader (dequeuer). E.g. when transfering data from
117    the OS to an application, if the OS indicates a buffer overrun,
118    Pm_SetOverflow() can be used to insure that the reader receives a
119    pmBufferOverflow result from Pm_Dequeue(). Returns pmBadPtr if queue
120    is NULL, returns pmBufferOverflow if buffer is already in an overflow
121    state, returns pmNoError if successfully set overflow state.
122 */
123PMEXPORT PmError Pm_SetOverflow(PmQueue *queue);
124
125#ifdef __cplusplus
126}
127#endif /* __cplusplus */
Property changes on: trunk/src/lib/portmidi/pmutil.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/osxsupport.m
r0r19990
1/*
2    osxsupport.m - Cocoa glue to emulated deprecated old Carbon path finder functions
3*/
4
5#import <Cocoa/Cocoa.h>
6#import <AvailabilityMacros.h>
7#include "osxsupport.h"
8
9// convert an NSString to a C string
10static char *StringToChar(NSString *str)
11{
12   const char *charstr = [str UTF8String];
13   char *resstr = (char *)malloc(strlen(charstr)+1);
14
15   strcpy(resstr, charstr);
16   return resstr;
17}
18
19char *FindPrefsDir(void)
20{
21   char *resstr = NULL;
22   NSArray *paths = NSSearchPathForDirectoriesInDomains(NSPreferencePanesDirectory, NSUserDomainMask, YES);
23
24   if ([paths count] > 0)
25   {
26      resstr = StringToChar([paths objectAtIndex:0]) ;
27   }
28
29   return resstr;
30}
Property changes on: trunk/src/lib/portmidi/osxsupport.m
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmwinmm.c
r0r19990
1/* pmwinmm.c -- system specific definitions */
2
3#ifdef _MSC_VER
4 #pragma warning(disable: 4133) // stop warnings about implicit typecasts
5#endif
6
7#ifndef _WIN32_WINNT
8    /* without this define, InitializeCriticalSectionAndSpinCount is
9     * undefined. This version level means "Windows 2000 and higher"
10     */
11    #define _WIN32_WINNT 0x0500
12#endif
13#undef UNICODE
14#include "windows.h"
15#include "mmsystem.h"
16#include "portmidi.h"
17#include "pmutil.h"
18#include "pminternal.h"
19#include "pmwinmm.h"
20#include <string.h>
21#include "porttime.h"
22#include "osdcomm.h"
23
24#ifdef PTR64
25typedef UINT64 FPTR;
26#else
27typedef UINT32 FPTR;
28#endif
29
30/* asserts used to verify portMidi code logic is sound; later may want
31    something more graceful */
32#include <assert.h>
33#ifdef DEBUG
34/* this printf stuff really important for debugging client app w/host errors.
35    probably want to do something else besides read/write from/to console
36    for portability, however */
37#define STRING_MAX 80
38#include "stdio.h"
39#endif
40
41#define streql(x, y) (strcmp(x, y) == 0)
42
43#define MIDI_SYSEX      0xf0
44#define MIDI_EOX        0xf7
45
46/* callback routines */
47static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn,
48                                       WORD wMsg, DWORD dwInstance,
49                                       DWORD dwParam1, DWORD dwParam2);
50static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
51                                              DWORD dwInstance, DWORD dwParam1,
52                                              DWORD dwParam2);
53#ifdef USE_SYSEX_BUFFERS
54static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg,
55                                        DWORD dwInstance, DWORD dwParam1,
56                                        DWORD dwParam2);
57#endif
58
59extern pm_fns_node pm_winmm_in_dictionary;
60extern pm_fns_node pm_winmm_out_dictionary;
61
62static void winmm_out_delete(PmInternal *midi); /* forward reference */
63
64/*
65A note about buffers: WinMM seems to hold onto buffers longer than
66one would expect, e.g. when I tried using 2 small buffers to send
67long sysex messages, at some point WinMM held both buffers. This problem
68was fixed by making buffers bigger. Therefore, it seems that there should
69be enough buffer space to hold a whole sysex message.
70
71The bufferSize passed into Pm_OpenInput (passed into here as buffer_len)
72will be used to estimate the largest sysex message (= buffer_len * 4 bytes).
73Call that the max_sysex_len = buffer_len * 4.
74
75For simple midi output (latency == 0), allocate 3 buffers, each with half
76the size of max_sysex_len, but each at least 256 bytes.
77
78For stream output, there will already be enough space in very short
79buffers, so use them, but make sure there are at least 16.
80
81For input, use many small buffers rather than 2 large ones so that when
82there are short sysex messages arriving frequently (as in control surfaces)
83there will be more free buffers to fill. Use max_sysex_len / 64 buffers,
84but at least 16, of size 64 bytes each.
85
86The following constants help to represent these design parameters:
87*/
88#define NUM_SIMPLE_SYSEX_BUFFERS 3
89#define MIN_SIMPLE_SYSEX_LEN 256
90
91#define MIN_STREAM_BUFFERS 16
92#define STREAM_BUFFER_LEN 24
93
94#define INPUT_SYSEX_LEN 64
95#define MIN_INPUT_BUFFERS 16
96
97/* if we run out of space for output (assume this is due to a sysex msg,
98   expand by up to NUM_EXPANSION_BUFFERS in increments of EXPANSION_BUFFER_LEN
99 */
100#define NUM_EXPANSION_BUFFERS 128
101#define EXPANSION_BUFFER_LEN 1024
102
103/* A sysex buffer has 3 DWORDS as a header plus the actual message size */
104#define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3)
105/* A MIDIHDR with a sysex message is the buffer length plus the header size */
106#define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
107#ifdef USE_SYSEX_BUFFERS
108/* Size of a MIDIHDR with a buffer contaning multiple MIDIEVENT structures */
109#define MIDIHDR_SIZE(x) ((x) + sizeof(MIDIHDR))
110#endif
111
112/*
113==============================================================================
114win32 mmedia system specific structure passed to midi callbacks
115==============================================================================
116*/
117
118/* global winmm device info */
119MIDIINCAPS *midi_in_caps = NULL;
120MIDIINCAPS midi_in_mapper_caps;
121UINT midi_num_inputs = 0;
122MIDIOUTCAPS *midi_out_caps = NULL;
123MIDIOUTCAPS midi_out_mapper_caps;
124UINT midi_num_outputs = 0;
125
126/* per device info */
127typedef struct midiwinmm_struct {
128    union {
129        HMIDISTRM stream;   /* windows handle for stream */
130        HMIDIOUT out;       /* windows handle for out calls */
131        HMIDIIN in;         /* windows handle for in calls */
132    } handle;
133
134    /* midi output messages are sent in these buffers, which are allocated
135     * in a round-robin fashion, using next_buffer as an index
136     */
137    LPMIDIHDR *buffers;     /* pool of buffers for midi in or out data */
138    int max_buffers;        /* length of buffers array */
139    int buffers_expanded;   /* buffers array expanded for extra msgs? */
140    int num_buffers;        /* how many buffers allocated in buffers array */
141    int next_buffer;        /* index of next buffer to send */
142    HANDLE buffer_signal;   /* used to wait for buffer to become free */
143#ifdef USE_SYSEX_BUFFERS
144    /* sysex buffers will be allocated only when
145     * a sysex message is sent. The size of the buffer is fixed.
146     */
147    LPMIDIHDR sysex_buffers[NUM_SYSEX_BUFFERS]; /* pool of buffers for sysex data */
148    int next_sysex_buffer;      /* index of next sysexbuffer to send */
149#endif
150    unsigned long last_time;    /* last output time */
151    int first_message;          /* flag: treat first message differently */
152    int sysex_mode;             /* middle of sending sysex */
153    unsigned long sysex_word;   /* accumulate data when receiving sysex */
154    unsigned int sysex_byte_count; /* count how many received */
155    LPMIDIHDR hdr;              /* the message accumulating sysex to send */
156    unsigned long sync_time;    /* when did we last determine delta? */
157    long delta;                 /* difference between stream time and
158                                       real time */
159    int error;                  /* host error from doing port midi call */
160    CRITICAL_SECTION lock;      /* prevents reentrant callbacks (input only) */
161} midiwinmm_node, *midiwinmm_type;
162
163
164/*
165=============================================================================
166general MIDI device queries
167=============================================================================
168*/
169static void pm_winmm_general_inputs(void)
170{
171    UINT i;
172    WORD wRtn;
173    midi_num_inputs = midiInGetNumDevs();
174    midi_in_caps = (MIDIINCAPS *) pm_alloc(sizeof(MIDIINCAPS) *
175                                           midi_num_inputs);
176    if (midi_in_caps == NULL) {
177        /* if you can't open a particular system-level midi interface
178         * (such as winmm), we just consider that system or API to be
179         * unavailable and move on without reporting an error.
180         */
181        return;
182    }
183
184    for (i = 0; i < midi_num_inputs; i++) {
185        wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) & midi_in_caps[i],
186                                sizeof(MIDIINCAPS));
187        if (wRtn == MMSYSERR_NOERROR) {
188            /* ignore errors here -- if pm_descriptor_max is exceeded, some
189               devices will not be accessible. */
190            pm_add_device((char *)"MMSystem", midi_in_caps[i].szPname, TRUE,
191                    (void *)(FPTR)i,
192                    &pm_winmm_in_dictionary);
193        }
194    }
195}
196
197
198static void pm_winmm_mapper_input(void)
199{
200    WORD wRtn;
201    /* Note: if MIDIMAPPER opened as input (documentation implies you
202        can, but current system fails to retrieve input mapper
203        capabilities) then you still should retrieve some formof
204        setup info. */
205    wRtn = midiInGetDevCaps((UINT) MIDIMAPPER,
206                            (LPMIDIINCAPS) & midi_in_mapper_caps,
207                            sizeof(MIDIINCAPS));
208    if (wRtn == MMSYSERR_NOERROR) {
209        pm_add_device((char *)"MMSystem", midi_in_mapper_caps.szPname, TRUE,
210                      (void *) MIDIMAPPER, &pm_winmm_in_dictionary);
211    }
212}
213
214
215static void pm_winmm_general_outputs(void)
216{
217    UINT i;
218    DWORD wRtn;
219    midi_num_outputs = midiOutGetNumDevs();
220    midi_out_caps = pm_alloc( sizeof(MIDIOUTCAPS) * midi_num_outputs );
221
222    if (midi_out_caps == NULL) {
223        /* no error is reported -- see pm_winmm_general_inputs */
224        return ;
225    }
226
227    for (i = 0; i < midi_num_outputs; i++) {
228        wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) & midi_out_caps[i],
229                                 sizeof(MIDIOUTCAPS));
230        if (wRtn == MMSYSERR_NOERROR) {
231            pm_add_device((char *)"MMSystem", midi_out_caps[i].szPname, FALSE,
232                    (void *)(FPTR)i,
233                          &pm_winmm_out_dictionary);
234        }
235    }
236}
237
238
239static void pm_winmm_mapper_output(void)
240{
241    WORD wRtn;
242    /* Note: if MIDIMAPPER opened as output (pseudo MIDI device
243        maps device independent messages into device dependant ones,
244        via NT midimapper program) you still should get some setup info */
245    wRtn = midiOutGetDevCaps((UINT) MIDIMAPPER, (LPMIDIOUTCAPS)
246                             & midi_out_mapper_caps, sizeof(MIDIOUTCAPS));
247    if (wRtn == MMSYSERR_NOERROR) {
248        pm_add_device((char *)"MMSystem", midi_out_mapper_caps.szPname, FALSE,
249                      (void *) MIDIMAPPER, &pm_winmm_out_dictionary);
250    }
251}
252
253
254/*
255=========================================================================================
256host error handling
257=========================================================================================
258*/
259static unsigned int winmm_has_host_error(PmInternal * midi)
260{
261    midiwinmm_type m = (midiwinmm_type)midi->descriptor;
262    return m->error;
263}
264
265
266/* str_copy_len -- like strcat, but won't overrun the destination string */
267/*
268 * returns length of resulting string
269 */
270static int str_copy_len(char *dst, char *src, int len)
271{
272    strncpy(dst, src, len);
273    /* just in case suffex is greater then len, terminate with zero */
274    dst[len - 1] = 0;
275    return strlen(dst);
276}
277
278
279static void winmm_get_host_error(PmInternal * midi, char * msg, UINT len)
280{
281    /* precondition: midi != NULL */
282    midiwinmm_node * m = (midiwinmm_node *) midi->descriptor;
283    char *hdr1 = (char *)"Host error: ";
284    //char *hdr2 = (char *)"Host callback error: ";
285
286    msg[0] = 0; /* initialize result string to empty */
287
288    if (descriptors[midi->device_id].pub.input) {
289        /* input and output use different winmm API calls */
290        if (m) { /* make sure there is an open device to examine */
291            if (m->error != MMSYSERR_NOERROR) {
292                int n = str_copy_len(msg, hdr1, len);
293                /* read and record host error */
294                midiInGetErrorText(m->error, msg + n, len - n);
295                //assert(err == MMSYSERR_NOERROR);
296                m->error = MMSYSERR_NOERROR;
297            }
298        }
299    } else { /* output port */
300        if (m) {
301            if (m->error != MMSYSERR_NOERROR) {
302                int n = str_copy_len(msg, hdr1, len);
303                midiOutGetErrorText(m->error, msg + n, len - n);
304                //assert(err == MMSYSERR_NOERROR);
305                m->error = MMSYSERR_NOERROR;
306            }
307        }
308    }
309}
310
311
312/*
313=============================================================================
314buffer handling
315=============================================================================
316*/
317static MIDIHDR *allocate_buffer(long data_size)
318{
319    LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
320    MIDIEVENT *evt;
321    if (!hdr) return NULL;
322    evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
323    hdr->lpData = (LPSTR) evt;
324    hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size);
325    hdr->dwBytesRecorded = 0;
326    hdr->dwFlags = 0;
327    hdr->dwUser = hdr->dwBufferLength;
328    return hdr;
329}
330
331#ifdef USE_SYSEX_BUFFERS
332static MIDIHDR *allocate_sysex_buffer(long data_size)
333{
334    /* we're actually allocating more than data_size because the buffer
335     * will include the MIDIEVENT header in addition to the data
336     */
337    LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
338    MIDIEVENT *evt;
339    if (!hdr) return NULL;
340    evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
341    hdr->lpData = (LPSTR) evt;
342    hdr->dwFlags = 0;
343    hdr->dwUser = 0;
344    return hdr;
345}
346#endif
347
348static PmError allocate_buffers(midiwinmm_type m, long data_size, long count)
349{
350    int i;
351    /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
352    m->num_buffers = 0; /* in case no memory can be allocated */
353    m->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
354    if (!m->buffers) return pmInsufficientMemory;
355    m->max_buffers = count;
356    for (i = 0; i < count; i++) {
357        LPMIDIHDR hdr = allocate_buffer(data_size);
358        if (!hdr) { /* free everything allocated so far and return */
359            for (i = i - 1; i >= 0; i--) pm_free(m->buffers[i]);
360            pm_free(m->buffers);
361            m->max_buffers = 0;
362            return pmInsufficientMemory;
363        }
364        m->buffers[i] = hdr; /* this may be NULL if allocation fails */
365    }
366    m->num_buffers = count;
367    return pmNoError;
368}
369
370#ifdef USE_SYSEX_BUFFERS
371static PmError allocate_sysex_buffers(midiwinmm_type m, long data_size)
372{
373    PmError rslt = pmNoError;
374    /* sysex_buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
375    int i;
376    for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
377        LPMIDIHDR hdr = allocate_sysex_buffer(data_size);
378
379        if (!hdr) rslt = pmInsufficientMemory;
380        m->sysex_buffers[i] = hdr; /* this may be NULL if allocation fails */
381        hdr->dwFlags = 0; /* mark as free */
382    }
383    return rslt;
384}
385#endif
386
387#ifdef USE_SYSEX_BUFFERS
388static LPMIDIHDR get_free_sysex_buffer(PmInternal *midi)
389{
390    LPMIDIHDR r = NULL;
391    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
392    if (!m->sysex_buffers[0]) {
393        if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER)) {
394            return NULL;
395        }
396    }
397    /* busy wait until we find a free buffer */
398    while (TRUE) {
399        int i;
400        for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
401            /* cycle through buffers, modulo NUM_SYSEX_BUFFERS */
402            m->next_sysex_buffer++;
403            if (m->next_sysex_buffer >= NUM_SYSEX_BUFFERS) m->next_sysex_buffer = 0;
404            r = m->sysex_buffers[m->next_sysex_buffer];
405            if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_sysex_buffer;
406        }
407        /* after scanning every buffer and not finding anything, block */
408        if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
409#ifdef DEBUG
410            printf("PortMidi warning: get_free_sysex_buffer() wait timed out after 1000ms\n");
411#endif
412        }
413    }
414found_sysex_buffer:
415    r->dwBytesRecorded = 0;
416    r->dwBufferLength = 0; /* changed to correct value later */
417    return r;
418}
419#endif
420
421static LPMIDIHDR get_free_output_buffer(PmInternal *midi)
422{
423    LPMIDIHDR r = NULL;
424    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
425    while (TRUE) {
426        int i;
427        for (i = 0; i < m->num_buffers; i++) {
428            /* cycle through buffers, modulo m->num_buffers */
429            m->next_buffer++;
430            if (m->next_buffer >= m->num_buffers) m->next_buffer = 0;
431            r = m->buffers[m->next_buffer];
432            if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer;
433        }
434        /* after scanning every buffer and not finding anything, block */
435        if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
436#ifdef DEBUG
437            printf("PortMidi warning: get_free_output_buffer() wait timed out after 1000ms\n");
438#endif
439            /* if we're trying to send a sysex message, maybe the
440             * message is too big and we need more message buffers.
441             * Expand the buffer pool by 128KB using 1024-byte buffers.
442             */
443            /* first, expand the buffers array if necessary */
444            if (!m->buffers_expanded) {
445                LPMIDIHDR *new_buffers = (LPMIDIHDR *) pm_alloc(
446                        (m->num_buffers + NUM_EXPANSION_BUFFERS) *
447                        sizeof(LPMIDIHDR));
448                /* if no memory, we could return a no-memory error, but user
449                 * probably will be unprepared to deal with it. Maybe the
450                 * MIDI driver is temporarily hung so we should just wait.
451                 * I don't know the right answer, but waiting is easier.
452                 */
453                if (!new_buffers) continue;
454                /* copy buffers to new_buffers and replace buffers */
455                memcpy(new_buffers, m->buffers,
456                       m->num_buffers * sizeof(LPMIDIHDR));
457                pm_free(m->buffers);
458                m->buffers = new_buffers;
459                m->max_buffers = m->num_buffers + NUM_EXPANSION_BUFFERS;
460                m->buffers_expanded = TRUE;
461            }
462            /* next, add one buffer and return it */
463            if (m->num_buffers < m->max_buffers) {
464                r = allocate_buffer(EXPANSION_BUFFER_LEN);
465                /* again, if there's no memory, we may not really be
466                 * dead -- maybe the system is temporarily hung and
467                 * we can just wait longer for a message buffer */
468                if (!r) continue;
469                m->buffers[m->num_buffers++] = r;
470                goto found_buffer; /* break out of 2 loops */
471            }
472            /* else, we've allocated all NUM_EXPANSION_BUFFERS buffers,
473             * and we have no free buffers to send. We'll just keep
474             * polling to see if any buffers show up.
475             */
476        }
477    }
478found_buffer:
479    r->dwBytesRecorded = 0;
480    /* actual buffer length is saved in dwUser field */
481    r->dwBufferLength = (DWORD) r->dwUser;
482    return r;
483}
484
485#ifdef EXPANDING_SYSEX_BUFFERS
486note: this is not working code, but might be useful if you want
487      to grow sysex buffers.
488static PmError resize_sysex_buffer(PmInternal *midi, long old_size, long new_size)
489{
490    LPMIDIHDR big;
491    int i;
492    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
493    /* buffer must be smaller than 64k, but be also a multiple of 4 */
494    if (new_size > 65520) {
495        if (old_size >= 65520)
496            return pmBufferMaxSize;
497        else
498            new_size = 65520;
499    }
500    /* allocate a bigger message  */
501    big = allocate_sysex_buffer(new_size);
502    /* printf("expand to %d bytes\n", new_size);*/
503    if (!big) return pmInsufficientMemory;
504    m->error = midiOutPrepareHeader(m->handle.out, big, sizeof(MIDIHDR));
505    if (m->error) {
506        pm_free(big);
507        return pmHostError;
508    }
509    /* make sure we're not going to overwrite any memory */
510    assert(old_size <= new_size);
511    memcpy(big->lpData, m->hdr->lpData, old_size);
512    /* keep track of how many sysex bytes are in message so far */
513    big->dwBytesRecorded = m->hdr->dwBytesRecorded;
514    big->dwBufferLength = new_size;
515    /* find which buffer this was, and replace it */
516    for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
517        if (m->sysex_buffers[i] == m->hdr) {
518            m->sysex_buffers[i] = big;
519            m->sysex_buffer_size[i] = new_size;
520            pm_free(m->hdr);
521            m->hdr = big;
522            break;
523        }
524    }
525    assert(i != NUM_SYSEX_BUFFERS);
526
527    return pmNoError;
528}
529#endif
530
531/*
532=========================================================================================
533begin midi input implementation
534=========================================================================================
535*/
536
537
538static PmError allocate_input_buffer(HMIDIIN h, long buffer_len)
539{
540    LPMIDIHDR hdr = allocate_buffer(buffer_len);
541    if (!hdr) return pmInsufficientMemory;
542    pm_hosterror = midiInPrepareHeader(h, hdr, sizeof(MIDIHDR));
543    if (pm_hosterror) {
544        pm_free(hdr);
545        return pm_hosterror;
546    }
547    pm_hosterror = midiInAddBuffer(h, hdr, sizeof(MIDIHDR));
548    return pm_hosterror;
549}
550
551
552static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
553{
554    DWORD dwDevice;
555    int i = midi->device_id;
556    int max_sysex_len = midi->buffer_len * 4;
557    int num_input_buffers = max_sysex_len / INPUT_SYSEX_LEN;
558    midiwinmm_type m;
559
560   dwDevice = (DWORD)(FPTR)descriptors[i].descriptor;
561
562    /* create system dependent device data */
563    m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
564    midi->descriptor = m;
565    if (!m) goto no_memory;
566    m->handle.in = NULL;
567    m->buffers = NULL; /* not used for input */
568    m->num_buffers = 0; /* not used for input */
569    m->max_buffers = FALSE; /* not used for input */
570    m->buffers_expanded = 0; /* not used for input */
571    m->next_buffer = 0; /* not used for input */
572    m->buffer_signal = 0; /* not used for input */
573#ifdef USE_SYSEX_BUFFERS
574    for (i = 0; i < NUM_SYSEX_BUFFERS; i++)
575        m->sysex_buffers[i] = NULL; /* not used for input */
576    m->next_sysex_buffer = 0; /* not used for input */
577#endif
578    m->last_time = 0;
579    m->first_message = TRUE; /* not used for input */
580    m->sysex_mode = FALSE;
581    m->sysex_word = 0;
582    m->sysex_byte_count = 0;
583    m->hdr = NULL; /* not used for input */
584    m->sync_time = 0;
585    m->delta = 0;
586    m->error = MMSYSERR_NOERROR;
587    /* 4000 is based on Windows documentation -- that's the value used in the
588       memory manager. It's small enough that it should not hurt performance even
589       if it's not optimal.
590     */
591    InitializeCriticalSectionAndSpinCount(&m->lock, 4000);
592    /* open device */
593    pm_hosterror = midiInOpen(
594       &(m->handle.in),  /* input device handle */
595       dwDevice,  /* device ID */
596       (DWORD_PTR) winmm_in_callback,  /* callback address */
597       (DWORD_PTR) midi,  /* callback instance data */
598       CALLBACK_FUNCTION); /* callback is a procedure */
599    if (pm_hosterror) goto free_descriptor;
600
601    if (num_input_buffers < MIN_INPUT_BUFFERS)
602        num_input_buffers = MIN_INPUT_BUFFERS;
603    for (i = 0; i < num_input_buffers; i++) {
604        if (allocate_input_buffer(m->handle.in, INPUT_SYSEX_LEN)) {
605            /* either pm_hosterror was set, or the proper return code
606               is pmInsufficientMemory */
607            goto close_device;
608        }
609    }
610    /* start device */
611    pm_hosterror = midiInStart(m->handle.in);
612    if (pm_hosterror) goto reset_device;
613    return pmNoError;
614
615    /* undo steps leading up to the detected error */
616reset_device:
617    /* ignore return code (we already have an error to report) */
618    midiInReset(m->handle.in);
619close_device:
620    midiInClose(m->handle.in); /* ignore return code */
621free_descriptor:
622    midi->descriptor = NULL;
623    pm_free(m);
624no_memory:
625    if (pm_hosterror) {
626        midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
627                                     PM_HOST_ERROR_MSG_LEN);
628        //assert(err == MMSYSERR_NOERROR);
629        return pmHostError;
630    }
631    /* if !pm_hosterror, then the error must be pmInsufficientMemory */
632    return pmInsufficientMemory;
633    /* note: if we return an error code, the device will be
634       closed and memory will be freed. It's up to the caller
635       to free the parameter midi */
636}
637
638static PmError winmm_in_poll(PmInternal *midi) {
639    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
640    return m->error;
641}
642
643
644
645/* winmm_in_close -- close an open midi input device */
646/*
647 * assume midi is non-null (checked by caller)
648 */
649static PmError winmm_in_close(PmInternal *midi)
650{
651    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
652    if (!m) return pmBadPtr;
653    /* device to close */
654    if ((pm_hosterror = midiInStop(m->handle.in))) {
655        midiInReset(m->handle.in); /* try to reset and close port */
656        midiInClose(m->handle.in);
657    } else if ((pm_hosterror = midiInReset(m->handle.in))) {
658        midiInClose(m->handle.in); /* best effort to close midi port */
659    } else {
660        pm_hosterror = midiInClose(m->handle.in);
661    }
662    midi->descriptor = NULL;
663    DeleteCriticalSection(&m->lock);
664    pm_free(m); /* delete */
665    if (pm_hosterror) {
666        midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
667                                     PM_HOST_ERROR_MSG_LEN);
668        //assert(err == MMSYSERR_NOERROR);
669        return pmHostError;
670    }
671    return pmNoError;
672}
673
674
675/* Callback function executed via midiInput SW interrupt (via midiInOpen). */
676static void FAR PASCAL winmm_in_callback(
677    HMIDIIN hMidiIn,    /* midiInput device Handle */
678    WORD wMsg,          /* midi msg */
679    DWORD dwInstance,   /* application data */
680    DWORD dwParam1,     /* MIDI data */
681    DWORD dwParam2)    /* device timestamp (wrt most recent midiInStart) */
682{
683    //static int entry = 0;
684   PmInternal *midi = (PmInternal *)(FPTR) dwInstance;
685    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
686
687    /* NOTE: we do not just EnterCriticalSection() here because an
688     * MIM_CLOSE message arrives when the port is closed, but then
689     * the m->lock has been destroyed.
690     */
691
692    switch (wMsg) {
693    case MIM_DATA: {
694        /* if this callback is reentered with data, we're in trouble.
695         * It's hard to imagine that Microsoft would allow callbacks
696         * to be reentrant -- isn't the model that this is like a
697         * hardware interrupt? -- but I've seen reentrant behavior
698         * using a debugger, so it happens.
699         */
700        //long new_driver_time;
701        EnterCriticalSection(&m->lock);
702
703        /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
704                message LOB;
705           dwParam2 is time message received by input device driver, specified
706            in [ms] from when midiInStart called.
707           each message is expanded to include the status byte */
708
709        //new_driver_time = dwParam2;
710
711        if ((dwParam1 & 0x80) == 0) {
712            /* not a status byte -- ignore it. This happened running the
713               sysex.c test under Win2K with MidiMan USB 1x1 interface,
714               but I can't reproduce it. -RBD
715             */
716            /* printf("non-status byte found\n"); */
717        } else { /* data to process */
718            PmEvent event;
719            if (midi->time_proc)
720                dwParam2 = (*midi->time_proc)(midi->time_info);
721            event.timestamp = dwParam2;
722            event.message = dwParam1;
723            pm_read_short(midi, &event);
724        }
725        LeaveCriticalSection(&m->lock);
726        break;
727    }
728    case MIM_LONGDATA: {
729      MIDIHDR *lpMidiHdr = (MIDIHDR *)(FPTR)dwParam1;
730        unsigned char *data = (unsigned char *) lpMidiHdr->lpData;
731        unsigned int processed = 0;
732        int remaining = lpMidiHdr->dwBytesRecorded;
733
734        EnterCriticalSection(&m->lock);
735        /* printf("midi_in_callback -- lpMidiHdr %x, %d bytes, %2x...\n",
736                lpMidiHdr, lpMidiHdr->dwBytesRecorded, *data); */
737        if (midi->time_proc)
738            dwParam2 = (*midi->time_proc)(midi->time_info);
739        /* can there be more than one message in one buffer? */
740        /* assume yes and iterate through them */
741        while (remaining > 0) {
742            unsigned int amt = pm_read_bytes(midi, data + processed,
743                                             remaining, dwParam2);
744            remaining -= amt;
745            processed += amt;
746        }
747
748        /* when a device is closed, the pending MIM_LONGDATA buffers are
749           returned to this callback with dwBytesRecorded == 0. In this
750           case, we do not want to send them back to the interface (if
751           we do, the interface will not close, and Windows OS may hang). */
752        if (lpMidiHdr->dwBytesRecorded > 0) {
753            //MMRESULT rslt;
754            lpMidiHdr->dwBytesRecorded = 0;
755            lpMidiHdr->dwFlags = 0;
756         
757            /* note: no error checking -- can this actually fail? */
758            midiInPrepareHeader(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
759            //assert(rslt == MMSYSERR_NOERROR);
760            /* note: I don't think this can fail except possibly for
761             * MMSYSERR_NOMEM, but the pain of reporting this
762             * unlikely but probably catastrophic error does not seem
763             * worth it.
764             */
765            midiInAddBuffer(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
766            //assert(rslt == MMSYSERR_NOERROR);
767            LeaveCriticalSection(&m->lock);
768        } else {
769            midiInUnprepareHeader(hMidiIn,lpMidiHdr,sizeof(MIDIHDR));
770            LeaveCriticalSection(&m->lock);
771            pm_free(lpMidiHdr);
772        }
773        break;
774    }
775    case MIM_OPEN:
776        break;
777    case MIM_CLOSE:
778        break;
779    case MIM_ERROR:
780        /* printf("MIM_ERROR\n"); */
781        break;
782    case MIM_LONGERROR:
783        /* printf("MIM_LONGERROR\n"); */
784        break;
785    default:
786        break;
787    }
788}
789
790/*
791=========================================================================================
792begin midi output implementation
793=========================================================================================
794*/
795
796/* begin helper routines used by midiOutStream interface */
797
798/* add_to_buffer -- adds timestamped short msg to buffer, returns fullp */
799static int add_to_buffer(midiwinmm_type m, LPMIDIHDR hdr,
800                         unsigned long delta, unsigned long msg)
801{
802    unsigned long *ptr = (unsigned long *)
803                         (hdr->lpData + hdr->dwBytesRecorded);
804    *ptr++ = delta; /* dwDeltaTime */
805    *ptr++ = 0;     /* dwStream */
806    *ptr++ = msg;   /* dwEvent */
807    hdr->dwBytesRecorded += 3 * sizeof(long);
808    /* if the addition of three more words (a message) would extend beyond
809       the buffer length, then return TRUE (full)
810     */
811    return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength;
812}
813
814
815static PmTimestamp pm_time_get(midiwinmm_type m)
816{
817    MMTIME mmtime;
818    //MMRESULT wRtn;
819    mmtime.wType = TIME_TICKS;
820    mmtime.u.ticks = 0;
821    midiStreamPosition(m->handle.stream, &mmtime, sizeof(mmtime));
822    //assert(wRtn == MMSYSERR_NOERROR);
823    return mmtime.u.ticks;
824}
825
826
827/* end helper routines used by midiOutStream interface */
828
829
830static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
831{
832    DWORD dwDevice;
833    int i = midi->device_id;
834    midiwinmm_type m;
835    MIDIPROPTEMPO propdata;
836    MIDIPROPTIMEDIV divdata;
837    int max_sysex_len = midi->buffer_len * 4;
838    int output_buffer_len;
839    int num_buffers;
840   dwDevice = (DWORD)(FPTR) descriptors[i].descriptor;
841    /* create system dependent device data */
842    m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
843    midi->descriptor = m;
844    if (!m) goto no_memory;
845    m->handle.out = NULL;
846    m->buffers = NULL;
847    m->num_buffers = 0;
848    m->max_buffers = 0;
849    m->buffers_expanded = FALSE;
850    m->next_buffer = 0;
851#ifdef USE_SYSEX_BUFFERS
852    m->sysex_buffers[0] = NULL;
853    m->sysex_buffers[1] = NULL;
854    m->next_sysex_buffer = 0;
855#endif
856    m->last_time = 0;
857    m->first_message = TRUE; /* we treat first message as special case */
858    m->sysex_mode = FALSE;
859    m->sysex_word = 0;
860    m->sysex_byte_count = 0;
861    m->hdr = NULL;
862    m->sync_time = 0;
863    m->delta = 0;
864    m->error = MMSYSERR_NOERROR;
865
866    /* create a signal */
867    m->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
868
869    /* this should only fail when there are very serious problems */
870    assert(m->buffer_signal);
871
872    /* open device */
873    if (midi->latency == 0) {
874        /* use simple midi out calls */
875        pm_hosterror = midiOutOpen(
876                (LPHMIDIOUT) & m->handle.out,  /* device Handle */
877      dwDevice,  /* device ID  */
878      /* note: same callback fn as for StreamOpen: */
879      (DWORD_PTR) winmm_streamout_callback, /* callback fn */
880      (DWORD_PTR) midi,  /* callback instance data */
881      CALLBACK_FUNCTION); /* callback type */
882    } else {
883        /* use stream-based midi output (schedulable in future) */
884        pm_hosterror = midiStreamOpen(
885           &m->handle.stream,  /* device Handle */
886      (LPUINT) & dwDevice,  /* device ID pointer */
887      1,  /* reserved, must be 1 */
888      (DWORD_PTR) winmm_streamout_callback,
889      (DWORD_PTR) midi,  /* callback instance data */
890      CALLBACK_FUNCTION);
891    }
892    if (pm_hosterror != MMSYSERR_NOERROR) {
893        goto free_descriptor;
894    }
895
896    if (midi->latency == 0) {
897        num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
898        output_buffer_len = max_sysex_len / num_buffers;
899        if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
900            output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
901    } else {
902        //long dur = 0;
903        //num_buffers = (int)(double)max((double)midi->buffer_len, (double)midi->latency / 2);
904      if (midi->buffer_len > (midi->latency / 2))
905      {
906         num_buffers = midi->buffer_len;
907      }
908      else
909      {
910         num_buffers = (midi->latency / 2);
911      }
912
913        if (num_buffers < MIN_STREAM_BUFFERS)
914            num_buffers = MIN_STREAM_BUFFERS;
915        output_buffer_len = STREAM_BUFFER_LEN;
916
917        propdata.cbStruct = sizeof(MIDIPROPTEMPO);
918        propdata.dwTempo = 480000; /* microseconds per quarter */
919        pm_hosterror = midiStreamProperty(m->handle.stream,
920                                          (LPBYTE) & propdata,
921                                          MIDIPROP_SET | MIDIPROP_TEMPO);
922        if (pm_hosterror) goto close_device;
923
924        divdata.cbStruct = sizeof(MIDIPROPTEMPO);
925        divdata.dwTimeDiv = 480;   /* divisions per quarter */
926        pm_hosterror = midiStreamProperty(m->handle.stream,
927                                          (LPBYTE) & divdata,
928                                          MIDIPROP_SET | MIDIPROP_TIMEDIV);
929        if (pm_hosterror) goto close_device;
930    }
931    /* allocate buffers */
932    if (allocate_buffers(m, output_buffer_len, num_buffers))
933        goto free_buffers;
934    /* start device */
935    if (midi->latency != 0) {
936        pm_hosterror = midiStreamRestart(m->handle.stream);
937        if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
938    }
939    return pmNoError;
940
941free_buffers:
942    /* buffers are freed below by winmm_out_delete */
943close_device:
944    midiOutClose(m->handle.out);
945free_descriptor:
946    midi->descriptor = NULL;
947    winmm_out_delete(midi); /* frees buffers and m */
948no_memory:
949    if (pm_hosterror) {
950        midiOutGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
951                                      PM_HOST_ERROR_MSG_LEN);
952        //assert(err == MMSYSERR_NOERROR);
953        return pmHostError;
954    }
955    return pmInsufficientMemory;
956}
957
958
959/* winmm_out_delete -- carefully free data associated with midi */
960/**/
961static void winmm_out_delete(PmInternal *midi)
962{
963    int i;
964    /* delete system dependent device data */
965    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
966    if (m) {
967        if (m->buffer_signal) {
968            /* don't report errors -- better not to stop cleanup */
969            CloseHandle(m->buffer_signal);
970        }
971        /* if using stream output, free buffers */
972        for (i = 0; i < m->num_buffers; i++) {
973            if (m->buffers[i]) pm_free(m->buffers[i]);
974        }
975        m->num_buffers = 0;
976        pm_free(m->buffers);
977        m->max_buffers = 0;
978#ifdef USE_SYSEX_BUFFERS
979        /* free sysex buffers */
980        for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
981            if (m->sysex_buffers[i]) pm_free(m->sysex_buffers[i]);
982        }
983#endif
984    }
985    midi->descriptor = NULL;
986    pm_free(m); /* delete */
987}
988
989
990/* see comments for winmm_in_close */
991static PmError winmm_out_close(PmInternal *midi)
992{
993    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
994    if (m->handle.out) {
995        /* device to close */
996        if (midi->latency == 0) {
997            pm_hosterror = midiOutClose(m->handle.out);
998        } else {
999            pm_hosterror = midiStreamClose(m->handle.stream);
1000        }
1001        /* regardless of outcome, free memory */
1002        winmm_out_delete(midi);
1003    }
1004    if (pm_hosterror) {
1005        midiOutGetErrorText(pm_hosterror,
1006                                      (char *) pm_hosterror_text,
1007                                      PM_HOST_ERROR_MSG_LEN);
1008        //assert(err == MMSYSERR_NOERROR);
1009        return pmHostError;
1010    }
1011    return pmNoError;
1012}
1013
1014
1015static PmError winmm_out_abort(PmInternal *midi)
1016{
1017    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1018    m->error = MMSYSERR_NOERROR;
1019
1020    /* only stop output streams */
1021    if (midi->latency > 0) {
1022        m->error = midiStreamStop(m->handle.stream);
1023    }
1024    return m->error ? pmHostError : pmNoError;
1025}
1026
1027
1028static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp)
1029{
1030    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1031    assert(m);
1032    if (m->hdr) {
1033        m->error = midiOutPrepareHeader(m->handle.out, m->hdr,
1034                                        sizeof(MIDIHDR));
1035        if (m->error) {
1036            /* do not send message */
1037        } else if (midi->latency == 0) {
1038            /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded
1039             * should be zero. This is set in get_free_sysex_buffer().
1040             * The msg length goes in dwBufferLength in spite of what
1041             * Microsoft documentation says (or doesn't say). */
1042            m->hdr->dwBufferLength = m->hdr->dwBytesRecorded;
1043            m->hdr->dwBytesRecorded = 0;
1044            m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
1045        } else {
1046            m->error = midiStreamOut(m->handle.stream, m->hdr,
1047                                     sizeof(MIDIHDR));
1048        }
1049        midi->fill_base = NULL;
1050        m->hdr = NULL;
1051        if (m->error) {
1052            m->hdr->dwFlags = 0; /* release the buffer */
1053            return pmHostError;
1054        }
1055    }
1056    return pmNoError;
1057}
1058
1059
1060
1061#ifdef GARBAGE
1062static PmError winmm_write_sysex_byte(PmInternal *midi, unsigned char byte)
1063{
1064    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1065    unsigned char *msg_buffer;
1066
1067    /* at the beginning of sysex, m->hdr is NULL */
1068    if (!m->hdr) { /* allocate a buffer if none allocated yet */
1069        m->hdr = get_free_output_buffer(midi);
1070        if (!m->hdr) return pmInsufficientMemory;
1071        m->sysex_byte_count = 0;
1072    }
1073    /* figure out where to write byte */
1074    msg_buffer = (unsigned char *) (m->hdr->lpData);
1075    assert(m->hdr->lpData == (char *) (m->hdr + 1));
1076
1077    /* check for overflow */
1078    if (m->sysex_byte_count >= m->hdr->dwBufferLength) {
1079        /* allocate a bigger message -- double it every time */
1080        LPMIDIHDR big = allocate_buffer(m->sysex_byte_count * 2);
1081        /* printf("expand to %d bytes\n", m->sysex_byte_count * 2); */
1082        if (!big) return pmInsufficientMemory;
1083        m->error = midiOutPrepareHeader(m->handle.out, big,
1084                                        sizeof(MIDIHDR));
1085        if (m->error) {
1086            m->hdr = NULL;
1087            return pmHostError;
1088        }
1089        memcpy(big->lpData, msg_buffer, m->sysex_byte_count);
1090        msg_buffer = (unsigned char *) (big->lpData);
1091        if (m->buffers[0] == m->hdr) {
1092            m->buffers[0] = big;
1093            pm_free(m->hdr);
1094            /* printf("freed m->hdr\n"); */
1095        } else if (m->buffers[1] == m->hdr) {
1096            m->buffers[1] = big;
1097            pm_free(m->hdr);
1098            /* printf("freed m->hdr\n"); */
1099        }
1100        m->hdr = big;
1101    }
1102
1103    /* append byte to message */
1104    msg_buffer[m->sysex_byte_count++] = byte;
1105
1106    /* see if we have a complete message */
1107    if (byte == MIDI_EOX) {
1108        m->hdr->dwBytesRecorded = m->sysex_byte_count;
1109        /*
1110        { int i; int len = m->hdr->dwBytesRecorded;
1111          printf("OutLongMsg %d ", len);
1112          for (i = 0; i < len; i++) {
1113              printf("%2x ", msg_buffer[i]);
1114          }
1115        }
1116        */
1117        m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
1118        m->hdr = NULL; /* stop using this message buffer */
1119        if (m->error) return pmHostError;
1120    }
1121    return pmNoError;
1122}
1123#endif
1124
1125
1126static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
1127{
1128    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1129    PmError rslt = pmNoError;
1130    assert(m);
1131
1132    if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
1133        m->error = midiOutShortMsg(m->handle.out, event->message);
1134        if (m->error) rslt = pmHostError;
1135    } else {  /* use midiStream interface -- pass data through buffers */
1136        unsigned long when = event->timestamp;
1137        unsigned long delta;
1138        int full;
1139        if (when == 0) when = midi->now;
1140        /* when is in real_time; translate to intended stream time */
1141        when = when + m->delta + midi->latency;
1142        /* make sure we don't go backward in time */
1143        if (when < m->last_time) when = m->last_time;
1144        delta = when - m->last_time;
1145        m->last_time = when;
1146        /* before we insert any data, we must have a buffer */
1147        if (m->hdr == NULL) {
1148            /* stream interface: buffers allocated when stream is opened */
1149            m->hdr = get_free_output_buffer(midi);
1150        }
1151        full = add_to_buffer(m, m->hdr, delta, event->message);
1152        if (full) rslt = winmm_write_flush(midi, when);
1153    }
1154    return rslt;
1155}
1156
1157#define winmm_begin_sysex winmm_write_flush
1158#ifndef winmm_begin_sysex
1159static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp)
1160{
1161    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1162    PmError rslt = pmNoError;
1163
1164    if (midi->latency == 0) {
1165        /* do nothing -- it's handled in winmm_write_byte */
1166    } else {
1167        /* sysex expects an empty sysex buffer, so send whatever is here */
1168        rslt = winmm_write_flush(midi);
1169    }
1170    return rslt;
1171}
1172#endif
1173
1174static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp)
1175{
1176    /* could check for callback_error here, but I haven't checked
1177     * what happens if we exit early and don't finish the sysex msg
1178     * and clean up
1179     */
1180    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1181    PmError rslt = pmNoError;
1182    LPMIDIHDR hdr = m->hdr;
1183    if (!hdr) return rslt; /* something bad happened earlier,
1184            do not report an error because it would have been
1185            reported (at least) once already */
1186    /* a(n old) version of MIDI YOKE requires a zero byte after
1187     * the sysex message, but do not increment dwBytesRecorded: */
1188    hdr->lpData[hdr->dwBytesRecorded] = 0;
1189    if (midi->latency == 0) {
1190#ifdef DEBUG_PRINT_BEFORE_SENDING_SYSEX
1191        /* DEBUG CODE: */
1192        { int i; int len = m->hdr->dwBufferLength;
1193          printf("OutLongMsg %d ", len);
1194          for (i = 0; i < len; i++) {
1195              printf("%2x ", (unsigned char) (m->hdr->lpData[i]));
1196          }
1197        }
1198#endif
1199    } else {
1200        /* Using stream interface. There are accumulated bytes in m->hdr
1201           to send using midiStreamOut
1202         */
1203        /* add bytes recorded to MIDIEVENT length, but don't
1204           count the MIDIEVENT data (3 longs) */
1205        MIDIEVENT *evt = (MIDIEVENT *) (hdr->lpData);
1206        evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
1207        /* round up BytesRecorded to multiple of 4 */
1208        hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
1209    }
1210    rslt = winmm_write_flush(midi, timestamp);
1211    return rslt;
1212}
1213
1214
1215static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
1216                                PmTimestamp timestamp)
1217{
1218    /* write a sysex byte */
1219    PmError rslt = pmNoError;
1220    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1221    LPMIDIHDR hdr = m->hdr;
1222    unsigned char *msg_buffer;
1223    assert(m);
1224    if (!hdr) {
1225        m->hdr = hdr = get_free_output_buffer(midi);
1226        assert(hdr);
1227      midi->fill_base = (unsigned char *)(FPTR) m->hdr->lpData;
1228      midi->fill_offset_ptr = (uint32_t *)&(hdr->dwBytesRecorded);
1229       
1230        /* when buffer fills, Pm_WriteSysEx will revert to calling
1231         * pmwin_write_byte, which expect to have space, so leave
1232         * one byte free for pmwin_write_byte. Leave another byte
1233         * of space for zero after message to make early version of
1234         * MIDI YOKE driver happy -- therefore dwBufferLength - 2 */
1235        midi->fill_length = hdr->dwBufferLength - 2;
1236        if (midi->latency != 0) {
1237            unsigned long when = (unsigned long) timestamp;
1238            unsigned long delta;
1239            unsigned long *ptr;
1240            if (when == 0) when = midi->now;
1241            /* when is in real_time; translate to intended stream time */
1242            when = when + m->delta + midi->latency;
1243            /* make sure we don't go backward in time */
1244            if (when < m->last_time) when = m->last_time;
1245            delta = when - m->last_time;
1246            m->last_time = when;
1247
1248            ptr = (unsigned long *) hdr->lpData;
1249            *ptr++ = delta;
1250            *ptr++ = 0;
1251            *ptr = MEVT_F_LONG;
1252            hdr->dwBytesRecorded = 3 * sizeof(long);
1253            /* data will be added at an offset of dwBytesRecorded ... */
1254        }
1255    }
1256    /* add the data byte */
1257    msg_buffer = (unsigned char *) (hdr->lpData);
1258    msg_buffer[hdr->dwBytesRecorded++] = byte;
1259
1260    /* see if buffer is full, leave one byte extra for pad */
1261    if (hdr->dwBytesRecorded >= hdr->dwBufferLength - 1) {
1262        /* write what we've got and continue */
1263        rslt = winmm_end_sysex(midi, timestamp);
1264    }
1265    return rslt;
1266}
1267
1268#ifdef EXPANDING_SYSEX_BUFFERS
1269note: this code is here as an aid in case you want sysex buffers
1270      to expand to hold large messages completely. If so, you
1271      will want to change SYSEX_BYTES_PER_BUFFER above to some
1272      variable that remembers the buffer size. A good place to
1273      put this value would be in the hdr->dwUser field.
1274
1275            rslt = resize_sysex_buffer(midi, m->sysex_byte_count,
1276                                       m->sysex_byte_count * 2);
1277
1278            if (rslt == pmBufferMaxSize) /* if the buffer can't be resized */
1279#endif
1280#ifdef EXPANDING_SYSEX_BUFFERS
1281            int bytesRecorded = hdr->dwBytesRecorded; /* this field gets wiped out, so we'll save it */
1282            rslt = resize_sysex_buffer(midi, bytesRecorded, 2 * bytesRecorded);
1283            hdr->dwBytesRecorded = bytesRecorded;
1284
1285            if (rslt == pmBufferMaxSize) /* if buffer can't be resized */
1286#endif
1287
1288
1289
1290static PmTimestamp winmm_synchronize(PmInternal *midi)
1291{
1292    midiwinmm_type m;
1293    unsigned long pm_stream_time_2;
1294    unsigned long real_time;
1295    unsigned long pm_stream_time;
1296
1297    /* only synchronize if we are using stream interface */
1298    if (midi->latency == 0) return 0;
1299
1300    /* figure out the time */
1301    m = (midiwinmm_type) midi->descriptor;
1302    pm_stream_time_2 = pm_time_get(m);
1303
1304    do {
1305        /* read real_time between two reads of stream time */
1306        pm_stream_time = pm_stream_time_2;
1307        real_time = (*midi->time_proc)(midi->time_info);
1308        pm_stream_time_2 = pm_time_get(m);
1309        /* repeat if more than 1ms elapsed */
1310    } while (pm_stream_time_2 > pm_stream_time + 1);
1311    m->delta = pm_stream_time - real_time;
1312    m->sync_time = real_time;
1313    return real_time;
1314}
1315
1316#ifdef USE_SYSEX_BUFFERS
1317/* winmm_out_callback -- recycle sysex buffers */
1318static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg,
1319                                        DWORD dwInstance, DWORD dwParam1,
1320                                        DWORD dwParam2)
1321{
1322    PmInternal *midi = (PmInternal *) dwInstance;
1323    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1324    LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
1325    int err = 0;  /* set to 0 so that no buffer match will also be an error */
1326
1327    /* Future optimization: eliminate UnprepareHeader calls -- they aren't
1328       necessary; however, this code uses the prepared-flag to indicate which
1329       buffers are free, so we need to do something to flag empty buffers if
1330       we leave them prepared
1331     */
1332    /*
1333    printf("out_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
1334           hdr, wMsg, MOM_DONE);
1335    */
1336    if (wMsg == MOM_DONE) {
1337        MMRESULT ret = midiOutUnprepareHeader(m->handle.out, hdr,
1338                                              sizeof(MIDIHDR));
1339        assert(ret == MMSYSERR_NOERROR);
1340    }
1341    /* notify waiting sender that a buffer is available */
1342    err = SetEvent(m->buffer_signal);
1343    assert(err); /* false -> error */
1344}
1345#endif
1346
1347/* winmm_streamout_callback -- unprepare (free) buffer header */
1348static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
1349        DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
1350{
1351   PmInternal *midi = (PmInternal *)(FPTR) dwInstance;
1352   LPMIDIHDR hdr = (LPMIDIHDR)(FPTR) dwParam1;
1353    midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1354    //int err;
1355
1356    /* Even if an error is pending, I think we should unprepare msgs and
1357       signal their arrival
1358     */
1359    /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
1360           hdr, wMsg, MOM_DONE); */
1361    if (wMsg == MOM_DONE) {
1362        midiOutUnprepareHeader(m->handle.out, hdr,
1363                                              sizeof(MIDIHDR));
1364        //assert(ret == MMSYSERR_NOERROR);
1365    }
1366    /* signal client in case it is blocked waiting for buffer */
1367    SetEvent(m->buffer_signal);
1368    //assert(err); /* false -> error */
1369}
1370
1371
1372/*
1373=========================================================================================
1374begin exported functions
1375=========================================================================================
1376*/
1377
1378#define winmm_in_abort pm_fail_fn
1379pm_fns_node pm_winmm_in_dictionary = {
1380                                         none_write_short,
1381                                         none_sysex,
1382                                         none_sysex,
1383                                         none_write_byte,
1384                                         none_write_short,
1385                                         none_write_flush,
1386                                         winmm_synchronize,
1387                                         winmm_in_open,
1388                                         winmm_in_abort,
1389                                         winmm_in_close,
1390                                         winmm_in_poll,
1391                                         winmm_has_host_error,
1392                                         winmm_get_host_error
1393                                     };
1394
1395pm_fns_node pm_winmm_out_dictionary = {
1396                                          winmm_write_short,
1397                                          winmm_begin_sysex,
1398                                          winmm_end_sysex,
1399                                          winmm_write_byte,
1400                                          winmm_write_short,  /* short realtime message */
1401                                          winmm_write_flush,
1402                                          winmm_synchronize,
1403                                          winmm_out_open,
1404                                          winmm_out_abort,
1405                                          winmm_out_close,
1406                                          none_poll,
1407                                          winmm_has_host_error,
1408                                          winmm_get_host_error
1409                                      };
1410
1411
1412/* initialize winmm interface. Note that if there is something wrong
1413   with winmm (e.g. it is not supported or installed), it is not an
1414   error. We should simply return without having added any devices to
1415   the table. Hence, no error code is returned. Furthermore, this init
1416   code is called along with every other supported interface, so the
1417   user would have a very hard time figuring out what hardware and API
1418   generated the error. Finally, it would add complexity to pmwin.c to
1419   remember where the error code came from in order to convert to text.
1420 */
1421void pm_winmm_init( void )
1422{
1423    pm_winmm_mapper_input();
1424    pm_winmm_mapper_output();
1425    pm_winmm_general_inputs();
1426    pm_winmm_general_outputs();
1427}
1428
1429
1430/* no error codes are returned, even if errors are encountered, because
1431   there is probably nothing the user could do (e.g. it would be an error
1432   to retry.
1433 */
1434void pm_winmm_term( void )
1435{
1436    int i;
1437#ifdef DEBUG
1438    char msg[PM_HOST_ERROR_MSG_LEN];
1439#endif
1440    //int doneAny = 0;
1441#ifdef DEBUG
1442    printf("pm_winmm_term called\n");
1443#endif
1444    for (i = 0; i < pm_descriptor_index; i++) {
1445        PmInternal * midi = descriptors[i].internalDescriptor;
1446        if (midi) {
1447            midiwinmm_type m = (midiwinmm_type) midi->descriptor;
1448            if (m->handle.out) {
1449                /* close next open device*/
1450#ifdef DEBUG
1451                if (doneAny == 0) {
1452                    printf("begin closing open devices...\n");
1453                    doneAny = 1;
1454                }
1455                /* report any host errors; this EXTEREMELY useful when
1456                   trying to debug client app */
1457                if (winmm_has_host_error(midi)) {
1458                    winmm_get_host_error(midi, msg, PM_HOST_ERROR_MSG_LEN);
1459                    printf("%s\n", msg);
1460                }
1461#endif
1462                /* close all open ports */
1463                (*midi->dictionary->close)(midi);
1464            }
1465        }
1466    }
1467    if (midi_in_caps) {
1468        pm_free(midi_in_caps);
1469        midi_in_caps = NULL;
1470    }
1471    if (midi_out_caps) {
1472        pm_free(midi_out_caps);
1473        midi_out_caps = NULL;
1474    }
1475#ifdef DEBUG
1476    if (doneAny) {
1477        printf("warning: devices were left open. They have been closed.\n");
1478    }
1479    printf("pm_winmm_term exiting\n");
1480#endif
1481    pm_descriptor_index = 0;
1482}
Property changes on: trunk/src/lib/portmidi/pmwinmm.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/finddefault.c
r0r19990
1/* finddefault.c -- find_default_device() implementation
2   Roger Dannenberg, June 2008
3*/
4
5#include <stdlib.h>
6#include <string.h>
7#include "portmidi.h"
8#include "pmutil.h"
9#include "pminternal.h"
10#include "pmmacosxcm.h"
11#include "readbinaryplist.h"
12
13/* Parse preference files, find default device, search devices --
14   This parses the preference file(s) once for input and once for
15   output, which is inefficient but much simpler to manage. Note
16   that using the readbinaryplist.c module, you cannot keep two
17   plist files (user and system) open at once (due to a simple
18   memory management scheme).
19*/
20PmDeviceID find_default_device(char *path, int input, PmDeviceID id)
21/* path -- the name of the preference we are searching for
22   input -- true iff this is an input device
23   id -- current default device id
24   returns matching device id if found, otherwise id
25*/
26{
27    static char *pref_file = (char *)"com.apple.java.util.prefs.plist";
28    char *pref_str = NULL;
29    // read device preferences
30    value_ptr prefs = bplist_read_user_pref(pref_file);
31    if (prefs) {
32        value_ptr pref_val = value_dict_lookup_using_path(prefs, path);
33        if (pref_val) {
34            pref_str = value_get_asciistring(pref_val);
35        }
36    }
37    if (!pref_str) {
38        bplist_free_data(); /* look elsewhere */
39        prefs = bplist_read_system_pref(pref_file);
40        if (prefs) {
41            value_ptr pref_val = value_dict_lookup_using_path(prefs, path);
42            if (pref_val) {
43                pref_str = value_get_asciistring(pref_val);
44            }
45        }
46    }
47    if (pref_str) { /* search devices for match */
48        int i = pm_find_default_device(pref_str, input);
49        if (i != pmNoDevice) {
50            id = i;
51   }
52    }
53    if (prefs) {
54        bplist_free_data();
55    }
56    return id;
57}
Property changes on: trunk/src/lib/portmidi/finddefault.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/ptmacosx_mach.c
r0r19990
1/* ptmacosx.c -- portable timer implementation for mac os x */
2
3#include <stdlib.h>
4#include <stdio.h>
5#include <CoreAudio/HostTime.h>
6
7#import <mach/mach.h>
8#import <mach/mach_error.h>
9#import <mach/mach_time.h>
10#import <mach/clock.h>
11#include <unistd.h>
12
13#include "porttime.h"
14#include "sys/time.h"
15#include "pthread.h"
16
17//#define NSEC_PER_MSEC 1000000
18#define THREAD_IMPORTANCE 30
19
20static int time_started_flag = FALSE;
21static UInt64 start_time;
22static pthread_t pt_thread_pid;
23
24/* note that this is static data -- we only need one copy */
25typedef struct {
26    int id;
27    int resolution;
28    PtCallback *callback;
29    void *userData;
30} pt_callback_parameters;
31
32static int pt_callback_proc_id = 0;
33
34static void *Pt_CallbackProc(void *p)
35{
36    pt_callback_parameters *parameters = (pt_callback_parameters *) p;
37    int mytime = 1;
38
39    kern_return_t error;
40    thread_extended_policy_data_t extendedPolicy;
41    thread_precedence_policy_data_t precedencePolicy;
42
43    extendedPolicy.timeshare = 0;
44    error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
45                              (thread_policy_t)&extendedPolicy,
46                              THREAD_EXTENDED_POLICY_COUNT);
47    if (error != KERN_SUCCESS) {
48        mach_error("Couldn't set thread timeshare policy", error);
49    }
50
51    precedencePolicy.importance = THREAD_IMPORTANCE;
52    error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
53                              (thread_policy_t)&precedencePolicy,
54                              THREAD_PRECEDENCE_POLICY_COUNT);
55    if (error != KERN_SUCCESS) {
56        mach_error("Couldn't set thread precedence policy", error);
57    }
58   
59   
60    /* to kill a process, just increment the pt_callback_proc_id */
61    /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */
62    while (pt_callback_proc_id == parameters->id) {
63        /* wait for a multiple of resolution ms */
64        UInt64 wait_time;
65        int delay = mytime++ * parameters->resolution - Pt_Time();
66   PtTimestamp timestamp;
67        if (delay < 0) delay = 0;
68        wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC);
69        wait_time += AudioGetCurrentHostTime();
70        error = mach_wait_until(wait_time);
71   timestamp = Pt_Time();
72        (*(parameters->callback))(timestamp, parameters->userData);
73    }
74    free(parameters);
75    return NULL;
76}
77
78
79PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
80{
81    if (time_started_flag) return ptAlreadyStarted;
82    start_time = AudioGetCurrentHostTime();
83   
84    if (callback) {
85        int res;
86        pt_callback_parameters *parms;
87
88        parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters));
89        if (!parms) return ptInsufficientMemory;
90        parms->id = pt_callback_proc_id;
91        parms->resolution = resolution;
92        parms->callback = callback;
93        parms->userData = userData;
94        res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms);
95        if (res != 0) return ptHostError;
96    }
97   
98    time_started_flag = TRUE;
99    return ptNoError;
100}
101
102
103PtError Pt_Stop()
104{
105    /* printf("Pt_Stop called\n"); */
106    pt_callback_proc_id++;
107    pthread_join(pt_thread_pid, NULL);
108    time_started_flag = FALSE;
109    return ptNoError;
110}
111
112
113int Pt_Started()
114{
115    return time_started_flag;
116}
117
118
119PtTimestamp Pt_Time()
120{
121    UInt64 clock_time, nsec_time;
122    clock_time = AudioGetCurrentHostTime() - start_time;
123    nsec_time = AudioConvertHostTimeToNanos(clock_time);
124    return (PtTimestamp)(nsec_time / NSEC_PER_MSEC);
125}
126
127
128void Pt_Sleep(int32_t duration)
129{
130    usleep(duration * 1000);
131}
Property changes on: trunk/src/lib/portmidi/ptmacosx_mach.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/portmidi.c
r0r19990
1#ifdef _MSC_VER
2 #pragma warning(disable: 4244) // stop warnings about downsize typecasts
3 #pragma warning(disable: 4018) // stop warnings about signed/unsigned
4#endif
5
6#include "stdlib.h"
7#include "string.h"
8#include "portmidi.h"
9#include "porttime.h"
10#include "pmutil.h"
11#include "pminternal.h"
12#include <assert.h>
13
14#define MIDI_CLOCK      0xf8
15#define MIDI_ACTIVE     0xfe
16#define MIDI_STATUS_MASK 0x80
17#define MIDI_SYSEX      0xf0
18#define MIDI_EOX        0xf7
19#define MIDI_START      0xFA
20#define MIDI_STOP       0xFC
21#define MIDI_CONTINUE   0xFB
22#define MIDI_F9         0xF9
23#define MIDI_FD         0xFD
24#define MIDI_RESET      0xFF
25#define MIDI_NOTE_ON    0x90
26#define MIDI_NOTE_OFF   0x80
27#define MIDI_CHANNEL_AT 0xD0
28#define MIDI_POLY_AT    0xA0
29#define MIDI_PROGRAM    0xC0
30#define MIDI_CONTROL    0xB0
31#define MIDI_PITCHBEND  0xE0
32#define MIDI_MTC        0xF1
33#define MIDI_SONGPOS    0xF2
34#define MIDI_SONGSEL    0xF3
35#define MIDI_TUNE       0xF6
36
37#define is_empty(midi) ((midi)->tail == (midi)->head)
38
39/* this is not static so that pm_init can set it directly if
40 *   (see pmmac.c:pm_init())
41 */
42int pm_initialized = FALSE;
43
44int pm_hosterror;
45char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
46
47#ifdef PM_CHECK_ERRORS
48
49#include <stdio.h>
50
51#define STRING_MAX 80
52
53static void prompt_and_exit(void)
54{
55    char line[STRING_MAX];
56    printf("type ENTER...");
57    fgets(line, STRING_MAX, stdin);
58    /* this will clean up open ports: */
59    exit(-1);
60}
61
62
63static PmError pm_errmsg(PmError err)
64{
65    if (err == pmHostError) {
66        /* it seems pointless to allocate memory and copy the string,
67         * so I will do the work of Pm_GetHostErrorText directly
68         */
69        printf("PortMidi found host error...\n  %s\n", pm_hosterror_text);
70        pm_hosterror = FALSE;
71        pm_hosterror_text[0] = 0; /* clear the message */
72        prompt_and_exit();
73    } else if (err < 0) {
74        printf("PortMidi call failed...\n  %s\n", Pm_GetErrorText(err));
75        prompt_and_exit();
76    }
77    return err;
78}
79#else
80#define pm_errmsg(err) err
81#endif
82
83/*
84====================================================================
85system implementation of portmidi interface
86====================================================================
87*/
88
89int pm_descriptor_max = 0;
90int pm_descriptor_index = 0;
91descriptor_type descriptors = NULL;
92
93/* pm_add_device -- describe interface/device pair to library
94 *
95 * This is called at intialization time, once for each
96 * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1)
97 * The strings are retained but NOT COPIED, so do not destroy them!
98 *
99 * returns pmInvalidDeviceId if device memory is exceeded
100 * otherwise returns pmNoError
101 */
102PmError pm_add_device(char *interf, char *name, int input,
103                      void *descriptor, pm_fns_type dictionary) {
104    if (pm_descriptor_index >= pm_descriptor_max) {
105        // expand descriptors
106        descriptor_type new_descriptors = (descriptor_type)
107            pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32));
108        if (!new_descriptors) return pmInsufficientMemory;
109        if (descriptors) {
110            memcpy(new_descriptors, descriptors,
111                   sizeof(descriptor_node) * pm_descriptor_max);
112            free(descriptors);
113        }
114        pm_descriptor_max += 32;
115        descriptors = new_descriptors;
116    }
117    descriptors[pm_descriptor_index].pub.interf = interf;
118    descriptors[pm_descriptor_index].pub.name = name;
119    descriptors[pm_descriptor_index].pub.input = input;
120    descriptors[pm_descriptor_index].pub.output = !input;
121
122    /* default state: nothing to close (for automatic device closing) */
123    descriptors[pm_descriptor_index].pub.opened = FALSE;
124
125    /* ID number passed to win32 multimedia API open */
126    descriptors[pm_descriptor_index].descriptor = descriptor;
127   
128    /* points to PmInternal, allows automatic device closing */
129    descriptors[pm_descriptor_index].internalDescriptor = NULL;
130
131    descriptors[pm_descriptor_index].dictionary = dictionary;
132   
133    pm_descriptor_index++;
134   
135    return pmNoError;
136}
137
138
139/* utility to look up device, given a pattern,
140   note: pattern is modified
141 */
142int pm_find_default_device(char *pattern, int is_input)
143{
144    int id = pmNoDevice;
145    int i;
146    /* first parse pattern into name, interf parts */
147    char *interf_pref = (char *)""; /* initially assume it is not there */
148    char *name_pref = strstr(pattern, ", ");
149
150    if (name_pref) { /* found separator, adjust the pointer */
151        interf_pref = pattern;
152        name_pref[0] = 0;
153        name_pref += 2;
154    } else {
155        name_pref = pattern; /* whole string is the name pattern */
156    }
157    for (i = 0; i < pm_descriptor_index; i++) {
158        const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
159        if (info->input == is_input &&
160            strstr(info->name, name_pref) &&
161            strstr(info->interf, interf_pref)) {
162            id = i;
163            break;
164        }
165    }   
166    return id;
167}
168
169
170/*
171====================================================================
172portmidi implementation
173====================================================================
174*/
175
176PMEXPORT int Pm_CountDevices( void ) {
177    Pm_Initialize();
178    /* no error checking -- Pm_Initialize() does not fail */
179    return pm_descriptor_index;
180}
181
182
183PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) {
184    Pm_Initialize(); /* no error check needed */
185    if (id >= 0 && id < pm_descriptor_index) {
186        return &descriptors[id].pub;
187    }
188    return NULL;
189}
190
191/* pm_success_fn -- "noop" function pointer */
192PmError pm_success_fn(PmInternal *midi) {
193    return pmNoError;
194}
195
196/* none_write -- returns an error if called */
197PmError none_write_short(PmInternal *midi, PmEvent *buffer) {
198    return pmBadPtr;
199}
200
201/* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */
202PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) {
203    return pmBadPtr;
204}
205
206PmError none_write_byte(PmInternal *midi, unsigned char byte,
207                        PmTimestamp timestamp) {
208    return pmBadPtr;
209}
210
211/* pm_fail_fn -- generic function, returns error if called */
212PmError pm_fail_fn(PmInternal *midi) {
213    return pmBadPtr;
214}
215
216static PmError none_open(PmInternal *midi, void *driverInfo) {
217    return pmBadPtr;
218}
219static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) {
220    *msg = 0; // empty string
221}
222static unsigned int none_has_host_error(PmInternal * midi) {
223    return FALSE;
224}
225PmTimestamp none_synchronize(PmInternal *midi) {
226    return 0;
227}
228
229#define none_abort pm_fail_fn
230#define none_close pm_fail_fn
231
232pm_fns_node pm_none_dictionary = {
233    none_write_short,
234    none_sysex,
235    none_sysex,
236    none_write_byte,
237    none_write_short,
238    none_write_flush,
239    none_synchronize,
240    none_open,
241    none_abort,
242    none_close,
243    none_poll,
244    none_has_host_error,
245    none_get_host_error
246};
247
248
249PMEXPORT const char *Pm_GetErrorText( PmError errnum ) {
250    const char *msg;
251
252    switch(errnum)
253    {
254    case pmNoError:                 
255        msg = "";
256        break;
257    case pmHostError:               
258        msg = "PortMidi: `Host error'";
259        break;
260    case pmInvalidDeviceId:         
261        msg = "PortMidi: `Invalid device ID'";
262        break;
263    case pmInsufficientMemory:       
264        msg = "PortMidi: `Insufficient memory'";
265        break;
266    case pmBufferTooSmall:           
267        msg = "PortMidi: `Buffer too small'";
268        break;
269    case pmBadPtr:                   
270        msg = "PortMidi: `Bad pointer'";
271        break;
272    case pmInternalError:           
273        msg = "PortMidi: `Internal PortMidi Error'";
274        break;
275    case pmBufferOverflow:
276        msg = "PortMidi: `Buffer overflow'";
277        break;
278    case pmBadData:
279        msg = "PortMidi: `Invalid MIDI message Data'";
280        break;
281    case pmBufferMaxSize:
282        msg = "PortMidi: `Buffer cannot be made larger'";
283        break;
284    default:                         
285        msg = "PortMidi: `Illegal error number'";
286        break;
287    }
288    return msg;
289}
290
291
292/* This can be called whenever you get a pmHostError return value.
293 * The error will always be in the global pm_hosterror_text.
294 */
295PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) {
296    assert(msg);
297    assert(len > 0);
298    if (pm_hosterror) {
299        strncpy(msg, (char *) pm_hosterror_text, len);
300        pm_hosterror = FALSE;
301        pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it
302                                     might help with debugging */
303        msg[len - 1] = 0; /* make sure string is terminated */
304    } else {
305        msg[0] = 0; /* no string to return */
306    }
307}
308
309
310PMEXPORT int Pm_HasHostError(PortMidiStream * stream) {
311    if (pm_hosterror) return TRUE;
312    if (stream) {
313        PmInternal * midi = (PmInternal *) stream;
314        pm_hosterror = (*midi->dictionary->has_host_error)(midi);
315        if (pm_hosterror) {
316            midi->dictionary->host_error(midi, pm_hosterror_text,
317                                         PM_HOST_ERROR_MSG_LEN);
318            /* now error message is global */
319            return TRUE;
320        }
321    }
322    return FALSE;
323}
324
325
326PMEXPORT PmError Pm_Initialize( void ) {
327    if (!pm_initialized) {
328        pm_hosterror = FALSE;
329        pm_hosterror_text[0] = 0; /* the null string */
330        pm_init();
331        pm_initialized = TRUE;
332    }
333    return pmNoError;
334}
335
336
337PMEXPORT PmError Pm_Terminate( void ) {
338    if (pm_initialized) {
339        pm_term();
340        // if there are no devices, descriptors might still be NULL
341        if (descriptors != NULL) {
342            free(descriptors);
343            descriptors = NULL;
344        }
345        pm_descriptor_index = 0;
346        pm_descriptor_max = 0;
347        pm_initialized = FALSE;
348    }
349    return pmNoError;
350}
351
352
353/* Pm_Read -- read up to length messages from source into buffer */
354/*
355 * returns number of messages actually read, or error code
356 */
357PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) {
358    PmInternal *midi = (PmInternal *) stream;
359    int n = 0;
360    PmError err = pmNoError;
361    pm_hosterror = FALSE;
362    /* arg checking */
363    if(midi == NULL)
364        err = pmBadPtr;
365    else if(!descriptors[midi->device_id].pub.opened)
366        err = pmBadPtr;
367    else if(!descriptors[midi->device_id].pub.input)
368        err = pmBadPtr;   
369    /* First poll for data in the buffer...
370     * This either simply checks for data, or attempts first to fill the buffer
371     * with data from the MIDI hardware; this depends on the implementation.
372     * We could call Pm_Poll here, but that would redo a lot of redundant
373     * parameter checking, so I copied some code from Pm_Poll to here: */
374    else err = (*(midi->dictionary->poll))(midi);
375
376    if (err != pmNoError) {
377        if (err == pmHostError) {
378            midi->dictionary->host_error(midi, pm_hosterror_text,
379                                         PM_HOST_ERROR_MSG_LEN);
380          pm_hosterror = TRUE;
381        }
382        return pm_errmsg(err);
383    }
384
385    while (n < length) {
386        PmError err = Pm_Dequeue(midi->queue, buffer++);
387        if (err == pmBufferOverflow) {
388            /* ignore the data we have retreived so far */
389            return pm_errmsg(pmBufferOverflow);
390        } else if (err == 0) { /* empty queue */
391            break;
392        }
393        n++;
394    }
395    return n;
396}
397
398PMEXPORT PmError Pm_Poll( PortMidiStream *stream )
399{
400    PmInternal *midi = (PmInternal *) stream;
401    PmError err;
402
403    pm_hosterror = FALSE;
404    /* arg checking */
405    if(midi == NULL)
406        err = pmBadPtr;
407    else if (!descriptors[midi->device_id].pub.opened)
408        err = pmBadPtr;
409    else if (!descriptors[midi->device_id].pub.input)
410        err = pmBadPtr;
411    else
412        err = (*(midi->dictionary->poll))(midi);
413
414    if (err != pmNoError) {
415        if (err == pmHostError) {
416            midi->dictionary->host_error(midi, pm_hosterror_text,
417                                         PM_HOST_ERROR_MSG_LEN);
418           pm_hosterror = TRUE;
419        }
420        return pm_errmsg(err);
421    }
422
423    return !Pm_QueueEmpty(midi->queue);
424}
425
426
427/* this is called from Pm_Write and Pm_WriteSysEx to issue a
428 * call to the system-dependent end_sysex function and handle
429 * the error return
430 */
431static PmError pm_end_sysex(PmInternal *midi)
432{
433    PmError err = (*midi->dictionary->end_sysex)(midi, 0);
434    midi->sysex_in_progress = FALSE;
435    if (err == pmHostError) {
436        midi->dictionary->host_error(midi, pm_hosterror_text,
437                                     PM_HOST_ERROR_MSG_LEN);
438        pm_hosterror = TRUE;
439    }
440    return err;
441}
442
443
444/* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and
445   Pm_WriteSysEx all operate a state machine that "outputs" calls to
446   write_short, begin_sysex, write_byte, end_sysex, and write_realtime */
447
448PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length)
449{
450    PmInternal *midi = (PmInternal *) stream;
451    PmError err = pmNoError;
452    int i;
453    int bits;
454   
455    pm_hosterror = FALSE;
456    /* arg checking */
457    if(midi == NULL)
458        err = pmBadPtr;
459    else if(!descriptors[midi->device_id].pub.opened)
460        err = pmBadPtr;
461    else if(!descriptors[midi->device_id].pub.output)
462        err = pmBadPtr;
463    else
464        err = pmNoError;
465   
466    if (err != pmNoError) goto pm_write_error;
467   
468    if (midi->latency == 0) {
469        midi->now = 0;
470    } else {
471        midi->now = (*(midi->time_proc))(midi->time_info);
472        if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) {
473            /* time to resync */
474            midi->now = (*midi->dictionary->synchronize)(midi);
475            midi->first_message = FALSE;
476        }
477    }
478    /* error recovery: when a sysex is detected, we call
479     *   dictionary->begin_sysex() followed by calls to
480     *   dictionary->write_byte() and dictionary->write_realtime()
481     *   until an end-of-sysex is detected, when we call
482     *   dictionary->end_sysex(). After an error occurs,
483     *   Pm_Write() continues to call functions. For example,
484     *   it will continue to call write_byte() even after
485     *   an error sending a sysex message, and end_sysex() will be
486     *   called when an EOX or non-real-time status is found.
487     * When errors are detected, Pm_Write() returns immediately,
488     *   so it is possible that this will drop data and leave
489     *   sysex messages in a partially transmitted state.
490     */
491    for (i = 0; i < length; i++) {
492        uint32_t msg = buffer[i].message;
493        bits = 0;
494        /* is this a sysex message? */
495        if (Pm_MessageStatus(msg) == MIDI_SYSEX) {
496            if (midi->sysex_in_progress) {
497                /* error: previous sysex was not terminated by EOX */
498                midi->sysex_in_progress = FALSE;
499                err = pmBadData;
500                goto pm_write_error;
501            }
502            midi->sysex_in_progress = TRUE;
503            if ((err = (*midi->dictionary->begin_sysex)(midi,
504                               buffer[i].timestamp)) != pmNoError)
505                goto pm_write_error;
506            if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX,
507                               buffer[i].timestamp)) != pmNoError)
508                goto pm_write_error;
509            bits = 8;
510            /* fall through to continue sysex processing */
511        } else if ((msg & MIDI_STATUS_MASK) &&
512                   (Pm_MessageStatus(msg) != MIDI_EOX)) {
513            /* a non-sysex message */
514            if (midi->sysex_in_progress) {
515                /* this should be a realtime message */
516                if (is_real_time(msg)) {
517                    if ((err = (*midi->dictionary->write_realtime)(midi,
518                                       &(buffer[i]))) != pmNoError)
519                        goto pm_write_error;
520                } else {
521                    midi->sysex_in_progress = FALSE;
522                    err = pmBadData;
523                    /* ignore any error from this, because we already have one */
524                    /* pass 0 as timestamp -- it's ignored */
525                    (*midi->dictionary->end_sysex)(midi, 0);
526                    goto pm_write_error;
527                }
528            } else { /* regular short midi message */
529                if ((err = (*midi->dictionary->write_short)(midi,
530                                   &(buffer[i]))) != pmNoError)
531                    goto pm_write_error;
532                continue;
533            }
534        }
535        if (midi->sysex_in_progress) { /* send sysex bytes until EOX */
536            /* see if we can accelerate data transfer */
537            if (bits == 0 && midi->fill_base && /* 4 bytes to copy */
538                (*midi->fill_offset_ptr) + 4 <= midi->fill_length &&
539                (msg & 0x80808080) == 0) { /* all data */
540                    /* copy 4 bytes from msg to fill_base + fill_offset */
541                    unsigned char *ptr = midi->fill_base +
542                                         *(midi->fill_offset_ptr);
543                    ptr[0] = msg; ptr[1] = msg >> 8;
544                    ptr[2] = msg >> 16; ptr[3] = msg >> 24;
545                    (*midi->fill_offset_ptr) += 4;
546                     continue;
547            }
548            /* no acceleration, so do byte-by-byte copying */
549            while (bits < 32) {
550                unsigned char midi_byte = (unsigned char) (msg >> bits);
551                if ((err = (*midi->dictionary->write_byte)(midi, midi_byte,
552                                   buffer[i].timestamp)) != pmNoError)
553                    goto pm_write_error;
554                if (midi_byte == MIDI_EOX) {
555                    err = pm_end_sysex(midi);
556                    if (err != pmNoError) goto error_exit;
557                    break; /* from while loop */
558                }
559                bits += 8;
560            }
561        } else {
562            /* not in sysex mode, but message did not start with status */
563            err = pmBadData;
564            goto pm_write_error;
565        }
566    }
567    /* after all messages are processed, send the data */
568    if (!midi->sysex_in_progress)
569        err = (*midi->dictionary->write_flush)(midi, 0);
570pm_write_error:
571    if (err == pmHostError) {
572        midi->dictionary->host_error(midi, pm_hosterror_text,
573                                     PM_HOST_ERROR_MSG_LEN);
574        pm_hosterror = TRUE;
575    }
576error_exit:
577    return pm_errmsg(err);
578}
579
580
581PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, PmMessage msg)
582{
583    PmEvent event;
584   
585    event.timestamp = when;
586    event.message = msg;
587    return Pm_Write(stream, &event, 1);
588}
589
590
591PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when,
592                      unsigned char *msg)
593{
594    /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */
595    /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */
596    #define BUFLEN ((int) (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage)))
597    PmEvent buffer[BUFLEN];
598    int buffer_size = 1; /* first time, send 1. After that, it's BUFLEN */
599    PmInternal *midi = (PmInternal *) stream;
600    /* the next byte in the buffer is represented by an index, bufx, and
601       a shift in bits */
602    int shift = 0;
603    int bufx = 0;
604    buffer[0].message = 0;
605    buffer[0].timestamp = when;
606
607    while (1) {
608        /* insert next byte into buffer */
609        buffer[bufx].message |= ((*msg) << shift);
610        shift += 8;
611        if (*msg++ == MIDI_EOX) break;
612        if (shift == 32) {
613            shift = 0;
614            bufx++;
615            if (bufx == buffer_size) {
616                PmError err = Pm_Write(stream, buffer, buffer_size);
617                /* note: Pm_Write has already called errmsg() */
618                if (err) return err;
619                /* prepare to fill another buffer */
620                bufx = 0;
621                buffer_size = BUFLEN;
622                /* optimization: maybe we can just copy bytes */
623                if (midi->fill_base) {
624                    PmError err;
625                    while (*(midi->fill_offset_ptr) < midi->fill_length) {
626                        midi->fill_base[(*midi->fill_offset_ptr)++] = *msg;
627                        if (*msg++ == MIDI_EOX) {
628                            err = pm_end_sysex(midi);
629                            if (err != pmNoError) return pm_errmsg(err);
630                            goto end_of_sysex;
631                        }
632                    }
633                    /* I thought that I could do a pm_Write here and
634                     * change this if to a loop, avoiding calls in Pm_Write
635                     * to the slower write_byte, but since
636                     * sysex_in_progress is true, this will not flush
637                     * the buffer, and we'll infinite loop: */
638                    /* err = Pm_Write(stream, buffer, 0);
639                       if (err) return err; */
640                    /* instead, the way this works is that Pm_Write calls
641                     * write_byte on 4 bytes. The first, since the buffer
642                     * is full, will flush the buffer and allocate a new
643                     * one. This primes the buffer so
644                     * that we can return to the loop above and fill it
645                     * efficiently without a lot of function calls.
646                     */
647                    buffer_size = 1; /* get another message started */
648                }
649            }
650            buffer[bufx].message = 0;
651            buffer[bufx].timestamp = when;
652        }
653        /* keep inserting bytes until you find MIDI_EOX */
654    }
655end_of_sysex:
656    /* we're finished sending full buffers, but there may
657     * be a partial one left.
658     */
659    if (shift != 0) bufx++; /* add partial message to buffer len */
660    if (bufx) { /* bufx is number of PmEvents to send from buffer */
661        PmError err = Pm_Write(stream, buffer, bufx);
662        if (err) return err;
663    }
664    return pmNoError;
665}
666
667
668
669PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream,
670                     PmDeviceID inputDevice,
671                     void *inputDriverInfo,
672                     int32_t bufferSize,
673                     PmTimeProcPtr time_proc,
674                     void *time_info)
675{
676    PmInternal *midi;
677    PmError err = pmNoError;
678    pm_hosterror = FALSE;
679    *stream = NULL;
680   
681    /* arg checking */
682    if (inputDevice < 0 || inputDevice >= pm_descriptor_index)
683        err = pmInvalidDeviceId;
684    else if (!descriptors[inputDevice].pub.input)
685        err =  pmInvalidDeviceId;
686    else if(descriptors[inputDevice].pub.opened)
687        err =  pmInvalidDeviceId;
688   
689    if (err != pmNoError)
690        goto error_return;
691
692    /* create portMidi internal data */
693    midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
694    *stream = midi;
695    if (!midi) {
696        err = pmInsufficientMemory;
697        goto error_return;
698    }
699    midi->device_id = inputDevice;
700    midi->write_flag = FALSE;
701    midi->time_proc = time_proc;
702    midi->time_info = time_info;
703    /* windows adds timestamps in the driver and these are more accurate than
704       using a time_proc, so do not automatically provide a time proc. Non-win
705       implementations may want to provide a default time_proc in their
706       system-specific midi_out_open() method.
707     */
708    if (bufferSize <= 0) bufferSize = 256; /* default buffer size */
709    midi->queue = Pm_QueueCreate(bufferSize, (int32_t) sizeof(PmEvent));
710    if (!midi->queue) {
711        /* free portMidi data */
712        *stream = NULL;
713        pm_free(midi);
714        err = pmInsufficientMemory;
715        goto error_return;
716    }
717    midi->buffer_len = bufferSize; /* portMidi input storage */
718    midi->latency = 0; /* not used */
719    midi->sysex_in_progress = FALSE;
720    midi->sysex_message = 0;
721    midi->sysex_message_count = 0;
722    midi->filters = PM_FILT_ACTIVE;
723    midi->channel_mask = 0xFFFF;
724    midi->sync_time = 0;
725    midi->first_message = TRUE;
726    midi->dictionary = descriptors[inputDevice].dictionary;
727    midi->fill_base = NULL;
728    midi->fill_offset_ptr = NULL;
729    midi->fill_length = 0;
730    descriptors[inputDevice].internalDescriptor = midi;
731    /* open system dependent input device */
732    err = (*midi->dictionary->open)(midi, inputDriverInfo);
733    if (err) {
734        *stream = NULL;
735        descriptors[inputDevice].internalDescriptor = NULL;
736        /* free portMidi data */
737        Pm_QueueDestroy(midi->queue);
738        pm_free(midi);
739    } else {
740        /* portMidi input open successful */
741        descriptors[inputDevice].pub.opened = TRUE;
742    }
743error_return:
744    /* note: if there is a pmHostError, it is the responsibility
745     * of the system-dependent code (*midi->dictionary->open)()
746     * to set pm_hosterror and pm_hosterror_text
747     */
748    return pm_errmsg(err);
749}
750
751
752PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream,
753                      PmDeviceID outputDevice,
754                      void *outputDriverInfo,
755                      int32_t bufferSize,
756                      PmTimeProcPtr time_proc,
757                      void *time_info,
758                      int32_t latency )
759{
760    PmInternal *midi;
761    PmError err = pmNoError;
762    pm_hosterror = FALSE;
763    *stream =  NULL;
764   
765    /* arg checking */
766    if (outputDevice < 0 || outputDevice >= pm_descriptor_index)
767        err = pmInvalidDeviceId;
768    else if (!descriptors[outputDevice].pub.output)
769        err = pmInvalidDeviceId;
770    else if (descriptors[outputDevice].pub.opened)
771        err = pmInvalidDeviceId;
772    if (err != pmNoError)
773        goto error_return;
774
775    /* create portMidi internal data */
776    midi = (PmInternal *) pm_alloc(sizeof(PmInternal));
777    *stream = midi;                 
778    if (!midi) {
779        err = pmInsufficientMemory;
780        goto error_return;
781    }
782    midi->device_id = outputDevice;
783    midi->write_flag = TRUE;
784    midi->time_proc = time_proc;
785    /* if latency > 0, we need a time reference. If none is provided,
786       use PortTime library */
787    if (time_proc == NULL && latency != 0) {
788        if (!Pt_Started())
789            Pt_Start(1, 0, 0);
790        /* time_get does not take a parameter, so coerce */
791        midi->time_proc = (PmTimeProcPtr) Pt_Time;
792    }
793    midi->time_info = time_info;
794    midi->buffer_len = bufferSize;
795    midi->queue = NULL; /* unused by output */
796    /* if latency zero, output immediate (timestamps ignored) */
797    /* if latency < 0, use 0 but don't return an error */
798    if (latency < 0) latency = 0;
799    midi->latency = latency;
800    midi->sysex_in_progress = FALSE;
801    midi->sysex_message = 0; /* unused by output */
802    midi->sysex_message_count = 0; /* unused by output */
803    midi->filters = 0; /* not used for output */
804    midi->channel_mask = 0xFFFF;
805    midi->sync_time = 0;
806    midi->first_message = TRUE;
807    midi->dictionary = descriptors[outputDevice].dictionary;
808    midi->fill_base = NULL;
809    midi->fill_offset_ptr = NULL;
810    midi->fill_length = 0;
811    descriptors[outputDevice].internalDescriptor = midi;
812    /* open system dependent output device */
813    err = (*midi->dictionary->open)(midi, outputDriverInfo);
814    if (err) {
815        *stream = NULL;
816        descriptors[outputDevice].internalDescriptor = NULL;
817        /* free portMidi data */
818        pm_free(midi);
819    } else {
820        /* portMidi input open successful */
821        descriptors[outputDevice].pub.opened = TRUE;
822    }
823error_return:
824    /* note: system-dependent code must set pm_hosterror and
825     * pm_hosterror_text if a pmHostError occurs
826     */
827    return pm_errmsg(err);
828}
829
830
831PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask)
832{
833    PmInternal *midi = (PmInternal *) stream;
834    PmError err = pmNoError;
835
836    if (midi == NULL)
837        err = pmBadPtr;
838    else
839        midi->channel_mask = mask;
840
841    return pm_errmsg(err);
842}
843
844
845PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) {
846    PmInternal *midi = (PmInternal *) stream;
847    PmError err = pmNoError;
848
849    /* arg checking */
850    if (midi == NULL)
851        err = pmBadPtr;
852    else if (!descriptors[midi->device_id].pub.opened)
853        err = pmBadPtr;
854    else
855        midi->filters = filters;
856    return pm_errmsg(err);
857}
858
859
860PMEXPORT PmError Pm_Close( PortMidiStream *stream ) {
861    PmInternal *midi = (PmInternal *) stream;
862    PmError err = pmNoError;
863
864    pm_hosterror = FALSE;
865    /* arg checking */
866    if (midi == NULL) /* midi must point to something */
867        err = pmBadPtr;
868    /* if it is an open device, the device_id will be valid */
869    else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index)
870        err = pmBadPtr;
871    /* and the device should be in the opened state */
872    else if (!descriptors[midi->device_id].pub.opened)
873        err = pmBadPtr;
874   
875    if (err != pmNoError)
876        goto error_return;
877
878    /* close the device */
879    err = (*midi->dictionary->close)(midi);
880    /* even if an error occurred, continue with cleanup */
881    descriptors[midi->device_id].internalDescriptor = NULL;
882    descriptors[midi->device_id].pub.opened = FALSE;
883    if (midi->queue) Pm_QueueDestroy(midi->queue);
884    pm_free(midi);
885error_return:
886    /* system dependent code must set pm_hosterror and
887     * pm_hosterror_text if a pmHostError occurs.
888     */
889    return pm_errmsg(err);
890}
891
892PmError Pm_Synchronize( PortMidiStream* stream ) {
893    PmInternal *midi = (PmInternal *) stream;
894    PmError err = pmNoError;
895    if (midi == NULL)
896        err = pmBadPtr;
897    else if (!descriptors[midi->device_id].pub.output)
898        err = pmBadPtr;
899    else if (!descriptors[midi->device_id].pub.opened)
900        err = pmBadPtr;
901    else
902        midi->first_message = TRUE;
903    return err;
904}
905
906PMEXPORT PmError Pm_Abort( PortMidiStream* stream ) {
907    PmInternal *midi = (PmInternal *) stream;
908    PmError err;
909    /* arg checking */
910    if (midi == NULL)
911        err = pmBadPtr;
912    else if (!descriptors[midi->device_id].pub.output)
913        err = pmBadPtr;
914    else if (!descriptors[midi->device_id].pub.opened)
915        err = pmBadPtr;
916    else
917        err = (*midi->dictionary->abort)(midi);
918
919    if (err == pmHostError) {
920        midi->dictionary->host_error(midi, pm_hosterror_text,
921                                     PM_HOST_ERROR_MSG_LEN);
922        pm_hosterror = TRUE;
923    }
924    return pm_errmsg(err);
925}
926
927
928
929/* pm_channel_filtered returns non-zero if the channel mask is blocking the current channel */
930#define pm_channel_filtered(status, mask) \
931    ((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask))))
932
933
934/* The following two functions will checks to see if a MIDI message matches
935   the filtering criteria.  Since the sysex routines only want to filter realtime messages,
936   we need to have separate routines.
937 */
938
939
940/* pm_realtime_filtered returns non-zero if the filter will kill the current message.
941   Note that only realtime messages are checked here.
942 */
943#define pm_realtime_filtered(status, filters) \
944    ((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters)))
945
946/*
947    return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE))
948            ||  ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK))
949            ||  ((status == MIDI_START) && (filters & PM_FILT_PLAY))
950            ||  ((status == MIDI_STOP) && (filters & PM_FILT_PLAY))
951            ||  ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY))
952            ||  ((status == MIDI_F9) && (filters & PM_FILT_F9))
953            ||  ((status == MIDI_FD) && (filters & PM_FILT_FD))
954            ||  ((status == MIDI_RESET) && (filters & PM_FILT_RESET))
955            ||  ((status == MIDI_MTC) && (filters & PM_FILT_MTC))
956            ||  ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION))
957            ||  ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT))
958            ||  ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE));
959}*/
960
961
962/* pm_status_filtered returns non-zero if a filter will kill the current message, based on status.
963   Note that sysex and real time are not checked.  It is up to the subsystem (winmm, core midi, alsa)
964   to filter sysex, as it is handled more easily and efficiently at that level.
965   Realtime message are filtered in pm_realtime_filtered.
966 */
967#define pm_status_filtered(status, filters) ((1 << (16 + ((status) >> 4))) & (filters))
968
969
970/*
971    return  ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE))
972            ||  ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE))
973            ||  ((status == MIDI_CHANNEL_AT) && (filters & PM_FILT_CHANNEL_AFTERTOUCH))
974            ||  ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH))
975            ||  ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM))
976            ||  ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL))
977            ||  ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND));
978
979}
980*/
981
982static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp)
983{
984    PmEvent event;
985   
986    /* there may be nothing in the buffer */
987    if (midi->sysex_message_count == 0) return; /* nothing to flush */
988   
989    event.message = midi->sysex_message;
990    event.timestamp = timestamp;
991    /* copied from pm_read_short, avoids filtering */
992    if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
993        midi->sysex_in_progress = FALSE;
994    }
995    midi->sysex_message_count = 0;
996    midi->sysex_message = 0;
997}
998
999
1000/* pm_read_short and pm_read_bytes
1001   are the interface between system-dependent MIDI input handlers
1002   and the system-independent PortMIDI code.
1003   The input handler MUST obey these rules:
1004   1) all short input messages must be sent to pm_read_short, which
1005      enqueues them to a FIFO for the application.
1006   2) each buffer of sysex bytes should be reported by calling pm_read_bytes
1007      (which sets midi->sysex_in_progress). After the eox byte,
1008      pm_read_bytes will clear sysex_in_progress
1009 */
1010
1011/* pm_read_short is the place where all input messages arrive from
1012   system-dependent code such as pmwinmm.c. Here, the messages
1013   are entered into the PortMidi input buffer.
1014 */
1015void pm_read_short(PmInternal *midi, PmEvent *event)
1016{
1017    int status;
1018    /* arg checking */
1019    assert(midi != NULL);
1020    /* midi filtering is applied here */
1021    status = Pm_MessageStatus(event->message);
1022    if (!pm_status_filtered(status, midi->filters)
1023        && (!is_real_time(status) ||
1024            !pm_realtime_filtered(status, midi->filters))
1025        && !pm_channel_filtered(status, midi->channel_mask)) {
1026        /* if sysex is in progress and we get a status byte, it had
1027           better be a realtime message or the starting SYSEX byte;
1028           otherwise, we exit the sysex_in_progress state
1029         */
1030        if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) {
1031            /* two choices: real-time or not. If it's real-time, then
1032             * this should be delivered as a sysex byte because it is
1033             * embedded in a sysex message
1034             */
1035            if (is_real_time(status)) {
1036                midi->sysex_message |=
1037                        (status << (8 * midi->sysex_message_count++));
1038                if (midi->sysex_message_count == 4) {
1039                    pm_flush_sysex(midi, event->timestamp);
1040                }
1041            } else { /* otherwise, it's not real-time. This interrupts
1042                      * a sysex message in progress */
1043                midi->sysex_in_progress = FALSE;
1044            }
1045        } else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) {
1046            midi->sysex_in_progress = FALSE;
1047        }
1048    }
1049}
1050
1051/* pm_read_bytes -- read one (partial) sysex msg from MIDI data */
1052/*
1053 * returns how many bytes processed
1054 */
1055unsigned int pm_read_bytes(PmInternal *midi, const unsigned char *data,
1056                    int len, PmTimestamp timestamp)
1057{
1058    int i = 0; /* index into data, must not be unsigned (!) */
1059    PmEvent event;
1060    event.timestamp = timestamp;
1061    assert(midi);
1062    /* note that since buffers may not have multiples of 4 bytes,
1063     * pm_read_bytes may be called in the middle of an outgoing
1064     * 4-byte PortMidi message. sysex_in_progress indicates that
1065     * a sysex has been sent but no eox.
1066     */
1067    if (len == 0) return 0; /* sanity check */
1068    if (!midi->sysex_in_progress) {
1069        while (i < len) { /* process all data */
1070            unsigned char byte = data[i++];
1071            if (byte == MIDI_SYSEX &&
1072                !pm_realtime_filtered(byte, midi->filters)) {
1073                midi->sysex_in_progress = TRUE;
1074                i--; /* back up so code below will get SYSEX byte */
1075                break; /* continue looping below to process msg */
1076            } else if (byte == MIDI_EOX) {
1077                midi->sysex_in_progress = FALSE;
1078                return i; /* done with one message */
1079            } else if (byte & MIDI_STATUS_MASK) {
1080                /* We're getting MIDI but no sysex in progress.
1081                 * Either the SYSEX status byte was dropped or
1082                 * the message was filtered. Drop the data, but
1083                 * send any embedded realtime bytes.
1084                 */
1085                /* assume that this is a real-time message:
1086                 * it is an error to pass non-real-time messages
1087                 * to pm_read_bytes
1088                 */
1089                event.message = byte;
1090                pm_read_short(midi, &event);
1091            }
1092        } /* all bytes in the buffer are processed */
1093    }
1094    /* Now, i<len implies sysex_in_progress. If sysex_in_progress
1095     * becomes false in the loop, there must have been an overflow
1096     * and we can just drop all remaining bytes
1097     */
1098    while (i < len && midi->sysex_in_progress) {
1099        if (midi->sysex_message_count == 0 && i <= len - 4 &&
1100            ((event.message = (((PmMessage) data[i]) |
1101                             (((PmMessage) data[i+1]) << 8) |
1102                             (((PmMessage) data[i+2]) << 16) |
1103                             (((PmMessage) data[i+3]) << 24))) &
1104             0x80808080) == 0) { /* all data, no status */
1105            if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) {
1106                midi->sysex_in_progress = FALSE;
1107            }
1108            i += 4;
1109        } else {
1110            while (i < len) {
1111                /* send one byte at a time */
1112                unsigned char byte = data[i++];
1113                if (is_real_time(byte) &&
1114                    pm_realtime_filtered(byte, midi->filters)) {
1115                    continue; /* real-time data is filtered, so omit */
1116                }
1117                midi->sysex_message |=
1118                    (byte << (8 * midi->sysex_message_count++));
1119                if (byte == MIDI_EOX) {
1120                    midi->sysex_in_progress = FALSE;
1121                    pm_flush_sysex(midi, event.timestamp);
1122                    return i;
1123                } else if (midi->sysex_message_count == 4) {
1124                    pm_flush_sysex(midi, event.timestamp);
1125                    /* after handling at least one non-data byte
1126                     * and reaching a 4-byte message boundary,
1127                     * resume trying to send 4 at a time in outer loop
1128                     */
1129                    break;
1130                }
1131            }
1132        }
1133    }
1134    return i;
1135}
1136
1137
Property changes on: trunk/src/lib/portmidi/portmidi.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmwinmm.h
r0r19990
1/* midiwin32.h -- system-specific definitions */
2
3void pm_winmm_init( void );
4void pm_winmm_term( void );
5
Property changes on: trunk/src/lib/portmidi/pmwinmm.h
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/pmwin.c
r0r19990
1/* pmwin.c -- PortMidi os-dependent code */
2
3/* This file only needs to implement:
4       pm_init(), which calls various routines to register the
5           available midi devices,
6       Pm_GetDefaultInputDeviceID(), and
7       Pm_GetDefaultOutputDeviceID().
8   This file must
9   be separate from the main portmidi.c file because it is system
10   dependent, and it is separate from, say, pmwinmm.c, because it
11   might need to register devices for winmm, directx, and others.
12
13 */
14
15#include "stdlib.h"
16#include "portmidi.h"
17#include "pmutil.h"
18#include "pminternal.h"
19#include "pmwinmm.h"
20#ifdef DEBUG
21#include "stdio.h"
22#endif
23#undef UNICODE
24#include <windows.h>
25
26/* pm_exit is called when the program exits.
27   It calls pm_term to make sure PortMidi is properly closed.
28   If DEBUG is on, we prompt for input to avoid losing error messages.
29 */
30static void pm_exit(void) {
31    pm_term();
32#ifdef DEBUG
33#define STRING_MAX 80
34    {
35        char line[STRING_MAX];
36        printf("Type ENTER...\n");
37        /* note, w/o this prompting, client console application can not see one
38           of its errors before closing. */
39        fgets(line, STRING_MAX, stdin);
40    }
41#endif
42}
43
44
45/* pm_init is the windows-dependent initialization.*/
46void pm_init(void)
47{
48    atexit(pm_exit);
49#ifdef DEBUG
50    printf("registered pm_exit with atexit()\n");
51#endif
52    pm_winmm_init();
53    /* initialize other APIs (DirectX?) here */
54}
55
56
57void pm_term(void) {
58    pm_winmm_term();
59}
60
61
62static PmDeviceID pm_get_default_device_id(int is_input, char *key) {
63    HKEY hkey;
64#define PATTERN_MAX 256
65    char pattern[PATTERN_MAX];
66    DWORD pattern_max = PATTERN_MAX;
67    DWORD dwType;
68    /* Find first input or device -- this is the default. */
69    PmDeviceID id = pmNoDevice;
70    int i, j;
71    Pm_Initialize(); /* make sure descriptors exist! */
72    for (i = 0; i < pm_descriptor_index; i++) {
73        if (descriptors[i].pub.input == is_input) {
74            id = i;
75            break;
76        }
77    }
78    /* Look in registry for a default device name pattern. */
79    if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software", 0, KEY_READ, &hkey) !=
80        ERROR_SUCCESS) {
81        return id;
82    }
83    if (RegOpenKeyExA(hkey, "JavaSoft", 0, KEY_READ, &hkey) !=
84        ERROR_SUCCESS) {
85        return id;
86    }
87    if (RegOpenKeyExA(hkey, "Prefs", 0, KEY_READ, &hkey) !=
88        ERROR_SUCCESS) {
89        return id;
90    }
91    if (RegOpenKeyExA(hkey, "/Port/Midi", 0, KEY_READ, &hkey) !=
92        ERROR_SUCCESS) {
93        return id;
94    }
95    if (RegQueryValueExA(hkey, key, NULL, &dwType, (BYTE *)pattern, &pattern_max) !=
96   ERROR_SUCCESS) {
97        return id;
98    }
99
100    /* decode pattern: upper case encoded with "/" prefix */
101    i = j = 0;
102    while (pattern[i]) {
103        if (pattern[i] == '/' && pattern[i + 1]) {
104            pattern[j++] = toupper(pattern[++i]);
105   } else {
106            pattern[j++] = tolower(pattern[i]);
107   }
108        i++;
109    }
110    pattern[j] = 0; /* end of string */
111
112    /* now pattern is the string from the registry; search for match */
113    i = pm_find_default_device(pattern, is_input);
114    if (i != pmNoDevice) {
115        id = i;
116    }
117    return id;
118}
119
120
121PmDeviceID Pm_GetDefaultInputDeviceID() {
122    return pm_get_default_device_id(TRUE,
123           (char *)"/P/M_/R/E/C/O/M/M/E/N/D/E/D_/I/N/P/U/T_/D/E/V/I/C/E");
124}
125
126
127PmDeviceID Pm_GetDefaultOutputDeviceID() {
128  return pm_get_default_device_id(FALSE,
129          (char *)"/P/M_/R/E/C/O/M/M/E/N/D/E/D_/O/U/T/P/U/T_/D/E/V/I/C/E");
130}
131
132
133#include "stdio.h"
134
135void *pm_alloc(size_t s) {
136    return malloc(s);
137}
138
139
140void pm_free(void *ptr) {
141    free(ptr);
142}
143
144
Property changes on: trunk/src/lib/portmidi/pmwin.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/ptwinmm.c
r0r19990
1/* ptwinmm.c -- portable timer implementation for win32 */
2
3
4#include "porttime.h"
5#include "windows.h"
6#include "time.h"
7
8
9TIMECAPS caps;
10
11static long time_offset = 0;
12static int time_started_flag = FALSE;
13static long time_resolution;
14static MMRESULT timer_id;
15static PtCallback *time_callback;
16
17void CALLBACK winmm_time_callback(UINT uID, UINT uMsg, DWORD_PTR dwUser,
18                                  DWORD_PTR dw1, DWORD_PTR dw2)
19{
20    (*time_callback)(Pt_Time(), (void *) dwUser);
21}
22 
23
24PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
25{
26    if (time_started_flag) return ptAlreadyStarted;
27    timeBeginPeriod(resolution);
28    time_resolution = resolution;
29    time_offset = timeGetTime();
30    time_started_flag = TRUE;
31    time_callback = callback;
32    if (callback) {
33        timer_id = timeSetEvent(resolution, 1, winmm_time_callback,
34            (DWORD_PTR) userData, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
35        if (!timer_id) return ptHostError;
36    }
37    return ptNoError;
38}
39
40
41PMEXPORT PtError Pt_Stop()
42{
43    if (!time_started_flag) return ptAlreadyStopped;
44    if (time_callback && timer_id) {
45        timeKillEvent(timer_id);
46        time_callback = NULL;
47        timer_id = 0;
48    }
49    time_started_flag = FALSE;
50    timeEndPeriod(time_resolution);
51    return ptNoError;
52}
53
54
55PMEXPORT int Pt_Started()
56{
57    return time_started_flag;
58}
59
60
61PMEXPORT PtTimestamp Pt_Time()
62{
63    return timeGetTime() - time_offset;
64}
65
66
67PMEXPORT void Pt_Sleep(int32_t duration)
68{
69    Sleep(duration);
70}
Property changes on: trunk/src/lib/portmidi/ptwinmm.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/readbinaryplist.c
r0r19990
1/*
2
3readbinaryplist.c -- Roger B. Dannenberg, Jun 2008
4Based on ReadBinaryPList.m by Jens Ayton, 2007
5
6Note that this code is intended to read preference files and has an upper
7bound on file size (currently 100MB) and assumes in some places that 32 bit
8offsets are sufficient.
9
10Here are his comments:
11
12Reader for binary property list files (version 00).
13
14This has been found to work on all 566 binary plists in my ~/Library/Preferences/
15and /Library/Preferences/ directories. This probably does not provide full
16test coverage. It has also been found to provide different data to Apple's
17implementation when presented with a key-value archive. This is because Apple's
18implementation produces undocumented CFKeyArchiverUID objects. My implementation
19produces dictionaries instead, matching the in-file representation used in XML
20and OpenStep plists. See extract_uid().
21
22Full disclosure: in implementing this software, I read one comment and one
23struct defintion in CFLite, Apple's implementation, which is under the APSL
24license. I also deduced the information about CFKeyArchiverUID from that code.
25However, none of the implementation was copied.
26
27Copyright (C) 2007 Jens Ayton
28
29Permission is hereby granted, free of charge, to any person obtaining a copy
30of this software and associated documentation files (the "Software"), to deal
31in the Software without restriction, including without limitation the rights
32to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
33copies of the Software, and to permit persons to whom the Software is
34furnished to do so, subject to the following conditions:
35
36The above copyright notice and this permission notice shall be included in all
37copies or substantial portions of the Software.
38
39THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
40IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
41FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
42AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
43LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
44OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
45SOFTWARE.
46
47*/
48
49/* A note about memory management:
50Strings and possibly other values are unique and because the values
51associated with IDs are cached, you end up with a directed graph rather
52than a tree. It is tricky to free the data because if you do a simple
53depth-first search to free nodes, you will free nodes twice. I decided
54to allocate memory from blocks of 1024 bytes and keep the blocks in a
55list associated with but private to this module. So the user should
56access this module by calling:
57    bplist_read_file() or bplist_read_user_pref() or
58    bplist_read_system_pref()
59which returns a value. When you are done with the value, call
60    bplist_free_data()
61This will of course free the value_ptr returned by bplist_read_*()
62
63To deal with memory exhaustion (what happens when malloc returns
64NULL?), use setjmp/longjmp -- a single setjmp protects the whole
65parser, and allocate uses longjmp to abort. After abort, memory
66is freed and NULL is returned to caller. There is not much here
67in the way of error reporting.
68
69Memory is obtained by calling allocate which either returns the
70memory requested or calls longjmp, so callers don't have to check.
71
72*/
73
74#include <sys/types.h>
75#include <stdlib.h>
76#include <string.h>
77#include <assert.h>
78#include <stdio.h>
79#include <sys/stat.h>
80#include "readbinaryplist.h"
81#include "osxsupport.h"
82#include <Carbon/Carbon.h>
83
84#define NO 0
85#define YES 1
86#define BOOL int
87
88//#define MAXPATHLEN 256
89
90/* there are 2 levels of error logging/printing:
91 *   BPLIST_LOG and BPLIST_LOG_VERBOSE
92 * either or both can be set to non-zero to turn on
93 * If BPLIST_LOG_VERBOSE is true, then BPLIST_LOG
94 * is also true.
95 *
96 * In the code, logging is done by calling either
97 * bplist_log() or bplist_log_verbose(), which take
98 * parameters like printf but might be a no-op.
99 */
100 
101#define BPLIST_LOG_VERBOSE 0
102#define BPLIST_LOG 0
103
104#if BPLIST_LOG_VERBOSE
105    #ifndef BPLIST_LOG
106        #define BPLIST_LOG 1
107    #endif
108#endif
109
110#if BPLIST_LOG
111    #define bplist_log printf
112#else
113    #define bplist_log(...)
114#endif
115
116#if BPLIST_LOG_VERBOSE
117    #define bplist_log_verbose bplist_log
118#else
119    #define bplist_log_verbose(...)
120#endif
121
122
123/********* MEMORY MANAGEMENT ********/
124#define BLOCK_SIZE 1024
125// memory is aligned to multiples of this; assume malloc automatically
126// aligns to this number and assume this number is > sizeof(void *)
127#define ALIGNMENT 8
128static void *block_list = NULL;
129static char *free_ptr = NULL;
130static char *end_ptr = NULL;
131static jmp_buf abort_parsing;
132
133static void *allocate(size_t size)
134{
135    void *result;
136    if (free_ptr + size > end_ptr) {
137        size_t how_much = BLOCK_SIZE;
138        // align everything to 8 bytes
139        if (size > BLOCK_SIZE - ALIGNMENT) {
140            how_much = size + ALIGNMENT;
141        }
142        result = malloc(how_much);
143        if (result == NULL) {
144            /* serious problem */
145            longjmp(abort_parsing, 1);
146        }
147        *((void **)result) = block_list;
148        block_list = result;
149        free_ptr = ((char *) result) + ALIGNMENT;
150        end_ptr = ((char *) result) + how_much;
151    }
152    // now, there is enough rooom at free_ptr
153    result = free_ptr;
154    free_ptr += size;
155    return result;
156}
157
158void bplist_free_data()
159{
160    while (block_list) {
161        void *next = *(void **)block_list;
162        free(block_list);
163        block_list = next;
164    }
165    free_ptr = NULL;
166    end_ptr = NULL;
167}
168
169// layout of trailer -- last 32 bytes in plist data
170    uint8_t unused[6];
171    uint8_t offset_int_size;
172    uint8_t object_ref_size;
173    uint64_t object_count;
174    uint64_t top_level_object;
175    uint64_t offset_table_offset;
176
177
178enum
179{
180    kHEADER_SIZE = 8,
181    kTRAILER_SIZE = 32, //sizeof(bplist_trailer_node),
182    kMINIMUM_SANE_SIZE = kHEADER_SIZE + kTRAILER_SIZE
183};
184
185
186static const char kHEADER_BYTES[kHEADER_SIZE] = "bplist00";
187
188// map from UID key to previously parsed value
189typedef struct cache_struct {
190    uint64_t key;
191    value_ptr value;
192    struct cache_struct *next;
193} cache_node, *cache_ptr;
194
195
196typedef struct bplist_info
197{
198    uint64_t object_count;
199    const uint8_t *data_bytes;
200    uint64_t length;
201    uint64_t offset_table_offset;
202    uint8_t offset_int_size;
203    uint8_t object_ref_size;
204    cache_ptr cache;
205} bplist_info_node, *bplist_info_ptr;
206
207
208static value_ptr bplist_read_pldata(pldata_ptr data);
209static value_ptr bplist_read_pref(char *filename, OSType folder_type);
210static uint64_t read_sized_int(bplist_info_ptr bplist, uint64_t offset, uint8_t size);
211static uint64_t read_offset(bplist_info_ptr bplist, uint64_t index);
212static BOOL read_self_sized_int(bplist_info_ptr bplist, uint64_t offset, uint64_t *outValue, size_t *outSize);
213
214static value_ptr extract_object(bplist_info_ptr bplist, uint64_t objectRef);
215static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset);
216static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset);
217static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset);
218static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset);
219static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset);
220static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset);
221static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset);
222static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset);
223static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset);
224static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset);
225
226
227value_ptr value_create(void)
228{
229    value_ptr value = (value_ptr) allocate(sizeof(value_node));
230    return value;
231}
232
233
234void value_set_integer(value_ptr v, int64_t i) {
235    v->tag = kTAG_INT; v->integer = i;
236}
237
238void value_set_real(value_ptr v, double d) {
239    v->tag = kTAG_REAL; v->real = d;
240}
241
242// d is seconds since 1 January 2001
243void value_set_date(value_ptr v, double d) {
244    v->tag = kTAG_DATE; v->real = d;
245}
246
247void value_set_ascii_string(value_ptr v, const uint8_t *s, size_t len) {
248    v->tag = kTAG_ASCIISTRING;
249    v->string = (char *) allocate(len + 1);
250    memcpy(v->string, s, len);
251    v->string[len] = 0;
252}
253
254void value_set_unicode_string(value_ptr v, const uint8_t *s, size_t len) {
255    v->tag = kTAG_UNICODESTRING;
256    v->string = (char *) allocate(len + 1);
257    memcpy(v->string, s, len);
258    v->string[len] = 0;
259}
260
261void value_set_uid(value_ptr v, uint64_t uid)
262{
263    v->tag = kTAG_UID; v->uinteger = uid;
264}
265
266// v->data points to a pldata that points to the actual bytes
267// the bytes are copied, so caller must free byte source (*data)
268void value_set_data(value_ptr v, const uint8_t *data, size_t len) {
269    v->tag = kTAG_DATA;
270    pldata_ptr pldata = (pldata_ptr) allocate(sizeof(pldata_node));
271    pldata->data = (uint8_t *) allocate(len);
272    memcpy(pldata->data, data, len);
273    pldata->len = len;
274    v->data = pldata;
275    printf("value at %p gets data at %p\n", v, pldata);
276}
277
278// caller releases ownership of array to value_ptr v
279void value_set_array(value_ptr v, value_ptr *array, size_t length) {
280    array_ptr a = (array_ptr) allocate(sizeof(array_node));
281    a->array = array;
282    a->length = length;
283    v->tag = kTAG_ARRAY;
284    v->array = a;
285}
286
287// caller releases ownership of dict to value_ptr v
288void value_set_dict(value_ptr v, dict_ptr dict) {
289    v->tag = kTAG_DICTIONARY;
290    v->dict = dict;
291}
292
293
294// look up an objectref in the cache, a ref->value_ptr mapping
295value_ptr cache_lookup(cache_ptr cache, uint64_t ref)
296{
297    while (cache) {
298        if (cache->key == ref) {
299            return cache->value;
300        }
301        cache = cache->next;
302    }
303    return NULL;
304}
305
306
307// insert an objectref and value in the cache
308void cache_insert(cache_ptr *cache, uint64_t ref, value_ptr value)
309{
310    cache_ptr c = (cache_ptr) allocate(sizeof(cache_node));
311    c->key = ref;
312    c->value = value;
313    c->next = *cache;
314    *cache = c;
315}
316
317
318// insert an objectref and value in a dictionary
319void dict_insert(dict_ptr *dict, value_ptr key, value_ptr value)
320{
321    dict_ptr d = (dict_ptr) allocate(sizeof(dict_node));
322    d->key = key;
323    d->value = value;
324    d->next = *dict;
325    *dict = d;
326}
327
328
329BOOL is_binary_plist(pldata_ptr data)
330{
331    if (data->len < kMINIMUM_SANE_SIZE)  return NO;
332    return memcmp(data->data, kHEADER_BYTES, kHEADER_SIZE) == 0;
333}
334
335
336value_ptr bplist_read_file(char *filename)
337{
338    struct stat stbuf;
339    pldata_node pldata;
340    FILE *file;
341    size_t n;
342    value_ptr value;
343    int rslt = stat(filename, &stbuf);
344    if (rslt) {
345        #if BPLIST_LOG
346            perror("in stat");
347        #endif
348        bplist_log("Could not stat %s, error %d\n", filename, rslt);
349        return NULL;
350    }
351    // if file is >100MB, assume it is not a preferences file and give up
352    if (stbuf.st_size > 100000000) {
353        bplist_log("Large file %s encountered (%llu bytes) -- not read\n",
354                   filename, stbuf.st_size);
355        return NULL;
356    }
357    pldata.len = (size_t) stbuf.st_size;
358    // note: this is supposed to be malloc, not allocate. It is separate
359    // from the graph structure, large, and easy to free right after
360    // parsing.
361    pldata.data = (uint8_t *) malloc(pldata.len);
362    if (!pldata.data) {
363        bplist_log("Could not allocate %lu bytes for %s\n",
364                   (unsigned long) pldata.len, filename);
365        return NULL;
366    }
367    file = fopen(filename, "rb");
368    if (!file) {
369        bplist_log("Could not open %s\n", filename);
370        return NULL;
371    }
372    n = fread(pldata.data, 1, pldata.len, file);
373    if (n != pldata.len) {
374        bplist_log("Error reading from %s\n", filename);
375        return NULL;
376    }
377    value = bplist_read_pldata(&pldata);
378    free(pldata.data);
379    return value;
380}
381
382
383value_ptr bplist_read_pref(char *filename, OSType folder_type)
384{
385    char cstr[MAXPATHLEN];
386   char *foundstr;
387
388   memset(cstr, 0, MAXPATHLEN);
389
390   // for later OS X, the user preferences folder (~/Library/Preferences) is not available directly from Cocoa,
391   // Apple documentation suggests just using POSIX APIs like so.
392   if (folder_type == kPreferencesFolderType)
393   {
394      strlcpy(cstr, getenv("HOME"), MAXPATHLEN);
395      strlcat(cstr, "/Library/Preferences", MAXPATHLEN);
396   }
397   else // the system preferences folder (~/Library/PreferencePanes) is accessible from Cocoa however
398   {
399      foundstr = FindPrefsDir();
400
401      if (!foundstr) {
402         bplist_log("Error finding preferences folder\n");
403         return NULL;
404      }
405
406      strlcat(cstr, foundstr, MAXPATHLEN);
407      free(foundstr);
408      foundstr = NULL;
409   }
410
411    strlcat(cstr, "/", MAXPATHLEN);
412    strlcat(cstr, filename, MAXPATHLEN);
413
414   return bplist_read_file(cstr);
415}
416
417
418value_ptr bplist_read_system_pref(char *filename) {
419    return bplist_read_pref(filename, kSystemPreferencesFolderType);
420}
421
422
423value_ptr bplist_read_user_pref(char *filename) {
424    return bplist_read_pref(filename, kPreferencesFolderType);
425}
426
427
428// data is stored with high-order bytes first.
429// read from plist data in a machine-independent fashion
430//
431uint64_t convert_uint64(uint8_t *ptr)
432{
433    uint64_t rslt = 0;
434    int i;
435    // shift in bytes, high-order first
436    for (i = 0; i < sizeof(uint64_t); i++) {
437        rslt <<= 8;
438        rslt += ptr[i];
439    }
440    return rslt;
441}
442
443
444value_ptr bplist_read_pldata(pldata_ptr data)
445{
446    value_ptr result = NULL;
447    bplist_info_node bplist;
448    uint8_t *ptr;
449    uint64_t top_level_object;
450    int i;
451
452    if (data == NULL)  return NULL;
453    if (!is_binary_plist(data)) {
454        bplist_log("Bad binary plist: too short or invalid header.\n");
455        return NULL;
456    }
457       
458    // read trailer
459    ptr = (uint8_t *) (data->data + data->len - kTRAILER_SIZE);
460    bplist.offset_int_size = ptr[6];
461    bplist.object_ref_size = ptr[7];
462    bplist.object_count = convert_uint64(ptr + 8);
463    top_level_object = convert_uint64(ptr + 16);
464    bplist.offset_table_offset = convert_uint64(ptr + 24);
465       
466    // Basic sanity checks
467    if (bplist.offset_int_size < 1 || bplist.offset_int_size > 8 ||
468        bplist.object_ref_size < 1 || bplist.object_ref_size > 8 ||
469        bplist.offset_table_offset < kHEADER_SIZE) {
470        bplist_log("Bad binary plist: trailer declared insane.\n");
471        return NULL;               
472    }
473       
474    // Ensure offset table is inside file
475    uint64_t offsetTableSize = bplist.offset_int_size * bplist.object_count;
476    if (offsetTableSize + bplist.offset_table_offset + kTRAILER_SIZE >
477        data->len) {
478        bplist_log("Bad binary plist: offset table overlaps end of container.\n");
479        return NULL;
480    }
481       
482    bplist.data_bytes = data->data;
483    bplist.length = data->len;
484    bplist.cache = NULL; /* dictionary is empty */
485
486    bplist_log_verbose("Got a sane bplist with %llu items, offset_int_size: %u, object_ref_size: %u\n",
487                      bplist.object_count, bplist.offset_int_size,
488                      bplist.object_ref_size);
489    /* at this point, we are ready to do some parsing which allocates
490        memory for the result data structure. If memory allocation (using
491        allocate fails, a longjmp will return to here and we simply give up
492     */
493    i = setjmp(abort_parsing);
494    if (i == 0) {
495        result = extract_object(&bplist, top_level_object);
496    } else {
497        bplist_log("allocate() failed to allocate memory. Giving up.\n");
498        result = NULL;
499    }
500    if (!result) {
501        bplist_free_data();
502    }
503    return result;
504}
505
506
507static value_ptr extract_object(bplist_info_ptr bplist, uint64_t objectRef)
508{
509    uint64_t offset;
510    value_ptr result = NULL;
511    uint8_t objectTag;
512   
513    if (objectRef >= bplist->object_count) {
514        // Out-of-range object reference.
515        bplist_log("Bad binary plist: object index is out of range.\n");
516        return NULL;
517    }
518       
519    // Use cached object if it exists
520    result = cache_lookup(bplist->cache, objectRef);
521    if (result != NULL)  return result;
522       
523    // Otherwise, find object in file.
524    offset = read_offset(bplist, objectRef);
525    if (offset > bplist->length) {
526        // Out-of-range offset.
527        bplist_log("Bad binary plist: object outside container.\n");
528        return NULL;
529    }
530    objectTag = *(bplist->data_bytes + offset);
531    switch (objectTag & 0xF0) {
532    case kTAG_SIMPLE:
533        result = extract_simple(bplist, offset);
534        break;
535               
536    case kTAG_INT:
537        result = extract_int(bplist, offset);
538        break;
539                       
540    case kTAG_REAL:
541        result = extract_real(bplist, offset);
542        break;
543                       
544    case kTAG_DATE:
545        result = extract_date(bplist, offset);
546        break;
547                       
548    case kTAG_DATA:
549        result = extract_data(bplist, offset);
550        break;
551                       
552    case kTAG_ASCIISTRING:
553        result = extract_ascii_string(bplist, offset);
554        break;
555                       
556    case kTAG_UNICODESTRING:
557        result = extract_unicode_string(bplist, offset);
558        break;
559       
560    case kTAG_UID:
561        result = extract_uid(bplist, offset);
562        break;
563       
564    case kTAG_ARRAY:
565        result = extract_array(bplist, offset);
566        break;
567       
568    case kTAG_DICTIONARY:
569        result = extract_dictionary(bplist, offset);
570        break;
571       
572    default:
573        // Unknown tag.
574        bplist_log("Bad binary plist: unknown tag 0x%X.\n",
575                   (objectTag & 0x0F) >> 4);
576        result = NULL;
577    }
578   
579    // Cache and return result.
580    if (result != NULL) 
581        cache_insert(&bplist->cache, objectRef, result);
582    return result;
583}
584
585
586static uint64_t read_sized_int(bplist_info_ptr bplist, uint64_t offset,
587                               uint8_t size)
588{
589    assert(bplist->data_bytes != NULL && size >= 1 && size <= 8 &&
590           offset + size <= bplist->length);
591       
592    uint64_t result = 0;
593    const uint8_t *byte = bplist->data_bytes + offset;
594       
595    do {
596        // note that ints seem to be high-order first
597        result = (result << 8) | *byte++;
598    } while (--size);
599       
600    return result;
601}
602
603
604static uint64_t read_offset(bplist_info_ptr bplist, uint64_t index)
605{
606    assert(index < bplist->object_count);
607       
608    return read_sized_int(bplist,
609            bplist->offset_table_offset + bplist->offset_int_size * index,
610            bplist->offset_int_size);
611}
612
613
614static BOOL read_self_sized_int(bplist_info_ptr bplist, uint64_t offset,
615                             uint64_t *outValue, size_t *outSize)
616{
617    uint32_t size;
618    int64_t value;
619       
620    assert(bplist->data_bytes != NULL && offset < bplist->length);
621       
622    size = 1 << (bplist->data_bytes[offset] & 0x0F);
623    if (size > 8) {
624        // Maximum allowable size in this implementation is 1<<3 = 8 bytes.
625        // This also happens to be the biggest we can handle.
626        return NO;
627    }
628       
629    if (offset + 1 + size > bplist->length) {
630        // Out of range.
631        return NO;
632    }
633       
634    value = read_sized_int(bplist, offset + 1, size);
635   
636    if (outValue != NULL) *outValue = value;
637    if (outSize != NULL) *outSize = size + 1; // +1 for tag byte.
638    return YES;
639}
640
641
642static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset)
643{
644    assert(bplist->data_bytes != NULL && offset < bplist->length);
645    value_ptr value = value_create();
646       
647    switch (bplist->data_bytes[offset]) {
648    case kVALUE_NULL:
649        value->tag = kVALUE_NULL;
650        return value;
651       
652    case kVALUE_TRUE:
653        value->tag = kVALUE_TRUE;
654        return value;
655                       
656    case kVALUE_FALSE:
657        value->tag = kVALUE_FALSE;
658        return value;
659    }
660       
661    // Note: kVALUE_FILLER is treated as invalid, because it, er, is.
662    bplist_log("Bad binary plist: invalid atom.\n");
663    free(value);
664    return NULL;
665}
666
667
668static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset)
669{
670    value_ptr value = value_create();
671    value->tag = kTAG_INT;
672
673    if (!read_self_sized_int(bplist, offset, &value->uinteger, NULL)) {
674        bplist_log("Bad binary plist: invalid integer object.\n");
675    }
676       
677    /* NOTE: originally, I sign-extended here. This was the wrong thing; it
678       turns out that negative ints are always stored as 64-bit, and smaller
679       ints are unsigned.
680    */
681    return value;
682}
683
684
685static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset)
686{
687    value_ptr value = value_create();
688    uint32_t size;
689       
690    assert(bplist->data_bytes != NULL && offset < bplist->length);
691   
692    size = 1 << (bplist->data_bytes[offset] & 0x0F);
693       
694    // FIXME: what to do if faced with other sizes for float/double?
695    assert (sizeof (float) == sizeof (uint32_t) &&
696            sizeof (double) == sizeof (uint64_t));
697       
698    if (offset + 1 + size > bplist->length) {
699        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
700                  "floating-point number");
701        free(value);
702        return NULL;
703    }
704       
705    if (size == sizeof (float)) {
706        // cast is ok because we know size is 4 bytes
707        uint32_t i = (uint32_t) read_sized_int(bplist, offset + 1, size);
708        // Note that this handles byte swapping.
709        value_set_real(value, *(float *)&i);
710        return value;
711    } else if (size == sizeof (double)) {
712        uint64_t i = read_sized_int(bplist, offset + 1, size);
713        // Note that this handles byte swapping.
714        value_set_real(value, *(double *)&i);
715        return value;
716    } else {
717        // Can't handle floats of other sizes.
718        bplist_log("Bad binary plist: can't handle %u-byte float.\n", size);
719        free(value);
720        return NULL;
721    }
722}
723
724
725static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset)
726{
727    value_ptr value;
728    assert(bplist->data_bytes != NULL && offset < bplist->length);
729       
730    // Data has size code like int and real, but only 3 (meaning 8 bytes) is valid.
731    if (bplist->data_bytes[offset] != kVALUE_FULLDATETAG) {
732        bplist_log("Bad binary plist: invalid size for date object.\n");
733        return NULL;
734    }
735       
736    if (offset + 1 + sizeof (double) > bplist->length) {
737        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
738                  "date");
739        return NULL;
740    }
741       
742    // FIXME: what to do if faced with other sizes for double?
743    assert (sizeof (double) == sizeof (uint64_t));
744       
745    uint64_t date = read_sized_int(bplist, offset + 1, sizeof(double));
746    // Note that this handles byte swapping.
747    value = value_create();
748    value_set_date(value, *(double *)&date);
749    return value;
750}
751
752
753uint64_t bplist_get_a_size(bplist_info_ptr bplist,
754                           uint64_t *offset_ptr, char *msg)
755{
756    uint64_t size = bplist->data_bytes[*offset_ptr] & 0x0F;
757    (*offset_ptr)++;
758    if (size == 0x0F) {
759        // 0x0F means separate int size follows.
760        // Smaller values are used for short data.
761        size_t extra; // the length of the data size we are about to read
762        if ((bplist->data_bytes[*offset_ptr] & 0xF0) != kTAG_INT) {
763            // Bad data, mistagged size int
764            bplist_log("Bad binary plist: %s object size is not tagged as int.\n",
765                       msg);
766            return UINT64_MAX; // error
767        }
768               
769        // read integer data as size, extra tells how many bytes to skip
770        if (!read_self_sized_int(bplist, *offset_ptr, &size, &extra)) {
771            bplist_log("Bad binary plist: invalid %s object size tag.\n",
772                      "data");
773            return UINT64_MAX; // error
774        }
775        (*offset_ptr) += extra;
776    }
777
778    if (*offset_ptr + size > bplist->length) {
779        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
780                  "data");
781        return UINT64_MAX; // error
782    }
783    return size;
784}
785
786
787static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset)
788{
789    uint64_t size;
790    value_ptr value;
791       
792    assert(bplist->data_bytes != NULL && offset < bplist->length);
793       
794    if ((size = bplist_get_a_size(bplist, &offset, (char *)"data")) == UINT64_MAX)
795        return NULL;
796       
797    value = value_create();
798    // cast is ok because we only allow files up to 100MB:
799    value_set_data(value, bplist->data_bytes + (size_t) offset, (size_t) size);
800    return value;
801}
802
803
804static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset)
805{
806    uint64_t size;
807    value_ptr value; // return value
808       
809    assert(bplist->data_bytes != NULL && offset < bplist->length);
810       
811    if ((size = bplist_get_a_size(bplist, &offset, (char *)"ascii string")) ==
812        UINT64_MAX)
813        return NULL;
814
815    value = value_create();
816    // cast is ok because we only allow 100MB files
817    value_set_ascii_string(value, bplist->data_bytes + (size_t) offset,
818                           (size_t) size);
819    return value;
820}
821
822
823static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset)
824{
825    uint64_t size;
826    value_ptr value;
827       
828    assert(bplist->data_bytes != NULL && offset < bplist->length);
829       
830    if ((size = bplist_get_a_size(bplist, &offset, (char *)"unicode string")) ==
831        UINT64_MAX)
832        return NULL;
833       
834    value = value_create();
835    // cast is ok because we only allow 100MB files
836    value_set_unicode_string(value, bplist->data_bytes + (size_t) offset,
837                             (size_t) size);
838    return value;
839}
840
841
842static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset)
843{
844    /* UIDs are used by Cocoa's key-value coder.
845       When writing other plist formats, they are expanded to dictionaries of
846       the form <dict><key>CF$UID</key><integer>value</integer></dict>, so we
847       do the same here on reading. This results in plists identical to what
848       running plutil -convert xml1 gives us. However, this is not the same
849       result as [Core]Foundation's plist parser, which extracts them as un-
850       introspectable CF objects. In fact, it even seems to convert the CF$UID
851       dictionaries from XML plists on the fly.
852    */
853       
854    value_ptr value;
855    uint64_t uid;
856       
857    if (!read_self_sized_int(bplist, offset, &uid, NULL)) {
858        bplist_log("Bad binary plist: invalid UID object.\n");
859        return NULL;
860    }
861       
862    // assert(NO); // original code suggests using a string for a key
863    // but our dictionaries all use big ints for keys, so I don't know
864    // what to do here
865   
866    // In practice, I believe this code is never executed by PortMidi.
867    // I changed it to do something and not raise compiler warnings, but
868    // not sure what the code should do.
869
870    value = value_create();
871    value_set_uid(value, uid);
872    // return [NSDictionary dictionaryWithObject:
873    //         [NSNumber numberWithUnsignedLongLong:value]
874    //         forKey:"CF$UID"];
875    return value;
876}
877
878
879static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset)
880{
881    uint64_t i, count;
882    uint64_t size;
883    uint64_t elementID;
884    value_ptr element = NULL;
885    value_ptr *array = NULL;
886    value_ptr value = NULL;
887    BOOL ok = YES;
888       
889    assert(bplist->data_bytes != NULL && offset < bplist->length);
890       
891    if ((count = bplist_get_a_size(bplist, &offset, (char *)"array")) == UINT64_MAX)
892        return NULL;
893       
894    if (count > UINT64_MAX / bplist->object_ref_size - offset) {
895        // Offset overflow.
896        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
897                   "array");
898        return NULL;
899    }
900       
901    size = bplist->object_ref_size * count;
902    if (size + offset > bplist->length) {
903        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
904                   "array");
905        return NULL;
906    }
907       
908    // got count, the number of array elements
909
910    value = value_create();
911    assert(value);
912
913    if (count == 0) {
914        // count must be size_t or smaller because max file size is 100MB
915        value_set_array(value, array, (size_t) count);
916        return value;
917    }
918       
919    array = allocate(sizeof(value_ptr) * (size_t) count);
920       
921    for (i = 0; i != count; ++i) {
922        bplist_log_verbose("[%u]\n", i);
923        elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size,
924                                 bplist->object_ref_size);
925        element = extract_object(bplist, elementID);
926        if (element != NULL) {
927            array[i] = element;
928        } else {
929            ok = NO;
930            break;
931        }
932    }
933    if (ok) { // count is smaller than size_t max because of 100MB file limit
934        value_set_array(value, array, (size_t) count);
935    }
936
937    return value;
938}
939
940
941static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset)
942{
943    uint64_t i, count;
944    uint64_t size;
945    uint64_t elementID;
946    value_ptr value = NULL;
947    dict_ptr dict = NULL;
948    BOOL ok = YES;
949       
950    assert(bplist->data_bytes != NULL && offset < bplist->length);
951       
952       
953    if ((count = bplist_get_a_size(bplist, &offset, (char *)"array")) == UINT64_MAX)
954        return NULL;
955
956    if (count > UINT64_MAX / (bplist->object_ref_size * 2) - offset) {
957        // Offset overflow.
958        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
959                   "dictionary");
960        return NULL;
961    }
962   
963    size = bplist->object_ref_size * count * 2;
964    if (size + offset > bplist->length) {
965        bplist_log("Bad binary plist: %s object overlaps end of container.\n",
966                   "dictionary");
967        return NULL;
968    }
969   
970    value = value_create();
971    if (count == 0) {
972        value_set_dict(value, NULL);
973        return value;
974    }
975
976    for (i = 0; i != count; ++i) {
977        value_ptr key;
978        value_ptr val;
979        elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size,
980                                 bplist->object_ref_size);
981        key = extract_object(bplist, elementID);
982        if (key != NULL) {
983            bplist_log_verbose("key: %p\n", key);
984        } else {
985            ok = NO;
986            break;
987        }
988                   
989        elementID = read_sized_int(bplist,
990                            offset + (i + count) * bplist->object_ref_size,
991                            bplist->object_ref_size);
992        val = extract_object(bplist, elementID);
993        if (val != NULL) {
994            dict_insert(&dict, key, val);
995        } else {
996            ok = NO;
997            break;
998        }
999    }
1000    if (ok) {
1001        value_set_dict(value, dict);
1002    }
1003   
1004    return value;
1005}
1006
1007/*************** functions for accessing values ****************/
1008
1009
1010char *value_get_asciistring(value_ptr v)
1011{
1012    if (v->tag != kTAG_ASCIISTRING) return NULL;
1013    return v->string;
1014}
1015
1016
1017value_ptr value_dict_lookup_using_string(value_ptr v, char *key)
1018{
1019    dict_ptr dict;
1020    if (v->tag != kTAG_DICTIONARY) return NULL; // not a dictionary
1021    dict = v->dict;
1022    /* search for key */
1023    while (dict) {
1024        if (dict->key && dict->key->tag == kTAG_ASCIISTRING &&
1025            strcmp(key, dict->key->string) == 0) { // found it
1026            return dict->value;
1027        }
1028        dict = dict->next;
1029    }
1030    return NULL; /* not found */
1031}
1032
1033value_ptr value_dict_lookup_using_path(value_ptr v, char *path)
1034{
1035    char key[MAX_KEY_SIZE];
1036    while (*path) { /* more to the path */
1037        int i = 0;
1038        while (i < MAX_KEY_SIZE - 1) {
1039            key[i] = *path++;
1040            if (key[i] == '/') { /* end of entry in path */
1041                key[i + 1] = 0;
1042                break;
1043            }
1044            if (!key[i]) {
1045                path--; /* back up to end of string char */
1046                break;  /* this will cause outer loop to exit */
1047            }
1048            i++;
1049        }
1050        if (!v || v->tag != kTAG_DICTIONARY) return NULL;
1051        /* now, look up the key to get next value */
1052        v = value_dict_lookup_using_string(v, key);
1053        if (v == NULL) return NULL;
1054    }
1055    return v;
1056}
1057               
1058
1059/*************** functions for debugging ***************/
1060
1061void plist_print(value_ptr v)
1062{
1063    size_t i;
1064    int comma_needed;
1065    dict_ptr dict;
1066    if (!v) {
1067        printf("NULL");
1068        return;
1069    }
1070    switch (v->tag & 0xF0) {
1071    case kTAG_SIMPLE:
1072        switch (v->tag) {
1073        case kVALUE_NULL:
1074            printf("NULL@%p", v); break;
1075        case kVALUE_FALSE:
1076            printf("FALSE@%p", v); break;
1077        case kVALUE_TRUE:
1078            printf("TRUE@%p", v); break;
1079        default:
1080            printf("UNKNOWN tag=%x@%p", v->tag, v); break;
1081        }
1082        break;
1083    case kTAG_INT:
1084        printf("%lld@%p", v->integer, v); break;
1085    case kTAG_REAL:
1086        printf("%g@%p", v->real, v); break;
1087    case kTAG_DATE:
1088        printf("date:%g@%p", v->real, v); break;
1089    case kTAG_DATA:
1090        printf("data@%p->%p:[%p:", v, v->data, v->data->data);
1091        for (i = 0; i < v->data->len; i++) {
1092            printf(" %2x", v->data->data[i]);
1093        }
1094        printf("]"); break;
1095    case kTAG_ASCIISTRING:
1096        printf("%p:\"%s\"@%p", v->string, v->string, v); break;
1097    case kTAG_UNICODESTRING:
1098        printf("unicode:%p:\"%s\"@%p", v->string, v->string, v); break;
1099    case kTAG_UID:
1100        printf("UID:%llu@%p", v->uinteger, v); break;
1101    case kTAG_ARRAY:
1102        comma_needed = FALSE;
1103        printf("%p->%p:[%p:", v, v->array, v->array->array);
1104        for (i = 0; i < v->array->length; i++) {
1105            if (comma_needed) printf(", ");
1106            plist_print(v->array->array[i]);
1107            comma_needed = TRUE;
1108        }
1109        printf("]"); break;
1110    case kTAG_DICTIONARY:
1111        comma_needed = FALSE;
1112        printf("%p:[", v);
1113        dict = v->dict;
1114        while (dict) {
1115            if (comma_needed) printf(", ");
1116            printf("%p:", dict);
1117            plist_print(dict->key);
1118            printf("->");
1119            plist_print(dict->value);
1120            comma_needed = TRUE;
1121            dict = dict->next;
1122        }
1123        printf("]"); break;
1124    default:
1125        printf("UNKNOWN tag=%x", v->tag);
1126        break;
1127    }
1128}
1129
1130           
Property changes on: trunk/src/lib/portmidi/readbinaryplist.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/portmidi/portmidi.h
r0r19990
1#ifndef PORT_MIDI_H
2#define PORT_MIDI_H
3#ifdef __cplusplus
4extern "C" {
5#endif /* __cplusplus */
6
7/*
8 * PortMidi Portable Real-Time MIDI Library
9 * PortMidi API Header File
10 * Latest version available at: http://sourceforge.net/projects/portmedia
11 *
12 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
13 * Copyright (c) 2001-2006 Roger B. Dannenberg
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining
16 * a copy of this software and associated documentation files
17 * (the "Software"), to deal in the Software without restriction,
18 * including without limitation the rights to use, copy, modify, merge,
19 * publish, distribute, sublicense, and/or sell copies of the Software,
20 * and to permit persons to whom the Software is furnished to do so,
21 * subject to the following conditions:
22 *
23 * The above copyright notice and this permission notice shall be
24 * included in all copies or substantial portions of the Software.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
29 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
30 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
31 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 */
34
35/*
36 * The text above constitutes the entire PortMidi license; however,
37 * the PortMusic community also makes the following non-binding requests:
38 *
39 * Any person wishing to distribute modifications to the Software is
40 * requested to send the modifications to the original developer so that
41 * they can be incorporated into the canonical version. It is also
42 * requested that these non-binding requests be included along with the
43 * license above.
44 */
45
46/* CHANGELOG FOR PORTMIDI
47 *     (see ../CHANGELOG.txt)
48 *
49 * NOTES ON HOST ERROR REPORTING:
50 *
51 *    PortMidi errors (of type PmError) are generic, system-independent errors.
52 *    When an error does not map to one of the more specific PmErrors, the
53 *    catch-all code pmHostError is returned. This means that PortMidi has
54 *    retained a more specific system-dependent error code. The caller can
55 *    get more information by calling Pm_HasHostError() to test if there is
56 *    a pending host error, and Pm_GetHostErrorText() to get a text string
57 *    describing the error. Host errors are reported on a per-device basis
58 *    because only after you open a device does PortMidi have a place to
59 *    record the host error code. I.e. only
60 *    those routines that receive a (PortMidiStream *) argument check and
61 *    report errors. One exception to this is that Pm_OpenInput() and
62 *    Pm_OpenOutput() can report errors even though when an error occurs,
63 *    there is no PortMidiStream* to hold the error. Fortunately, both
64 *    of these functions return any error immediately, so we do not really
65 *    need per-device error memory. Instead, any host error code is stored
66 *    in a global, pmHostError is returned, and the user can call
67 *    Pm_GetHostErrorText() to get the error message (and the invalid stream
68 *    parameter will be ignored.) The functions
69 *    pm_init and pm_term do not fail or raise
70 *    errors. The job of pm_init is to locate all available devices so that
71 *    the caller can get information via PmDeviceInfo(). If an error occurs,
72 *    the device is simply not listed as available.
73 *
74 *    Host errors come in two flavors:
75 *      a) host error
76 *      b) host error during callback
77 *    These can occur w/midi input or output devices. (b) can only happen
78 *    asynchronously (during callback routines), whereas (a) only occurs while
79 *    synchronously running PortMidi and any resulting system dependent calls.
80 *    Both (a) and (b) are reported by the next read or write call. You can
81 *    also query for asynchronous errors (b) at any time by calling
82 *    Pm_HasHostError().
83 *
84 * NOTES ON COMPILE-TIME SWITCHES
85 *
86 *    DEBUG assumes stdio and a console. Use this if you want automatic, simple
87 *        error reporting, e.g. for prototyping. If you are using MFC or some
88 *        other graphical interface with no console, DEBUG probably should be
89 *        undefined.
90 *    PM_CHECK_ERRORS more-or-less takes over error checking for return values,
91 *        stopping your program and printing error messages when an error
92 *        occurs. This also uses stdio for console text I/O.
93 */
94
95#ifndef WIN32
96// Linux and OS X have stdint.h
97#include <stdint.h>
98#else
99#ifndef INT32_DEFINED
100// rather than having users install a special .h file for windows,
101// just put the required definitions inline here. porttime.h uses
102// these too, so the definitions are (unfortunately) duplicated there
103typedef int int32_t;
104typedef unsigned int uint32_t;
105#define INT32_DEFINED
106#endif
107#endif
108
109//#ifdef _WINDLL
110//#define PMEXPORT __declspec(dllexport)
111//#else
112#define PMEXPORT
113//#endif
114
115#ifndef FALSE
116    #define FALSE 0
117#endif
118#ifndef TRUE
119    #define TRUE 1
120#endif
121
122/* default size of buffers for sysex transmission: */
123#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024
124
125/** List of portmidi errors.*/
126typedef enum {
127    pmNoError = 0,
128    pmNoData = 0, /**< A "no error" return that also indicates no data avail. */
129    pmGotData = 1, /**< A "no error" return that also indicates data available */
130    pmHostError = -10000,
131    pmInvalidDeviceId, /** out of range or
132                        * output device when input is requested or
133                        * input device when output is requested or
134                        * device is already opened
135                        */
136    pmInsufficientMemory,
137    pmBufferTooSmall,
138    pmBufferOverflow,
139    pmBadPtr, /* PortMidiStream parameter is NULL or
140               * stream is not opened or
141               * stream is output when input is required or
142               * stream is input when output is required */
143    pmBadData, /** illegal midi data, e.g. missing EOX */
144    pmInternalError,
145    pmBufferMaxSize /** buffer is already as large as it can be */
146    /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */
147} PmError;
148
149/**
150    Pm_Initialize() is the library initialisation function - call this before
151    using the library.
152*/
153PMEXPORT PmError Pm_Initialize( void );
154
155/**
156    Pm_Terminate() is the library termination function - call this after
157    using the library.
158*/
159PMEXPORT PmError Pm_Terminate( void );
160
161/**  A single PortMidiStream is a descriptor for an open MIDI device.
162*/
163typedef void PortMidiStream;
164#define PmStream PortMidiStream
165
166/**
167    Test whether stream has a pending host error. Normally, the client finds
168    out about errors through returned error codes, but some errors can occur
169    asynchronously where the client does not
170    explicitly call a function, and therefore cannot receive an error code.
171    The client can test for a pending error using Pm_HasHostError(). If true,
172    the error can be accessed and cleared by calling Pm_GetErrorText().
173    Errors are also cleared by calling other functions that can return
174    errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The
175    client does not need to call Pm_HasHostError(). Any pending error will be
176    reported the next time the client performs an explicit function call on
177    the stream, e.g. an input or output operation. Until the error is cleared,
178    no new error codes will be obtained, even for a different stream.
179*/
180PMEXPORT int Pm_HasHostError( PortMidiStream * stream );
181
182
183/**  Translate portmidi error number into human readable message.
184    These strings are constants (set at compile time) so client has
185    no need to allocate storage
186*/
187PMEXPORT const char *Pm_GetErrorText( PmError errnum );
188
189/**  Translate portmidi host error into human readable message.
190    These strings are computed at run time, so client has to allocate storage.
191    After this routine executes, the host error is cleared.
192*/
193PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len);
194
195#define HDRLENGTH 50
196#define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less
197                                      than this number of characters */
198
199/**
200    Device enumeration mechanism.
201
202    Device ids range from 0 to Pm_CountDevices()-1.
203
204*/
205typedef int PmDeviceID;
206#define pmNoDevice -1
207typedef struct {
208    int structVersion; /**< this internal structure version */
209    const char *interf; /**< underlying MIDI API, e.g. MMSystem or DirectX */
210    const char *name;   /**< device name, e.g. USB MidiSport 1x1 */
211    int input; /**< true iff input is available */
212    int output; /**< true iff output is available */
213    int opened; /**< used by generic PortMidi code to do error checking on arguments */
214
215} PmDeviceInfo;
216
217/**  Get devices count, ids range from 0 to Pm_CountDevices()-1. */
218PMEXPORT int Pm_CountDevices( void );
219/**
220    Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID()
221
222    Return the default device ID or pmNoDevice if there are no devices.
223    The result (but not pmNoDevice) can be passed to Pm_OpenMidi().
224   
225    The default device can be specified using a small application
226    named pmdefaults that is part of the PortMidi distribution. This
227    program in turn uses the Java Preferences object created by
228    java.util.prefs.Preferences.userRoot().node("/PortMidi"); the
229    preference is set by calling
230        prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName);
231    or  prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName);
232   
233    In the statements above, prefName is a string describing the
234    MIDI device in the form "interf, name" where interf identifies
235    the underlying software system or API used by PortMdi to access
236    devices and name is the name of the device. These correspond to
237    the interf and name fields of a PmDeviceInfo. (Currently supported
238    interfaces are "MMSystem" for Win32, "ALSA" for Linux, and
239    "CoreMIDI" for OS X, so in fact, there is no choice of interface.)
240    In "interf, name", the strings are actually substrings of
241    the full interface and name strings. For example, the preference
242    "Core, Sport" will match a device with interface "CoreMIDI"
243    and name "In USB MidiSport 1x1". It will also match "CoreMIDI"
244    and "In USB MidiSport 2x2". The devices are enumerated in device
245    ID order, so the lowest device ID that matches the pattern becomes
246    the default device. Finally, if the comma-space (", ") separator
247    between interface and name parts of the preference is not found,
248    the entire preference string is interpreted as a name, and the
249    interface part is the empty string, which matches anything.
250
251    On the MAC, preferences are stored in
252      /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist
253    which is a binary file. In addition to the pmdefaults program,
254    there are utilities that can read and edit this preference file.
255
256    On the PC,
257
258    On Linux,
259
260*/
261PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID( void );
262/** see PmDeviceID Pm_GetDefaultInputDeviceID() */
263PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID( void );
264
265/**
266    PmTimestamp is used to represent a millisecond clock with arbitrary
267    start time. The type is used for all MIDI timestampes and clocks.
268*/
269typedef int32_t PmTimestamp;
270typedef PmTimestamp (*PmTimeProcPtr)(void *time_info);
271
272/** TRUE if t1 before t2 */
273#define PmBefore(t1,t2) ((t1-t2) < 0)
274/**
275    \defgroup grp_device Input/Output Devices Handling
276    @{
277*/
278/**
279    Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure
280    referring to the device specified by id.
281    If id is out of range the function returns NULL.
282
283    The returned structure is owned by the PortMidi implementation and must
284    not be manipulated or freed. The pointer is guaranteed to be valid
285    between calls to Pm_Initialize() and Pm_Terminate().
286*/
287PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id );
288
289/**
290    Pm_OpenInput() and Pm_OpenOutput() open devices.
291
292    stream is the address of a PortMidiStream pointer which will receive
293    a pointer to the newly opened stream.
294
295    inputDevice is the id of the device used for input (see PmDeviceID above).
296
297    inputDriverInfo is a pointer to an optional driver specific data structure
298    containing additional information for device setup or handle processing.
299    inputDriverInfo is never required for correct operation. If not used
300    inputDriverInfo should be NULL.
301
302    outputDevice is the id of the device used for output (see PmDeviceID above.)
303
304    outputDriverInfo is a pointer to an optional driver specific data structure
305    containing additional information for device setup or handle processing.
306    outputDriverInfo is never required for correct operation. If not used
307    outputDriverInfo should be NULL.
308
309    For input, the buffersize specifies the number of input events to be
310    buffered waiting to be read using Pm_Read(). For output, buffersize
311    specifies the number of output events to be buffered waiting for output.
312    (In some cases -- see below -- PortMidi does not buffer output at all
313    and merely passes data to a lower-level API, in which case buffersize
314    is ignored.)
315   
316    latency is the delay in milliseconds applied to timestamps to determine
317    when the output should actually occur. (If latency is < 0, 0 is assumed.)
318    If latency is zero, timestamps are ignored and all output is delivered
319    immediately. If latency is greater than zero, output is delayed until the
320    message timestamp plus the latency. (NOTE: the time is measured relative
321    to the time source indicated by time_proc. Timestamps are absolute,
322    not relative delays or offsets.) In some cases, PortMidi can obtain
323    better timing than your application by passing timestamps along to the
324    device driver or hardware. Latency may also help you to synchronize midi
325    data to audio data by matching midi latency to the audio buffer latency.
326
327    time_proc is a pointer to a procedure that returns time in milliseconds. It
328    may be NULL, in which case a default millisecond timebase (PortTime) is
329    used. If the application wants to use PortTime, it should start the timer
330    (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the
331    application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput,
332    it may get a ptAlreadyStarted error from Pt_Start, and the application's
333    preferred time resolution and callback function will be ignored.
334    time_proc result values are appended to incoming MIDI data, and time_proc
335    times are used to schedule outgoing MIDI data (when latency is non-zero).
336
337    time_info is a pointer passed to time_proc.
338
339    Example: If I provide a timestamp of 5000, latency is 1, and time_proc
340    returns 4990, then the desired output time will be when time_proc returns
341    timestamp+latency = 5001. This will be 5001-4990 = 11ms from now.
342
343    return value:
344    Upon success Pm_Open() returns PmNoError and places a pointer to a
345    valid PortMidiStream in the stream argument.
346    If a call to Pm_Open() fails a nonzero error code is returned (see
347    PMError above) and the value of port is invalid.
348
349    Any stream that is successfully opened should eventually be closed
350    by calling Pm_Close().
351
352*/
353PMEXPORT PmError Pm_OpenInput( PortMidiStream** stream,
354                PmDeviceID inputDevice,
355                void *inputDriverInfo,
356                int32_t bufferSize,
357                PmTimeProcPtr time_proc,
358                void *time_info );
359
360PMEXPORT PmError Pm_OpenOutput( PortMidiStream** stream,
361                PmDeviceID outputDevice,
362                void *outputDriverInfo,
363                int32_t bufferSize,
364                PmTimeProcPtr time_proc,
365                void *time_info,
366                int32_t latency );
367  /** @} */
368
369/**
370   \defgroup grp_events_filters Events and Filters Handling
371   @{
372*/
373
374/*  \function PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters )
375    Pm_SetFilter() sets filters on an open input stream to drop selected
376    input types. By default, only active sensing messages are filtered.
377    To prohibit, say, active sensing and sysex messages, call
378    Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX);
379
380    Filtering is useful when midi routing or midi thru functionality is being
381    provided by the user application.
382    For example, you may want to exclude timing messages (clock, MTC, start/stop/continue),
383    while allowing note-related messages to pass.
384    Or you may be using a sequencer or drum-machine for MIDI clock information but want to
385    exclude any notes it may play.
386 */
387   
388/* Filter bit-mask definitions */
389/** filter active sensing messages (0xFE): */
390#define PM_FILT_ACTIVE (1 << 0x0E)
391/** filter system exclusive messages (0xF0): */
392#define PM_FILT_SYSEX (1 << 0x00)
393/** filter MIDI clock message (0xF8) */
394#define PM_FILT_CLOCK (1 << 0x08)
395/** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */
396#define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B))
397/** filter tick messages (0xF9) */
398#define PM_FILT_TICK (1 << 0x09)
399/** filter undefined FD messages */
400#define PM_FILT_FD (1 << 0x0D)
401/** filter undefined real-time messages */
402#define PM_FILT_UNDEFINED PM_FILT_FD
403/** filter reset messages (0xFF) */
404#define PM_FILT_RESET (1 << 0x0F)
405/** filter all real-time messages */
406#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \
407    PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK)
408/** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */
409#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18))
410/** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/
411#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D)
412/** per-note aftertouch (0xA0-0xAF) */
413#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A)
414/** filter both channel and poly aftertouch */
415#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH)
416/** Program changes (0xC0-0xCF) */
417#define PM_FILT_PROGRAM (1 << 0x1C)
418/** Control Changes (CC's) (0xB0-0xBF)*/
419#define PM_FILT_CONTROL (1 << 0x1B)
420/** Pitch Bender (0xE0-0xEF*/
421#define PM_FILT_PITCHBEND (1 << 0x1E)
422/** MIDI Time Code (0xF1)*/
423#define PM_FILT_MTC (1 << 0x01)
424/** Song Position (0xF2) */
425#define PM_FILT_SONG_POSITION (1 << 0x02)
426/** Song Select (0xF3)*/
427#define PM_FILT_SONG_SELECT (1 << 0x03)
428/** Tuning request (0xF6)*/
429#define PM_FILT_TUNE (1 << 0x06)
430/** All System Common messages (mtc, song position, song select, tune request) */
431#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE)
432
433
434PMEXPORT PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters );
435
436#define Pm_Channel(channel) (1<<(channel))
437/**
438    Pm_SetChannelMask() filters incoming messages based on channel.
439    The mask is a 16-bit bitfield corresponding to appropriate channels.
440    The Pm_Channel macro can assist in calling this function.
441    i.e. to set receive only input on channel 1, call with
442    Pm_SetChannelMask(Pm_Channel(1));
443    Multiple channels should be OR'd together, like
444    Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11))
445
446    Note that channels are numbered 0 to 15 (not 1 to 16). Most
447    synthesizer and interfaces number channels starting at 1, but
448    PortMidi numbers channels starting at 0.
449
450    All channels are allowed by default
451*/
452PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask);
453
454/**
455    Pm_Abort() terminates outgoing messages immediately
456    The caller should immediately close the output port;
457    this call may result in transmission of a partial midi message.
458    There is no abort for Midi input because the user can simply
459    ignore messages in the buffer and close an input device at
460    any time.
461 */
462PMEXPORT PmError Pm_Abort( PortMidiStream* stream );
463     
464/**
465    Pm_Close() closes a midi stream, flushing any pending buffers.
466    (PortMidi attempts to close open streams when the application
467    exits -- this is particularly difficult under Windows.)
468*/
469PMEXPORT PmError Pm_Close( PortMidiStream* stream );
470
471/**
472    Pm_Synchronize() instructs PortMidi to (re)synchronize to the
473    time_proc passed when the stream was opened. Typically, this
474    is used when the stream must be opened before the time_proc
475    reference is actually advancing. In this case, message timing
476    may be erratic, but since timestamps of zero mean
477    "send immediately," initialization messages with zero timestamps
478    can be written without a functioning time reference and without
479    problems. Before the first MIDI message with a non-zero
480    timestamp is written to the stream, the time reference must
481    begin to advance (for example, if the time_proc computes time
482    based on audio samples, time might begin to advance when an
483    audio stream becomes active). After time_proc return values
484    become valid, and BEFORE writing the first non-zero timestamped
485    MIDI message, call Pm_Synchronize() so that PortMidi can observe
486    the difference between the current time_proc value and its
487    MIDI stream time.
488   
489    In the more normal case where time_proc
490    values advance continuously, there is no need to call
491    Pm_Synchronize. PortMidi will always synchronize at the
492    first output message and periodically thereafter.
493*/
494PmError Pm_Synchronize( PortMidiStream* stream );
495
496
497/**
498    Pm_Message() encodes a short Midi message into a 32-bit word. If data1
499    and/or data2 are not present, use zero.
500
501    Pm_MessageStatus(), Pm_MessageData1(), and
502    Pm_MessageData2() extract fields from a 32-bit midi message.
503*/
504#define Pm_Message(status, data1, data2) \
505         ((((data2) << 16) & 0xFF0000) | \
506          (((data1) << 8) & 0xFF00) | \
507          ((status) & 0xFF))
508#define Pm_MessageStatus(msg) ((msg) & 0xFF)
509#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF)
510#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF)
511
512typedef int32_t PmMessage; /**< see PmEvent */
513/**
514   All midi data comes in the form of PmEvent structures. A sysex
515   message is encoded as a sequence of PmEvent structures, with each
516   structure carrying 4 bytes of the message, i.e. only the first
517   PmEvent carries the status byte.
518
519   Note that MIDI allows nested messages: the so-called "real-time" MIDI
520   messages can be inserted into the MIDI byte stream at any location,
521   including within a sysex message. MIDI real-time messages are one-byte
522   messages used mainly for timing (see the MIDI spec). PortMidi retains
523   the order of non-real-time MIDI messages on both input and output, but
524   it does not specify exactly how real-time messages are processed. This
525   is particulary problematic for MIDI input, because the input parser
526   must either prepare to buffer an unlimited number of sysex message
527   bytes or to buffer an unlimited number of real-time messages that
528   arrive embedded in a long sysex message. To simplify things, the input
529   parser is allowed to pass real-time MIDI messages embedded within a
530   sysex message, and it is up to the client to detect, process, and
531   remove these messages as they arrive.
532
533   When receiving sysex messages, the sysex message is terminated
534   by either an EOX status byte (anywhere in the 4 byte messages) or
535   by a non-real-time status byte in the low order byte of the message.
536   If you get a non-real-time status byte but there was no EOX byte, it
537   means the sysex message was somehow truncated. This is not
538   considered an error; e.g., a missing EOX can result from the user
539   disconnecting a MIDI cable during sysex transmission.
540
541   A real-time message can occur within a sysex message. A real-time
542   message will always occupy a full PmEvent with the status byte in
543   the low-order byte of the PmEvent message field. (This implies that
544   the byte-order of sysex bytes and real-time message bytes may not
545   be preserved -- for example, if a real-time message arrives after
546   3 bytes of a sysex message, the real-time message will be delivered
547   first. The first word of the sysex message will be delivered only
548   after the 4th byte arrives, filling the 4-byte PmEvent message field.
549   
550   The timestamp field is observed when the output port is opened with
551   a non-zero latency. A timestamp of zero means "use the current time",
552   which in turn means to deliver the message with a delay of
553   latency (the latency parameter used when opening the output port.)
554   Do not expect PortMidi to sort data according to timestamps --
555   messages should be sent in the correct order, and timestamps MUST
556   be non-decreasing. See also "Example" for Pm_OpenOutput() above.
557
558   A sysex message will generally fill many PmEvent structures. On
559   output to a PortMidiStream with non-zero latency, the first timestamp
560   on sysex message data will determine the time to begin sending the
561   message. PortMidi implementations may ignore timestamps for the
562   remainder of the sysex message.
563   
564   On input, the timestamp ideally denotes the arrival time of the
565   status byte of the message. The first timestamp on sysex message
566   data will be valid. Subsequent timestamps may denote
567   when message bytes were actually received, or they may be simply
568   copies of the first timestamp.
569
570   Timestamps for nested messages: If a real-time message arrives in
571   the middle of some other message, it is enqueued immediately with
572   the timestamp corresponding to its arrival time. The interrupted
573   non-real-time message or 4-byte packet of sysex data will be enqueued
574   later. The timestamp of interrupted data will be equal to that of
575   the interrupting real-time message to insure that timestamps are
576   non-decreasing.
577 */
578typedef struct {
579    PmMessage      message;
580    PmTimestamp    timestamp;
581} PmEvent;
582
583/**
584    @}
585*/
586/** \defgroup grp_io Reading and Writing Midi Messages
587    @{
588*/
589/**
590    Pm_Read() retrieves midi data into a buffer, and returns the number
591    of events read. Result is a non-negative number unless an error occurs,
592    in which case a PmError value will be returned.
593
594    Buffer Overflow
595
596    The problem: if an input overflow occurs, data will be lost, ultimately
597    because there is no flow control all the way back to the data source.
598    When data is lost, the receiver should be notified and some sort of
599    graceful recovery should take place, e.g. you shouldn't resume receiving
600    in the middle of a long sysex message.
601
602    With a lock-free fifo, which is pretty much what we're stuck with to
603    enable portability to the Mac, it's tricky for the producer and consumer
604    to synchronously reset the buffer and resume normal operation.
605
606    Solution: the buffer managed by PortMidi will be flushed when an overflow
607    occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow)
608    and ordinary processing resumes as soon as a new message arrives. The
609    remainder of a partial sysex message is not considered to be a "new
610    message" and will be flushed as well.
611
612*/
613PMEXPORT int Pm_Read( PortMidiStream *stream, PmEvent *buffer, int32_t length );
614
615/**
616    Pm_Poll() tests whether input is available,
617    returning TRUE, FALSE, or an error value.
618*/
619PMEXPORT PmError Pm_Poll( PortMidiStream *stream);
620
621/**
622    Pm_Write() writes midi data from a buffer. This may contain:
623        - short messages
624    or
625        - sysex messages that are converted into a sequence of PmEvent
626          structures, e.g. sending data from a file or forwarding them
627          from midi input.
628
629    Use Pm_WriteSysEx() to write a sysex message stored as a contiguous
630    array of bytes.
631
632    Sysex data may contain embedded real-time messages.
633*/
634PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length );
635
636/**
637    Pm_WriteShort() writes a timestamped non-system-exclusive midi message.
638    Messages are delivered in order as received, and timestamps must be
639    non-decreasing. (But timestamps are ignored if the stream was opened
640    with latency = 0.)
641*/
642PMEXPORT PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, int32_t msg);
643
644/**
645    Pm_WriteSysEx() writes a timestamped system-exclusive midi message.
646*/
647PMEXPORT PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg);
648
649/** @} */
650
651#ifdef __cplusplus
652}
653#endif /* __cplusplus */
654#endif /* PORT_MIDI_H */
Property changes on: trunk/src/lib/portmidi/portmidi.h
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/pmlinux.c
r0r19990
1/* pmlinux.c -- PortMidi os-dependent code */
2
3/* This file only needs to implement pm_init(), which calls various
4   routines to register the available midi devices. This file must
5   be separate from the main portmidi.c file because it is system
6   dependent, and it is separate from, pmlinuxalsa.c, because it
7   might need to register non-alsa devices as well.
8
9   NOTE: if you add non-ALSA support, you need to fix :alsa_poll()
10   in pmlinuxalsa.c, which assumes all input devices are ALSA.
11 */
12
13#include "stdlib.h"
14#include "portmidi.h"
15#include "pmutil.h"
16#include "pminternal.h"
17
18#ifdef PMALSA
19  #include "pmlinuxalsa.h"
20#endif
21
22#ifdef PMNULL
23  #include "pmlinuxnull.h"
24#endif
25
26PmDeviceID pm_default_input_device_id = -1;
27PmDeviceID pm_default_output_device_id = -1;
28
29extern PmDeviceID find_default_device(char *path, int input, PmDeviceID id);
30
31void pm_init()
32{
33    /* Note: it is not an error for PMALSA to fail to initialize.
34     * It may be a design error that the client cannot query what subsystems
35     * are working properly other than by looking at the list of available
36     * devices.
37     */
38    #ifdef PMALSA
39   pm_linuxalsa_init();
40    #endif
41    #ifdef PMNULL
42        pm_linuxnull_init();
43    #endif
44    // this is set when we return to Pm_Initialize, but we need it
45    // now in order to (successfully) call Pm_CountDevices()
46    pm_initialized = TRUE;     
47    pm_default_input_device_id = find_default_device(
48        (char *)"/PortMidi/PM_RECOMMENDED_INPUT_DEVICE", TRUE,
49        pm_default_input_device_id);
50    pm_default_output_device_id = find_default_device(
51      (char *)"/PortMidi/PM_RECOMMENDED_OUTPUT_DEVICE", FALSE,
52        pm_default_output_device_id);
53}
54
55void pm_term(void)
56{
57    #ifdef PMALSA
58        pm_linuxalsa_term();
59    #endif
60}
61
62PmDeviceID Pm_GetDefaultInputDeviceID() {
63    Pm_Initialize();
64    return pm_default_input_device_id;
65}
66
67PmDeviceID Pm_GetDefaultOutputDeviceID() {
68    Pm_Initialize();
69    return pm_default_output_device_id;
70}
71
72void *pm_alloc(size_t s) { return malloc(s); }
73
74void pm_free(void *ptr) { free(ptr); }
75
Property changes on: trunk/src/lib/portmidi/pmlinux.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/pmlinuxalsa.c
r0r19990
1/*
2 * pmlinuxalsa.c -- system specific definitions
3 *
4 * written by:
5 *  Roger Dannenberg (port to Alsa 0.9.x)
6 *  Clemens Ladisch (provided code examples and invaluable consulting)
7 *  Jason Cohen, Rico Colon, Matt Filippone (Alsa 0.5.x implementation)
8 */
9
10#include "stdlib.h"
11#include "portmidi.h"
12#include "pmutil.h"
13#include "pminternal.h"
14#include "pmlinuxalsa.h"
15#include "string.h"
16#include "porttime.h"
17#include "pmlinux.h"
18#include "osdcomm.h"
19
20#ifdef PTR64
21typedef UINT64 FPTR;
22#else
23typedef UINT32 FPTR;
24#endif
25
26#include <alsa/asoundlib.h>
27
28/* I used many print statements to debug this code. I left them in the
29 * source, and you can turn them on by changing false to true below:
30 */
31#define VERBOSE_ON 0
32#define VERBOSE if (VERBOSE_ON)
33
34#define MIDI_SYSEX      0xf0
35#define MIDI_EOX        0xf7
36
37#if SND_LIB_MAJOR == 0 && SND_LIB_MINOR < 9
38#error needs ALSA 0.9.0 or later
39#endif
40
41/* to store client/port in the device descriptor */
42
43#define MAKE_DESCRIPTOR(client, port) ((void*)(FPTR)(((client) << 8) | (port)))
44#define GET_DESCRIPTOR_CLIENT(info) ((((int)(FPTR)(info)) >> 8) & 0xff)
45#define GET_DESCRIPTOR_PORT(info) (((int)(FPTR)(info)) & 0xff)
46
47#define BYTE unsigned char
48
49extern pm_fns_node pm_linuxalsa_in_dictionary;
50extern pm_fns_node pm_linuxalsa_out_dictionary;
51
52static snd_seq_t *seq = NULL; // all input comes here,
53                              // output queue allocated on seq
54static int queue, queue_used; /* one for all ports, reference counted */
55
56typedef struct alsa_descriptor_struct {
57    int client;
58    int port;
59    int this_port;
60    int in_sysex;
61    snd_midi_event_t *parser;
62    int error; /* host error code */
63} alsa_descriptor_node, *alsa_descriptor_type;
64
65
66/* get_alsa_error_text -- copy error text to potentially short string */
67/**/
68static void get_alsa_error_text(char *msg, int len, int err)
69{
70    int errlen = strlen(snd_strerror(err));
71    if (errlen < len) {
72        strcpy(msg, snd_strerror(err));
73    } else if (len > 20) {
74        sprintf(msg, "Alsa error %d", err);
75    } else if (len > 4) {
76        strcpy(msg, "Alsa");
77    } else {
78        msg[0] = 0;
79    }
80}
81
82
83/* queue is shared by both input and output, reference counted */
84static PmError alsa_use_queue(void)
85{
86    if (queue_used == 0) {
87        snd_seq_queue_tempo_t *tempo;
88
89        queue = snd_seq_alloc_queue(seq);
90        if (queue < 0) {
91            pm_hosterror = queue;
92            return pmHostError;
93        }
94        snd_seq_queue_tempo_alloca(&tempo);
95        snd_seq_queue_tempo_set_tempo(tempo, 480000);
96        snd_seq_queue_tempo_set_ppq(tempo, 480);
97        pm_hosterror = snd_seq_set_queue_tempo(seq, queue, tempo);
98        if (pm_hosterror < 0)
99            return pmHostError;
100
101        snd_seq_start_queue(seq, queue, NULL);
102        snd_seq_drain_output(seq);
103    }
104    ++queue_used;
105    return pmNoError;
106}
107
108
109static void alsa_unuse_queue(void)
110{
111    if (--queue_used == 0) {
112        snd_seq_stop_queue(seq, queue, NULL);
113        snd_seq_drain_output(seq);
114        snd_seq_free_queue(seq, queue);
115        VERBOSE printf("queue freed\n");
116    }
117}
118
119
120/* midi_message_length -- how many bytes in a message? */
121static int midi_message_length(PmMessage message)
122{
123    message &= 0xff;
124    if (message < 0x80) {
125        return 0;
126    } else if (message < 0xf0) {
127        static const int length[] = {3, 3, 3, 3, 2, 2, 3};
128        return length[(message - 0x80) >> 4];
129    } else {
130        static const int length[] = {
131            -1, 2, 3, 2, 0, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1, 1};
132        return length[message - 0xf0];
133    }
134}
135
136
137static PmError alsa_out_open(PmInternal *midi, void *driverInfo)
138{
139    void *client_port = descriptors[midi->device_id].descriptor;
140    alsa_descriptor_type desc = (alsa_descriptor_type)
141        pm_alloc(sizeof(alsa_descriptor_node));
142    snd_seq_port_info_t *info;
143    int err;
144
145    if (!desc) return pmInsufficientMemory;
146   
147    snd_seq_port_info_alloca(&info);
148    snd_seq_port_info_set_port(info, midi->device_id);
149    snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE |
150                                     SND_SEQ_PORT_CAP_READ);
151    snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
152                                     SND_SEQ_PORT_TYPE_APPLICATION);
153    snd_seq_port_info_set_port_specified(info, 1);
154    err = snd_seq_create_port(seq, info);
155    if (err < 0) goto free_desc;
156
157    /* fill in fields of desc, which is passed to pm_write routines */
158    midi->descriptor = desc;
159    desc->client = GET_DESCRIPTOR_CLIENT(client_port);
160    desc->port = GET_DESCRIPTOR_PORT(client_port);
161    desc->this_port = midi->device_id;
162    desc->in_sysex = 0;
163
164    desc->error = 0;
165
166    err = snd_midi_event_new(PM_DEFAULT_SYSEX_BUFFER_SIZE, &desc->parser);
167    if (err < 0) goto free_this_port;
168
169    if (midi->latency > 0) { /* must delay output using a queue */
170        err = alsa_use_queue();
171        if (err < 0) goto free_parser;
172
173        err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port);
174        if (err < 0) goto unuse_queue;  /* clean up and return on error */
175    } else {
176        err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port);
177        if (err < 0) goto free_parser;  /* clean up and return on error */
178    }       
179    return pmNoError;
180
181 unuse_queue:
182    alsa_unuse_queue();
183 free_parser:
184    snd_midi_event_free(desc->parser);
185 free_this_port:
186    snd_seq_delete_port(seq, desc->this_port);
187 free_desc:
188    pm_free(desc);
189    pm_hosterror = err;
190    if (err < 0) {
191        get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err);
192    }
193    return pmHostError;
194}
195   
196
197static PmError alsa_write_byte(PmInternal *midi, unsigned char byte,
198                        PmTimestamp timestamp)
199{
200    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
201    snd_seq_event_t ev;
202    int err;
203
204    snd_seq_ev_clear(&ev);
205    if (snd_midi_event_encode_byte(desc->parser, byte, &ev) == 1) {
206        snd_seq_ev_set_dest(&ev, desc->client, desc->port);
207        snd_seq_ev_set_source(&ev, desc->this_port);
208        if (midi->latency > 0) {
209            /* compute relative time of event = timestamp - now + latency */
210            PmTimestamp now = (midi->time_proc ?
211                               midi->time_proc(midi->time_info) :
212                               Pt_Time());
213            int when = timestamp;
214            /* if timestamp is zero, send immediately */
215            /* otherwise compute time delay and use delay if positive */
216            if (when == 0) when = now;
217            when = (when - now) + midi->latency;
218            if (when < 0) when = 0;
219            VERBOSE printf("timestamp %d now %d latency %d, ",
220                           (int) timestamp, (int) now, midi->latency);
221            VERBOSE printf("scheduling event after %d\n", when);
222            /* message is sent in relative ticks, where 1 tick = 1 ms */
223            snd_seq_ev_schedule_tick(&ev, queue, 1, when);
224            /* NOTE: for cases where the user does not supply a time function,
225               we could optimize the code by not starting Pt_Time and using
226               the alsa tick time instead. I didn't do this because it would
227               entail changing the queue management to start the queue tick
228               count when PortMidi is initialized and keep it running until
229               PortMidi is terminated. (This should be simple, but it's not
230               how the code works now.) -RBD */
231        } else { /* send event out without queueing */
232            VERBOSE printf("direct\n");
233            /* ev.queue = SND_SEQ_QUEUE_DIRECT;
234               ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS; */
235            snd_seq_ev_set_direct(&ev);
236        }
237        VERBOSE printf("sending event\n");
238        err = snd_seq_event_output(seq, &ev);
239        if (err < 0) {
240            desc->error = err;
241            return pmHostError;
242        }
243    }
244    return pmNoError;
245}
246
247
248static PmError alsa_out_close(PmInternal *midi)
249{
250    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
251    if (!desc) return pmBadPtr;
252
253    if ((pm_hosterror = snd_seq_disconnect_to(seq, desc->this_port,
254                                             desc->client, desc->port))) {
255        // if there's an error, try to delete the port anyway, but don't
256        // change the pm_hosterror value so we retain the first error
257        snd_seq_delete_port(seq, desc->this_port);
258    } else { // if there's no error, delete the port and retain any error
259        pm_hosterror = snd_seq_delete_port(seq, desc->this_port);
260    }
261    if (midi->latency > 0) alsa_unuse_queue();
262    snd_midi_event_free(desc->parser);
263    midi->descriptor = NULL; /* destroy the pointer to signify "closed" */
264    pm_free(desc);
265    if (pm_hosterror) {
266        get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
267                            pm_hosterror);
268        return pmHostError;
269    }
270    return pmNoError;
271}
272
273
274static PmError alsa_in_open(PmInternal *midi, void *driverInfo)
275{
276    void *client_port = descriptors[midi->device_id].descriptor;
277    alsa_descriptor_type desc = (alsa_descriptor_type)
278        pm_alloc(sizeof(alsa_descriptor_node));
279    snd_seq_port_info_t *info;
280    snd_seq_port_subscribe_t *sub;
281    snd_seq_addr_t addr;
282    int err;
283
284    if (!desc) return pmInsufficientMemory;
285   
286    err = alsa_use_queue();
287    if (err < 0) goto free_desc;
288
289    snd_seq_port_info_alloca(&info);
290    snd_seq_port_info_set_port(info, midi->device_id);
291    snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE |
292                                     SND_SEQ_PORT_CAP_READ);
293    snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC |
294                                     SND_SEQ_PORT_TYPE_APPLICATION);
295    snd_seq_port_info_set_port_specified(info, 1);
296    err = snd_seq_create_port(seq, info);
297    if (err < 0) goto free_queue;
298
299    /* fill in fields of desc, which is passed to pm_write routines */
300    midi->descriptor = desc;
301    desc->client = GET_DESCRIPTOR_CLIENT(client_port);
302    desc->port = GET_DESCRIPTOR_PORT(client_port);
303    desc->this_port = midi->device_id;
304    desc->in_sysex = 0;
305
306    desc->error = 0;
307
308    VERBOSE printf("snd_seq_connect_from: %d %d %d\n",
309                   desc->this_port, desc->client, desc->port);
310    snd_seq_port_subscribe_alloca(&sub);
311    addr.client = snd_seq_client_id(seq);
312    addr.port = desc->this_port;
313    snd_seq_port_subscribe_set_dest(sub, &addr);
314    addr.client = desc->client;
315    addr.port = desc->port;
316    snd_seq_port_subscribe_set_sender(sub, &addr);
317    snd_seq_port_subscribe_set_time_update(sub, 1);
318    /* this doesn't seem to work: messages come in with real timestamps */
319    snd_seq_port_subscribe_set_time_real(sub, 0);
320    err = snd_seq_subscribe_port(seq, sub);
321    /* err =
322       snd_seq_connect_from(seq, desc->this_port, desc->client, desc->port); */
323    if (err < 0) goto free_this_port;  /* clean up and return on error */
324    return pmNoError;
325
326 free_this_port:
327    snd_seq_delete_port(seq, desc->this_port);
328 free_queue:
329    alsa_unuse_queue();
330 free_desc:
331    pm_free(desc);
332    pm_hosterror = err;
333    if (err < 0) {
334        get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err);
335    }
336    return pmHostError;
337}
338
339static PmError alsa_in_close(PmInternal *midi)
340{
341    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
342    if (!desc) return pmBadPtr;
343    if ((pm_hosterror = snd_seq_disconnect_from(seq, desc->this_port,
344                                               desc->client, desc->port))) {
345        snd_seq_delete_port(seq, desc->this_port); /* try to close port */
346    } else {
347        pm_hosterror = snd_seq_delete_port(seq, desc->this_port);
348    }
349    alsa_unuse_queue();
350    pm_free(desc);
351    if (pm_hosterror) {
352        get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
353                            pm_hosterror);
354        return pmHostError;
355    }
356    return pmNoError;
357}
358       
359
360static PmError alsa_abort(PmInternal *midi)
361{
362    /* NOTE: ALSA documentation is vague. This is supposed to
363     * remove any pending output messages. If you can test and
364     * confirm this code is correct, please update this comment. -RBD
365     */
366    /* Unfortunately, I can't even compile it -- my ALSA version
367     * does not implement snd_seq_remove_events_t, so this does
368     * not compile. I'll try again, but it looks like I'll need to
369     * upgrade my entire Linux OS -RBD
370     */
371    /*
372    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
373    snd_seq_remove_events_t info;
374    snd_seq_addr_t addr;
375    addr.client = desc->client;
376    addr.port = desc->port;
377    snd_seq_remove_events_set_dest(&info, &addr);
378    snd_seq_remove_events_set_condition(&info, SND_SEQ_REMOVE_DEST);
379    pm_hosterror = snd_seq_remove_events(seq, &info);
380    if (pm_hosterror) {
381        get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN,
382                            pm_hosterror);
383        return pmHostError;
384    }
385    */
386    printf("WARNING: alsa_abort not implemented\n");
387    return pmNoError;
388}
389
390
391#ifdef GARBAGE
392This is old code here temporarily for reference
393static PmError alsa_write(PmInternal *midi, PmEvent *buffer, int32_t length)
394{
395    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
396    int i, bytes;
397    unsigned char byte;
398    PmMessage msg;
399
400    desc->error = 0;
401    for (; length > 0; length--, buffer++) {
402        VERBOSE printf("message 0x%x\n", buffer->message);
403        if (Pm_MessageStatus(buffer->message) == MIDI_SYSEX)
404            desc->in_sysex = TRUE;
405        if (desc->in_sysex) {
406            msg = buffer->message;
407            for (i = 0; i < 4; i++) {
408                byte = msg;  /* extract next byte to send */
409                alsa_write_byte(midi, byte, buffer->timestamp);
410                if (byte == MIDI_EOX) {
411                    desc->in_sysex = FALSE;
412                    break;
413                }
414                if (desc->error < 0) break;
415                msg >>= 8; /* shift next byte into position */
416            }
417        } else {
418            bytes = midi_message_length(buffer->message);
419            msg = buffer->message;
420            for (i = 0; i < bytes; i++) {
421                byte = msg; /* extract next byte to send */
422                VERBOSE printf("sending 0x%x\n", byte);
423                alsa_write_byte(midi, byte, buffer->timestamp);
424                if (desc->error < 0) break;
425                msg >>= 8; /* shift next byte into position */
426            }
427        }
428    }
429    if (desc->error < 0) return pmHostError;
430
431    VERBOSE printf("snd_seq_drain_output: 0x%x\n", (unsigned int) seq);
432    desc->error = snd_seq_drain_output(seq);
433    if (desc->error < 0) return pmHostError;
434
435    desc->error = pmNoError;
436    return pmNoError;
437}
438#endif
439
440
441static PmError alsa_write_flush(PmInternal *midi, PmTimestamp timestamp)
442{
443    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
444    VERBOSE printf("snd_seq_drain_output: 0x%x\n", (unsigned int)(FPTR) seq);
445    desc->error = snd_seq_drain_output(seq);
446    if (desc->error < 0) return pmHostError;
447
448    desc->error = pmNoError;
449    return pmNoError;
450}
451
452
453static PmError alsa_write_short(PmInternal *midi, PmEvent *event)
454{
455    int bytes = midi_message_length(event->message);
456    PmMessage msg = event->message;
457    int i;
458    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
459    for (i = 0; i < bytes; i++) {
460        unsigned char byte = msg;
461        VERBOSE printf("sending 0x%x\n", byte);
462        alsa_write_byte(midi, byte, event->timestamp);
463        if (desc->error < 0) break;
464        msg >>= 8; /* shift next byte into position */
465    }
466    if (desc->error < 0) return pmHostError;
467    desc->error = pmNoError;
468    return pmNoError;
469}
470
471
472/* alsa_sysex -- implements begin_sysex and end_sysex */
473PmError alsa_sysex(PmInternal *midi, PmTimestamp timestamp) {
474    return pmNoError;
475}
476
477
478static PmTimestamp alsa_synchronize(PmInternal *midi)
479{
480    return 0; /* linux implementation does not use this synchronize function */
481    /* Apparently, Alsa data is relative to the time you send it, and there
482       is no reference. If this is true, this is a serious shortcoming of
483       Alsa. If not true, then PortMidi has a serious shortcoming -- it
484       should be scheduling relative to Alsa's time reference. */
485}
486
487
488static void handle_event(snd_seq_event_t *ev)
489{
490    int device_id = ev->dest.port;
491    PmInternal *midi = descriptors[device_id].internalDescriptor;
492    PmEvent pm_ev;
493    PmTimeProcPtr time_proc = midi->time_proc;
494    PmTimestamp timestamp;
495
496    /* time stamp should be in ticks, using our queue where 1 tick = 1ms */
497    assert((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_TICK);
498
499    /* if no time_proc, just return "native" ticks (ms) */
500    if (time_proc == NULL) {
501        timestamp = ev->time.tick;
502    } else { /* translate time to time_proc basis */
503        snd_seq_queue_status_t *queue_status;
504        snd_seq_queue_status_alloca(&queue_status);
505        snd_seq_get_queue_status(seq, queue, queue_status);
506        /* return (now - alsa_now) + alsa_timestamp */
507        timestamp = (*time_proc)(midi->time_info) + ev->time.tick -
508                    snd_seq_queue_status_get_tick_time(queue_status);
509    }
510    pm_ev.timestamp = timestamp;
511    switch (ev->type) {
512    case SND_SEQ_EVENT_NOTEON:
513        pm_ev.message = Pm_Message(0x90 | ev->data.note.channel,
514                                   ev->data.note.note & 0x7f,
515                                   ev->data.note.velocity & 0x7f);
516        pm_read_short(midi, &pm_ev);
517        break;
518    case SND_SEQ_EVENT_NOTEOFF:
519        pm_ev.message = Pm_Message(0x80 | ev->data.note.channel,
520                                   ev->data.note.note & 0x7f,
521                                   ev->data.note.velocity & 0x7f);
522        pm_read_short(midi, &pm_ev);
523        break;
524    case SND_SEQ_EVENT_KEYPRESS:
525        pm_ev.message = Pm_Message(0xa0 | ev->data.note.channel,
526                                   ev->data.note.note & 0x7f,
527                                   ev->data.note.velocity & 0x7f);
528        pm_read_short(midi, &pm_ev);
529        break;
530    case SND_SEQ_EVENT_CONTROLLER:
531        pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
532                                   ev->data.control.param & 0x7f,
533                                   ev->data.control.value & 0x7f);
534        pm_read_short(midi, &pm_ev);
535        break;
536    case SND_SEQ_EVENT_PGMCHANGE:
537        pm_ev.message = Pm_Message(0xc0 | ev->data.note.channel,
538                                   ev->data.control.value & 0x7f, 0);
539        pm_read_short(midi, &pm_ev);
540        break;
541    case SND_SEQ_EVENT_CHANPRESS:
542        pm_ev.message = Pm_Message(0xd0 | ev->data.note.channel,
543                                   ev->data.control.value & 0x7f, 0);
544        pm_read_short(midi, &pm_ev);
545        break;
546    case SND_SEQ_EVENT_PITCHBEND:
547        pm_ev.message = Pm_Message(0xe0 | ev->data.note.channel,
548                            (ev->data.control.value + 0x2000) & 0x7f,
549                            ((ev->data.control.value + 0x2000) >> 7) & 0x7f);
550        pm_read_short(midi, &pm_ev);
551        break;
552    case SND_SEQ_EVENT_CONTROL14:
553        if (ev->data.control.param < 0x20) {
554            pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
555                                       ev->data.control.param,
556                                       (ev->data.control.value >> 7) & 0x7f);
557            pm_read_short(midi, &pm_ev);
558            pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
559                                       ev->data.control.param + 0x20,
560                                       ev->data.control.value & 0x7f);
561            pm_read_short(midi, &pm_ev);
562        } else {
563            pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel,
564                                       ev->data.control.param & 0x7f,
565                                       ev->data.control.value & 0x7f);
566
567            pm_read_short(midi, &pm_ev);
568        }
569        break;
570    case SND_SEQ_EVENT_SONGPOS:
571        pm_ev.message = Pm_Message(0xf2,
572                                   ev->data.control.value & 0x7f,
573                                   (ev->data.control.value >> 7) & 0x7f);
574        pm_read_short(midi, &pm_ev);
575        break;
576    case SND_SEQ_EVENT_SONGSEL:
577        pm_ev.message = Pm_Message(0xf3,
578                                   ev->data.control.value & 0x7f, 0);
579        pm_read_short(midi, &pm_ev);
580        break;
581    case SND_SEQ_EVENT_QFRAME:
582        pm_ev.message = Pm_Message(0xf1,
583                                   ev->data.control.value & 0x7f, 0);
584        pm_read_short(midi, &pm_ev);
585        break;
586    case SND_SEQ_EVENT_START:
587        pm_ev.message = Pm_Message(0xfa, 0, 0);
588        pm_read_short(midi, &pm_ev);
589        break;
590    case SND_SEQ_EVENT_CONTINUE:
591        pm_ev.message = Pm_Message(0xfb, 0, 0);
592        pm_read_short(midi, &pm_ev);
593        break;
594    case SND_SEQ_EVENT_STOP:
595        pm_ev.message = Pm_Message(0xfc, 0, 0);
596        pm_read_short(midi, &pm_ev);
597        break;
598    case SND_SEQ_EVENT_CLOCK:
599        pm_ev.message = Pm_Message(0xf8, 0, 0);
600        pm_read_short(midi, &pm_ev);
601        break;
602    case SND_SEQ_EVENT_TUNE_REQUEST:
603        pm_ev.message = Pm_Message(0xf6, 0, 0);
604        pm_read_short(midi, &pm_ev);
605        break;
606    case SND_SEQ_EVENT_RESET:
607        pm_ev.message = Pm_Message(0xff, 0, 0);
608        pm_read_short(midi, &pm_ev);
609        break;
610    case SND_SEQ_EVENT_SENSING:
611        pm_ev.message = Pm_Message(0xfe, 0, 0);
612        pm_read_short(midi, &pm_ev);
613        break;
614    case SND_SEQ_EVENT_SYSEX: {
615        const BYTE *ptr = (const BYTE *) ev->data.ext.ptr;
616        /* assume there is one sysex byte to process */
617        pm_read_bytes(midi, ptr, ev->data.ext.len, timestamp);
618        break;
619    }
620    }
621}
622
623
624static PmError alsa_poll(PmInternal *midi)
625{
626    snd_seq_event_t *ev;
627    /* expensive check for input data, gets data from device: */
628    while (snd_seq_event_input_pending(seq, TRUE) > 0) {
629        /* cheap check on local input buffer */
630        while (snd_seq_event_input_pending(seq, FALSE) > 0) {
631            /* check for and ignore errors, e.g. input overflow */
632            /* note: if there's overflow, this should be reported
633             * all the way through to client. Since input from all
634             * devices is merged, we need to find all input devices
635             * and set all to the overflow state.
636             * NOTE: this assumes every input is ALSA based.
637             */
638            int rslt = snd_seq_event_input(seq, &ev);
639            if (rslt >= 0) {
640                handle_event(ev);
641            } else if (rslt == -ENOSPC) {
642                int i;
643                for (i = 0; i < pm_descriptor_index; i++) {
644                    if (descriptors[i].pub.input) {
645                        PmInternal *midi = (PmInternal *)
646                                descriptors[i].internalDescriptor;
647                        /* careful, device may not be open! */
648                        if (midi) Pm_SetOverflow(midi->queue);
649                    }
650                }
651            }
652        }
653    }
654    return pmNoError;
655}
656
657
658static unsigned int alsa_has_host_error(PmInternal *midi)
659{
660    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
661    return desc->error;
662}
663
664
665static void alsa_get_host_error(PmInternal *midi, char *msg, unsigned int len)
666{
667    alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor;
668    int err = (pm_hosterror || desc->error);
669    get_alsa_error_text(msg, len, err);
670}
671
672
673pm_fns_node pm_linuxalsa_in_dictionary = {
674    none_write_short,
675    none_sysex,
676    none_sysex,
677    none_write_byte,
678    none_write_short,
679    none_write_flush,
680    alsa_synchronize,
681    alsa_in_open,
682    alsa_abort,
683    alsa_in_close,
684    alsa_poll,
685    alsa_has_host_error,
686    alsa_get_host_error
687};
688
689pm_fns_node pm_linuxalsa_out_dictionary = {
690    alsa_write_short,
691    alsa_sysex,
692    alsa_sysex,
693    alsa_write_byte,
694    alsa_write_short, /* short realtime message */
695    alsa_write_flush,
696    alsa_synchronize,
697    alsa_out_open,
698    alsa_abort,
699    alsa_out_close,
700    none_poll,
701    alsa_has_host_error,
702    alsa_get_host_error
703};
704
705
706/* pm_strdup -- copy a string to the heap. Use this rather than strdup so
707 *    that we call pm_alloc, not malloc. This allows portmidi to avoid
708 *    malloc which might cause priority inversion. Probably ALSA is going
709 *    to call malloc anyway, so this extra work here may be pointless.
710 */
711char *pm_strdup(const char *s)
712{
713    int len = strlen(s);
714    char *dup = (char *) pm_alloc(len + 1);
715    strcpy(dup, s);
716    return dup;
717}
718
719
720PmError pm_linuxalsa_init( void )
721{
722    int  err;
723    snd_seq_client_info_t *cinfo;
724    snd_seq_port_info_t *pinfo;
725    unsigned int caps;
726
727    /* Previously, the last parameter was SND_SEQ_NONBLOCK, but this
728     * would cause messages to be dropped if the ALSA buffer fills up.
729     * The correct behavior is for writes to block until there is
730     * room to send all the data. The client should normally allocate
731     * a large enough buffer to avoid blocking on output.
732     * Now that blocking is enabled, the seq_event_input() will block
733     * if there is no input data. This is not what we want, so must
734     * call seq_event_input_pending() to avoid blocking.
735     */
736    err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0);
737    if (err < 0) return err;
738   
739    snd_seq_client_info_alloca(&cinfo);
740    snd_seq_port_info_alloca(&pinfo);
741
742    snd_seq_client_info_set_client(cinfo, -1);
743    while (snd_seq_query_next_client(seq, cinfo) == 0) {
744        snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo));
745        snd_seq_port_info_set_port(pinfo, -1);
746        while (snd_seq_query_next_port(seq, pinfo) == 0) {
747            if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM)
748                continue; /* ignore Timer and Announce ports on client 0 */
749            caps = snd_seq_port_info_get_capability(pinfo);
750            if (!(caps & (SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE)))
751                continue; /* ignore if you cannot read or write port */
752            if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE) {
753                if (pm_default_output_device_id == -1)
754                    pm_default_output_device_id = pm_descriptor_index;
755                pm_add_device((char *)"ALSA",
756                              pm_strdup(snd_seq_port_info_get_name(pinfo)),
757                              FALSE,
758                              MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo),
759                                              snd_seq_port_info_get_port(pinfo)),
760                              &pm_linuxalsa_out_dictionary);
761            }
762            if (caps & SND_SEQ_PORT_CAP_SUBS_READ) {
763                if (pm_default_input_device_id == -1)
764                    pm_default_input_device_id = pm_descriptor_index;
765                pm_add_device((char *)"ALSA",
766                              pm_strdup(snd_seq_port_info_get_name(pinfo)),
767                              TRUE,
768                              MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo),
769                                              snd_seq_port_info_get_port(pinfo)),
770                              &pm_linuxalsa_in_dictionary);
771            }
772        }
773    }
774    return pmNoError;
775}
776   
777
778void pm_linuxalsa_term(void)
779{
780    if (seq) {
781        snd_seq_close(seq);
782        pm_free(descriptors);
783        descriptors = NULL;
784        pm_descriptor_index = 0;
785        pm_descriptor_max = 0;
786    }
787}
Property changes on: trunk/src/lib/portmidi/pmlinuxalsa.c
Added: svn:eol-style
   + native
Added: svn:mime-type
   + text/plain
trunk/src/lib/portmidi/pmmac.c
r0r19990
1/* pmmac.c -- PortMidi os-dependent code */
2
3/* This file only needs to implement:
4pm_init(), which calls various routines to register the
5available midi devices,
6Pm_GetDefaultInputDeviceID(), and
7Pm_GetDefaultOutputDeviceID().
8It is seperate from pmmacosxcm because we might want to register
9non-CoreMIDI devices.
10*/
11
12#include "stdlib.h"
13#include "portmidi.h"
14#include "pmutil.h"
15#include "pminternal.h"
16#include "pmmacosxcm.h"
17
18PmDeviceID pm_default_input_device_id = -1;
19PmDeviceID pm_default_output_device_id = -1;
20
21void pm_init()
22{
23    PmError err = pm_macosxcm_init();
24    // this is set when we return to Pm_Initialize, but we need it
25    // now in order to (successfully) call Pm_CountDevices()
26    pm_initialized = TRUE;
27    if (!err) {
28        pm_default_input_device_id = find_default_device(
29                (char *)"/PortMidi/PM_RECOMMENDED_INPUT_DEVICE", TRUE,
30                pm_default_input_device_id);
31        pm_default_output_device_id = find_default_device(
32            (char *)"/PortMidi/PM_RECOMMENDED_OUTPUT_DEVICE", FALSE,
33                pm_default_output_device_id);
34    }
35}
36
37
38void pm_term(void)
39{
40    pm_macosxcm_term();
41}
42
43
44PmDeviceID Pm_GetDefaultInputDeviceID()
45{
46    Pm_Initialize();
47    return pm_default_input_device_id;
48}
49
50PmDeviceID Pm_GetDefaultOutputDeviceID() {
51    Pm_Initialize();
52    return pm_default_output_device_id;
53}
54
55void *pm_alloc(size_t s) { return malloc(s); }
56
57void pm_free(void *ptr) { free(ptr); }
58
59
Property changes on: trunk/src/lib/portmidi/pmmac.c
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native
trunk/src/lib/lib.mak
r19989r19990
2222   $(LIBOBJ)/libjpeg \
2323   $(LIBOBJ)/libflac \
2424   $(LIBOBJ)/lib7z \
25   $(LIBOBJ)/portmidi \
2526
2627
27
2828#-------------------------------------------------
2929# utility library objects
3030#-------------------------------------------------
r19989r19990
382382$(LIBOBJ)/lib7z/%.o: $(LIBSRC)/lib7z/%.c | $(OSPREBUILD)
383383   @echo Compiling $<...
384384   $(CC) $(CDEFS) $(7ZOPTS) $(CCOMFLAGS) $(CONLYFLAGS) -I$(LIBSRC)/lib7z/ -c $< -o $@
385
386#-------------------------------------------------
387# portmidi library objects
388#-------------------------------------------------
389
390PMOPTS =
391
392# common objects
393LIBPMOBJS = \
394   $(LIBOBJ)/portmidi/portmidi.o \
395   $(LIBOBJ)/portmidi/porttime.o \
396   $(LIBOBJ)/portmidi/pmutil.o
397
398ifeq ($(TARGETOS),linux)
399PMOPTS = -DPMALSA=1
400
401LIBPMOBJS += \
402   $(LIBOBJ)/portmidi/pmlinux.o \
403   $(LIBOBJ)/portmidi/pmlinuxalsa.o \
404   $(LIBOBJ)/portmidi/finddefaultlinux.o \
405   $(LIBOBJ)/portmidi/ptlinux.o
406
407endif
408
409ifeq ($(TARGETOS),macosx)
410LIBPMOBJS += \
411   $(LIBOBJ)/portmidi/pmmac.o \
412   $(LIBOBJ)/portmidi/pmmacosxcm.o \
413   $(LIBOBJ)/portmidi/finddefault.o \
414   $(LIBOBJ)/portmidi/readbinaryplist.o \
415   $(LIBOBJ)/portmidi/ptmacosx_mach.o \
416   $(LIBOBJ)/portmidi/osxsupport.o
417endif
418
419ifeq ($(TARGETOS),win32)
420LIBPMOBJS += \
421   $(LIBOBJ)/portmidi/pmwin.o \
422   $(LIBOBJ)/portmidi/pmwinmm.o \
423   $(LIBOBJ)/portmidi/ptwinmm.o
424endif
425
426$(OBJ)/portmidi.a: $(LIBPMOBJS)
427
428$(LIBOBJ)/portmidi/%.o: $(LIBSRC)/portmidi/%.c | $(OSPREBUILD)
429   @echo Compiling $<...
430   $(CC) $(CDEFS) $(PMOPTS) $(CCOMFLAGS) $(CONLYFLAGS) -I$(LIBSRC)/portmidi/ -c $< -o $@
431

Previous 199869 Revisions Next


© 1997-2024 The MAME Team