diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2014-02-24 14:39:10 -0500 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2014-02-24 14:49:13 -0500 |
commit | 1de00ab6bb799db975edf4ebb41b6a27720ee820 (patch) | |
tree | d57825d82c8c8342f3217ea6925b282527065c82 | |
parent | 0a6af1420f61c9ffdb5099c5799f9b477cb2d4d1 (diff) |
new audio engine backend for native CoreAudio audio I/O, and PortMIDI for MIDI.
Code builds, runs and functions. Full code review still pending, and some possibly changes to organization of code within the backend is possible
84 files changed, 21936 insertions, 0 deletions
diff --git a/libs/backends/wavesaudio/portmidi/pmutil.h b/libs/backends/wavesaudio/portmidi/pmutil.h new file mode 100644 index 0000000000..40dabbff0e --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/pmutil.h @@ -0,0 +1,127 @@ +/* pmutil.h -- some helpful utilities for building midi
+ applications that use PortMidi
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+typedef void PmQueue;
+
+/*
+ A single-reader, single-writer queue is created by
+ Pm_QueueCreate(), which takes the number of messages and
+ the message size as parameters. The queue only accepts
+ fixed sized messages. Returns NULL if memory cannot be allocated.
+
+ This queue implementation uses the "light pipe" algorithm which
+ operates correctly even with multi-processors and out-of-order
+ memory writes. (see Alexander Dokumentov, "Lock-free Interprocess
+ Communication," Dr. Dobbs Portal, http://www.ddj.com/,
+ articleID=189401457, June 15, 2006. This algorithm requires
+ that messages be translated to a form where no words contain
+ zeros. Each word becomes its own "data valid" tag. Because of
+ this translation, we cannot return a pointer to data still in
+ the queue when the "peek" method is called. Instead, a buffer
+ is preallocated so that data can be copied there. Pm_QueuePeek()
+ dequeues a message into this buffer and returns a pointer to
+ it. A subsequent Pm_Dequeue() will copy from this buffer.
+
+ This implementation does not try to keep reader/writer data in
+ separate cache lines or prevent thrashing on cache lines.
+ However, this algorithm differs by doing inserts/removals in
+ units of messages rather than units of machine words. Some
+ performance improvement might be obtained by not clearing data
+ immediately after a read, but instead by waiting for the end
+ of the cache line, especially if messages are smaller than
+ cache lines. See the Dokumentov article for explanation.
+
+ The algorithm is extended to handle "overflow" reporting. To report
+ an overflow, the sender writes the current tail position to a field.
+ The receiver must acknowlege receipt by zeroing the field. The sender
+ will not send more until the field is zeroed.
+
+ Pm_QueueDestroy() destroys the queue and frees its storage.
+ */
+
+PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg);
+PMEXPORT PmError Pm_QueueDestroy(PmQueue *queue);
+
+/*
+ Pm_Dequeue() removes one item from the queue, copying it into msg.
+ Returns 1 if successful, and 0 if the queue is empty.
+ Returns pmBufferOverflow if what would have been the next thing
+ in the queue was dropped due to overflow. (So when overflow occurs,
+ the receiver can receive a queue full of messages before getting the
+ overflow report. This protocol ensures that the reader will be
+ notified when data is lost due to overflow.
+ */
+PMEXPORT PmError Pm_Dequeue(PmQueue *queue, void *msg);
+
+
+/*
+ Pm_Enqueue() inserts one item into the queue, copying it from msg.
+ Returns pmNoError if successful and pmBufferOverflow if the queue was
+ already full. If pmBufferOverflow is returned, the overflow flag is set.
+ */
+PMEXPORT PmError Pm_Enqueue(PmQueue *queue, void *msg);
+
+
+/*
+ Pm_QueueFull() returns non-zero if the queue is full
+ Pm_QueueEmpty() returns non-zero if the queue is empty
+
+ Either condition may change immediately because a parallel
+ enqueue or dequeue operation could be in progress. Furthermore,
+ Pm_QueueEmpty() is optimistic: it may say false, when due to
+ out-of-order writes, the full message has not arrived. Therefore,
+ Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns
+ false. On the other hand, Pm_QueueFull() is pessimistic: if it
+ returns false, then Pm_Enqueue() is guaranteed to succeed.
+
+ Error conditions: Pm_QueueFull() returns pmBadPtr if queue is NULL.
+ Pm_QueueEmpty() returns FALSE if queue is NULL.
+ */
+PMEXPORT int Pm_QueueFull(PmQueue *queue);
+PMEXPORT int Pm_QueueEmpty(PmQueue *queue);
+
+
+/*
+ Pm_QueuePeek() returns a pointer to the item at the head of the queue,
+ or NULL if the queue is empty. The item is not removed from the queue.
+ Pm_QueuePeek() will not indicate when an overflow occurs. If you want
+ to get and check pmBufferOverflow messages, use the return value of
+ Pm_QueuePeek() *only* as an indication that you should call
+ Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would
+ return pmBufferOverflow, Pm_QueuePeek() will return NULL but internally
+ clear the pmBufferOverflow flag, enabling Pm_Enqueue() to resume
+ enqueuing messages. A subsequent call to Pm_QueuePeek()
+ will return a pointer to the first message *after* the overflow.
+ Using this as an indication to call Pm_Dequeue(), the first call
+ to Pm_Dequeue() will return pmBufferOverflow. The second call will
+ return success, copying the same message pointed to by the previous
+ Pm_QueuePeek().
+
+ When to use Pm_QueuePeek(): (1) when you need to look at the message
+ data to decide who should be called to receive it. (2) when you need
+ to know a message is ready but cannot accept the message.
+
+ Note that Pm_QueuePeek() is not a fast check, so if possible, you
+ might as well just call Pm_Dequeue() and accept the data if it is there.
+ */
+PMEXPORT void *Pm_QueuePeek(PmQueue *queue);
+
+/*
+ Pm_SetOverflow() allows the writer (enqueuer) to signal an overflow
+ condition to the reader (dequeuer). E.g. when transfering data from
+ the OS to an application, if the OS indicates a buffer overrun,
+ Pm_SetOverflow() can be used to insure that the reader receives a
+ pmBufferOverflow result from Pm_Dequeue(). Returns pmBadPtr if queue
+ is NULL, returns pmBufferOverflow if buffer is already in an overflow
+ state, returns pmNoError if successfully set overflow state.
+ */
+PMEXPORT PmError Pm_SetOverflow(PmQueue *queue);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
diff --git a/libs/backends/wavesaudio/portmidi/portmidi.h b/libs/backends/wavesaudio/portmidi/portmidi.h new file mode 100644 index 0000000000..e07991e0d6 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/portmidi.h @@ -0,0 +1,654 @@ +#ifndef PORT_MIDI_H +#define PORT_MIDI_H +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * PortMidi Portable Real-Time MIDI Library + * PortMidi API Header File + * Latest version available at: http://sourceforge.net/projects/portmedia + * + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * Copyright (c) 2001-2006 Roger B. Dannenberg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* + * The text above constitutes the entire PortMidi license; however, + * the PortMusic community also makes the following non-binding requests: + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. It is also + * requested that these non-binding requests be included along with the + * license above. + */ + +/* CHANGELOG FOR PORTMIDI + * (see ../CHANGELOG.txt) + * + * NOTES ON HOST ERROR REPORTING: + * + * PortMidi errors (of type PmError) are generic, system-independent errors. + * When an error does not map to one of the more specific PmErrors, the + * catch-all code pmHostError is returned. This means that PortMidi has + * retained a more specific system-dependent error code. The caller can + * get more information by calling Pm_HasHostError() to test if there is + * a pending host error, and Pm_GetHostErrorText() to get a text string + * describing the error. Host errors are reported on a per-device basis + * because only after you open a device does PortMidi have a place to + * record the host error code. I.e. only + * those routines that receive a (PortMidiStream *) argument check and + * report errors. One exception to this is that Pm_OpenInput() and + * Pm_OpenOutput() can report errors even though when an error occurs, + * there is no PortMidiStream* to hold the error. Fortunately, both + * of these functions return any error immediately, so we do not really + * need per-device error memory. Instead, any host error code is stored + * in a global, pmHostError is returned, and the user can call + * Pm_GetHostErrorText() to get the error message (and the invalid stream + * parameter will be ignored.) The functions + * pm_init and pm_term do not fail or raise + * errors. The job of pm_init is to locate all available devices so that + * the caller can get information via PmDeviceInfo(). If an error occurs, + * the device is simply not listed as available. + * + * Host errors come in two flavors: + * a) host error + * b) host error during callback + * These can occur w/midi input or output devices. (b) can only happen + * asynchronously (during callback routines), whereas (a) only occurs while + * synchronously running PortMidi and any resulting system dependent calls. + * Both (a) and (b) are reported by the next read or write call. You can + * also query for asynchronous errors (b) at any time by calling + * Pm_HasHostError(). + * + * NOTES ON COMPILE-TIME SWITCHES + * + * DEBUG assumes stdio and a console. Use this if you want automatic, simple + * error reporting, e.g. for prototyping. If you are using MFC or some + * other graphical interface with no console, DEBUG probably should be + * undefined. + * PM_CHECK_ERRORS more-or-less takes over error checking for return values, + * stopping your program and printing error messages when an error + * occurs. This also uses stdio for console text I/O. + */ + +#ifndef WIN32 +// Linux and OS X have stdint.h +#include <stdint.h> +#else +#ifndef INT32_DEFINED +// rather than having users install a special .h file for windows, +// just put the required definitions inline here. porttime.h uses +// these too, so the definitions are (unfortunately) duplicated there +typedef int int32_t; +typedef unsigned int uint32_t; +#define INT32_DEFINED +#endif +#endif + +#ifdef _WINDLL +#define PMEXPORT __declspec(dllexport) +#else +#define PMEXPORT +#endif + +#ifndef FALSE + #define FALSE 0 +#endif +#ifndef TRUE + #define TRUE 1 +#endif + +/* default size of buffers for sysex transmission: */ +#define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 + +/** List of portmidi errors.*/ +typedef enum { + pmNoError = 0, + pmNoData = 0, /**< A "no error" return that also indicates no data avail. */ + pmGotData = 1, /**< A "no error" return that also indicates data available */ + pmHostError = -10000, + pmInvalidDeviceId, /** out of range or + * output device when input is requested or + * input device when output is requested or + * device is already opened + */ + pmInsufficientMemory, + pmBufferTooSmall, + pmBufferOverflow, + pmBadPtr, /* PortMidiStream parameter is NULL or + * stream is not opened or + * stream is output when input is required or + * stream is input when output is required */ + pmBadData, /** illegal midi data, e.g. missing EOX */ + pmInternalError, + pmBufferMaxSize /** buffer is already as large as it can be */ + /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */ +} PmError; + +/** + Pm_Initialize() is the library initialisation function - call this before + using the library. +*/ +PMEXPORT PmError Pm_Initialize( void ); + +/** + Pm_Terminate() is the library termination function - call this after + using the library. +*/ +PMEXPORT PmError Pm_Terminate( void ); + +/** A single PortMidiStream is a descriptor for an open MIDI device. +*/ +typedef void PortMidiStream; +#define PmStream PortMidiStream + +/** + Test whether stream has a pending host error. Normally, the client finds + out about errors through returned error codes, but some errors can occur + asynchronously where the client does not + explicitly call a function, and therefore cannot receive an error code. + The client can test for a pending error using Pm_HasHostError(). If true, + the error can be accessed and cleared by calling Pm_GetErrorText(). + Errors are also cleared by calling other functions that can return + errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The + client does not need to call Pm_HasHostError(). Any pending error will be + reported the next time the client performs an explicit function call on + the stream, e.g. an input or output operation. Until the error is cleared, + no new error codes will be obtained, even for a different stream. +*/ +PMEXPORT int Pm_HasHostError( PortMidiStream * stream ); + + +/** Translate portmidi error number into human readable message. + These strings are constants (set at compile time) so client has + no need to allocate storage +*/ +PMEXPORT const char *Pm_GetErrorText( PmError errnum ); + +/** Translate portmidi host error into human readable message. + These strings are computed at run time, so client has to allocate storage. + After this routine executes, the host error is cleared. +*/ +PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len); + +#define HDRLENGTH 50 +#define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less + than this number of characters */ + +/** + Device enumeration mechanism. + + Device ids range from 0 to Pm_CountDevices()-1. + +*/ +typedef int PmDeviceID; +#define pmNoDevice -1 +typedef struct { + int structVersion; /**< this internal structure version */ + const char *interf; /**< underlying MIDI API, e.g. MMSystem or DirectX */ + const char *name; /**< device name, e.g. USB MidiSport 1x1 */ + int input; /**< true iff input is available */ + int output; /**< true iff output is available */ + int opened; /**< used by generic PortMidi code to do error checking on arguments */ + +} PmDeviceInfo; + +/** Get devices count, ids range from 0 to Pm_CountDevices()-1. */ +PMEXPORT int Pm_CountDevices( void ); +/** + Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() + + Return the default device ID or pmNoDevice if there are no devices. + The result (but not pmNoDevice) can be passed to Pm_OpenMidi(). + + The default device can be specified using a small application + named pmdefaults that is part of the PortMidi distribution. This + program in turn uses the Java Preferences object created by + java.util.prefs.Preferences.userRoot().node("/PortMidi"); the + preference is set by calling + prefs.put("PM_RECOMMENDED_OUTPUT_DEVICE", prefName); + or prefs.put("PM_RECOMMENDED_INPUT_DEVICE", prefName); + + In the statements above, prefName is a string describing the + MIDI device in the form "interf, name" where interf identifies + the underlying software system or API used by PortMdi to access + devices and name is the name of the device. These correspond to + the interf and name fields of a PmDeviceInfo. (Currently supported + interfaces are "MMSystem" for Win32, "ALSA" for Linux, and + "CoreMIDI" for OS X, so in fact, there is no choice of interface.) + In "interf, name", the strings are actually substrings of + the full interface and name strings. For example, the preference + "Core, Sport" will match a device with interface "CoreMIDI" + and name "In USB MidiSport 1x1". It will also match "CoreMIDI" + and "In USB MidiSport 2x2". The devices are enumerated in device + ID order, so the lowest device ID that matches the pattern becomes + the default device. Finally, if the comma-space (", ") separator + between interface and name parts of the preference is not found, + the entire preference string is interpreted as a name, and the + interface part is the empty string, which matches anything. + + On the MAC, preferences are stored in + /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist + which is a binary file. In addition to the pmdefaults program, + there are utilities that can read and edit this preference file. + + On the PC, + + On Linux, + +*/ +PMEXPORT PmDeviceID Pm_GetDefaultInputDeviceID( void ); +/** see PmDeviceID Pm_GetDefaultInputDeviceID() */ +PMEXPORT PmDeviceID Pm_GetDefaultOutputDeviceID( void ); + +/** + PmTimestamp is used to represent a millisecond clock with arbitrary + start time. The type is used for all MIDI timestampes and clocks. +*/ +typedef int32_t PmTimestamp; +typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); + +/** TRUE if t1 before t2 */ +#define PmBefore(t1,t2) ((t1-t2) < 0) +/** + \defgroup grp_device Input/Output Devices Handling + @{ +*/ +/** + Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure + referring to the device specified by id. + If id is out of range the function returns NULL. + + The returned structure is owned by the PortMidi implementation and must + not be manipulated or freed. The pointer is guaranteed to be valid + between calls to Pm_Initialize() and Pm_Terminate(). +*/ +PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); + +/** + Pm_OpenInput() and Pm_OpenOutput() open devices. + + stream is the address of a PortMidiStream pointer which will receive + a pointer to the newly opened stream. + + inputDevice is the id of the device used for input (see PmDeviceID above). + + inputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or handle processing. + inputDriverInfo is never required for correct operation. If not used + inputDriverInfo should be NULL. + + outputDevice is the id of the device used for output (see PmDeviceID above.) + + outputDriverInfo is a pointer to an optional driver specific data structure + containing additional information for device setup or handle processing. + outputDriverInfo is never required for correct operation. If not used + outputDriverInfo should be NULL. + + For input, the buffersize specifies the number of input events to be + buffered waiting to be read using Pm_Read(). For output, buffersize + specifies the number of output events to be buffered waiting for output. + (In some cases -- see below -- PortMidi does not buffer output at all + and merely passes data to a lower-level API, in which case buffersize + is ignored.) + + latency is the delay in milliseconds applied to timestamps to determine + when the output should actually occur. (If latency is < 0, 0 is assumed.) + If latency is zero, timestamps are ignored and all output is delivered + immediately. If latency is greater than zero, output is delayed until the + message timestamp plus the latency. (NOTE: the time is measured relative + to the time source indicated by time_proc. Timestamps are absolute, + not relative delays or offsets.) In some cases, PortMidi can obtain + better timing than your application by passing timestamps along to the + device driver or hardware. Latency may also help you to synchronize midi + data to audio data by matching midi latency to the audio buffer latency. + + time_proc is a pointer to a procedure that returns time in milliseconds. It + may be NULL, in which case a default millisecond timebase (PortTime) is + used. If the application wants to use PortTime, it should start the timer + (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the + application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput, + it may get a ptAlreadyStarted error from Pt_Start, and the application's + preferred time resolution and callback function will be ignored. + time_proc result values are appended to incoming MIDI data, and time_proc + times are used to schedule outgoing MIDI data (when latency is non-zero). + + time_info is a pointer passed to time_proc. + + Example: If I provide a timestamp of 5000, latency is 1, and time_proc + returns 4990, then the desired output time will be when time_proc returns + timestamp+latency = 5001. This will be 5001-4990 = 11ms from now. + + return value: + Upon success Pm_Open() returns PmNoError and places a pointer to a + valid PortMidiStream in the stream argument. + If a call to Pm_Open() fails a nonzero error code is returned (see + PMError above) and the value of port is invalid. + + Any stream that is successfully opened should eventually be closed + by calling Pm_Close(). + +*/ +PMEXPORT PmError Pm_OpenInput( PortMidiStream** stream, + PmDeviceID inputDevice, + void *inputDriverInfo, + int32_t bufferSize, + PmTimeProcPtr time_proc, + void *time_info ); + +PMEXPORT PmError Pm_OpenOutput( PortMidiStream** stream, + PmDeviceID outputDevice, + void *outputDriverInfo, + int32_t bufferSize, + PmTimeProcPtr time_proc, + void *time_info, + int32_t latency ); + /** @} */ + +/** + \defgroup grp_events_filters Events and Filters Handling + @{ +*/ + +/* \function PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters ) + Pm_SetFilter() sets filters on an open input stream to drop selected + input types. By default, only active sensing messages are filtered. + To prohibit, say, active sensing and sysex messages, call + Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); + + Filtering is useful when midi routing or midi thru functionality is being + provided by the user application. + For example, you may want to exclude timing messages (clock, MTC, start/stop/continue), + while allowing note-related messages to pass. + Or you may be using a sequencer or drum-machine for MIDI clock information but want to + exclude any notes it may play. + */ + +/* Filter bit-mask definitions */ +/** filter active sensing messages (0xFE): */ +#define PM_FILT_ACTIVE (1 << 0x0E) +/** filter system exclusive messages (0xF0): */ +#define PM_FILT_SYSEX (1 << 0x00) +/** filter MIDI clock message (0xF8) */ +#define PM_FILT_CLOCK (1 << 0x08) +/** filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ +#define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) +/** filter tick messages (0xF9) */ +#define PM_FILT_TICK (1 << 0x09) +/** filter undefined FD messages */ +#define PM_FILT_FD (1 << 0x0D) +/** filter undefined real-time messages */ +#define PM_FILT_UNDEFINED PM_FILT_FD +/** filter reset messages (0xFF) */ +#define PM_FILT_RESET (1 << 0x0F) +/** filter all real-time messages */ +#define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ + PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) +/** filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ +#define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) +/** filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ +#define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) +/** per-note aftertouch (0xA0-0xAF) */ +#define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) +/** filter both channel and poly aftertouch */ +#define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) +/** Program changes (0xC0-0xCF) */ +#define PM_FILT_PROGRAM (1 << 0x1C) +/** Control Changes (CC's) (0xB0-0xBF)*/ +#define PM_FILT_CONTROL (1 << 0x1B) +/** Pitch Bender (0xE0-0xEF*/ +#define PM_FILT_PITCHBEND (1 << 0x1E) +/** MIDI Time Code (0xF1)*/ +#define PM_FILT_MTC (1 << 0x01) +/** Song Position (0xF2) */ +#define PM_FILT_SONG_POSITION (1 << 0x02) +/** Song Select (0xF3)*/ +#define PM_FILT_SONG_SELECT (1 << 0x03) +/** Tuning request (0xF6)*/ +#define PM_FILT_TUNE (1 << 0x06) +/** All System Common messages (mtc, song position, song select, tune request) */ +#define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) + + +PMEXPORT PmError Pm_SetFilter( PortMidiStream* stream, int32_t filters ); + +#define Pm_Channel(channel) (1<<(channel)) +/** + Pm_SetChannelMask() filters incoming messages based on channel. + The mask is a 16-bit bitfield corresponding to appropriate channels. + The Pm_Channel macro can assist in calling this function. + i.e. to set receive only input on channel 1, call with + Pm_SetChannelMask(Pm_Channel(1)); + Multiple channels should be OR'd together, like + Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) + + Note that channels are numbered 0 to 15 (not 1 to 16). Most + synthesizer and interfaces number channels starting at 1, but + PortMidi numbers channels starting at 0. + + All channels are allowed by default +*/ +PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); + +/** + Pm_Abort() terminates outgoing messages immediately + The caller should immediately close the output port; + this call may result in transmission of a partial midi message. + There is no abort for Midi input because the user can simply + ignore messages in the buffer and close an input device at + any time. + */ +PMEXPORT PmError Pm_Abort( PortMidiStream* stream ); + +/** + Pm_Close() closes a midi stream, flushing any pending buffers. + (PortMidi attempts to close open streams when the application + exits -- this is particularly difficult under Windows.) +*/ +PMEXPORT PmError Pm_Close( PortMidiStream* stream ); + +/** + Pm_Synchronize() instructs PortMidi to (re)synchronize to the + time_proc passed when the stream was opened. Typically, this + is used when the stream must be opened before the time_proc + reference is actually advancing. In this case, message timing + may be erratic, but since timestamps of zero mean + "send immediately," initialization messages with zero timestamps + can be written without a functioning time reference and without + problems. Before the first MIDI message with a non-zero + timestamp is written to the stream, the time reference must + begin to advance (for example, if the time_proc computes time + based on audio samples, time might begin to advance when an + audio stream becomes active). After time_proc return values + become valid, and BEFORE writing the first non-zero timestamped + MIDI message, call Pm_Synchronize() so that PortMidi can observe + the difference between the current time_proc value and its + MIDI stream time. + + In the more normal case where time_proc + values advance continuously, there is no need to call + Pm_Synchronize. PortMidi will always synchronize at the + first output message and periodically thereafter. +*/ +PmError Pm_Synchronize( PortMidiStream* stream ); + + +/** + Pm_Message() encodes a short Midi message into a 32-bit word. If data1 + and/or data2 are not present, use zero. + + Pm_MessageStatus(), Pm_MessageData1(), and + Pm_MessageData2() extract fields from a 32-bit midi message. +*/ +#define Pm_Message(status, data1, data2) \ + ((((data2) << 16) & 0xFF0000) | \ + (((data1) << 8) & 0xFF00) | \ + ((status) & 0xFF)) +#define Pm_MessageStatus(msg) ((msg) & 0xFF) +#define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) +#define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) + +typedef int32_t PmMessage; /**< see PmEvent */ +/** + All midi data comes in the form of PmEvent structures. A sysex + message is encoded as a sequence of PmEvent structures, with each + structure carrying 4 bytes of the message, i.e. only the first + PmEvent carries the status byte. + + Note that MIDI allows nested messages: the so-called "real-time" MIDI + messages can be inserted into the MIDI byte stream at any location, + including within a sysex message. MIDI real-time messages are one-byte + messages used mainly for timing (see the MIDI spec). PortMidi retains + the order of non-real-time MIDI messages on both input and output, but + it does not specify exactly how real-time messages are processed. This + is particulary problematic for MIDI input, because the input parser + must either prepare to buffer an unlimited number of sysex message + bytes or to buffer an unlimited number of real-time messages that + arrive embedded in a long sysex message. To simplify things, the input + parser is allowed to pass real-time MIDI messages embedded within a + sysex message, and it is up to the client to detect, process, and + remove these messages as they arrive. + + When receiving sysex messages, the sysex message is terminated + by either an EOX status byte (anywhere in the 4 byte messages) or + by a non-real-time status byte in the low order byte of the message. + If you get a non-real-time status byte but there was no EOX byte, it + means the sysex message was somehow truncated. This is not + considered an error; e.g., a missing EOX can result from the user + disconnecting a MIDI cable during sysex transmission. + + A real-time message can occur within a sysex message. A real-time + message will always occupy a full PmEvent with the status byte in + the low-order byte of the PmEvent message field. (This implies that + the byte-order of sysex bytes and real-time message bytes may not + be preserved -- for example, if a real-time message arrives after + 3 bytes of a sysex message, the real-time message will be delivered + first. The first word of the sysex message will be delivered only + after the 4th byte arrives, filling the 4-byte PmEvent message field. + + The timestamp field is observed when the output port is opened with + a non-zero latency. A timestamp of zero means "use the current time", + which in turn means to deliver the message with a delay of + latency (the latency parameter used when opening the output port.) + Do not expect PortMidi to sort data according to timestamps -- + messages should be sent in the correct order, and timestamps MUST + be non-decreasing. See also "Example" for Pm_OpenOutput() above. + + A sysex message will generally fill many PmEvent structures. On + output to a PortMidiStream with non-zero latency, the first timestamp + on sysex message data will determine the time to begin sending the + message. PortMidi implementations may ignore timestamps for the + remainder of the sysex message. + + On input, the timestamp ideally denotes the arrival time of the + status byte of the message. The first timestamp on sysex message + data will be valid. Subsequent timestamps may denote + when message bytes were actually received, or they may be simply + copies of the first timestamp. + + Timestamps for nested messages: If a real-time message arrives in + the middle of some other message, it is enqueued immediately with + the timestamp corresponding to its arrival time. The interrupted + non-real-time message or 4-byte packet of sysex data will be enqueued + later. The timestamp of interrupted data will be equal to that of + the interrupting real-time message to insure that timestamps are + non-decreasing. + */ +typedef struct { + PmMessage message; + PmTimestamp timestamp; +} PmEvent; + +/** + @} +*/ +/** \defgroup grp_io Reading and Writing Midi Messages + @{ +*/ +/** + Pm_Read() retrieves midi data into a buffer, and returns the number + of events read. Result is a non-negative number unless an error occurs, + in which case a PmError value will be returned. + + Buffer Overflow + + The problem: if an input overflow occurs, data will be lost, ultimately + because there is no flow control all the way back to the data source. + When data is lost, the receiver should be notified and some sort of + graceful recovery should take place, e.g. you shouldn't resume receiving + in the middle of a long sysex message. + + With a lock-free fifo, which is pretty much what we're stuck with to + enable portability to the Mac, it's tricky for the producer and consumer + to synchronously reset the buffer and resume normal operation. + + Solution: the buffer managed by PortMidi will be flushed when an overflow + occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) + and ordinary processing resumes as soon as a new message arrives. The + remainder of a partial sysex message is not considered to be a "new + message" and will be flushed as well. + +*/ +PMEXPORT int Pm_Read( PortMidiStream *stream, PmEvent *buffer, int32_t length ); + +/** + Pm_Poll() tests whether input is available, + returning TRUE, FALSE, or an error value. +*/ +PMEXPORT PmError Pm_Poll( PortMidiStream *stream); + +/** + Pm_Write() writes midi data from a buffer. This may contain: + - short messages + or + - sysex messages that are converted into a sequence of PmEvent + structures, e.g. sending data from a file or forwarding them + from midi input. + + Use Pm_WriteSysEx() to write a sysex message stored as a contiguous + array of bytes. + + Sysex data may contain embedded real-time messages. +*/ +PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length ); + +/** + Pm_WriteShort() writes a timestamped non-system-exclusive midi message. + Messages are delivered in order as received, and timestamps must be + non-decreasing. (But timestamps are ignored if the stream was opened + with latency = 0.) +*/ +PMEXPORT PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, int32_t msg); + +/** + Pm_WriteSysEx() writes a timestamped system-exclusive midi message. +*/ +PMEXPORT PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg); + +/** @} */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* PORT_MIDI_H */ diff --git a/libs/backends/wavesaudio/portmidi/porttime.h b/libs/backends/wavesaudio/portmidi/porttime.h new file mode 100644 index 0000000000..ff22de9d5a --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/porttime.h @@ -0,0 +1,92 @@ +/* porttime.h -- portable interface to millisecond timer */ + +/* CHANGE LOG FOR PORTTIME + 10-Jun-03 Mark Nelson & RBD + boost priority of timer thread in ptlinux.c implementation + */ + +/* Should there be a way to choose the source of time here? */ + +#ifdef WIN32 +#ifndef INT32_DEFINED +// rather than having users install a special .h file for windows, +// just put the required definitions inline here. portmidi.h uses +// these too, so the definitions are (unfortunately) duplicated there +typedef int int32_t; +typedef unsigned int uint32_t; +#define INT32_DEFINED +#endif +#else +#include <stdint.h> // needed for int32_t +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef PMEXPORT +#ifdef _WINDLL +#define PMEXPORT __declspec(dllexport) +#else +#define PMEXPORT +#endif +#endif + +typedef enum { + ptNoError = 0, /* success */ + ptHostError = -10000, /* a system-specific error occurred */ + ptAlreadyStarted, /* cannot start timer because it is already started */ + ptAlreadyStopped, /* cannot stop timer because it is already stopped */ + ptInsufficientMemory /* memory could not be allocated */ +} PtError; + + +typedef int32_t PtTimestamp; + +typedef void (PtCallback)( PtTimestamp timestamp, void *userData ); + +/* + Pt_Start() starts a real-time service. + + resolution is the timer resolution in ms. The time will advance every + resolution ms. + + callback is a function pointer to be called every resolution ms. + + userData is passed to callback as a parameter. + + return value: + Upon success, returns ptNoError. See PtError for other values. +*/ +PMEXPORT PtError Pt_Start(int resolution, PtCallback *callback, void *userData); + +/* + Pt_Stop() stops the timer. + + return value: + Upon success, returns ptNoError. See PtError for other values. +*/ +PMEXPORT PtError Pt_Stop(); + +/* + Pt_Started() returns true iff the timer is running. +*/ +PMEXPORT int Pt_Started(); + +/* + Pt_Time() returns the current time in ms. +*/ +PMEXPORT PtTimestamp Pt_Time(); + +/* + Pt_Sleep() pauses, allowing other threads to run. + + duration is the length of the pause in ms. The true duration + of the pause may be rounded to the nearest or next clock tick + as determined by resolution in Pt_Start(). +*/ +PMEXPORT void Pt_Sleep(int32_t duration); + +#ifdef __cplusplus +} +#endif diff --git a/libs/backends/wavesaudio/portmidi/src/pm_common/pminternal.h b/libs/backends/wavesaudio/portmidi/src/pm_common/pminternal.h new file mode 100644 index 0000000000..f7c62705b7 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_common/pminternal.h @@ -0,0 +1,178 @@ +/* pminternal.h -- header for interface implementations */
+
+/* this file is included by files that implement library internals */
+/* Here is a guide to implementers:
+ provide an initialization function similar to pm_winmm_init()
+ add your initialization function to pm_init()
+ Note that your init function should never require not-standard
+ libraries or fail in any way. If the interface is not available,
+ simply do not call pm_add_device. This means that non-standard
+ libraries should try to do dynamic linking at runtime using a DLL
+ and return without error if the DLL cannot be found or if there
+ is any other failure.
+ implement functions as indicated in pm_fns_type to open, read, write,
+ close, etc.
+ call pm_add_device() for each input and output device, passing it a
+ pm_fns_type structure.
+ assumptions about pm_fns_type functions are given below.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int pm_initialized; /* see note in portmidi.c */
+
+/* these are defined in system-specific file */
+void *pm_alloc(size_t s);
+void pm_free(void *ptr);
+
+/* if an error occurs while opening or closing a midi stream, set these: */
+extern int pm_hosterror;
+extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN];
+
+struct pm_internal_struct;
+
+/* these do not use PmInternal because it is not defined yet... */
+typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi,
+ PmEvent *buffer);
+typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi,
+ PmTimestamp timestamp);
+typedef PmError (*pm_end_sysex_fn)(struct pm_internal_struct *midi,
+ PmTimestamp timestamp);
+typedef PmError (*pm_write_byte_fn)(struct pm_internal_struct *midi,
+ unsigned char byte, PmTimestamp timestamp);
+typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi,
+ PmEvent *buffer);
+typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi,
+ PmTimestamp timestamp);
+typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi);
+/* pm_open_fn should clean up all memory and close the device if any part
+ of the open fails */
+typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi,
+ void *driverInfo);
+typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi);
+/* pm_close_fn should clean up all memory and close the device if any
+ part of the close fails. */
+typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi);
+typedef PmError (*pm_poll_fn)(struct pm_internal_struct *midi);
+typedef void (*pm_host_error_fn)(struct pm_internal_struct *midi, char * msg,
+ unsigned int len);
+typedef unsigned int (*pm_has_host_error_fn)(struct pm_internal_struct *midi);
+
+typedef struct {
+ pm_write_short_fn write_short; /* output short MIDI msg */
+ pm_begin_sysex_fn begin_sysex; /* prepare to send a sysex message */
+ pm_end_sysex_fn end_sysex; /* marks end of sysex message */
+ pm_write_byte_fn write_byte; /* accumulate one more sysex byte */
+ pm_write_realtime_fn write_realtime; /* send real-time message within sysex */
+ pm_write_flush_fn write_flush; /* send any accumulated but unsent data */
+ pm_synchronize_fn synchronize; /* synchronize portmidi time to stream time */
+ pm_open_fn open; /* open MIDI device */
+ pm_abort_fn abort; /* abort */
+ pm_close_fn close; /* close device */
+ pm_poll_fn poll; /* read pending midi events into portmidi buffer */
+ pm_has_host_error_fn has_host_error; /* true when device has had host
+ error message */
+ pm_host_error_fn host_error; /* provide text readable host error message
+ for device (clears and resets) */
+} pm_fns_node, *pm_fns_type;
+
+
+/* when open fails, the dictionary gets this set of functions: */
+extern pm_fns_node pm_none_dictionary;
+
+typedef struct {
+ PmDeviceInfo pub; /* some portmidi state also saved in here (for autmatic
+ device closing (see PmDeviceInfo struct) */
+ void *descriptor; /* ID number passed to win32 multimedia API open */
+ void *internalDescriptor; /* points to PmInternal device, allows automatic
+ device closing */
+ pm_fns_type dictionary;
+} descriptor_node, *descriptor_type;
+
+extern int pm_descriptor_max;
+extern descriptor_type descriptors;
+extern int pm_descriptor_index;
+
+typedef uint32_t (*time_get_proc_type)(void *time_info);
+
+typedef struct pm_internal_struct {
+ int device_id; /* which device is open (index to descriptors) */
+ short write_flag; /* MIDI_IN, or MIDI_OUT */
+
+ PmTimeProcPtr time_proc; /* where to get the time */
+ void *time_info; /* pass this to get_time() */
+ int32_t buffer_len; /* how big is the buffer or queue? */
+ PmQueue *queue;
+
+ int32_t latency; /* time delay in ms between timestamps and actual output */
+ /* set to zero to get immediate, simple blocking output */
+ /* if latency is zero, timestamps will be ignored; */
+ /* if midi input device, this field ignored */
+
+ int sysex_in_progress; /* when sysex status is seen, this flag becomes
+ * true until EOX is seen. When true, new data is appended to the
+ * stream of outgoing bytes. When overflow occurs, sysex data is
+ * dropped (until an EOX or non-real-timei status byte is seen) so
+ * that, if the overflow condition is cleared, we don't start
+ * sending data from the middle of a sysex message. If a sysex
+ * message is filtered, sysex_in_progress is false, causing the
+ * message to be dropped. */
+ PmMessage sysex_message; /* buffer for 4 bytes of sysex data */
+ int sysex_message_count; /* how many bytes in sysex_message so far */
+
+ int32_t filters; /* flags that filter incoming message classes */
+ int32_t channel_mask; /* filter incoming messages based on channel */
+ PmTimestamp last_msg_time; /* timestamp of last message */
+ PmTimestamp sync_time; /* time of last synchronization */
+ PmTimestamp now; /* set by PmWrite to current time */
+ int first_message; /* initially true, used to run first synchronization */
+ pm_fns_type dictionary; /* implementation functions */
+ void *descriptor; /* system-dependent state */
+ /* the following are used to expedite sysex data */
+ /* on windows, in debug mode, based on some profiling, these optimizations
+ * cut the time to process sysex bytes from about 7.5 to 0.26 usec/byte,
+ * but this does not count time in the driver, so I don't know if it is
+ * important
+ */
+ unsigned char *fill_base; /* addr of ptr to sysex data */
+ uint32_t *fill_offset_ptr; /* offset of next sysex byte */
+ int32_t fill_length; /* how many sysex bytes to write */
+} PmInternal;
+
+
+/* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */
+void pm_init(void);
+void pm_term(void);
+
+/* defined by portMidi, used by pmwinmm */
+PmError none_write_short(PmInternal *midi, PmEvent *buffer);
+PmError none_write_byte(PmInternal *midi, unsigned char byte,
+ PmTimestamp timestamp);
+PmTimestamp none_synchronize(PmInternal *midi);
+
+PmError pm_fail_fn(PmInternal *midi);
+PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp);
+PmError pm_success_fn(PmInternal *midi);
+PmError pm_add_device(char *interf, char *name, int input, void *descriptor,
+ pm_fns_type dictionary);
+uint32_t pm_read_bytes(PmInternal *midi, const unsigned char *data, int len,
+ PmTimestamp timestamp);
+void pm_read_short(PmInternal *midi, PmEvent *event);
+
+#define none_write_flush pm_fail_timestamp_fn
+#define none_sysex pm_fail_timestamp_fn
+#define none_poll pm_fail_fn
+#define success_poll pm_success_fn
+
+#define MIDI_REALTIME_MASK 0xf8
+#define is_real_time(msg) \
+ ((Pm_MessageStatus(msg) & MIDI_REALTIME_MASK) == MIDI_REALTIME_MASK)
+
+int pm_find_default_device(char *pattern, int is_input);
+
+#ifdef __cplusplus
+}
+#endif
+
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_common/pmutil.c b/libs/backends/wavesaudio/portmidi/src/pm_common/pmutil.c new file mode 100644 index 0000000000..a70fe2fa1f --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_common/pmutil.c @@ -0,0 +1,284 @@ +/* pmutil.c -- some helpful utilities for building midi + applications that use PortMidi + */ +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include "portmidi.h" +#include "pmutil.h" +#include "pminternal.h" + +#ifdef WIN32 +#define bzero(addr, siz) memset(addr, 0, siz) +#endif + +// #define QUEUE_DEBUG 1 +#ifdef QUEUE_DEBUG +#include "stdio.h" +#endif + +typedef struct { + long head; + long tail; + long len; + long overflow; + int32_t msg_size; /* number of int32_t in a message including extra word */ + int32_t peek_overflow; + int32_t *buffer; + int32_t *peek; + int32_t peek_flag; +} PmQueueRep; + + +PMEXPORT PmQueue *Pm_QueueCreate(long num_msgs, int32_t bytes_per_msg) +{ + int32_t int32s_per_msg = + (int32_t) (((bytes_per_msg + sizeof(int32_t) - 1) & + ~(sizeof(int32_t) - 1)) / sizeof(int32_t)); + PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep)); + if (!queue) /* memory allocation failed */ + return NULL; + + /* need extra word per message for non-zero encoding */ + queue->len = num_msgs * (int32s_per_msg + 1); + queue->buffer = (int32_t *) pm_alloc(queue->len * sizeof(int32_t)); + bzero(queue->buffer, queue->len * sizeof(int32_t)); + if (!queue->buffer) { + pm_free(queue); + return NULL; + } else { /* allocate the "peek" buffer */ + queue->peek = (int32_t *) pm_alloc(int32s_per_msg * sizeof(int32_t)); + if (!queue->peek) { + /* free everything allocated so far and return */ + pm_free(queue->buffer); + pm_free(queue); + return NULL; + } + } + bzero(queue->buffer, queue->len * sizeof(int32_t)); + queue->head = 0; + queue->tail = 0; + /* msg_size is in words */ + queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */ + queue->overflow = FALSE; + queue->peek_overflow = FALSE; + queue->peek_flag = FALSE; + return queue; +} + + +PMEXPORT PmError Pm_QueueDestroy(PmQueue *q) +{ + PmQueueRep *queue = (PmQueueRep *) q; + + /* arg checking */ + if (!queue || !queue->buffer || !queue->peek) + return pmBadPtr; + + pm_free(queue->peek); + pm_free(queue->buffer); + pm_free(queue); + return pmNoError; +} + + +PMEXPORT PmError Pm_Dequeue(PmQueue *q, void *msg) +{ + long head; + PmQueueRep *queue = (PmQueueRep *) q; + int i; + int32_t *msg_as_int32 = (int32_t *) msg; + + /* arg checking */ + if (!queue) + return pmBadPtr; + /* a previous peek operation encountered an overflow, but the overflow + * has not yet been reported to client, so do it now. No message is + * returned, but on the next call, we will return the peek buffer. + */ + if (queue->peek_overflow) { + queue->peek_overflow = FALSE; + return pmBufferOverflow; + } + if (queue->peek_flag) { + memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32_t)); + queue->peek_flag = FALSE; + return pmGotData; + } + + head = queue->head; + /* if writer overflows, it writes queue->overflow = tail+1 so that + * when the reader gets to that position in the buffer, it can + * return the overflow condition to the reader. The problem is that + * at overflow, things have wrapped around, so tail == head, and the + * reader will detect overflow immediately instead of waiting until + * it reads everything in the buffer, wrapping around again to the + * point where tail == head. So the condition also checks that + * queue->buffer[head] is zero -- if so, then the buffer is now + * empty, and we're at the point in the msg stream where overflow + * occurred. It's time to signal overflow to the reader. If + * queue->buffer[head] is non-zero, there's a message there and we + * should read all the way around the buffer before signalling overflow. + * There is a write-order dependency here, but to fail, the overflow + * field would have to be written while an entire buffer full of + * writes are still pending. I'm assuming out-of-order writes are + * possible, but not that many. + */ + if (queue->overflow == head + 1 && !queue->buffer[head]) { + queue->overflow = 0; /* non-overflow condition */ + return pmBufferOverflow; + } + + /* test to see if there is data in the queue -- test from back + * to front so if writer is simultaneously writing, we don't + * waste time discovering the write is not finished + */ + for (i = queue->msg_size - 1; i >= 0; i--) { + if (!queue->buffer[head + i]) { + return pmNoData; + } + } + memcpy(msg, (char *) &queue->buffer[head + 1], + sizeof(int32_t) * (queue->msg_size - 1)); + /* fix up zeros */ + i = queue->buffer[head]; + while (i < queue->msg_size) { + int32_t j; + i--; /* msg does not have extra word so shift down */ + j = msg_as_int32[i]; + msg_as_int32[i] = 0; + i = j; + } + /* signal that data has been removed by zeroing: */ + bzero((char *) &queue->buffer[head], sizeof(int32_t) * queue->msg_size); + + /* update head */ + head += queue->msg_size; + if (head == queue->len) head = 0; + queue->head = head; + return pmGotData; /* success */ +} + + + +PMEXPORT PmError Pm_SetOverflow(PmQueue *q) +{ + PmQueueRep *queue = (PmQueueRep *) q; + long tail; + /* arg checking */ + if (!queue) + return pmBadPtr; + /* no more enqueue until receiver acknowledges overflow */ + if (queue->overflow) return pmBufferOverflow; + tail = queue->tail; + queue->overflow = tail + 1; + return pmBufferOverflow; +} + + +PMEXPORT PmError Pm_Enqueue(PmQueue *q, void *msg) +{ + PmQueueRep *queue = (PmQueueRep *) q; + long tail; + int i; + int32_t *src = (int32_t *) msg; + int32_t *ptr; + int32_t *dest; + int rslt; + if (!queue) + return pmBadPtr; + /* no more enqueue until receiver acknowledges overflow */ + if (queue->overflow) return pmBufferOverflow; + rslt = Pm_QueueFull(q); + /* already checked above: if (rslt == pmBadPtr) return rslt; */ + tail = queue->tail; + if (rslt) { + queue->overflow = tail + 1; + return pmBufferOverflow; + } + + /* queue is has room for message, and overflow flag is cleared */ + ptr = &queue->buffer[tail]; + dest = ptr + 1; + for (i = 1; i < queue->msg_size; i++) { + int32_t j = src[i - 1]; + if (!j) { + *ptr = i; + ptr = dest; + } else { + *dest = j; + } + dest++; + } + *ptr = i; + tail += queue->msg_size; + if (tail == queue->len) tail = 0; + queue->tail = tail; + return pmNoError; +} + + +PMEXPORT int Pm_QueueEmpty(PmQueue *q) +{ + PmQueueRep *queue = (PmQueueRep *) q; + return (!queue) || /* null pointer -> return "empty" */ + (queue->buffer[queue->head] == 0 && !queue->peek_flag); +} + + +PMEXPORT int Pm_QueueFull(PmQueue *q) +{ + long tail; + int i; + PmQueueRep *queue = (PmQueueRep *) q; + /* arg checking */ + if (!queue) + return pmBadPtr; + tail = queue->tail; + /* test to see if there is space in the queue */ + for (i = 0; i < queue->msg_size; i++) { + if (queue->buffer[tail + i]) { + return TRUE; + } + } + return FALSE; +} + + +PMEXPORT void *Pm_QueuePeek(PmQueue *q) +{ + PmError rslt; + int32_t temp; + PmQueueRep *queue = (PmQueueRep *) q; + /* arg checking */ + if (!queue) + return NULL; + + if (queue->peek_flag) { + return queue->peek; + } + /* this is ugly: if peek_overflow is set, then Pm_Dequeue() + * returns immediately with pmBufferOverflow, but here, we + * want Pm_Dequeue() to really check for data. If data is + * there, we can return it + */ + temp = queue->peek_overflow; + queue->peek_overflow = FALSE; + rslt = Pm_Dequeue(q, queue->peek); + queue->peek_overflow = temp; + + if (rslt == 1) { + queue->peek_flag = TRUE; + return queue->peek; + } else if (rslt == pmBufferOverflow) { + /* when overflow is indicated, the queue is empty and the + * first message that was dropped by Enqueue (signalling + * pmBufferOverflow to its caller) would have been the next + * message in the queue. Pm_QueuePeek will return NULL, but + * remember that an overflow occurred. (see Pm_Dequeue) + */ + queue->peek_overflow = TRUE; + } + return NULL; +} + diff --git a/libs/backends/wavesaudio/portmidi/src/pm_common/portmidi.c b/libs/backends/wavesaudio/portmidi/src/pm_common/portmidi.c new file mode 100644 index 0000000000..b7161700d6 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_common/portmidi.c @@ -0,0 +1,1137 @@ +#ifdef _MSC_VER + #pragma warning(disable: 4244) // stop warnings about downsize typecasts + #pragma warning(disable: 4018) // stop warnings about signed/unsigned +#endif + +#include "stdlib.h" +#include "string.h" +#include "portmidi.h" +#include "porttime.h" +#include "pmutil.h" +#include "pminternal.h" +#include <assert.h> + +#define MIDI_CLOCK 0xf8 +#define MIDI_ACTIVE 0xfe +#define MIDI_STATUS_MASK 0x80 +#define MIDI_SYSEX 0xf0 +#define MIDI_EOX 0xf7 +#define MIDI_START 0xFA +#define MIDI_STOP 0xFC +#define MIDI_CONTINUE 0xFB +#define MIDI_F9 0xF9 +#define MIDI_FD 0xFD +#define MIDI_RESET 0xFF +#define MIDI_NOTE_ON 0x90 +#define MIDI_NOTE_OFF 0x80 +#define MIDI_CHANNEL_AT 0xD0 +#define MIDI_POLY_AT 0xA0 +#define MIDI_PROGRAM 0xC0 +#define MIDI_CONTROL 0xB0 +#define MIDI_PITCHBEND 0xE0 +#define MIDI_MTC 0xF1 +#define MIDI_SONGPOS 0xF2 +#define MIDI_SONGSEL 0xF3 +#define MIDI_TUNE 0xF6 + +#define is_empty(midi) ((midi)->tail == (midi)->head) + +/* this is not static so that pm_init can set it directly if + * (see pmmac.c:pm_init()) + */ +int pm_initialized = FALSE; + +int pm_hosterror; +char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; + +#ifdef PM_CHECK_ERRORS + +#include <stdio.h> + +#define STRING_MAX 80 + +static void prompt_and_exit(void) +{ + char line[STRING_MAX]; + printf("type ENTER..."); + fgets(line, STRING_MAX, stdin); + /* this will clean up open ports: */ + exit(-1); +} + + +static PmError pm_errmsg(PmError err) +{ + if (err == pmHostError) { + /* it seems pointless to allocate memory and copy the string, + * so I will do the work of Pm_GetHostErrorText directly + */ + printf("PortMidi found host error...\n %s\n", pm_hosterror_text); + pm_hosterror = FALSE; + pm_hosterror_text[0] = 0; /* clear the message */ + prompt_and_exit(); + } else if (err < 0) { + printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); + prompt_and_exit(); + } + return err; +} +#else +#define pm_errmsg(err) err +#endif + +/* +==================================================================== +system implementation of portmidi interface +==================================================================== +*/ + +int pm_descriptor_max = 0; +int pm_descriptor_index = 0; +descriptor_type descriptors = NULL; + +/* pm_add_device -- describe interface/device pair to library + * + * This is called at intialization time, once for each + * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1) + * The strings are retained but NOT COPIED, so do not destroy them! + * + * returns pmInvalidDeviceId if device memory is exceeded + * otherwise returns pmNoError + */ +PmError pm_add_device(char *interf, char *name, int input, + void *descriptor, pm_fns_type dictionary) { + if (pm_descriptor_index >= pm_descriptor_max) { + // expand descriptors + descriptor_type new_descriptors = (descriptor_type) + pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32)); + if (!new_descriptors) return pmInsufficientMemory; + if (descriptors) { + memcpy(new_descriptors, descriptors, + sizeof(descriptor_node) * pm_descriptor_max); + free(descriptors); + } + pm_descriptor_max += 32; + descriptors = new_descriptors; + } + descriptors[pm_descriptor_index].pub.interf = interf; + descriptors[pm_descriptor_index].pub.name = name; + descriptors[pm_descriptor_index].pub.input = input; + descriptors[pm_descriptor_index].pub.output = !input; + + /* default state: nothing to close (for automatic device closing) */ + descriptors[pm_descriptor_index].pub.opened = FALSE; + + /* ID number passed to win32 multimedia API open */ + descriptors[pm_descriptor_index].descriptor = descriptor; + + /* points to PmInternal, allows automatic device closing */ + descriptors[pm_descriptor_index].internalDescriptor = NULL; + + descriptors[pm_descriptor_index].dictionary = dictionary; + + pm_descriptor_index++; + + return pmNoError; +} + + +/* utility to look up device, given a pattern, + note: pattern is modified + */ +int pm_find_default_device(char *pattern, int is_input) +{ + int id = pmNoDevice; + int i; + /* first parse pattern into name, interf parts */ + char *interf_pref = ""; /* initially assume it is not there */ + char *name_pref = strstr(pattern, ", "); + + if (name_pref) { /* found separator, adjust the pointer */ + interf_pref = pattern; + name_pref[0] = 0; + name_pref += 2; + } else { + name_pref = pattern; /* whole string is the name pattern */ + } + for (i = 0; i < pm_descriptor_index; i++) { + const PmDeviceInfo *info = Pm_GetDeviceInfo(i); + if (info->input == is_input && + strstr(info->name, name_pref) && + strstr(info->interf, interf_pref)) { + id = i; + break; + } + } + return id; +} + + +/* +==================================================================== +portmidi implementation +==================================================================== +*/ + +PMEXPORT int Pm_CountDevices( void ) { + Pm_Initialize(); + /* no error checking -- Pm_Initialize() does not fail */ + return pm_descriptor_index; +} + + +PMEXPORT const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) { + Pm_Initialize(); /* no error check needed */ + if (id >= 0 && id < pm_descriptor_index) { + return &descriptors[id].pub; + } + return NULL; +} + +/* pm_success_fn -- "noop" function pointer */ +PmError pm_success_fn(PmInternal *midi) { + return pmNoError; +} + +/* none_write -- returns an error if called */ +PmError none_write_short(PmInternal *midi, PmEvent *buffer) { + return pmBadPtr; +} + +/* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */ +PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) { + return pmBadPtr; +} + +PmError none_write_byte(PmInternal *midi, unsigned char byte, + PmTimestamp timestamp) { + return pmBadPtr; +} + +/* pm_fail_fn -- generic function, returns error if called */ +PmError pm_fail_fn(PmInternal *midi) { + return pmBadPtr; +} + +static PmError none_open(PmInternal *midi, void *driverInfo) { + return pmBadPtr; +} +static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) { + *msg = 0; // empty string +} +static unsigned int none_has_host_error(PmInternal * midi) { + return FALSE; +} +PmTimestamp none_synchronize(PmInternal *midi) { + return 0; +} + +#define none_abort pm_fail_fn +#define none_close pm_fail_fn + +pm_fns_node pm_none_dictionary = { + none_write_short, + none_sysex, + none_sysex, + none_write_byte, + none_write_short, + none_write_flush, + none_synchronize, + none_open, + none_abort, + none_close, + none_poll, + none_has_host_error, + none_get_host_error +}; + + +PMEXPORT const char *Pm_GetErrorText( PmError errnum ) { + const char *msg; + + switch(errnum) + { + case pmNoError: + msg = ""; + break; + case pmHostError: + msg = "PortMidi: `Host error'"; + break; + case pmInvalidDeviceId: + msg = "PortMidi: `Invalid device ID'"; + break; + case pmInsufficientMemory: + msg = "PortMidi: `Insufficient memory'"; + break; + case pmBufferTooSmall: + msg = "PortMidi: `Buffer too small'"; + break; + case pmBadPtr: + msg = "PortMidi: `Bad pointer'"; + break; + case pmInternalError: + msg = "PortMidi: `Internal PortMidi Error'"; + break; + case pmBufferOverflow: + msg = "PortMidi: `Buffer overflow'"; + break; + case pmBadData: + msg = "PortMidi: `Invalid MIDI message Data'"; + break; + case pmBufferMaxSize: + msg = "PortMidi: `Buffer cannot be made larger'"; + break; + default: + msg = "PortMidi: `Illegal error number'"; + break; + } + return msg; +} + + +/* This can be called whenever you get a pmHostError return value. + * The error will always be in the global pm_hosterror_text. + */ +PMEXPORT void Pm_GetHostErrorText(char * msg, unsigned int len) { + assert(msg); + assert(len > 0); + if (pm_hosterror) { + strncpy(msg, (char *) pm_hosterror_text, len); + pm_hosterror = FALSE; + pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it + might help with debugging */ + msg[len - 1] = 0; /* make sure string is terminated */ + } else { + msg[0] = 0; /* no string to return */ + } +} + + +PMEXPORT int Pm_HasHostError(PortMidiStream * stream) { + if (pm_hosterror) return TRUE; + if (stream) { + PmInternal * midi = (PmInternal *) stream; + pm_hosterror = (*midi->dictionary->has_host_error)(midi); + if (pm_hosterror) { + midi->dictionary->host_error(midi, pm_hosterror_text, + PM_HOST_ERROR_MSG_LEN); + /* now error message is global */ + return TRUE; + } + } + return FALSE; +} + + +PMEXPORT PmError Pm_Initialize( void ) { + if (!pm_initialized) { + pm_hosterror = FALSE; + pm_hosterror_text[0] = 0; /* the null string */ + pm_init(); + pm_initialized = TRUE; + } + return pmNoError; +} + + +PMEXPORT PmError Pm_Terminate( void ) { + if (pm_initialized) { + pm_term(); + // if there are no devices, descriptors might still be NULL + if (descriptors != NULL) { + free(descriptors); + descriptors = NULL; + } + pm_descriptor_index = 0; + pm_descriptor_max = 0; + pm_initialized = FALSE; + } + return pmNoError; +} + + +/* Pm_Read -- read up to length messages from source into buffer */ +/* + * returns number of messages actually read, or error code + */ +PMEXPORT int Pm_Read(PortMidiStream *stream, PmEvent *buffer, int32_t length) { + PmInternal *midi = (PmInternal *) stream; + int n = 0; + PmError err = pmNoError; + pm_hosterror = FALSE; + /* arg checking */ + if(midi == NULL) + err = pmBadPtr; + else if(!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + else if(!descriptors[midi->device_id].pub.input) + err = pmBadPtr; + /* First poll for data in the buffer... + * This either simply checks for data, or attempts first to fill the buffer + * with data from the MIDI hardware; this depends on the implementation. + * We could call Pm_Poll here, but that would redo a lot of redundant + * parameter checking, so I copied some code from Pm_Poll to here: */ + else err = (*(midi->dictionary->poll))(midi); + + if (err != pmNoError) { + if (err == pmHostError) { + midi->dictionary->host_error(midi, pm_hosterror_text, + PM_HOST_ERROR_MSG_LEN); + pm_hosterror = TRUE; + } + return pm_errmsg(err); + } + + while (n < length) { + PmError err = Pm_Dequeue(midi->queue, buffer++); + if (err == pmBufferOverflow) { + /* ignore the data we have retreived so far */ + return pm_errmsg(pmBufferOverflow); + } else if (err == 0) { /* empty queue */ + break; + } + n++; + } + return n; +} + +PMEXPORT PmError Pm_Poll( PortMidiStream *stream ) +{ + PmInternal *midi = (PmInternal *) stream; + PmError err; + + pm_hosterror = FALSE; + /* arg checking */ + if(midi == NULL) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.input) + err = pmBadPtr; + else + err = (*(midi->dictionary->poll))(midi); + + if (err != pmNoError) { + if (err == pmHostError) { + midi->dictionary->host_error(midi, pm_hosterror_text, + PM_HOST_ERROR_MSG_LEN); + pm_hosterror = TRUE; + } + return pm_errmsg(err); + } + + return !Pm_QueueEmpty(midi->queue); +} + + +/* this is called from Pm_Write and Pm_WriteSysEx to issue a + * call to the system-dependent end_sysex function and handle + * the error return + */ +static PmError pm_end_sysex(PmInternal *midi) +{ + PmError err = (*midi->dictionary->end_sysex)(midi, 0); + midi->sysex_in_progress = FALSE; + if (err == pmHostError) { + midi->dictionary->host_error(midi, pm_hosterror_text, + PM_HOST_ERROR_MSG_LEN); + pm_hosterror = TRUE; + } + return err; +} + + +/* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and + Pm_WriteSysEx all operate a state machine that "outputs" calls to + write_short, begin_sysex, write_byte, end_sysex, and write_realtime */ + +PMEXPORT PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, int32_t length) +{ + PmInternal *midi = (PmInternal *) stream; + PmError err = pmNoError; + int i; + int bits; + + pm_hosterror = FALSE; + /* arg checking */ + if(midi == NULL) + err = pmBadPtr; + else if(!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + else if(!descriptors[midi->device_id].pub.output) + err = pmBadPtr; + else + err = pmNoError; + + if (err != pmNoError) goto pm_write_error; + + if (midi->latency == 0) { + midi->now = 0; + } else { + midi->now = (*(midi->time_proc))(midi->time_info); + if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) { + /* time to resync */ + midi->now = (*midi->dictionary->synchronize)(midi); + midi->first_message = FALSE; + } + } + /* error recovery: when a sysex is detected, we call + * dictionary->begin_sysex() followed by calls to + * dictionary->write_byte() and dictionary->write_realtime() + * until an end-of-sysex is detected, when we call + * dictionary->end_sysex(). After an error occurs, + * Pm_Write() continues to call functions. For example, + * it will continue to call write_byte() even after + * an error sending a sysex message, and end_sysex() will be + * called when an EOX or non-real-time status is found. + * When errors are detected, Pm_Write() returns immediately, + * so it is possible that this will drop data and leave + * sysex messages in a partially transmitted state. + */ + for (i = 0; i < length; i++) { + uint32_t msg = buffer[i].message; + bits = 0; + /* is this a sysex message? */ + if (Pm_MessageStatus(msg) == MIDI_SYSEX) { + if (midi->sysex_in_progress) { + /* error: previous sysex was not terminated by EOX */ + midi->sysex_in_progress = FALSE; + err = pmBadData; + goto pm_write_error; + } + midi->sysex_in_progress = TRUE; + if ((err = (*midi->dictionary->begin_sysex)(midi, + buffer[i].timestamp)) != pmNoError) + goto pm_write_error; + if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX, + buffer[i].timestamp)) != pmNoError) + goto pm_write_error; + bits = 8; + /* fall through to continue sysex processing */ + } else if ((msg & MIDI_STATUS_MASK) && + (Pm_MessageStatus(msg) != MIDI_EOX)) { + /* a non-sysex message */ + if (midi->sysex_in_progress) { + /* this should be a realtime message */ + if (is_real_time(msg)) { + if ((err = (*midi->dictionary->write_realtime)(midi, + &(buffer[i]))) != pmNoError) + goto pm_write_error; + } else { + midi->sysex_in_progress = FALSE; + err = pmBadData; + /* ignore any error from this, because we already have one */ + /* pass 0 as timestamp -- it's ignored */ + (*midi->dictionary->end_sysex)(midi, 0); + goto pm_write_error; + } + } else { /* regular short midi message */ + if ((err = (*midi->dictionary->write_short)(midi, + &(buffer[i]))) != pmNoError) + goto pm_write_error; + continue; + } + } + if (midi->sysex_in_progress) { /* send sysex bytes until EOX */ + /* see if we can accelerate data transfer */ + if (bits == 0 && midi->fill_base && /* 4 bytes to copy */ + (*midi->fill_offset_ptr) + 4 <= midi->fill_length && + (msg & 0x80808080) == 0) { /* all data */ + /* copy 4 bytes from msg to fill_base + fill_offset */ + unsigned char *ptr = midi->fill_base + + *(midi->fill_offset_ptr); + ptr[0] = msg; ptr[1] = msg >> 8; + ptr[2] = msg >> 16; ptr[3] = msg >> 24; + (*midi->fill_offset_ptr) += 4; + continue; + } + /* no acceleration, so do byte-by-byte copying */ + while (bits < 32) { + unsigned char midi_byte = (unsigned char) (msg >> bits); + if ((err = (*midi->dictionary->write_byte)(midi, midi_byte, + buffer[i].timestamp)) != pmNoError) + goto pm_write_error; + if (midi_byte == MIDI_EOX) { + err = pm_end_sysex(midi); + if (err != pmNoError) goto error_exit; + break; /* from while loop */ + } + bits += 8; + } + } else { + /* not in sysex mode, but message did not start with status */ + err = pmBadData; + goto pm_write_error; + } + } + /* after all messages are processed, send the data */ + if (!midi->sysex_in_progress) + err = (*midi->dictionary->write_flush)(midi, 0); +pm_write_error: + if (err == pmHostError) { + midi->dictionary->host_error(midi, pm_hosterror_text, + PM_HOST_ERROR_MSG_LEN); + pm_hosterror = TRUE; + } +error_exit: + return pm_errmsg(err); +} + + +PMEXPORT PmError Pm_WriteShort(PortMidiStream *stream, PmTimestamp when, PmMessage msg) +{ + PmEvent event; + + event.timestamp = when; + event.message = msg; + return Pm_Write(stream, &event, 1); +} + + +PMEXPORT PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, + unsigned char *msg) +{ + /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */ + /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */ + #define BUFLEN ((int) (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage))) + PmEvent buffer[BUFLEN]; + int buffer_size = 1; /* first time, send 1. After that, it's BUFLEN */ + PmInternal *midi = (PmInternal *) stream; + /* the next byte in the buffer is represented by an index, bufx, and + a shift in bits */ + int shift = 0; + int bufx = 0; + buffer[0].message = 0; + buffer[0].timestamp = when; + + while (1) { + /* insert next byte into buffer */ + buffer[bufx].message |= ((*msg) << shift); + shift += 8; + if (*msg++ == MIDI_EOX) break; + if (shift == 32) { + shift = 0; + bufx++; + if (bufx == buffer_size) { + PmError err = Pm_Write(stream, buffer, buffer_size); + /* note: Pm_Write has already called errmsg() */ + if (err) return err; + /* prepare to fill another buffer */ + bufx = 0; + buffer_size = BUFLEN; + /* optimization: maybe we can just copy bytes */ + if (midi->fill_base) { + PmError err; + while (*(midi->fill_offset_ptr) < midi->fill_length) { + midi->fill_base[(*midi->fill_offset_ptr)++] = *msg; + if (*msg++ == MIDI_EOX) { + err = pm_end_sysex(midi); + if (err != pmNoError) return pm_errmsg(err); + goto end_of_sysex; + } + } + /* I thought that I could do a pm_Write here and + * change this if to a loop, avoiding calls in Pm_Write + * to the slower write_byte, but since + * sysex_in_progress is true, this will not flush + * the buffer, and we'll infinite loop: */ + /* err = Pm_Write(stream, buffer, 0); + if (err) return err; */ + /* instead, the way this works is that Pm_Write calls + * write_byte on 4 bytes. The first, since the buffer + * is full, will flush the buffer and allocate a new + * one. This primes the buffer so + * that we can return to the loop above and fill it + * efficiently without a lot of function calls. + */ + buffer_size = 1; /* get another message started */ + } + } + buffer[bufx].message = 0; + buffer[bufx].timestamp = when; + } + /* keep inserting bytes until you find MIDI_EOX */ + } +end_of_sysex: + /* we're finished sending full buffers, but there may + * be a partial one left. + */ + if (shift != 0) bufx++; /* add partial message to buffer len */ + if (bufx) { /* bufx is number of PmEvents to send from buffer */ + PmError err = Pm_Write(stream, buffer, bufx); + if (err) return err; + } + return pmNoError; +} + + + +PMEXPORT PmError Pm_OpenInput(PortMidiStream** stream, + PmDeviceID inputDevice, + void *inputDriverInfo, + int32_t bufferSize, + PmTimeProcPtr time_proc, + void *time_info) +{ + PmInternal *midi; + PmError err = pmNoError; + pm_hosterror = FALSE; + *stream = NULL; + + /* arg checking */ + if (inputDevice < 0 || inputDevice >= pm_descriptor_index) + err = pmInvalidDeviceId; + else if (!descriptors[inputDevice].pub.input) + err = pmInvalidDeviceId; + else if(descriptors[inputDevice].pub.opened) + err = pmInvalidDeviceId; + + if (err != pmNoError) + goto error_return; + + /* create portMidi internal data */ + midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); + *stream = midi; + if (!midi) { + err = pmInsufficientMemory; + goto error_return; + } + midi->device_id = inputDevice; + midi->write_flag = FALSE; + midi->time_proc = time_proc; + midi->time_info = time_info; + /* windows adds timestamps in the driver and these are more accurate than + using a time_proc, so do not automatically provide a time proc. Non-win + implementations may want to provide a default time_proc in their + system-specific midi_out_open() method. + */ + if (bufferSize <= 0) bufferSize = 256; /* default buffer size */ + midi->queue = Pm_QueueCreate(bufferSize, (int32_t) sizeof(PmEvent)); + if (!midi->queue) { + /* free portMidi data */ + *stream = NULL; + pm_free(midi); + err = pmInsufficientMemory; + goto error_return; + } + midi->buffer_len = bufferSize; /* portMidi input storage */ + midi->latency = 0; /* not used */ + midi->sysex_in_progress = FALSE; + midi->sysex_message = 0; + midi->sysex_message_count = 0; + midi->filters = PM_FILT_ACTIVE; + midi->channel_mask = 0xFFFF; + midi->sync_time = 0; + midi->first_message = TRUE; + midi->dictionary = descriptors[inputDevice].dictionary; + midi->fill_base = NULL; + midi->fill_offset_ptr = NULL; + midi->fill_length = 0; + descriptors[inputDevice].internalDescriptor = midi; + /* open system dependent input device */ + err = (*midi->dictionary->open)(midi, inputDriverInfo); + if (err) { + *stream = NULL; + descriptors[inputDevice].internalDescriptor = NULL; + /* free portMidi data */ + Pm_QueueDestroy(midi->queue); + pm_free(midi); + } else { + /* portMidi input open successful */ + descriptors[inputDevice].pub.opened = TRUE; + } +error_return: + /* note: if there is a pmHostError, it is the responsibility + * of the system-dependent code (*midi->dictionary->open)() + * to set pm_hosterror and pm_hosterror_text + */ + return pm_errmsg(err); +} + + +PMEXPORT PmError Pm_OpenOutput(PortMidiStream** stream, + PmDeviceID outputDevice, + void *outputDriverInfo, + int32_t bufferSize, + PmTimeProcPtr time_proc, + void *time_info, + int32_t latency ) +{ + PmInternal *midi; + PmError err = pmNoError; + pm_hosterror = FALSE; + *stream = NULL; + + /* arg checking */ + if (outputDevice < 0 || outputDevice >= pm_descriptor_index) + err = pmInvalidDeviceId; + else if (!descriptors[outputDevice].pub.output) + err = pmInvalidDeviceId; + else if (descriptors[outputDevice].pub.opened) + err = pmInvalidDeviceId; + if (err != pmNoError) + goto error_return; + + /* create portMidi internal data */ + midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); + *stream = midi; + if (!midi) { + err = pmInsufficientMemory; + goto error_return; + } + midi->device_id = outputDevice; + midi->write_flag = TRUE; + midi->time_proc = time_proc; + /* if latency > 0, we need a time reference. If none is provided, + use PortTime library */ + if (time_proc == NULL && latency != 0) { + if (!Pt_Started()) + Pt_Start(1, 0, 0); + /* time_get does not take a parameter, so coerce */ + midi->time_proc = (PmTimeProcPtr) Pt_Time; + } + midi->time_info = time_info; + midi->buffer_len = bufferSize; + midi->queue = NULL; /* unused by output */ + /* if latency zero, output immediate (timestamps ignored) */ + /* if latency < 0, use 0 but don't return an error */ + if (latency < 0) latency = 0; + midi->latency = latency; + midi->sysex_in_progress = FALSE; + midi->sysex_message = 0; /* unused by output */ + midi->sysex_message_count = 0; /* unused by output */ + midi->filters = 0; /* not used for output */ + midi->channel_mask = 0xFFFF; + midi->sync_time = 0; + midi->first_message = TRUE; + midi->dictionary = descriptors[outputDevice].dictionary; + midi->fill_base = NULL; + midi->fill_offset_ptr = NULL; + midi->fill_length = 0; + descriptors[outputDevice].internalDescriptor = midi; + /* open system dependent output device */ + err = (*midi->dictionary->open)(midi, outputDriverInfo); + if (err) { + *stream = NULL; + descriptors[outputDevice].internalDescriptor = NULL; + /* free portMidi data */ + pm_free(midi); + } else { + /* portMidi input open successful */ + descriptors[outputDevice].pub.opened = TRUE; + } +error_return: + /* note: system-dependent code must set pm_hosterror and + * pm_hosterror_text if a pmHostError occurs + */ + return pm_errmsg(err); +} + + +PMEXPORT PmError Pm_SetChannelMask(PortMidiStream *stream, int mask) +{ + PmInternal *midi = (PmInternal *) stream; + PmError err = pmNoError; + + if (midi == NULL) + err = pmBadPtr; + else + midi->channel_mask = mask; + + return pm_errmsg(err); +} + + +PMEXPORT PmError Pm_SetFilter(PortMidiStream *stream, int32_t filters) { + PmInternal *midi = (PmInternal *) stream; + PmError err = pmNoError; + + /* arg checking */ + if (midi == NULL) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + else + midi->filters = filters; + return pm_errmsg(err); +} + + +PMEXPORT PmError Pm_Close( PortMidiStream *stream ) { + PmInternal *midi = (PmInternal *) stream; + PmError err = pmNoError; + + pm_hosterror = FALSE; + /* arg checking */ + if (midi == NULL) /* midi must point to something */ + err = pmBadPtr; + /* if it is an open device, the device_id will be valid */ + else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index) + err = pmBadPtr; + /* and the device should be in the opened state */ + else if (!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + + if (err != pmNoError) + goto error_return; + + /* close the device */ + err = (*midi->dictionary->close)(midi); + /* even if an error occurred, continue with cleanup */ + descriptors[midi->device_id].internalDescriptor = NULL; + descriptors[midi->device_id].pub.opened = FALSE; + if (midi->queue) Pm_QueueDestroy(midi->queue); + pm_free(midi); +error_return: + /* system dependent code must set pm_hosterror and + * pm_hosterror_text if a pmHostError occurs. + */ + return pm_errmsg(err); +} + +PmError Pm_Synchronize( PortMidiStream* stream ) { + PmInternal *midi = (PmInternal *) stream; + PmError err = pmNoError; + if (midi == NULL) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.output) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + else + midi->first_message = TRUE; + return err; +} + +PMEXPORT PmError Pm_Abort( PortMidiStream* stream ) { + PmInternal *midi = (PmInternal *) stream; + PmError err; + /* arg checking */ + if (midi == NULL) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.output) + err = pmBadPtr; + else if (!descriptors[midi->device_id].pub.opened) + err = pmBadPtr; + else + err = (*midi->dictionary->abort)(midi); + + if (err == pmHostError) { + midi->dictionary->host_error(midi, pm_hosterror_text, + PM_HOST_ERROR_MSG_LEN); + pm_hosterror = TRUE; + } + return pm_errmsg(err); +} + + + +/* pm_channel_filtered returns non-zero if the channel mask is blocking the current channel */ +#define pm_channel_filtered(status, mask) \ + ((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask)))) + + +/* The following two functions will checks to see if a MIDI message matches + the filtering criteria. Since the sysex routines only want to filter realtime messages, + we need to have separate routines. + */ + + +/* pm_realtime_filtered returns non-zero if the filter will kill the current message. + Note that only realtime messages are checked here. + */ +#define pm_realtime_filtered(status, filters) \ + ((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters))) + +/* + return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE)) + || ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK)) + || ((status == MIDI_START) && (filters & PM_FILT_PLAY)) + || ((status == MIDI_STOP) && (filters & PM_FILT_PLAY)) + || ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY)) + || ((status == MIDI_F9) && (filters & PM_FILT_F9)) + || ((status == MIDI_FD) && (filters & PM_FILT_FD)) + || ((status == MIDI_RESET) && (filters & PM_FILT_RESET)) + || ((status == MIDI_MTC) && (filters & PM_FILT_MTC)) + || ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION)) + || ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT)) + || ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE)); +}*/ + + +/* pm_status_filtered returns non-zero if a filter will kill the current message, based on status. + Note that sysex and real time are not checked. It is up to the subsystem (winmm, core midi, alsa) + to filter sysex, as it is handled more easily and efficiently at that level. + Realtime message are filtered in pm_realtime_filtered. + */ +#define pm_status_filtered(status, filters) ((1 << (16 + ((status) >> 4))) & (filters)) + + +/* + return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE)) + || ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE)) + || ((status == MIDI_CHANNEL_AT) && (filters & PM_FILT_CHANNEL_AFTERTOUCH)) + || ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH)) + || ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM)) + || ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL)) + || ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND)); + +} +*/ + +static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp) +{ + PmEvent event; + + /* there may be nothing in the buffer */ + if (midi->sysex_message_count == 0) return; /* nothing to flush */ + + event.message = midi->sysex_message; + event.timestamp = timestamp; + /* copied from pm_read_short, avoids filtering */ + if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { + midi->sysex_in_progress = FALSE; + } + midi->sysex_message_count = 0; + midi->sysex_message = 0; +} + + +/* pm_read_short and pm_read_bytes + are the interface between system-dependent MIDI input handlers + and the system-independent PortMIDI code. + The input handler MUST obey these rules: + 1) all short input messages must be sent to pm_read_short, which + enqueues them to a FIFO for the application. + 2) each buffer of sysex bytes should be reported by calling pm_read_bytes + (which sets midi->sysex_in_progress). After the eox byte, + pm_read_bytes will clear sysex_in_progress + */ + +/* pm_read_short is the place where all input messages arrive from + system-dependent code such as pmwinmm.c. Here, the messages + are entered into the PortMidi input buffer. + */ +void pm_read_short(PmInternal *midi, PmEvent *event) +{ + int status; + /* arg checking */ + assert(midi != NULL); + /* midi filtering is applied here */ + status = Pm_MessageStatus(event->message); + if (!pm_status_filtered(status, midi->filters) + && (!is_real_time(status) || + !pm_realtime_filtered(status, midi->filters)) + && !pm_channel_filtered(status, midi->channel_mask)) { + /* if sysex is in progress and we get a status byte, it had + better be a realtime message or the starting SYSEX byte; + otherwise, we exit the sysex_in_progress state + */ + if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) { + /* two choices: real-time or not. If it's real-time, then + * this should be delivered as a sysex byte because it is + * embedded in a sysex message + */ + if (is_real_time(status)) { + midi->sysex_message |= + (status << (8 * midi->sysex_message_count++)); + if (midi->sysex_message_count == 4) { + pm_flush_sysex(midi, event->timestamp); + } + } else { /* otherwise, it's not real-time. This interrupts + * a sysex message in progress */ + midi->sysex_in_progress = FALSE; + } + } else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) { + midi->sysex_in_progress = FALSE; + } + } +} + +/* pm_read_bytes -- read one (partial) sysex msg from MIDI data */ +/* + * returns how many bytes processed + */ +unsigned int pm_read_bytes(PmInternal *midi, const unsigned char *data, + int len, PmTimestamp timestamp) +{ + int i = 0; /* index into data, must not be unsigned (!) */ + PmEvent event; + event.timestamp = timestamp; + assert(midi); + /* note that since buffers may not have multiples of 4 bytes, + * pm_read_bytes may be called in the middle of an outgoing + * 4-byte PortMidi message. sysex_in_progress indicates that + * a sysex has been sent but no eox. + */ + if (len == 0) return 0; /* sanity check */ + if (!midi->sysex_in_progress) { + while (i < len) { /* process all data */ + unsigned char byte = data[i++]; + if (byte == MIDI_SYSEX && + !pm_realtime_filtered(byte, midi->filters)) { + midi->sysex_in_progress = TRUE; + i--; /* back up so code below will get SYSEX byte */ + break; /* continue looping below to process msg */ + } else if (byte == MIDI_EOX) { + midi->sysex_in_progress = FALSE; + return i; /* done with one message */ + } else if (byte & MIDI_STATUS_MASK) { + /* We're getting MIDI but no sysex in progress. + * Either the SYSEX status byte was dropped or + * the message was filtered. Drop the data, but + * send any embedded realtime bytes. + */ + /* assume that this is a real-time message: + * it is an error to pass non-real-time messages + * to pm_read_bytes + */ + event.message = byte; + pm_read_short(midi, &event); + } + } /* all bytes in the buffer are processed */ + } + /* Now, i<len implies sysex_in_progress. If sysex_in_progress + * becomes false in the loop, there must have been an overflow + * and we can just drop all remaining bytes + */ + while (i < len && midi->sysex_in_progress) { + if (midi->sysex_message_count == 0 && i <= len - 4 && + ((event.message = (((PmMessage) data[i]) | + (((PmMessage) data[i+1]) << 8) | + (((PmMessage) data[i+2]) << 16) | + (((PmMessage) data[i+3]) << 24))) & + 0x80808080) == 0) { /* all data, no status */ + if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { + midi->sysex_in_progress = FALSE; + } + i += 4; + } else { + while (i < len) { + /* send one byte at a time */ + unsigned char byte = data[i++]; + if (is_real_time(byte) && + pm_realtime_filtered(byte, midi->filters)) { + continue; /* real-time data is filtered, so omit */ + } + midi->sysex_message |= + (byte << (8 * midi->sysex_message_count++)); + if (byte == MIDI_EOX) { + midi->sysex_in_progress = FALSE; + pm_flush_sysex(midi, event.timestamp); + return i; + } else if (midi->sysex_message_count == 4) { + pm_flush_sysex(midi, event.timestamp); + /* after handling at least one non-data byte + * and reaching a 4-byte message boundary, + * resume trying to send 4 at a time in outer loop + */ + break; + } + } + } + } + return i; +} + + diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/Makefile.osx b/libs/backends/wavesaudio/portmidi/src/pm_mac/Makefile.osx new file mode 100644 index 0000000000..3832554812 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/Makefile.osx @@ -0,0 +1,129 @@ +# MAKEFILE FOR PORTMIDI
+
+# Roger B. Dannenberg
+# Sep 2009
+
+# NOTE: you can use
+# make -f pm_osx/Makefile.osx configuration=Release
+# to override the default Debug configuration
+configuration=Release
+
+PF=/usr/local
+
+# For debugging, define PM_CHECK_ERRORS
+ifeq ($(configuration),Release)
+ CONFIG = Release
+else
+ CONFIG = Debug
+endif
+
+current: all
+
+all: $(CONFIG)/CMakeCache.txt
+ cd $(CONFIG); make
+
+$(CONFIG)/CMakeCache.txt:
+ rm -f CMakeCache.txt
+ mkdir -p $(CONFIG)
+ cd $(CONFIG); cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=$(CONFIG)
+
+
+**** For instructions: make -f pm_mac\Makefile.osx help ****\n'
+
+help:
+ echo $$'\n\n\
+This is help for portmidi/pm_mac/Makefile.osx\n\n\
+Installation path for dylib is $(PF)\n\
+To build Release version libraries and test applications,\n \
+make -f pm_mac/Makefile.osx\n\
+To build Debug version libraries and test applications,\n \
+make -f pm_mac/Makefile.osx configuration=Debug\n\
+To install universal dynamic library,\n \
+sudo make -f pm_mac/Makefile.osx install\n\
+To install universal dynamic library with xcode,\n \
+make -f pm_mac/Makefile.osx install-with-xcode\n\
+To make PmDefaults Java application,\n \
+make -f pm_mac/Makefile.osx pmdefaults\n\n \
+configuration = $(configuration)\n'
+
+
+clean:
+ rm -f *.o *~ core* */*.o */*/*.o */*~ */core* pm_test/*/pm_dll.dll
+ rm -f *.opt *.ncb *.plg pm_win/Debug/pm_dll.lib pm_win/Release/pm_dll.lib
+ rm -f pm_test/*.opt pm_test/*.ncb
+ rm -f pm_java/pmjni/*.o pm_java/pmjni/*~ pm_java/*.h
+ rm -rf Release/CMakeFiles Debug/CMakeFiles
+ rm -rf pm_mac/pmdefaults/lib pm_mac/pmdefaults/src
+
+cleaner: clean
+ rm -rf pm_mac/build
+ rm -rf pm_mac/Debug pm_mac/Release pm_test/Debug pm_test/Release
+ rm -f Debug/*.dylib Release/*.dylib
+ rm -f pm_java/pmjni/Debug/*.jnilib
+ rm -f pm_java/pmjni/Release/*.jnilib
+
+cleanest: cleaner
+ rm -f Debug/libportmidi_s.a Release/libportmidi_s.a
+ rm -f pm_test/Debug/test pm_test/Debug/sysex pm_test/Debug/midithread
+ rm -f pm_test/Debug/latency pm_test/Debug/midithru
+ rm -f pm_test/Debug/qtest pm_test/Debug/mm
+ rm -f pm_test/Release/test pm_test/Release/sysex pm_test/Release/midithread
+ rm -f pm_test/Release/latency pm_test/Release/midithru
+ rm -f pm_test/Release/qtest pm_test/Release/mm
+ rm -f pm_java/*/*.class
+ rm -f pm_java/pmjni/jportmidi_JPortMidiApi_PortMidiStream.h
+
+backup: cleanest
+ cd ..; zip -r portmidi.zip portmidi
+
+install: porttime/porttime.h pm_common/portmidi.h \
+ $(CONFIG)/libportmidi.dylib
+ install porttime/porttime.h $(PF)/include/
+ install pm_common/portmidi.h $(PF)/include
+ install $(CONFIG)/libportmidi.dylib $(PF)/lib/
+
+# note - this uses xcode to build and install portmidi universal binaries
+install-with-xcode:
+ sudo xcodebuild -project pm_mac/pm_mac.xcodeproj \
+ -configuration Release install DSTROOT=/
+
+##### build pmdefault ######
+
+pm_java/pmjni/jportmidi_JPortMidiApi.h: pm_java/jportmidi/JPortMidiApi.class
+ cd pm_java; javah jportmidi.JPortMidiApi
+ mv pm_java/jportmidi_JportMidiApi.h pm_java/pmjni
+
+JAVASRC = pmdefaults/PmDefaultsFrame.java \
+ pmdefaults/PmDefaults.java \
+ jportmidi/JPortMidiApi.java jportmidi/JPortMidi.java \
+ jportmidi/JPortMidiException.java
+
+# this compiles ALL of the java code
+pm_java/jportmidi/JPortMidiApi.class: $(JAVASRC:%=pm_java/%)
+ cd pm_java; javac $(JAVASRC)
+
+$(CONFIG)/libpmjni.dylib:
+ mkdir -p $(CONFIG)
+ cd $(CONFIG); make -f ../pm_mac/$(MAKEFILE)
+
+pmdefaults: $(CONFIG)/libpmjni.dylib pm_java/jportmidi/JPortMidiApi.class
+ifeq ($(CONFIG),Debug)
+ echo "Error: you cannot build pmdefaults in a Debug configuration \n\
+ You should use configuration=Release in the Makefile command line. "
+ @exit 2
+endif
+ xcodebuild -project pm_mac/pm_mac.xcodeproj \
+ -configuration Release -target PmDefaults
+ echo "pmdefaults java application is made"
+
+###### test plist reader #######
+PLHDR = pm_mac/readbinaryplist.h
+PLSRC = pm_mac/plisttest.c pm_mac/readbinaryplist.c
+pm_mac/plisttest: $(PLHDR) $(PLSRC)
+ cc $(VFLAGS) -Ipm_mac \
+ -I/Developer/Headers/FlatCarbon \
+ -I/System/Library/Frameworks/CoreFoundation.framework/Headers \
+ -I/System/Library/Frameworks/CoreServices.framework/Headers \
+ $(PLSRC) -o pm_mac/$(CONFIG)/plisttest \
+ -framework CoreFoundation -framework CoreServices
+
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/README_MAC.txt b/libs/backends/wavesaudio/portmidi/src/pm_mac/README_MAC.txt new file mode 100644 index 0000000000..6fa6938402 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/README_MAC.txt @@ -0,0 +1,163 @@ +README_MAC.txt for PortMidi
+Roger Dannenberg
+20 nov 2009
+revised 20 Sep 2010 for Xcode 3.2.4 and CMake 8.2-2
+
+To build PortMidi for Mac OS X, you must install Xcode and
+CMake.
+
+CMake can build either command-line Makefiles or Xcode projects.
+These approaches are described in separate sections below.
+
+==== CLEANING UP ====
+(Skip this for now, but later you might want start from a clean
+slate.)
+
+Start in the portmedia/portmidi directory.
+
+make -f pm_mac/Makefile.osx clean
+
+will remove .o, CMakeFiles, and other intermediate files.
+
+Using "cleaner" instead of "clean" will also remove jni-related
+intermediate files.
+
+Using "cleanest" instead of "clean" or "cleaner" will also remove
+application binaries and the portmidi libraries. (It will not
+uninstall anything, however.)
+
+==== USING CMAKE (AND COMMAND LINE TOOLS) ====
+
+Start in the portmedia/portmidi directory.
+
+make -f pm_mac/Makefile.osx
+
+(Begin note: make will invoke cmake to build a Makefile and then make to
+build portmidi. This extra level allows you to correctly build
+both Release and Debug versions. Release is the default, so to get
+the Debug version, use:
+
+make -f pm_mac/Makefile.osx configuration=Debug
+)
+
+Release version executables and libraries are now in
+ portmedia/portmidi/Release
+
+Debug version executables and libraries are created in
+ portmedia/portmidi/Debug
+The Debug versions are compiled with PM_CHECK_ERRORS which
+prints an error message and aborts when an error code is returned
+by PortMidi functions. This is useful for small command line
+applications. Otherwise, you should check and handle error returns
+in your program.
+
+You can install portmidi as follows:
+
+cd Release; sudo make install
+
+This will install /usr/local/include/{portmidi.h, porttime.h}
+and /usr/local/lib/{libportmidi.dylib, libportmidi_s.a, libpmjni.dylib}
+
+You should now make the pmdefaults.app:
+
+make -f pm_mac/Makefile.osx pmdefaults
+
+NOTE: pmdefaults.app will be in pm_mac/Release/.
+
+Please copy pmdefaults.app to your Applications folder or wherever
+you would normally expect to find it.
+
+==== USING CMAKE TO BUILD Xcode PROJECT ====
+
+Before you can use Xcode, you need a portmidi.xcodeproj file.
+CMake builds a location-dependent Xcode project, so unfortunately
+it is not easy to provide an Xcode project that is ready to use.
+Therefore, you should make your own. Once you have it, you can
+use it almost like any other Xcode project, and you will not have
+to go back to CMake.
+
+(1) Install CMake if you do not have it already.
+
+(2) Open portmedia/portmidi/CMakeLists.txt with CMake
+
+(3) Use Configure and Generate buttons
+
+(4) This creates portmedia/portmidi/portmidi.xcodeproj.
+
+Note: You will also use pm_mac/pm_mac.xcodeproj, which
+is not generated by CMake.
+
+(5) Open portmidi/portmidi.xcodeproj with Xcode and
+build what you need. The simplest thing is to build the
+ALL_BUILD target. The default will be to build the Debug
+version, but you may want to change this to Release.
+
+NOTE: ALL_BUILD may report errors. Try simply building again
+or rebuilding specific targets that fail until they build
+without errors. There appears to be a race condition or
+missing dependencies in the build system.
+
+The Debug version is compiled with PM_CHECK_ERRORS, and the
+Release version is not. PM_CHECK_ERRORS will print an error
+message and exit your program if any error is returned from
+a call into PortMidi.
+
+CMake (currently) also creates MinSizRel and RelWithDebInfo
+versions, but only because I cannot figure out how to disable
+them.
+
+You will probably want the application PmDefaults, which sets
+default MIDI In and Out devices for PortMidi. You may also
+want to build a Java application using PortMidi. Since I have
+not figured out how to use CMake to make an OS X Java application,
+use pm_mac/pm_mac.xcodeproj as follows:
+
+(6) open pm_mac/pm_mac.xcodeproj
+
+(7) pm_java/pmjni/portmidi_JportmidiApi.h is needed
+by libpmjni.jnilib, the Java native interface library. Since
+portmidi_JportmidiApi.h is included with PortMidi, you can skip
+to step 8, but if you really want to rebuild everything from
+scratch, build the JPortMidiHeaders project first, and continue
+with step 8:
+
+(8) If you did not build libpmjni.dylib using portmidi.xcodeproj,
+do it now. (It depends on portmidi_JportmidiApi.h, and the
+PmDefaults project depends on libpmjni.dylib.)
+
+(9) Returning to pm_mac.xcodeproj, build the PmDefaults program.
+
+(10) If you wish, copy pm_mac/build/Deployment/PmDefaults.app to
+your applications folder.
+
+(11) If you want to install libportmidi.dylib, first make it with
+Xcode, then
+ sudo make -f pm_mac/Makefile.osx install
+This command will install /usr/local/include/{porttime.h, portmidi.h}
+and /usr/local/lib/libportmidi.dylib
+Note that the "install" function of xcode creates portmidi/Release
+and does not install the library to /usr/local/lib, so please use
+the command line installer.
+
+
+CHANGELOG
+
+20-Sep-2010 Roger B. Dannenberg
+ Adapted to Xcode 3.2.4
+20-Nov-2009 Roger B. Dannenberg
+ Added some install instructions
+26-Sep-2009 Roger B. Dannenberg
+ More changes for using CMake, Makefiles, XCode
+20-Sep-2009 Roger B. Dannenberg
+ Modifications for using CMake
+14-Sep-2009 Roger B. Dannenberg
+ Modifications for using CMake
+17-Jan-2007 Roger B. Dannenberg
+ Explicit instructions for Xcode
+15-Jan-2007 Roger B. Dannenberg
+ Changed instructions because of changes to Makefile.osx
+07-Oct-2006 Roger B. Dannenberg
+ Added directions for xcodebuild
+29-aug-2006 Roger B. Dannenberg
+ Updated this documentation.
+
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/finddefault.c b/libs/backends/wavesaudio/portmidi/src/pm_mac/finddefault.c new file mode 100644 index 0000000000..59e02a10be --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/finddefault.c @@ -0,0 +1,57 @@ +/* finddefault.c -- find_default_device() implementation + Roger Dannenberg, June 2008 +*/ + +#include <stdlib.h> +#include <string.h> +#include "portmidi.h" +#include "pmutil.h" +#include "pminternal.h" +#include "pmmacosxcm.h" +#include "readbinaryplist.h" + +/* Parse preference files, find default device, search devices -- + This parses the preference file(s) once for input and once for + output, which is inefficient but much simpler to manage. Note + that using the readbinaryplist.c module, you cannot keep two + plist files (user and system) open at once (due to a simple + memory management scheme). +*/ +PmDeviceID find_default_device(char *path, int input, PmDeviceID id) +/* path -- the name of the preference we are searching for + input -- true iff this is an input device + id -- current default device id + returns matching device id if found, otherwise id +*/ +{ + static char *pref_file = "com.apple.java.util.prefs.plist"; + char *pref_str = NULL; + // read device preferences + value_ptr prefs = bplist_read_user_pref(pref_file); + if (prefs) { + value_ptr pref_val = value_dict_lookup_using_path(prefs, path); + if (pref_val) { + pref_str = value_get_asciistring(pref_val); + } + } + if (!pref_str) { + bplist_free_data(); /* look elsewhere */ + prefs = bplist_read_system_pref(pref_file); + if (prefs) { + value_ptr pref_val = value_dict_lookup_using_path(prefs, path); + if (pref_val) { + pref_str = value_get_asciistring(pref_val); + } + } + } + if (pref_str) { /* search devices for match */ + int i = pm_find_default_device(pref_str, input); + if (i != pmNoDevice) { + id = i; + } + } + if (prefs) { + bplist_free_data(); + } + return id; +} diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.pbxproj b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..0d06e565ea --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.pbxproj @@ -0,0 +1,594 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 44; + objects = { + +/* Begin PBXAggregateTarget section */ + 3D634CAB1247805C0020F829 /* JPortMidiHeaders */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 3D634CAE1247807A0020F829 /* Build configuration list for PBXAggregateTarget "JPortMidiHeaders" */; + buildPhases = ( + 3D634CAA1247805C0020F829 /* ShellScript */, + ); + dependencies = ( + 3D634CB0124781580020F829 /* PBXTargetDependency */, + ); + name = JPortMidiHeaders; + productName = JPortMidiHeaders; + }; + 3DE2142D124662AA0033C839 /* CopyJavaSources */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 3DE21434124662FF0033C839 /* Build configuration list for PBXAggregateTarget "CopyJavaSources" */; + buildPhases = ( + 3DE2142C124662AA0033C839 /* CopyFiles */, + ); + comments = "The reason for copying files here is that the Compile Java target looks in a particular place for sources. It would be much better to simply have Compile Java look in the original location for all sources, but I don't know how to do that. -RBD\n"; + dependencies = ( + ); + name = CopyJavaSources; + productName = CopyJavaSources; + }; + 89D0F1C90F3B704E007831A7 /* PmDefaults */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 89D0F1D20F3B7080007831A7 /* Build configuration list for PBXAggregateTarget "PmDefaults" */; + buildPhases = ( + ); + dependencies = ( + 89D0F1D10F3B7062007831A7 /* PBXTargetDependency */, + 89D0F1CD0F3B7062007831A7 /* PBXTargetDependency */, + 3DE21431124662C50033C839 /* PBXTargetDependency */, + ); + name = PmDefaults; + productName = pmdefaults; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 3DE2137F124653FB0033C839 /* portmusic_logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 3DE2137E124653FB0033C839 /* portmusic_logo.png */; }; + 3DE21435124663860033C839 /* PmDefaultsFrame.java in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DE2137D124653CB0033C839 /* PmDefaultsFrame.java */; }; + 3DE214361246638A0033C839 /* PmDefaults.java in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DE2137B1246538B0033C839 /* PmDefaults.java */; }; + 3DE214371246638F0033C839 /* JPortMidiException.java in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DE21382124654DE0033C839 /* JPortMidiException.java */; }; + 3DE214381246638F0033C839 /* JPortMidiApi.java in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DE21381124654CF0033C839 /* JPortMidiApi.java */; }; + 3DE214391246638F0033C839 /* JPortMidi.java in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3DE21380124654BC0033C839 /* JPortMidi.java */; }; + 3DE216131246AC0E0033C839 /* libpmjni.dylib in Copy Java Resources */ = {isa = PBXBuildFile; fileRef = 3DE216101246ABE30033C839 /* libpmjni.dylib */; }; + 3DE216951246D57A0033C839 /* pmdefaults.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3DE216901246C6410033C839 /* pmdefaults.icns */; }; + 89C3F2920F5250A300B0048E /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = 89C3F2900F5250A300B0048E /* Credits.rtf */; }; + 89D0F0240F392F20007831A7 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 89D0F0210F392F20007831A7 /* InfoPlist.strings */; }; + 89D0F0410F39306C007831A7 /* JavaApplicationStub in Copy Executable */ = {isa = PBXBuildFile; fileRef = 89D0F03E0F39304A007831A7 /* JavaApplicationStub */; }; + 89D0F16A0F3A124E007831A7 /* pmdefaults.jar in Copy Java Resources */ = {isa = PBXBuildFile; fileRef = 89D0F15D0F3A0FF7007831A7 /* pmdefaults.jar */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 3D634CAF124781580020F829 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 89D0F1C90F3B704E007831A7; + remoteInfo = PmDefaults; + }; + 3DE21430124662C50033C839 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3DE2142D124662AA0033C839; + remoteInfo = CopyJavaSources; + }; + 3DE2145D124666900033C839 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3DE2142D124662AA0033C839; + remoteInfo = CopyJavaSources; + }; + 89D0F1CC0F3B7062007831A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8D1107260486CEB800E47090; + remoteInfo = "Assemble Application"; + }; + 89D0F1D00F3B7062007831A7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 89D0F0480F393A6F007831A7; + remoteInfo = "Compile Java"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3DE2142C124662AA0033C839 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "${PROJECT_DIR}/pmdefaults/src/java"; + dstSubfolderSpec = 0; + files = ( + 3DE21435124663860033C839 /* PmDefaultsFrame.java in CopyFiles */, + 3DE214361246638A0033C839 /* PmDefaults.java in CopyFiles */, + 3DE214371246638F0033C839 /* JPortMidiException.java in CopyFiles */, + 3DE214381246638F0033C839 /* JPortMidiApi.java in CopyFiles */, + 3DE214391246638F0033C839 /* JPortMidi.java in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 89D0F0440F393070007831A7 /* Copy Executable */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 6; + files = ( + 89D0F0410F39306C007831A7 /* JavaApplicationStub in Copy Executable */, + ); + name = "Copy Executable"; + runOnlyForDeploymentPostprocessing = 0; + }; + 89D0F11F0F394189007831A7 /* Copy Java Resources */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 15; + files = ( + 89D0F16A0F3A124E007831A7 /* pmdefaults.jar in Copy Java Resources */, + 3DE216131246AC0E0033C839 /* libpmjni.dylib in Copy Java Resources */, + ); + name = "Copy Java Resources"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3DE2137B1246538B0033C839 /* PmDefaults.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = PmDefaults.java; path = ../pm_java/pmdefaults/PmDefaults.java; sourceTree = SOURCE_ROOT; }; + 3DE2137D124653CB0033C839 /* PmDefaultsFrame.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = PmDefaultsFrame.java; path = ../pm_java/pmdefaults/PmDefaultsFrame.java; sourceTree = SOURCE_ROOT; }; + 3DE2137E124653FB0033C839 /* portmusic_logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = portmusic_logo.png; path = ../pm_java/pmdefaults/portmusic_logo.png; sourceTree = SOURCE_ROOT; }; + 3DE21380124654BC0033C839 /* JPortMidi.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = JPortMidi.java; path = ../pm_java/jportmidi/JPortMidi.java; sourceTree = SOURCE_ROOT; }; + 3DE21381124654CF0033C839 /* JPortMidiApi.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = JPortMidiApi.java; path = ../pm_java/jportmidi/JPortMidiApi.java; sourceTree = SOURCE_ROOT; }; + 3DE21382124654DE0033C839 /* JPortMidiException.java */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.java; name = JPortMidiException.java; path = ../pm_java/jportmidi/JPortMidiException.java; sourceTree = SOURCE_ROOT; }; + 3DE213841246555A0033C839 /* CoreMIDI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMIDI.framework; path = /System/Library/Frameworks/CoreMIDI.framework; sourceTree = "<absolute>"; }; + 3DE21390124655760033C839 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = "<absolute>"; }; + 3DE213BE1246557F0033C839 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = "<absolute>"; }; + 3DE216101246ABE30033C839 /* libpmjni.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpmjni.dylib; path = ../Release/libpmjni.dylib; sourceTree = SOURCE_ROOT; }; + 3DE216901246C6410033C839 /* pmdefaults.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = pmdefaults.icns; path = ../pm_java/pmdefaults/pmdefaults.icns; sourceTree = SOURCE_ROOT; }; + 89C3F2910F5250A300B0048E /* English */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = English; path = English.lproj/Credits.rtf; sourceTree = "<group>"; }; + 89D0F0220F392F20007831A7 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; }; + 89D0F0230F392F20007831A7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; + 89D0F03E0F39304A007831A7 /* JavaApplicationStub */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = JavaApplicationStub; path = /System/Library/Frameworks/JavaVM.framework/Versions/A/Resources/MacOS/JavaApplicationStub; sourceTree = "<absolute>"; }; + 89D0F0840F394066007831A7 /* JavaNativeFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaNativeFoundation.framework; path = /System/Library/Frameworks/JavaVM.framework/Versions/A/Frameworks/JavaNativeFoundation.framework; sourceTree = "<absolute>"; }; + 89D0F1390F3948A9007831A7 /* pmdefaults/make */ = {isa = PBXFileReference; lastKnownFileType = folder; path = pmdefaults/make; sourceTree = "<group>"; }; + 89D0F15D0F3A0FF7007831A7 /* pmdefaults.jar */ = {isa = PBXFileReference; lastKnownFileType = archive.jar; name = pmdefaults.jar; path = build/Release/pmdefaults.jar; sourceTree = SOURCE_ROOT; }; + 89D0F1860F3A2442007831A7 /* JavaVM.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaVM.framework; path = /System/Library/Frameworks/JavaVM.framework; sourceTree = "<absolute>"; }; + 8D1107320486CEB800E47090 /* PmDefaults.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PmDefaults.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 3DE213841246555A0033C839 /* CoreMIDI.framework */, + 3DE21390124655760033C839 /* CoreFoundation.framework */, + 3DE213BE1246557F0033C839 /* CoreAudio.framework */, + 89D0F1860F3A2442007831A7 /* JavaVM.framework */, + 89D0F0840F394066007831A7 /* JavaNativeFoundation.framework */, + ); + name = "Linked Frameworks"; + sourceTree = "<group>"; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = "Other Frameworks"; + sourceTree = "<group>"; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 89D0F15D0F3A0FF7007831A7 /* pmdefaults.jar */, + 8D1107320486CEB800E47090 /* PmDefaults.app */, + ); + name = Products; + sourceTree = "<group>"; + }; + 29B97314FDCFA39411CA2CEA /* pmdefaults */ = { + isa = PBXGroup; + children = ( + 3DE216101246ABE30033C839 /* libpmjni.dylib */, + 89D0F0260F392F48007831A7 /* Source */, + 89D0F0200F392F20007831A7 /* Resources */, + 89D0F1390F3948A9007831A7 /* pmdefaults/make */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = pmdefaults; + sourceTree = "<group>"; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + 3DE2136A124652E20033C839 /* pm_java */ = { + isa = PBXGroup; + children = ( + 3DE21379124653150033C839 /* pmdefaults */, + 3DE2137A1246531D0033C839 /* jportmidi */, + ); + name = pm_java; + path = ..; + sourceTree = "<group>"; + }; + 3DE21379124653150033C839 /* pmdefaults */ = { + isa = PBXGroup; + children = ( + 3DE2137D124653CB0033C839 /* PmDefaultsFrame.java */, + 3DE2137B1246538B0033C839 /* PmDefaults.java */, + ); + name = pmdefaults; + sourceTree = "<group>"; + }; + 3DE2137A1246531D0033C839 /* jportmidi */ = { + isa = PBXGroup; + children = ( + 3DE21382124654DE0033C839 /* JPortMidiException.java */, + 3DE21381124654CF0033C839 /* JPortMidiApi.java */, + 3DE21380124654BC0033C839 /* JPortMidi.java */, + ); + name = jportmidi; + sourceTree = "<group>"; + }; + 89D0F0200F392F20007831A7 /* Resources */ = { + isa = PBXGroup; + children = ( + 3DE216901246C6410033C839 /* pmdefaults.icns */, + 3DE2137E124653FB0033C839 /* portmusic_logo.png */, + 89C3F2900F5250A300B0048E /* Credits.rtf */, + 89D0F0230F392F20007831A7 /* Info.plist */, + 89D0F0210F392F20007831A7 /* InfoPlist.strings */, + 89D0F03E0F39304A007831A7 /* JavaApplicationStub */, + ); + name = Resources; + path = pmdefaults/resources; + sourceTree = "<group>"; + }; + 89D0F0260F392F48007831A7 /* Source */ = { + isa = PBXGroup; + children = ( + 3DE2136A124652E20033C839 /* pm_java */, + ); + name = Source; + path = pmdefaults/src; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + 89D0F0480F393A6F007831A7 /* Compile Java */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "-e -f \"${SRCROOT}/make/build.xml\" -debug \"$ACTION\""; + buildConfigurationList = 89D0F04B0F393AB7007831A7 /* Build configuration list for PBXLegacyTarget "Compile Java" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/ant; + buildWorkingDirectory = ""; + dependencies = ( + 3DE2145E124666900033C839 /* PBXTargetDependency */, + ); + name = "Compile Java"; + passBuildSettingsInEnvironment = 1; + productName = "Compile Java"; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* Assemble Application */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Assemble Application" */; + buildPhases = ( + 89D0F0440F393070007831A7 /* Copy Executable */, + 89D0F11F0F394189007831A7 /* Copy Java Resources */, + 8D1107290486CEB800E47090 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Assemble Application"; + productInstallPath = "$(HOME)/Applications"; + productName = pmdefaults; + productReference = 8D1107320486CEB800E47090 /* PmDefaults.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "pm_mac" */; + compatibilityVersion = "Xcode 3.0"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 29B97314FDCFA39411CA2CEA /* pmdefaults */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3D634CAB1247805C0020F829 /* JPortMidiHeaders */, + 89D0F1C90F3B704E007831A7 /* PmDefaults */, + 3DE2142D124662AA0033C839 /* CopyJavaSources */, + 89D0F0480F393A6F007831A7 /* Compile Java */, + 8D1107260486CEB800E47090 /* Assemble Application */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3DE216951246D57A0033C839 /* pmdefaults.icns in Resources */, + 89D0F0240F392F20007831A7 /* InfoPlist.strings in Resources */, + 89C3F2920F5250A300B0048E /* Credits.rtf in Resources */, + 3DE2137F124653FB0033C839 /* portmusic_logo.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3D634CAA1247805C0020F829 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo BUILT_PRODUCTS_DIR is ${BUILT_PRODUCTS_DIR}\njavah -classpath \"${BUILT_PRODUCTS_DIR}/pmdefaults.jar\" -force -o \"${BUILT_PRODUCTS_DIR}/jportmidi_JportMidiApi.h\" \"jportmidi.JPortMidiApi\"\nmv \"${BUILT_PRODUCTS_DIR}/jportmidi_JportMidiApi.h\" ../pm_java/pmjni/\necho \"Created ../pm_java/pmjni/jportmidi_JportMidiApi.h\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 3D634CB0124781580020F829 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 89D0F1C90F3B704E007831A7 /* PmDefaults */; + targetProxy = 3D634CAF124781580020F829 /* PBXContainerItemProxy */; + }; + 3DE21431124662C50033C839 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3DE2142D124662AA0033C839 /* CopyJavaSources */; + targetProxy = 3DE21430124662C50033C839 /* PBXContainerItemProxy */; + }; + 3DE2145E124666900033C839 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3DE2142D124662AA0033C839 /* CopyJavaSources */; + targetProxy = 3DE2145D124666900033C839 /* PBXContainerItemProxy */; + }; + 89D0F1CD0F3B7062007831A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8D1107260486CEB800E47090 /* Assemble Application */; + targetProxy = 89D0F1CC0F3B7062007831A7 /* PBXContainerItemProxy */; + }; + 89D0F1D10F3B7062007831A7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 89D0F0480F393A6F007831A7 /* Compile Java */; + targetProxy = 89D0F1D00F3B7062007831A7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 89C3F2900F5250A300B0048E /* Credits.rtf */ = { + isa = PBXVariantGroup; + children = ( + 89C3F2910F5250A300B0048E /* English */, + ); + name = Credits.rtf; + sourceTree = "<group>"; + }; + 89D0F0210F392F20007831A7 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 89D0F0220F392F20007831A7 /* English */, + ); + name = InfoPlist.strings; + sourceTree = "<group>"; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 3D634CAC1247805C0020F829 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = JPortMidiHeaders; + }; + name = Debug; + }; + 3D634CAD1247805C0020F829 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = JPortMidiHeaders; + ZERO_LINK = NO; + }; + name = Release; + }; + 3DE2142E124662AB0033C839 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = CopyJavaSources; + }; + name = Debug; + }; + 3DE2142F124662AB0033C839 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = CopyJavaSources; + ZERO_LINK = NO; + }; + name = Release; + }; + 89D0F0490F393A6F007831A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = pmdefaults; + SRCROOT = ./pmdefaults; + }; + name = Debug; + }; + 89D0F04A0F393A6F007831A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = pmdefaults; + SRCROOT = ./pmdefaults; + }; + name = Release; + }; + 89D0F1CA0F3B704F007831A7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = pmdefaults; + }; + name = Debug; + }; + 89D0F1CB0F3B704F007831A7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = pmdefaults; + }; + name = Release; + }; + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = pmdefaults/resources/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = pmdefaults; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CONFIGURATION_BUILD_DIR = "$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + INFOPLIST_FILE = pmdefaults/resources/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = PmDefaults; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_64_BIT_PRE_XCODE_3_1 = "x86_64 i386 ppc"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3D634CAE1247807A0020F829 /* Build configuration list for PBXAggregateTarget "JPortMidiHeaders" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3D634CAC1247805C0020F829 /* Debug */, + 3D634CAD1247805C0020F829 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3DE21434124662FF0033C839 /* Build configuration list for PBXAggregateTarget "CopyJavaSources" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3DE2142E124662AB0033C839 /* Debug */, + 3DE2142F124662AB0033C839 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 89D0F04B0F393AB7007831A7 /* Build configuration list for PBXLegacyTarget "Compile Java" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 89D0F0490F393A6F007831A7 /* Debug */, + 89D0F04A0F393A6F007831A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 89D0F1D20F3B7080007831A7 /* Build configuration list for PBXAggregateTarget "PmDefaults" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 89D0F1CA0F3B704F007831A7 /* Debug */, + 89D0F1CB0F3B704F007831A7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "Assemble Application" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "pm_mac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..570e6faa82 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:pm_mac.xcodeproj"> + </FileRef> +</Workspace> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/xcuserdata/VKamyshniy.xcuserdatad/UserInterfaceState.xcuserstate b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/xcuserdata/VKamyshniy.xcuserdatad/UserInterfaceState.xcuserstate Binary files differnew file mode 100644 index 0000000000..104c0fe910 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/xcuserdata/VKamyshniy.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/Assemble Application.xcscheme b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/Assemble Application.xcscheme new file mode 100644 index 0000000000..b2051a67b0 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/Assemble Application.xcscheme @@ -0,0 +1,86 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0460" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "8D1107260486CEB800E47090" + BuildableName = "PmDefaults.app" + BlueprintName = "Assemble Application" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + <MacroExpansion> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "8D1107260486CEB800E47090" + BuildableName = "PmDefaults.app" + BlueprintName = "Assemble Application" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </MacroExpansion> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "8D1107260486CEB800E47090" + BuildableName = "PmDefaults.app" + BlueprintName = "Assemble Application" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + <BuildableProductRunnable> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "8D1107260486CEB800E47090" + BuildableName = "PmDefaults.app" + BlueprintName = "Assemble Application" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildableProductRunnable> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/Compile Java.xcscheme b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/Compile Java.xcscheme new file mode 100644 index 0000000000..415b487914 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/Compile Java.xcscheme @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0460" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "89D0F0480F393A6F007831A7" + BuildableName = "Compile Java" + BlueprintName = "Compile Java" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/CopyJavaSources.xcscheme b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/CopyJavaSources.xcscheme new file mode 100644 index 0000000000..ad37276ccc --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/CopyJavaSources.xcscheme @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0460" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3DE2142D124662AA0033C839" + BuildableName = "CopyJavaSources" + BlueprintName = "CopyJavaSources" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/JPortMidiHeaders.xcscheme b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/JPortMidiHeaders.xcscheme new file mode 100644 index 0000000000..de0f0bcef7 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/JPortMidiHeaders.xcscheme @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0460" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "3D634CAB1247805C0020F829" + BuildableName = "JPortMidiHeaders" + BlueprintName = "JPortMidiHeaders" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/PmDefaults.xcscheme b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/PmDefaults.xcscheme new file mode 100644 index 0000000000..23d63e9bac --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/PmDefaults.xcscheme @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Scheme + LastUpgradeVersion = "0460" + version = "1.3"> + <BuildAction + parallelizeBuildables = "YES" + buildImplicitDependencies = "YES"> + <BuildActionEntries> + <BuildActionEntry + buildForTesting = "YES" + buildForRunning = "YES" + buildForProfiling = "YES" + buildForArchiving = "YES" + buildForAnalyzing = "YES"> + <BuildableReference + BuildableIdentifier = "primary" + BlueprintIdentifier = "89D0F1C90F3B704E007831A7" + BuildableName = "PmDefaults" + BlueprintName = "PmDefaults" + ReferencedContainer = "container:pm_mac.xcodeproj"> + </BuildableReference> + </BuildActionEntry> + </BuildActionEntries> + </BuildAction> + <TestAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + shouldUseLaunchSchemeArgsEnv = "YES" + buildConfiguration = "Debug"> + <Testables> + </Testables> + </TestAction> + <LaunchAction + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + launchStyle = "0" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Debug" + ignoresPersistentStateOnLaunch = "NO" + debugDocumentVersioning = "YES" + allowLocationSimulation = "YES"> + <AdditionalOptions> + </AdditionalOptions> + </LaunchAction> + <ProfileAction + shouldUseLaunchSchemeArgsEnv = "YES" + savedToolIdentifier = "" + useCustomWorkingDirectory = "NO" + buildConfiguration = "Release" + debugDocumentVersioning = "YES"> + </ProfileAction> + <AnalyzeAction + buildConfiguration = "Debug"> + </AnalyzeAction> + <ArchiveAction + buildConfiguration = "Release" + revealArchiveInOrganizer = "YES"> + </ArchiveAction> +</Scheme> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/xcschememanagement.plist b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000000..a57f870bb5 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/xcuserdata/VKamyshniy.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,62 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>SchemeUserState</key> + <dict> + <key>Assemble Application.xcscheme</key> + <dict> + <key>orderHint</key> + <integer>4</integer> + </dict> + <key>Compile Java.xcscheme</key> + <dict> + <key>orderHint</key> + <integer>3</integer> + </dict> + <key>CopyJavaSources.xcscheme</key> + <dict> + <key>orderHint</key> + <integer>2</integer> + </dict> + <key>JPortMidiHeaders.xcscheme</key> + <dict> + <key>orderHint</key> + <integer>0</integer> + </dict> + <key>PmDefaults.xcscheme</key> + <dict> + <key>orderHint</key> + <integer>1</integer> + </dict> + </dict> + <key>SuppressBuildableAutocreation</key> + <dict> + <key>3D634CAB1247805C0020F829</key> + <dict> + <key>primary</key> + <true/> + </dict> + <key>3DE2142D124662AA0033C839</key> + <dict> + <key>primary</key> + <true/> + </dict> + <key>89D0F0480F393A6F007831A7</key> + <dict> + <key>primary</key> + <true/> + </dict> + <key>89D0F1C90F3B704E007831A7</key> + <dict> + <key>primary</key> + <true/> + </dict> + <key>8D1107260486CEB800E47090</key> + <dict> + <key>primary</key> + <true/> + </dict> + </dict> +</dict> +</plist> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/make/build.xml b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/make/build.xml new file mode 100644 index 0000000000..bd08c68208 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/make/build.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project name="pmdefaults" default="jar" basedir=".."> + + <!-- Global Properties --> + <property environment="env"/> + + + <!-- building in Xcode --> + <condition property="product" value="${env.PRODUCT_NAME}"> + <isset property="env.PRODUCT_NAME"/> + </condition> + + <condition property="src" value="${env.SRCROOT}/src"> + <isset property="env.SRCROOT"/> + </condition> + + <condition property="obj" value="${env.OBJECT_FILE_DIR}"> + <isset property="env.OBJECT_FILE_DIR"/> + </condition> + + <condition property="dst" value="${env.BUILT_PRODUCTS_DIR}"> + <isset property="env.BUILT_PRODUCTS_DIR"/> + </condition> + + + <!-- building from the command line --> + <condition property="src" value="src"> + <not> + <isset property="src"/> + </not> + </condition> + + <condition property="obj" value="build/obj"> + <not> + <isset property="obj"/> + </not> + </condition> + + <condition property="dst" value="build"> + <not> + <isset property="dst"/> + </not> + </condition> + + <condition property="product" value="pmdefaults"> + <not> + <isset property="product"/> + </not> + </condition> + + + <!-- Targets --> + <target name="init" description="Create build directories"> + <mkdir dir="${obj}/${product}"/> + <mkdir dir="${dst}"/> + </target> + + <target name="compile" depends="init" description="Compile"> + <javac destdir="${obj}/${product}" deprecation="on" source="1.5" target="1.5" fork="true" debug="true" debuglevel="lines,source"> + <src path="${src}/java"/> + <classpath path="${src}/../lib/eawt-stubs.jar"/> + </javac> + </target> + + <target name="copy" depends="init" description="Copy resources"> + + </target> + + <target name="jar" depends="compile, copy" description="Assemble Jar file"> + <jar jarfile="${dst}/${product}.jar" basedir="${obj}/${product}" manifest="resources/Manifest" index="true"/> + </target> + + <target name="install" depends="jar" description="Alias for 'jar'"> + <!-- sent by Xcode --> + </target> + + <target name="clean" description="Removes build directories"> + <!-- sent by Xcode --> + <delete dir="${obj}/${product}"/> + <delete file="${dst}/${product}.jar"/> + </target> + + <target name="installhdrs" description=""> + <!-- sent by Xcode --> + <echo>"Nothing to do for install-headers phase"</echo> + </target> +</project> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/make/find-classrefs.sh b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/make/find-classrefs.sh new file mode 100644 index 0000000000..2217580d0d --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/make/find-classrefs.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# Prints all class references made by all classes in a Jar file +# Depends on the output formatting of javap + +# create a temporary working directory +dir=`mktemp -d $TMPDIR/classrefs.XXXXXX` + +asm_dump="$dir/asm_dump" +all_classes="$dir/all_classes" + +# for each class in a Jar file, dump the full assembly +javap -c -classpath "$1" `/usr/bin/jar tf "$1" | grep "\.class" | sort | xargs | sed -e 's/\.class//g'` > $asm_dump + +# dump the initial list of all classes in the Jar file +/usr/bin/jar tf $1 | grep "\.class" | sed -e 's/\.class//g' >> $all_classes + +# dump all static class references +cat $asm_dump | grep //class | awk -F"//class " '{print $2}' | sort | uniq >> $all_classes + +# dump all references to classes made in methods +cat $asm_dump | grep //Method | awk -F"//Method " '{print $2}' | sort | uniq | grep "\." | awk -F"." '{print $1}' | sort | uniq >> $all_classes + +# dump all references to classes by direct field access +cat $asm_dump | grep //Field | awk -F"//Field " '{print $2}' | sort | uniq | grep "\:L" | awk -F"\:L" '{print $2}' | sort | uniq | awk -F"\;" '{print $1}' >> $all_classes + +# sort and reformat +sort $all_classes | uniq | grep -v "\"" | sed -e 's/\//\./g' + +# cleanup +rm -rf $dir diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/Credits.rtf b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/Credits.rtf new file mode 100644 index 0000000000..18f83781e7 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/Credits.rtf @@ -0,0 +1,14 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf320 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\b\fs24 \cf0 Author: +\b0 \ + Roger B. Dannenberg\ +\ + +\b With special thanks to: +\b0 \ + National Science Foundation\ +}
\ No newline at end of file diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/InfoPlist.strings b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/InfoPlist.strings Binary files differnew file mode 100644 index 0000000000..c7e5600f98 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/InfoPlist.strings diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/Info.plist b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/Info.plist new file mode 100644 index 0000000000..58bedb4501 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/Info.plist @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>JavaApplicationStub</string> + <key>CFBundleIconFile</key> + <string>pmdefaults.icns</string> + <key>CFBundleIdentifier</key> + <string></string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>PmDefaults</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>CFBundleShortVersionString</key> + <string>1.0</string> + <key>Java</key> + <dict> + <key>ClassPath</key> + <string>$JAVAROOT/pmdefaults.jar</string> + <key>JVMVersion</key> + <string>1.5+</string> + <key>MainClass</key> + <string>pmdefaults.PmDefaults</string> + <key>Properties</key> + <dict> + <key>apple.laf.useScreenMenuBar</key> + <string>true</string> + </dict> + </dict> +</dict> +</plist> diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/Manifest b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/Manifest new file mode 100644 index 0000000000..5dee9b0dc1 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/Manifest @@ -0,0 +1 @@ +Main-Class: pmdefaults/PmDefaults diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmac.c b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmac.c new file mode 100644 index 0000000000..13ac683004 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmac.c @@ -0,0 +1,59 @@ +/* pmmac.c -- PortMidi os-dependent code */
+
+/* This file only needs to implement:
+pm_init(), which calls various routines to register the
+available midi devices,
+Pm_GetDefaultInputDeviceID(), and
+Pm_GetDefaultOutputDeviceID().
+It is seperate from pmmacosxcm because we might want to register
+non-CoreMIDI devices.
+*/
+
+#include "stdlib.h"
+#include "portmidi.h"
+#include "pmutil.h"
+#include "pminternal.h"
+#include "pmmacosxcm.h"
+
+PmDeviceID pm_default_input_device_id = -1;
+PmDeviceID pm_default_output_device_id = -1;
+
+void pm_init()
+{
+ PmError err = pm_macosxcm_init();
+ // this is set when we return to Pm_Initialize, but we need it
+ // now in order to (successfully) call Pm_CountDevices()
+ pm_initialized = TRUE;
+ if (!err) {
+ pm_default_input_device_id = find_default_device(
+ "/PortMidi/PM_RECOMMENDED_INPUT_DEVICE", TRUE,
+ pm_default_input_device_id);
+ pm_default_output_device_id = find_default_device(
+ "/PortMidi/PM_RECOMMENDED_OUTPUT_DEVICE", FALSE,
+ pm_default_output_device_id);
+ }
+}
+
+
+void pm_term(void)
+{
+ pm_macosxcm_term();
+}
+
+
+PmDeviceID Pm_GetDefaultInputDeviceID()
+{
+ Pm_Initialize();
+ return pm_default_input_device_id;
+}
+
+PmDeviceID Pm_GetDefaultOutputDeviceID() {
+ Pm_Initialize();
+ return pm_default_output_device_id;
+}
+
+void *pm_alloc(size_t s) { return malloc(s); }
+
+void pm_free(void *ptr) { free(ptr); }
+
+
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmac.h b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmac.h new file mode 100644 index 0000000000..2d714254ea --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmac.h @@ -0,0 +1,4 @@ +/* pmmac.h */
+
+extern PmDeviceID pm_default_input_device_id;
+extern PmDeviceID pm_default_output_device_id;
\ No newline at end of file diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmacosxcm.c b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmacosxcm.c new file mode 100644 index 0000000000..78513573d4 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmacosxcm.c @@ -0,0 +1,1010 @@ +/* + * Platform interface to the MacOS X CoreMIDI framework + * + * Jon Parise <jparise at cmu.edu> + * and subsequent work by Andrew Zeldis and Zico Kolter + * and Roger B. Dannenberg + * + * $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $ + */ + +/* Notes: + since the input and output streams are represented by MIDIEndpointRef + values and almost no other state, we store the MIDIEndpointRef on + descriptors[midi->device_id].descriptor. The only other state we need + is for errors: we need to know if there is an error and if so, what is + the error text. We use a structure with two kinds of + host error: "error" and "callback_error". That way, asynchronous callbacks + do not interfere with other error information. + + OS X does not seem to have an error-code-to-text function, so we will + just use text messages instead of error codes. + */ + +#include <stdlib.h> + +//#define CM_DEBUG 1 + +#include "portmidi.h" +#include "pmutil.h" +#include "pminternal.h" +#include "porttime.h" +#include "pmmac.h" +#include "pmmacosxcm.h" + +#include <stdio.h> +#include <string.h> + +#include <CoreServices/CoreServices.h> +#include <CoreMIDI/MIDIServices.h> +#include <CoreAudio/HostTime.h> +#include <unistd.h> + +#define PACKET_BUFFER_SIZE 1024 +/* maximum overall data rate (OS X limit is 15000 bytes/second) */ +#define MAX_BYTES_PER_S 14000 + +/* Apple reports that packets are dropped when the MIDI bytes/sec + exceeds 15000. This is computed by "tracking the number of MIDI + bytes scheduled into 1-second buckets over the last six seconds + and averaging these counts." + + This is apparently based on timestamps, not on real time, so + we have to avoid constructing packets that schedule high speed + output even if the actual writes are delayed (which was my first + solution). + + The LIMIT_RATE symbol, if defined, enables code to modify + timestamps as follows: + After each packet is formed, the next allowable timestamp is + computed as this_packet_time + this_packet_len * delay_per_byte + + This is the minimum timestamp allowed in the next packet. + + Note that this distorts accurate timestamps somewhat. + */ +#define LIMIT_RATE 1 + +#define SYSEX_BUFFER_SIZE 128 + +#define VERBOSE_ON 1 +#define VERBOSE if (VERBOSE_ON) + +#define MIDI_SYSEX 0xf0 +#define MIDI_EOX 0xf7 +#define MIDI_STATUS_MASK 0x80 + +// "Ref"s are pointers on 32-bit machines and ints on 64 bit machines +// NULL_REF is our representation of either 0 or NULL +#ifdef __LP64__ +#define NULL_REF 0 +#else +#define NULL_REF NULL +#endif + +static MIDIClientRef client = NULL_REF; /* Client handle to the MIDI server */ +static MIDIPortRef portIn = NULL_REF; /* Input port handle */ +static MIDIPortRef portOut = NULL_REF; /* Output port handle */ + +extern pm_fns_node pm_macosx_in_dictionary; +extern pm_fns_node pm_macosx_out_dictionary; + +typedef struct midi_macosxcm_struct { + PmTimestamp sync_time; /* when did we last determine delta? */ + UInt64 delta; /* difference between stream time and real time in ns */ + UInt64 last_time; /* last output time in host units*/ + int first_message; /* tells midi_write to sychronize timestamps */ + int sysex_mode; /* middle of sending sysex */ + uint32_t sysex_word; /* accumulate data when receiving sysex */ + uint32_t sysex_byte_count; /* count how many received */ + char error[PM_HOST_ERROR_MSG_LEN]; + char callback_error[PM_HOST_ERROR_MSG_LEN]; + Byte packetBuffer[PACKET_BUFFER_SIZE]; + MIDIPacketList *packetList; /* a pointer to packetBuffer */ + MIDIPacket *packet; + Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */ + MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */ + /* allow for running status (is running status possible here? -rbd): -cpr */ + unsigned char last_command; + int32_t last_msg_length; + /* limit midi data rate (a CoreMidi requirement): */ + UInt64 min_next_time; /* when can the next send take place? */ + int byte_count; /* how many bytes in the next packet list? */ + Float64 us_per_host_tick; /* host clock frequency, units of min_next_time */ + UInt64 host_ticks_per_byte; /* host clock units per byte at maximum rate */ +} midi_macosxcm_node, *midi_macosxcm_type; + +/* private function declarations */ +MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp); +PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp); + +char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint); + + +static int +midi_length(int32_t msg) +{ + int status, high, low; + static int high_lengths[] = { + 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ + 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ + }; + static int low_lengths[] = { + 1, 2, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ + 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ + }; + + status = msg & 0xFF; + high = status >> 4; + low = status & 15; + + return (high != 0xF) ? high_lengths[high] : low_lengths[low]; +} + +static PmTimestamp midi_synchronize(PmInternal *midi) +{ + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + UInt64 pm_stream_time_2 = + AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); + PmTimestamp real_time; + UInt64 pm_stream_time; + /* if latency is zero and this is an output, there is no + time reference and midi_synchronize should never be called */ + assert(midi->time_proc); + assert(!(midi->write_flag && midi->latency == 0)); + do { + /* read real_time between two reads of stream time */ + pm_stream_time = pm_stream_time_2; + real_time = (*midi->time_proc)(midi->time_info); + pm_stream_time_2 = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); + /* repeat if more than 0.5 ms has elapsed */ + } while (pm_stream_time_2 > pm_stream_time + 500000); + m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000); + m->sync_time = real_time; + return real_time; +} + + +static void +process_packet(MIDIPacket *packet, PmEvent *event, + PmInternal *midi, midi_macosxcm_type m) +{ + /* handle a packet of MIDI messages from CoreMIDI */ + /* there may be multiple short messages in one packet (!) */ + unsigned int remaining_length = packet->length; + unsigned char *cur_packet_data = packet->data; + while (remaining_length > 0) { + if (cur_packet_data[0] == MIDI_SYSEX || + /* are we in the middle of a sysex message? */ + (m->last_command == 0 && + !(cur_packet_data[0] & MIDI_STATUS_MASK))) { + m->last_command = 0; /* no running status */ + unsigned int amt = pm_read_bytes(midi, cur_packet_data, + remaining_length, + event->timestamp); + remaining_length -= amt; + cur_packet_data += amt; + } else if (cur_packet_data[0] == MIDI_EOX) { + /* this should never happen, because pm_read_bytes should + * get and read all EOX bytes*/ + midi->sysex_in_progress = FALSE; + m->last_command = 0; + } else if (cur_packet_data[0] & MIDI_STATUS_MASK) { + /* compute the length of the next (short) msg in packet */ + unsigned int cur_message_length = midi_length(cur_packet_data[0]); + if (cur_message_length > remaining_length) { +#ifdef DEBUG + printf("PortMidi debug msg: not enough data"); +#endif + /* since there's no more data, we're done */ + return; + } + m->last_msg_length = cur_message_length; + m->last_command = cur_packet_data[0]; + switch (cur_message_length) { + case 1: + event->message = Pm_Message(cur_packet_data[0], 0, 0); + break; + case 2: + event->message = Pm_Message(cur_packet_data[0], + cur_packet_data[1], 0); + break; + case 3: + event->message = Pm_Message(cur_packet_data[0], + cur_packet_data[1], + cur_packet_data[2]); + break; + default: + /* PortMIDI internal error; should never happen */ + assert(cur_message_length == 1); + return; /* give up on packet if continued after assert */ + } + pm_read_short(midi, event); + remaining_length -= m->last_msg_length; + cur_packet_data += m->last_msg_length; + } else if (m->last_msg_length > remaining_length + 1) { + /* we have running status, but not enough data */ +#ifdef DEBUG + printf("PortMidi debug msg: not enough data in CoreMIDI packet"); +#endif + /* since there's no more data, we're done */ + return; + } else { /* output message using running status */ + switch (m->last_msg_length) { + case 1: + event->message = Pm_Message(m->last_command, 0, 0); + break; + case 2: + event->message = Pm_Message(m->last_command, + cur_packet_data[0], 0); + break; + case 3: + event->message = Pm_Message(m->last_command, + cur_packet_data[0], + cur_packet_data[1]); + break; + default: + /* last_msg_length is invalid -- internal PortMIDI error */ + assert(m->last_msg_length == 1); + } + pm_read_short(midi, event); + remaining_length -= (m->last_msg_length - 1); + cur_packet_data += (m->last_msg_length - 1); + } + } +} + + + +/* called when MIDI packets are received */ +static void +readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon) +{ + PmInternal *midi; + midi_macosxcm_type m; + PmEvent event; + MIDIPacket *packet; + unsigned int packetIndex; + uint32_t now; + unsigned int status; + +#ifdef CM_DEBUG + printf("readProc: numPackets %d: ", newPackets->numPackets); +#endif + + /* Retrieve the context for this connection */ + midi = (PmInternal *) connRefCon; + m = (midi_macosxcm_type) midi->descriptor; + assert(m); + + /* synchronize time references every 100ms */ + now = (*midi->time_proc)(midi->time_info); + if (m->first_message || m->sync_time + 100 /*ms*/ < now) { + /* time to resync */ + now = midi_synchronize(midi); + m->first_message = FALSE; + } + + packet = (MIDIPacket *) &newPackets->packet[0]; + /* printf("readproc packet status %x length %d\n", packet->data[0], + packet->length); */ + for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) { + /* Set the timestamp and dispatch this message */ + event.timestamp = (PmTimestamp) /* explicit conversion */ ( + (AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) / + (UInt64) 1000000); + status = packet->data[0]; + /* process packet as sysex data if it begins with MIDI_SYSEX, or + MIDI_EOX or non-status byte with no running status */ +#ifdef CM_DEBUG + printf(" %d", packet->length); +#endif + if (status == MIDI_SYSEX || status == MIDI_EOX || + ((!(status & MIDI_STATUS_MASK)) && !m->last_command)) { + /* previously was: !(status & MIDI_STATUS_MASK)) { + * but this could mistake running status for sysex data + */ + /* reset running status data -cpr */ + m->last_command = 0; + m->last_msg_length = 0; + /* printf("sysex packet length: %d\n", packet->length); */ + pm_read_bytes(midi, packet->data, packet->length, event.timestamp); + } else { + process_packet(packet, &event, midi, m); + } + packet = MIDIPacketNext(packet); + } +#ifdef CM_DEBUG + printf("\n"); +#endif +} + +static PmError +midi_in_open(PmInternal *midi, void *driverInfo) +{ + MIDIEndpointRef endpoint; + midi_macosxcm_type m; + OSStatus macHostError; + + /* insure that we have a time_proc for timing */ + if (midi->time_proc == NULL) { + if (!Pt_Started()) + Pt_Start(1, 0, 0); + /* time_get does not take a parameter, so coerce */ + midi->time_proc = (PmTimeProcPtr) Pt_Time; + } + endpoint = (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor; + if (endpoint == NULL_REF) { + return pmInvalidDeviceId; + } + + m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */ + midi->descriptor = m; + if (!m) { + return pmInsufficientMemory; + } + m->error[0] = 0; + m->callback_error[0] = 0; + m->sync_time = 0; + m->delta = 0; + m->last_time = 0; + m->first_message = TRUE; + m->sysex_mode = FALSE; + m->sysex_word = 0; + m->sysex_byte_count = 0; + m->packetList = NULL; + m->packet = NULL; + m->last_command = 0; + m->last_msg_length = 0; + + macHostError = MIDIPortConnectSource(portIn, endpoint, midi); + if (macHostError != noErr) { + pm_hosterror = macHostError; + sprintf(pm_hosterror_text, + "Host error %ld: MIDIPortConnectSource() in midi_in_open()", + (long) macHostError); + midi->descriptor = NULL; + pm_free(m); + return pmHostError; + } + + return pmNoError; +} + +static PmError +midi_in_close(PmInternal *midi) +{ + MIDIEndpointRef endpoint; + OSStatus macHostError; + PmError err = pmNoError; + + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + + if (!m) return pmBadPtr; + + endpoint = (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor; + if (endpoint == NULL_REF) { + pm_hosterror = pmBadPtr; + } + + /* shut off the incoming messages before freeing data structures */ + macHostError = MIDIPortDisconnectSource(portIn, endpoint); + if (macHostError != noErr) { + pm_hosterror = macHostError; + sprintf(pm_hosterror_text, + "Host error %ld: MIDIPortDisconnectSource() in midi_in_close()", + (long) macHostError); + err = pmHostError; + } + + midi->descriptor = NULL; + pm_free(midi->descriptor); + + return err; +} + + +static PmError +midi_out_open(PmInternal *midi, void *driverInfo) +{ + midi_macosxcm_type m; + + m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */ + midi->descriptor = m; + if (!m) { + return pmInsufficientMemory; + } + m->error[0] = 0; + m->callback_error[0] = 0; + m->sync_time = 0; + m->delta = 0; + m->last_time = 0; + m->first_message = TRUE; + m->sysex_mode = FALSE; + m->sysex_word = 0; + m->sysex_byte_count = 0; + m->packetList = (MIDIPacketList *) m->packetBuffer; + m->packet = NULL; + m->last_command = 0; + m->last_msg_length = 0; + m->min_next_time = 0; + m->byte_count = 0; + m->us_per_host_tick = 1000000.0 / AudioGetHostClockFrequency(); + m->host_ticks_per_byte = (UInt64) (1000000.0 / + (m->us_per_host_tick * MAX_BYTES_PER_S)); + return pmNoError; +} + + +static PmError +midi_out_close(PmInternal *midi) +{ + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + if (!m) return pmBadPtr; + + midi->descriptor = NULL; + pm_free(midi->descriptor); + + return pmNoError; +} + +static PmError +midi_abort(PmInternal *midi) +{ + PmError err = pmNoError; + OSStatus macHostError; + MIDIEndpointRef endpoint = + (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor; + macHostError = MIDIFlushOutput(endpoint); + if (macHostError != noErr) { + pm_hosterror = macHostError; + sprintf(pm_hosterror_text, + "Host error %ld: MIDIFlushOutput()", (long) macHostError); + err = pmHostError; + } + return err; +} + + +static PmError +midi_write_flush(PmInternal *midi, PmTimestamp timestamp) +{ + OSStatus macHostError; + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + MIDIEndpointRef endpoint = + (MIDIEndpointRef) (long) descriptors[midi->device_id].descriptor; + assert(m); + assert(endpoint); + if (m->packet != NULL) { + /* out of space, send the buffer and start refilling it */ + /* before we can send, maybe delay to limit data rate. OS X allows + * 15KB/s. */ + UInt64 now = AudioGetCurrentHostTime(); + if (now < m->min_next_time) { + usleep((useconds_t) + ((m->min_next_time - now) * m->us_per_host_tick)); + } + macHostError = MIDISend(portOut, endpoint, m->packetList); + m->packet = NULL; /* indicate no data in packetList now */ + m->min_next_time = now + m->byte_count * m->host_ticks_per_byte; + m->byte_count = 0; + if (macHostError != noErr) goto send_packet_error; + } + return pmNoError; + +send_packet_error: + pm_hosterror = macHostError; + sprintf(pm_hosterror_text, + "Host error %ld: MIDISend() in midi_write()", + (long) macHostError); + return pmHostError; + +} + + +static PmError +send_packet(PmInternal *midi, Byte *message, unsigned int messageLength, + MIDITimeStamp timestamp) +{ + PmError err; + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + assert(m); + + /* printf("add %d to packet %p len %d\n", message[0], m->packet, messageLength); */ + m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), + m->packet, timestamp, messageLength, + message); + m->byte_count += messageLength; + if (m->packet == NULL) { + /* out of space, send the buffer and start refilling it */ + /* make midi->packet non-null to fool midi_write_flush into sending */ + m->packet = (MIDIPacket *) 4; + /* timestamp is 0 because midi_write_flush ignores timestamp since + * timestamps are already in packets. The timestamp parameter is here + * because other API's need it. midi_write_flush can be called + * from system-independent code that must be cross-API. + */ + if ((err = midi_write_flush(midi, 0)) != pmNoError) return err; + m->packet = MIDIPacketListInit(m->packetList); + assert(m->packet); /* if this fails, it's a programming error */ + m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), + m->packet, timestamp, messageLength, + message); + assert(m->packet); /* can't run out of space on first message */ + } + return pmNoError; +} + + +static PmError +midi_write_short(PmInternal *midi, PmEvent *event) +{ + PmTimestamp when = event->timestamp; + PmMessage what = event->message; + MIDITimeStamp timestamp; + UInt64 when_ns; + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + Byte message[4]; + unsigned int messageLength; + + if (m->packet == NULL) { + m->packet = MIDIPacketListInit(m->packetList); + /* this can never fail, right? failure would indicate something + unrecoverable */ + assert(m->packet); + } + + /* compute timestamp */ + if (when == 0) when = midi->now; + /* if latency == 0, midi->now is not valid. We will just set it to zero */ + if (midi->latency == 0) when = 0; + when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta; + timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); + + message[0] = Pm_MessageStatus(what); + message[1] = Pm_MessageData1(what); + message[2] = Pm_MessageData2(what); + messageLength = midi_length(what); + + /* make sure we go foreward in time */ + if (timestamp < m->min_next_time) timestamp = m->min_next_time; + + #ifdef LIMIT_RATE + if (timestamp < m->last_time) + timestamp = m->last_time; + m->last_time = timestamp + messageLength * m->host_ticks_per_byte; + #endif + + /* Add this message to the packet list */ + return send_packet(midi, message, messageLength, timestamp); +} + + +static PmError +midi_begin_sysex(PmInternal *midi, PmTimestamp when) +{ + UInt64 when_ns; + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + assert(m); + m->sysex_byte_count = 0; + + /* compute timestamp */ + if (when == 0) when = midi->now; + /* if latency == 0, midi->now is not valid. We will just set it to zero */ + if (midi->latency == 0) when = 0; + when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta; + m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); + + if (m->packet == NULL) { + m->packet = MIDIPacketListInit(m->packetList); + /* this can never fail, right? failure would indicate something + unrecoverable */ + assert(m->packet); + } + return pmNoError; +} + + +static PmError +midi_end_sysex(PmInternal *midi, PmTimestamp when) +{ + PmError err; + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + assert(m); + + /* make sure we go foreward in time */ + if (m->sysex_timestamp < m->min_next_time) + m->sysex_timestamp = m->min_next_time; + + #ifdef LIMIT_RATE + if (m->sysex_timestamp < m->last_time) + m->sysex_timestamp = m->last_time; + m->last_time = m->sysex_timestamp + m->sysex_byte_count * + m->host_ticks_per_byte; + #endif + + /* now send what's in the buffer */ + err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count, + m->sysex_timestamp); + m->sysex_byte_count = 0; + if (err != pmNoError) { + m->packet = NULL; /* flush everything in the packet list */ + return err; + } + return pmNoError; +} + + +static PmError +midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) +{ + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + assert(m); + if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) { + PmError err = midi_end_sysex(midi, timestamp); + if (err != pmNoError) return err; + } + m->sysex_buffer[m->sysex_byte_count++] = byte; + return pmNoError; +} + + +static PmError +midi_write_realtime(PmInternal *midi, PmEvent *event) +{ + /* to send a realtime message during a sysex message, first + flush all pending sysex bytes into packet list */ + PmError err = midi_end_sysex(midi, 0); + if (err != pmNoError) return err; + /* then we can just do a normal midi_write_short */ + return midi_write_short(midi, event); +} + +static unsigned int midi_has_host_error(PmInternal *midi) +{ + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + return (m->callback_error[0] != 0) || (m->error[0] != 0); +} + + +static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len) +{ + midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; + msg[0] = 0; /* initialize to empty string */ + if (m) { /* make sure there is an open device to examine */ + if (m->error[0]) { + strncpy(msg, m->error, len); + m->error[0] = 0; /* clear the error */ + } else if (m->callback_error[0]) { + strncpy(msg, m->callback_error, len); + m->callback_error[0] = 0; /* clear the error */ + } + msg[len - 1] = 0; /* make sure string is terminated */ + } +} + + +MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp) +{ + UInt64 nanos; + if (timestamp <= 0) { + return (MIDITimeStamp)0; + } else { + nanos = (UInt64)timestamp * (UInt64)1000000; + return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos); + } +} + +PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp) +{ + UInt64 nanos; + nanos = AudioConvertHostTimeToNanos(timestamp); + return (PmTimestamp)(nanos / (UInt64)1000000); +} + + +// +// Code taken from http://developer.apple.com/qa/qa2004/qa1374.html +////////////////////////////////////// +// Obtain the name of an endpoint without regard for whether it has connections. +// The result should be released by the caller. +CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal) +{ + CFMutableStringRef result = CFStringCreateMutable(NULL, 0); + CFStringRef str; + + // begin with the endpoint's name + str = NULL; + MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str); + if (str != NULL) { + CFStringAppend(result, str); + CFRelease(str); + } + + MIDIEntityRef entity = NULL_REF; + MIDIEndpointGetEntity(endpoint, &entity); + if (entity == NULL_REF) + // probably virtual + return result; + + if (CFStringGetLength(result) == 0) { + // endpoint name has zero length -- try the entity + str = NULL; + MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str); + if (str != NULL) { + CFStringAppend(result, str); + CFRelease(str); + } + } + // now consider the device's name + MIDIDeviceRef device = NULL_REF; + MIDIEntityGetDevice(entity, &device); + if (device == NULL_REF) + return result; + + str = NULL; + MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str); + if (CFStringGetLength(result) == 0) { + CFRelease(result); + return str; + } + if (str != NULL) { + // if an external device has only one entity, throw away + // the endpoint name and just use the device name + if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) { + CFRelease(result); + return str; + } else { + if (CFStringGetLength(str) == 0) { + CFRelease(str); + return result; + } + // does the entity name already start with the device name? + // (some drivers do this though they shouldn't) + // if so, do not prepend + if (CFStringCompareWithOptions( result, /* endpoint name */ + str /* device name */, + CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) { + // prepend the device name to the entity name + if (CFStringGetLength(result) > 0) + CFStringInsert(result, 0, CFSTR(" ")); + CFStringInsert(result, 0, str); + } + CFRelease(str); + } + } + return result; +} + + +// Obtain the name of an endpoint, following connections. +// The result should be released by the caller. +static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint) +{ + CFMutableStringRef result = CFStringCreateMutable(NULL, 0); + CFStringRef str; + OSStatus err; + long i; + + // Does the endpoint have connections? + CFDataRef connections = NULL; + long nConnected = 0; + bool anyStrings = false; + err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections); + if (connections != NULL) { + // It has connections, follow them + // Concatenate the names of all connected devices + nConnected = CFDataGetLength(connections) / (int32_t) sizeof(MIDIUniqueID); + if (nConnected) { + const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); + for (i = 0; i < nConnected; ++i, ++pid) { + MIDIUniqueID id = EndianS32_BtoN(*pid); + MIDIObjectRef connObject; + MIDIObjectType connObjectType; + err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType); + if (err == noErr) { + if (connObjectType == kMIDIObjectType_ExternalSource || + connObjectType == kMIDIObjectType_ExternalDestination) { + // Connected to an external device's endpoint (10.3 and later). + str = EndpointName((MIDIEndpointRef)(connObject), true); + } else { + // Connected to an external device (10.2) (or something else, catch-all) + str = NULL; + MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str); + } + if (str != NULL) { + if (anyStrings) + CFStringAppend(result, CFSTR(", ")); + else anyStrings = true; + CFStringAppend(result, str); + CFRelease(str); + } + } + } + } + CFRelease(connections); + } + if (anyStrings) + return result; + + // Here, either the endpoint had no connections, or we failed to obtain names for any of them. + return EndpointName(endpoint, false); +} + + +char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint) +{ +#ifdef OLDCODE + MIDIEntityRef entity; + MIDIDeviceRef device; + + CFStringRef endpointName = NULL; + CFStringRef deviceName = NULL; +#endif + CFStringRef fullName = NULL; + CFStringEncoding defaultEncoding; + char* newName; + + /* get the default string encoding */ + defaultEncoding = CFStringGetSystemEncoding(); + + fullName = ConnectedEndpointName(endpoint); + +#ifdef OLDCODE + /* get the entity and device info */ + MIDIEndpointGetEntity(endpoint, &entity); + MIDIEntityGetDevice(entity, &device); + + /* create the nicely formated name */ + MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName); + MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName); + if (deviceName != NULL) { + fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"), + deviceName, endpointName); + } else { + fullName = endpointName; + } +#endif + /* copy the string into our buffer */ + newName = (char *) malloc(CFStringGetLength(fullName) + 1); + CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1, + defaultEncoding); + + /* clean up */ +#ifdef OLDCODE + if (endpointName) CFRelease(endpointName); + if (deviceName) CFRelease(deviceName); +#endif + if (fullName) CFRelease(fullName); + + return newName; +} + + + +pm_fns_node pm_macosx_in_dictionary = { + none_write_short, + none_sysex, + none_sysex, + none_write_byte, + none_write_short, + none_write_flush, + none_synchronize, + midi_in_open, + midi_abort, + midi_in_close, + success_poll, + midi_has_host_error, + midi_get_host_error, +}; + +pm_fns_node pm_macosx_out_dictionary = { + midi_write_short, + midi_begin_sysex, + midi_end_sysex, + midi_write_byte, + midi_write_realtime, + midi_write_flush, + midi_synchronize, + midi_out_open, + midi_abort, + midi_out_close, + success_poll, + midi_has_host_error, + midi_get_host_error, +}; + + +PmError pm_macosxcm_init(void) +{ + ItemCount numInputs, numOutputs, numDevices; + MIDIEndpointRef endpoint; + int i; + OSStatus macHostError; + char *error_text; + + /* Determine the number of MIDI devices on the system */ + numDevices = MIDIGetNumberOfDevices(); + numInputs = MIDIGetNumberOfSources(); + numOutputs = MIDIGetNumberOfDestinations(); + + /* Return prematurely if no devices exist on the system + Note that this is not an error. There may be no devices. + Pm_CountDevices() will return zero, which is correct and + useful information + */ + if (numDevices <= 0) { + return pmNoError; + } + + + /* Initialize the client handle */ + macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client); + if (macHostError != noErr) { + error_text = "MIDIClientCreate() in pm_macosxcm_init()"; + goto error_return; + } + + /* Create the input port */ + macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc, + NULL, &portIn); + if (macHostError != noErr) { + error_text = "MIDIInputPortCreate() in pm_macosxcm_init()"; + goto error_return; + } + + /* Create the output port */ + macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut); + if (macHostError != noErr) { + error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()"; + goto error_return; + } + + /* Iterate over the MIDI input devices */ + for (i = 0; i < numInputs; i++) { + endpoint = MIDIGetSource(i); + if (endpoint == NULL_REF) { + continue; + } + + /* set the first input we see to the default */ + if (pm_default_input_device_id == -1) + pm_default_input_device_id = pm_descriptor_index; + + /* Register this device with PortMidi */ + pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint), + TRUE, (void *) (long) endpoint, &pm_macosx_in_dictionary); + } + + /* Iterate over the MIDI output devices */ + for (i = 0; i < numOutputs; i++) { + endpoint = MIDIGetDestination(i); + if (endpoint == NULL_REF) { + continue; + } + + /* set the first output we see to the default */ + if (pm_default_output_device_id == -1) + pm_default_output_device_id = pm_descriptor_index; + + /* Register this device with PortMidi */ + pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint), + FALSE, (void *) (long) endpoint, + &pm_macosx_out_dictionary); + } + return pmNoError; + +error_return: + pm_hosterror = macHostError; + sprintf(pm_hosterror_text, "Host error %ld: %s\n", (long) macHostError, + error_text); + pm_macosxcm_term(); /* clear out any opened ports */ + return pmHostError; +} + +void pm_macosxcm_term(void) +{ + if (client != NULL_REF) MIDIClientDispose(client); + if (portIn != NULL_REF) MIDIPortDispose(portIn); + if (portOut != NULL_REF) MIDIPortDispose(portOut); +} diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmacosxcm.h b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmacosxcm.h new file mode 100644 index 0000000000..97235b5dd2 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmmacosxcm.h @@ -0,0 +1,6 @@ +/* system-specific definitions */
+
+PmError pm_macosxcm_init(void);
+void pm_macosxcm_term(void);
+
+PmDeviceID find_default_device(char *path, int input, PmDeviceID id);
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/readbinaryplist.c b/libs/backends/wavesaudio/portmidi/src/pm_mac/readbinaryplist.c new file mode 100644 index 0000000000..bccd095183 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/readbinaryplist.c @@ -0,0 +1,1115 @@ +/*
+
+readbinaryplist.c -- Roger B. Dannenberg, Jun 2008
+Based on ReadBinaryPList.m by Jens Ayton, 2007
+
+Note that this code is intended to read preference files and has an upper
+bound on file size (currently 100MB) and assumes in some places that 32 bit
+offsets are sufficient.
+
+Here are his comments:
+
+Reader for binary property list files (version 00).
+
+This has been found to work on all 566 binary plists in my ~/Library/Preferences/
+and /Library/Preferences/ directories. This probably does not provide full
+test coverage. It has also been found to provide different data to Apple's
+implementation when presented with a key-value archive. This is because Apple's
+implementation produces undocumented CFKeyArchiverUID objects. My implementation
+produces dictionaries instead, matching the in-file representation used in XML
+and OpenStep plists. See extract_uid().
+
+Full disclosure: in implementing this software, I read one comment and one
+struct defintion in CFLite, Apple's implementation, which is under the APSL
+license. I also deduced the information about CFKeyArchiverUID from that code.
+However, none of the implementation was copied.
+
+Copyright (C) 2007 Jens Ayton
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+*/
+
+/* A note about memory management:
+Strings and possibly other values are unique and because the values
+associated with IDs are cached, you end up with a directed graph rather
+than a tree. It is tricky to free the data because if you do a simple
+depth-first search to free nodes, you will free nodes twice. I decided
+to allocate memory from blocks of 1024 bytes and keep the blocks in a
+list associated with but private to this module. So the user should
+access this module by calling:
+ bplist_read_file() or bplist_read_user_pref() or
+ bplist_read_system_pref()
+which returns a value. When you are done with the value, call
+ bplist_free_data()
+This will of course free the value_ptr returned by bplist_read_*()
+
+To deal with memory exhaustion (what happens when malloc returns
+NULL?), use setjmp/longjmp -- a single setjmp protects the whole
+parser, and allocate uses longjmp to abort. After abort, memory
+is freed and NULL is returned to caller. There is not much here
+in the way of error reporting.
+
+Memory is obtained by calling allocate which either returns the
+memory requested or calls longjmp, so callers don't have to check.
+
+*/
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include "readbinaryplist.h"
+#include <Carbon/Carbon.h>
+
+#define NO 0
+#define YES 1
+#define BOOL int
+
+#define MAXPATHLEN 256
+
+/* there are 2 levels of error logging/printing:
+ * BPLIST_LOG and BPLIST_LOG_VERBOSE
+ * either or both can be set to non-zero to turn on
+ * If BPLIST_LOG_VERBOSE is true, then BPLIST_LOG
+ * is also true.
+ *
+ * In the code, logging is done by calling either
+ * bplist_log() or bplist_log_verbose(), which take
+ * parameters like printf but might be a no-op.
+ */
+
+/* #define BPLIST_LOG_VERBOSE 1 */
+
+#if BPLIST_LOG_VERBOSE
+ #ifndef BPLIST_LOG
+ #define BPLIST_LOG 1
+ #endif
+#endif
+
+#if BPLIST_LOG
+ #define bplist_log printf
+#else
+ #define bplist_log(...)
+#endif
+
+#if BPLIST_LOG_VERBOSE
+ #define bplist_log_verbose bplist_log
+#else
+ #define bplist_log_verbose(...)
+#endif
+
+
+/********* MEMORY MANAGEMENT ********/
+#define BLOCK_SIZE 1024
+// memory is aligned to multiples of this; assume malloc automatically
+// aligns to this number and assume this number is > sizeof(void *)
+#define ALIGNMENT 8
+static void *block_list = NULL;
+static char *free_ptr = NULL;
+static char *end_ptr = NULL;
+static jmp_buf abort_parsing;
+
+static void *allocate(size_t size)
+{
+ void *result;
+ if (free_ptr + size > end_ptr) {
+ size_t how_much = BLOCK_SIZE;
+ // align everything to 8 bytes
+ if (size > BLOCK_SIZE - ALIGNMENT) {
+ how_much = size + ALIGNMENT;
+ }
+ result = malloc(how_much);
+ if (result == NULL) {
+ /* serious problem */
+ longjmp(abort_parsing, 1);
+ }
+ *((void **)result) = block_list;
+ block_list = result;
+ free_ptr = ((char *) result) + ALIGNMENT;
+ end_ptr = ((char *) result) + how_much;
+ }
+ // now, there is enough rooom at free_ptr
+ result = free_ptr;
+ free_ptr += size;
+ return result;
+}
+
+void bplist_free_data()
+{
+ while (block_list) {
+ void *next = *(void **)block_list;
+ free(block_list);
+ block_list = next;
+ }
+ free_ptr = NULL;
+ end_ptr = NULL;
+}
+
+// layout of trailer -- last 32 bytes in plist data
+ uint8_t unused[6];
+ uint8_t offset_int_size;
+ uint8_t object_ref_size;
+ uint64_t object_count;
+ uint64_t top_level_object;
+ uint64_t offset_table_offset;
+
+
+enum
+{
+ kHEADER_SIZE = 8,
+ kTRAILER_SIZE = 32, //sizeof(bplist_trailer_node),
+ kMINIMUM_SANE_SIZE = kHEADER_SIZE + kTRAILER_SIZE
+};
+
+
+static const char kHEADER_BYTES[kHEADER_SIZE] = "bplist00";
+
+// map from UID key to previously parsed value
+typedef struct cache_struct {
+ uint64_t key;
+ value_ptr value;
+ struct cache_struct *next;
+} cache_node, *cache_ptr;
+
+
+typedef struct bplist_info
+{
+ uint64_t object_count;
+ const uint8_t *data_bytes;
+ uint64_t length;
+ uint64_t offset_table_offset;
+ uint8_t offset_int_size;
+ uint8_t object_ref_size;
+ cache_ptr cache;
+} bplist_info_node, *bplist_info_ptr;
+
+
+static value_ptr bplist_read_pldata(pldata_ptr data);
+static value_ptr bplist_read_pref(char *filename, OSType folder_type);
+static uint64_t read_sized_int(bplist_info_ptr bplist, uint64_t offset, uint8_t size);
+static uint64_t read_offset(bplist_info_ptr bplist, uint64_t index);
+static BOOL read_self_sized_int(bplist_info_ptr bplist, uint64_t offset, uint64_t *outValue, size_t *outSize);
+
+static value_ptr extract_object(bplist_info_ptr bplist, uint64_t objectRef);
+static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset);
+static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset);
+
+
+value_ptr value_create()
+{
+ value_ptr value = (value_ptr) allocate(sizeof(value_node));
+ return value;
+}
+
+
+void value_set_integer(value_ptr v, int64_t i) {
+ v->tag = kTAG_INT; v->integer = i;
+}
+
+void value_set_real(value_ptr v, double d) {
+ v->tag = kTAG_REAL; v->real = d;
+}
+
+// d is seconds since 1 January 2001
+void value_set_date(value_ptr v, double d) {
+ v->tag = kTAG_DATE; v->real = d;
+}
+
+void value_set_ascii_string(value_ptr v, const uint8_t *s, size_t len) {
+ v->tag = kTAG_ASCIISTRING;
+ v->string = (char *) allocate(len + 1);
+ memcpy(v->string, s, len);
+ v->string[len] = 0;
+}
+
+void value_set_unicode_string(value_ptr v, const uint8_t *s, size_t len) {
+ v->tag = kTAG_UNICODESTRING;
+ v->string = (char *) allocate(len + 1);
+ memcpy(v->string, s, len);
+ v->string[len] = 0;
+}
+
+void value_set_uid(value_ptr v, uint64_t uid)
+{
+ v->tag = kTAG_UID; v->uinteger = uid;
+}
+
+// v->data points to a pldata that points to the actual bytes
+// the bytes are copied, so caller must free byte source (*data)
+void value_set_data(value_ptr v, const uint8_t *data, size_t len) {
+ v->tag = kTAG_DATA;
+ pldata_ptr pldata = (pldata_ptr) allocate(sizeof(pldata_node));
+ pldata->data = (uint8_t *) allocate(len);
+ memcpy(pldata->data, data, len);
+ pldata->len = len;
+ v->data = pldata;
+ printf("value at %p gets data at %p\n", v, pldata);
+}
+
+// caller releases ownership of array to value_ptr v
+void value_set_array(value_ptr v, value_ptr *array, size_t length) {
+ array_ptr a = (array_ptr) allocate(sizeof(array_node));
+ a->array = array;
+ a->length = length;
+ v->tag = kTAG_ARRAY;
+ v->array = a;
+}
+
+// caller releases ownership of dict to value_ptr v
+void value_set_dict(value_ptr v, dict_ptr dict) {
+ v->tag = kTAG_DICTIONARY;
+ v->dict = dict;
+}
+
+
+// look up an objectref in the cache, a ref->value_ptr mapping
+value_ptr cache_lookup(cache_ptr cache, uint64_t ref)
+{
+ while (cache) {
+ if (cache->key == ref) {
+ return cache->value;
+ }
+ cache = cache->next;
+ }
+ return NULL;
+}
+
+
+// insert an objectref and value in the cache
+void cache_insert(cache_ptr *cache, uint64_t ref, value_ptr value)
+{
+ cache_ptr c = (cache_ptr) allocate(sizeof(cache_node));
+ c->key = ref;
+ c->value = value;
+ c->next = *cache;
+ *cache = c;
+}
+
+
+// insert an objectref and value in a dictionary
+void dict_insert(dict_ptr *dict, value_ptr key, value_ptr value)
+{
+ dict_ptr d = (dict_ptr) allocate(sizeof(dict_node));
+ d->key = key;
+ d->value = value;
+ d->next = *dict;
+ *dict = d;
+}
+
+
+BOOL is_binary_plist(pldata_ptr data)
+{
+ if (data->len < kMINIMUM_SANE_SIZE) return NO;
+ return memcmp(data->data, kHEADER_BYTES, kHEADER_SIZE) == 0;
+}
+
+
+value_ptr bplist_read_file(char *filename)
+{
+ struct stat stbuf;
+ pldata_node pldata;
+ FILE *file;
+ size_t n;
+ value_ptr value;
+ int rslt = stat(filename, &stbuf);
+ if (rslt) {
+ #if BPLIST_LOG
+ perror("in stat");
+ #endif
+ bplist_log("Could not stat %s, error %d\n", filename, rslt);
+ return NULL;
+ }
+ // if file is >100MB, assume it is not a preferences file and give up
+ if (stbuf.st_size > 100000000) {
+ bplist_log("Large file %s encountered (%llu bytes) -- not read\n",
+ filename, stbuf.st_size);
+ return NULL;
+ }
+ pldata.len = (size_t) stbuf.st_size;
+ // note: this is supposed to be malloc, not allocate. It is separate
+ // from the graph structure, large, and easy to free right after
+ // parsing.
+ pldata.data = (uint8_t *) malloc(pldata.len);
+ if (!pldata.data) {
+ bplist_log("Could not allocate %lu bytes for %s\n",
+ (unsigned long) pldata.len, filename);
+ return NULL;
+ }
+ file = fopen(filename, "rb");
+ if (!file) {
+ bplist_log("Could not open %s\n", filename);
+ return NULL;
+ }
+ n = fread(pldata.data, 1, pldata.len, file);
+ if (n != pldata.len) {
+ bplist_log("Error reading from %s\n", filename);
+ return NULL;
+ }
+ value = bplist_read_pldata(&pldata);
+ free(pldata.data);
+ return value;
+}
+
+
+value_ptr bplist_read_pref(char *filename, OSType folder_type)
+{
+ FSRef prefdir;
+ char cstr[MAXPATHLEN];
+
+ OSErr err = FSFindFolder(kOnAppropriateDisk, folder_type,
+ FALSE, &prefdir);
+ if (err) {
+ bplist_log("Error finding preferences folder: %d\n", err);
+ return NULL;
+ }
+ err = FSRefMakePath(&prefdir, (UInt8 *) cstr, (UInt32) (MAXPATHLEN - 1));
+ if (err) {
+ bplist_log("Error making path name for preferences folder: %d\n", err);
+ return NULL;
+ }
+ strlcat(cstr, "/", MAXPATHLEN);
+ strlcat(cstr, filename, MAXPATHLEN);
+ return bplist_read_file(cstr);
+}
+
+
+value_ptr bplist_read_system_pref(char *filename) {
+ return bplist_read_pref(filename, kSystemPreferencesFolderType);
+}
+
+
+value_ptr bplist_read_user_pref(char *filename) {
+ return bplist_read_pref(filename, kPreferencesFolderType);
+}
+
+
+// data is stored with high-order bytes first.
+// read from plist data in a machine-independent fashion
+//
+uint64_t convert_uint64(uint8_t *ptr)
+{
+ uint64_t rslt = 0;
+ int i;
+ // shift in bytes, high-order first
+ for (i = 0; i < sizeof(uint64_t); i++) {
+ rslt <<= 8;
+ rslt += ptr[i];
+ }
+ return rslt;
+}
+
+
+value_ptr bplist_read_pldata(pldata_ptr data)
+{
+ value_ptr result = NULL;
+ bplist_info_node bplist;
+ uint8_t *ptr;
+ uint64_t top_level_object;
+ int i;
+
+ if (data == NULL) return NULL;
+ if (!is_binary_plist(data)) {
+ bplist_log("Bad binary plist: too short or invalid header.\n");
+ return NULL;
+ }
+
+ // read trailer
+ ptr = (uint8_t *) (data->data + data->len - kTRAILER_SIZE);
+ bplist.offset_int_size = ptr[6];
+ bplist.object_ref_size = ptr[7];
+ bplist.object_count = convert_uint64(ptr + 8);
+ top_level_object = convert_uint64(ptr + 16);
+ bplist.offset_table_offset = convert_uint64(ptr + 24);
+
+ // Basic sanity checks
+ if (bplist.offset_int_size < 1 || bplist.offset_int_size > 8 ||
+ bplist.object_ref_size < 1 || bplist.object_ref_size > 8 ||
+ bplist.offset_table_offset < kHEADER_SIZE) {
+ bplist_log("Bad binary plist: trailer declared insane.\n");
+ return NULL;
+ }
+
+ // Ensure offset table is inside file
+ uint64_t offsetTableSize = bplist.offset_int_size * bplist.object_count;
+ if (offsetTableSize + bplist.offset_table_offset + kTRAILER_SIZE >
+ data->len) {
+ bplist_log("Bad binary plist: offset table overlaps end of container.\n");
+ return NULL;
+ }
+
+ bplist.data_bytes = data->data;
+ bplist.length = data->len;
+ bplist.cache = NULL; /* dictionary is empty */
+
+ bplist_log_verbose("Got a sane bplist with %llu items, offset_int_size: %u, object_ref_size: %u\n",
+ bplist.object_count, bplist.offset_int_size,
+ bplist.object_ref_size);
+ /* at this point, we are ready to do some parsing which allocates
+ memory for the result data structure. If memory allocation (using
+ allocate fails, a longjmp will return to here and we simply give up
+ */
+ i = setjmp(abort_parsing);
+ if (i == 0) {
+ result = extract_object(&bplist, top_level_object);
+ } else {
+ bplist_log("allocate() failed to allocate memory. Giving up.\n");
+ result = NULL;
+ }
+ if (!result) {
+ bplist_free_data();
+ }
+ return result;
+}
+
+
+static value_ptr extract_object(bplist_info_ptr bplist, uint64_t objectRef)
+{
+ uint64_t offset;
+ value_ptr result = NULL;
+ uint8_t objectTag;
+
+ if (objectRef >= bplist->object_count) {
+ // Out-of-range object reference.
+ bplist_log("Bad binary plist: object index is out of range.\n");
+ return NULL;
+ }
+
+ // Use cached object if it exists
+ result = cache_lookup(bplist->cache, objectRef);
+ if (result != NULL) return result;
+
+ // Otherwise, find object in file.
+ offset = read_offset(bplist, objectRef);
+ if (offset > bplist->length) {
+ // Out-of-range offset.
+ bplist_log("Bad binary plist: object outside container.\n");
+ return NULL;
+ }
+ objectTag = *(bplist->data_bytes + offset);
+ switch (objectTag & 0xF0) {
+ case kTAG_SIMPLE:
+ result = extract_simple(bplist, offset);
+ break;
+
+ case kTAG_INT:
+ result = extract_int(bplist, offset);
+ break;
+
+ case kTAG_REAL:
+ result = extract_real(bplist, offset);
+ break;
+
+ case kTAG_DATE:
+ result = extract_date(bplist, offset);
+ break;
+
+ case kTAG_DATA:
+ result = extract_data(bplist, offset);
+ break;
+
+ case kTAG_ASCIISTRING:
+ result = extract_ascii_string(bplist, offset);
+ break;
+
+ case kTAG_UNICODESTRING:
+ result = extract_unicode_string(bplist, offset);
+ break;
+
+ case kTAG_UID:
+ result = extract_uid(bplist, offset);
+ break;
+
+ case kTAG_ARRAY:
+ result = extract_array(bplist, offset);
+ break;
+
+ case kTAG_DICTIONARY:
+ result = extract_dictionary(bplist, offset);
+ break;
+
+ default:
+ // Unknown tag.
+ bplist_log("Bad binary plist: unknown tag 0x%X.\n",
+ (objectTag & 0x0F) >> 4);
+ result = NULL;
+ }
+
+ // Cache and return result.
+ if (result != NULL)
+ cache_insert(&bplist->cache, objectRef, result);
+ return result;
+}
+
+
+static uint64_t read_sized_int(bplist_info_ptr bplist, uint64_t offset,
+ uint8_t size)
+{
+ assert(bplist->data_bytes != NULL && size >= 1 && size <= 8 &&
+ offset + size <= bplist->length);
+
+ uint64_t result = 0;
+ const uint8_t *byte = bplist->data_bytes + offset;
+
+ do {
+ // note that ints seem to be high-order first
+ result = (result << 8) | *byte++;
+ } while (--size);
+
+ return result;
+}
+
+
+static uint64_t read_offset(bplist_info_ptr bplist, uint64_t index)
+{
+ assert(index < bplist->object_count);
+
+ return read_sized_int(bplist,
+ bplist->offset_table_offset + bplist->offset_int_size * index,
+ bplist->offset_int_size);
+}
+
+
+static BOOL read_self_sized_int(bplist_info_ptr bplist, uint64_t offset,
+ uint64_t *outValue, size_t *outSize)
+{
+ uint32_t size;
+ int64_t value;
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ size = 1 << (bplist->data_bytes[offset] & 0x0F);
+ if (size > 8) {
+ // Maximum allowable size in this implementation is 1<<3 = 8 bytes.
+ // This also happens to be the biggest we can handle.
+ return NO;
+ }
+
+ if (offset + 1 + size > bplist->length) {
+ // Out of range.
+ return NO;
+ }
+
+ value = read_sized_int(bplist, offset + 1, size);
+
+ if (outValue != NULL) *outValue = value;
+ if (outSize != NULL) *outSize = size + 1; // +1 for tag byte.
+ return YES;
+}
+
+
+static value_ptr extract_simple(bplist_info_ptr bplist, uint64_t offset)
+{
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+ value_ptr value = value_create();
+
+ switch (bplist->data_bytes[offset]) {
+ case kVALUE_NULL:
+ value->tag = kVALUE_NULL;
+ return value;
+
+ case kVALUE_TRUE:
+ value->tag = kVALUE_TRUE;
+ return value;
+
+ case kVALUE_FALSE:
+ value->tag = kVALUE_FALSE;
+ return value;
+ }
+
+ // Note: kVALUE_FILLER is treated as invalid, because it, er, is.
+ bplist_log("Bad binary plist: invalid atom.\n");
+ free(value);
+ return NULL;
+}
+
+
+static value_ptr extract_int(bplist_info_ptr bplist, uint64_t offset)
+{
+ value_ptr value = value_create();
+ value->tag = kTAG_INT;
+
+ if (!read_self_sized_int(bplist, offset, &value->uinteger, NULL)) {
+ bplist_log("Bad binary plist: invalid integer object.\n");
+ }
+
+ /* NOTE: originally, I sign-extended here. This was the wrong thing; it
+ turns out that negative ints are always stored as 64-bit, and smaller
+ ints are unsigned.
+ */
+ return value;
+}
+
+
+static value_ptr extract_real(bplist_info_ptr bplist, uint64_t offset)
+{
+ value_ptr value = value_create();
+ uint32_t size;
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ size = 1 << (bplist->data_bytes[offset] & 0x0F);
+
+ // FIXME: what to do if faced with other sizes for float/double?
+ assert (sizeof (float) == sizeof (uint32_t) &&
+ sizeof (double) == sizeof (uint64_t));
+
+ if (offset + 1 + size > bplist->length) {
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "floating-point number");
+ free(value);
+ return NULL;
+ }
+
+ if (size == sizeof (float)) {
+ // cast is ok because we know size is 4 bytes
+ uint32_t i = (uint32_t) read_sized_int(bplist, offset + 1, size);
+ // Note that this handles byte swapping.
+ value_set_real(value, *(float *)&i);
+ return value;
+ } else if (size == sizeof (double)) {
+ uint64_t i = read_sized_int(bplist, offset + 1, size);
+ // Note that this handles byte swapping.
+ value_set_real(value, *(double *)&i);
+ return value;
+ } else {
+ // Can't handle floats of other sizes.
+ bplist_log("Bad binary plist: can't handle %u-byte float.\n", size);
+ free(value);
+ return NULL;
+ }
+}
+
+
+static value_ptr extract_date(bplist_info_ptr bplist, uint64_t offset)
+{
+ value_ptr value;
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ // Data has size code like int and real, but only 3 (meaning 8 bytes) is valid.
+ if (bplist->data_bytes[offset] != kVALUE_FULLDATETAG) {
+ bplist_log("Bad binary plist: invalid size for date object.\n");
+ return NULL;
+ }
+
+ if (offset + 1 + sizeof (double) > bplist->length) {
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "date");
+ return NULL;
+ }
+
+ // FIXME: what to do if faced with other sizes for double?
+ assert (sizeof (double) == sizeof (uint64_t));
+
+ uint64_t date = read_sized_int(bplist, offset + 1, sizeof(double));
+ // Note that this handles byte swapping.
+ value = value_create();
+ value_set_date(value, *(double *)&date);
+ return value;
+}
+
+
+uint64_t bplist_get_a_size(bplist_info_ptr bplist,
+ uint64_t *offset_ptr, char *msg)
+{
+ uint64_t size = bplist->data_bytes[*offset_ptr] & 0x0F;
+ (*offset_ptr)++;
+ if (size == 0x0F) {
+ // 0x0F means separate int size follows.
+ // Smaller values are used for short data.
+ size_t extra; // the length of the data size we are about to read
+ if ((bplist->data_bytes[*offset_ptr] & 0xF0) != kTAG_INT) {
+ // Bad data, mistagged size int
+ bplist_log("Bad binary plist: %s object size is not tagged as int.\n",
+ msg);
+ return UINT64_MAX; // error
+ }
+
+ // read integer data as size, extra tells how many bytes to skip
+ if (!read_self_sized_int(bplist, *offset_ptr, &size, &extra)) {
+ bplist_log("Bad binary plist: invalid %s object size tag.\n",
+ "data");
+ return UINT64_MAX; // error
+ }
+ (*offset_ptr) += extra;
+ }
+
+ if (*offset_ptr + size > bplist->length) {
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "data");
+ return UINT64_MAX; // error
+ }
+ return size;
+}
+
+
+static value_ptr extract_data(bplist_info_ptr bplist, uint64_t offset)
+{
+ uint64_t size;
+ value_ptr value;
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ if ((size = bplist_get_a_size(bplist, &offset, "data")) == UINT64_MAX)
+ return NULL;
+
+ value = value_create();
+ // cast is ok because we only allow files up to 100MB:
+ value_set_data(value, bplist->data_bytes + (size_t) offset, (size_t) size);
+ return value;
+}
+
+
+static value_ptr extract_ascii_string(bplist_info_ptr bplist, uint64_t offset)
+{
+ uint64_t size;
+ value_ptr value; // return value
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ if ((size = bplist_get_a_size(bplist, &offset, "ascii string")) ==
+ UINT64_MAX)
+ return NULL;
+
+ value = value_create();
+ // cast is ok because we only allow 100MB files
+ value_set_ascii_string(value, bplist->data_bytes + (size_t) offset,
+ (size_t) size);
+ return value;
+}
+
+
+static value_ptr extract_unicode_string(bplist_info_ptr bplist, uint64_t offset)
+{
+ uint64_t size;
+ value_ptr value;
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ if ((size = bplist_get_a_size(bplist, &offset, "unicode string")) ==
+ UINT64_MAX)
+ return NULL;
+
+ value = value_create();
+ // cast is ok because we only allow 100MB files
+ value_set_unicode_string(value, bplist->data_bytes + (size_t) offset,
+ (size_t) size);
+ return value;
+}
+
+
+static value_ptr extract_uid(bplist_info_ptr bplist, uint64_t offset)
+{
+ /* UIDs are used by Cocoa's key-value coder.
+ When writing other plist formats, they are expanded to dictionaries of
+ the form <dict><key>CF$UID</key><integer>value</integer></dict>, so we
+ do the same here on reading. This results in plists identical to what
+ running plutil -convert xml1 gives us. However, this is not the same
+ result as [Core]Foundation's plist parser, which extracts them as un-
+ introspectable CF objects. In fact, it even seems to convert the CF$UID
+ dictionaries from XML plists on the fly.
+ */
+
+ value_ptr value;
+ uint64_t uid;
+
+ if (!read_self_sized_int(bplist, offset, &uid, NULL)) {
+ bplist_log("Bad binary plist: invalid UID object.\n");
+ return NULL;
+ }
+
+ // assert(NO); // original code suggests using a string for a key
+ // but our dictionaries all use big ints for keys, so I don't know
+ // what to do here
+
+ // In practice, I believe this code is never executed by PortMidi.
+ // I changed it to do something and not raise compiler warnings, but
+ // not sure what the code should do.
+
+ value = value_create();
+ value_set_uid(value, uid);
+ // return [NSDictionary dictionaryWithObject:
+ // [NSNumber numberWithUnsignedLongLong:value]
+ // forKey:"CF$UID"];
+ return value;
+}
+
+
+static value_ptr extract_array(bplist_info_ptr bplist, uint64_t offset)
+{
+ uint64_t i, count;
+ uint64_t size;
+ uint64_t elementID;
+ value_ptr element = NULL;
+ value_ptr *array = NULL;
+ value_ptr value = NULL;
+ BOOL ok = YES;
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+ if ((count = bplist_get_a_size(bplist, &offset, "array")) == UINT64_MAX)
+ return NULL;
+
+ if (count > UINT64_MAX / bplist->object_ref_size - offset) {
+ // Offset overflow.
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "array");
+ return NULL;
+ }
+
+ size = bplist->object_ref_size * count;
+ if (size + offset > bplist->length) {
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "array");
+ return NULL;
+ }
+
+ // got count, the number of array elements
+
+ value = value_create();
+ assert(value);
+
+ if (count == 0) {
+ // count must be size_t or smaller because max file size is 100MB
+ value_set_array(value, array, (size_t) count);
+ return value;
+ }
+
+ array = allocate(sizeof(value_ptr) * (size_t) count);
+
+ for (i = 0; i != count; ++i) {
+ bplist_log_verbose("[%u]\n", i);
+ elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size,
+ bplist->object_ref_size);
+ element = extract_object(bplist, elementID);
+ if (element != NULL) {
+ array[i] = element;
+ } else {
+ ok = NO;
+ break;
+ }
+ }
+ if (ok) { // count is smaller than size_t max because of 100MB file limit
+ value_set_array(value, array, (size_t) count);
+ }
+
+ return value;
+}
+
+
+static value_ptr extract_dictionary(bplist_info_ptr bplist, uint64_t offset)
+{
+ uint64_t i, count;
+ uint64_t size;
+ uint64_t elementID;
+ value_ptr value = NULL;
+ dict_ptr dict = NULL;
+ BOOL ok = YES;
+
+ assert(bplist->data_bytes != NULL && offset < bplist->length);
+
+
+ if ((count = bplist_get_a_size(bplist, &offset, "array")) == UINT64_MAX)
+ return NULL;
+
+ if (count > UINT64_MAX / (bplist->object_ref_size * 2) - offset) {
+ // Offset overflow.
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "dictionary");
+ return NULL;
+ }
+
+ size = bplist->object_ref_size * count * 2;
+ if (size + offset > bplist->length) {
+ bplist_log("Bad binary plist: %s object overlaps end of container.\n",
+ "dictionary");
+ return NULL;
+ }
+
+ value = value_create();
+ if (count == 0) {
+ value_set_dict(value, NULL);
+ return value;
+ }
+
+ for (i = 0; i != count; ++i) {
+ value_ptr key;
+ value_ptr val;
+ elementID = read_sized_int(bplist, offset + i * bplist->object_ref_size,
+ bplist->object_ref_size);
+ key = extract_object(bplist, elementID);
+ if (key != NULL) {
+ bplist_log_verbose("key: %p\n", key);
+ } else {
+ ok = NO;
+ break;
+ }
+
+ elementID = read_sized_int(bplist,
+ offset + (i + count) * bplist->object_ref_size,
+ bplist->object_ref_size);
+ val = extract_object(bplist, elementID);
+ if (val != NULL) {
+ dict_insert(&dict, key, val);
+ } else {
+ ok = NO;
+ break;
+ }
+ }
+ if (ok) {
+ value_set_dict(value, dict);
+ }
+
+ return value;
+}
+
+/*************** functions for accessing values ****************/
+
+
+char *value_get_asciistring(value_ptr v)
+{
+ if (v->tag != kTAG_ASCIISTRING) return NULL;
+ return v->string;
+}
+
+
+value_ptr value_dict_lookup_using_string(value_ptr v, char *key)
+{
+ dict_ptr dict;
+ if (v->tag != kTAG_DICTIONARY) return NULL; // not a dictionary
+ dict = v->dict;
+ /* search for key */
+ while (dict) {
+ if (dict->key && dict->key->tag == kTAG_ASCIISTRING &&
+ strcmp(key, dict->key->string) == 0) { // found it
+ return dict->value;
+ }
+ dict = dict->next;
+ }
+ return NULL; /* not found */
+}
+
+value_ptr value_dict_lookup_using_path(value_ptr v, char *path)
+{
+ char key[MAX_KEY_SIZE];
+ while (*path) { /* more to the path */
+ int i = 0;
+ while (i < MAX_KEY_SIZE - 1) {
+ key[i] = *path++;
+ if (key[i] == '/') { /* end of entry in path */
+ key[i + 1] = 0;
+ break;
+ }
+ if (!key[i]) {
+ path--; /* back up to end of string char */
+ break; /* this will cause outer loop to exit */
+ }
+ i++;
+ }
+ if (!v || v->tag != kTAG_DICTIONARY) return NULL;
+ /* now, look up the key to get next value */
+ v = value_dict_lookup_using_string(v, key);
+ if (v == NULL) return NULL;
+ }
+ return v;
+}
+
+
+/*************** functions for debugging ***************/
+
+void plist_print(value_ptr v)
+{
+ size_t i;
+ int comma_needed;
+ dict_ptr dict;
+ if (!v) {
+ printf("NULL");
+ return;
+ }
+ switch (v->tag & 0xF0) {
+ case kTAG_SIMPLE:
+ switch (v->tag) {
+ case kVALUE_NULL:
+ printf("NULL@%p", v); break;
+ case kVALUE_FALSE:
+ printf("FALSE@%p", v); break;
+ case kVALUE_TRUE:
+ printf("TRUE@%p", v); break;
+ default:
+ printf("UNKNOWN tag=%x@%p", v->tag, v); break;
+ }
+ break;
+ case kTAG_INT:
+ printf("%lld@%p", v->integer, v); break;
+ case kTAG_REAL:
+ printf("%g@%p", v->real, v); break;
+ case kTAG_DATE:
+ printf("date:%g@%p", v->real, v); break;
+ case kTAG_DATA:
+ printf("data@%p->%p:[%p:", v, v->data, v->data->data);
+ for (i = 0; i < v->data->len; i++) {
+ printf(" %2x", v->data->data[i]);
+ }
+ printf("]"); break;
+ case kTAG_ASCIISTRING:
+ printf("%p:\"%s\"@%p", v->string, v->string, v); break;
+ case kTAG_UNICODESTRING:
+ printf("unicode:%p:\"%s\"@%p", v->string, v->string, v); break;
+ case kTAG_UID:
+ printf("UID:%llu@%p", v->uinteger, v); break;
+ case kTAG_ARRAY:
+ comma_needed = FALSE;
+ printf("%p->%p:[%p:", v, v->array, v->array->array);
+ for (i = 0; i < v->array->length; i++) {
+ if (comma_needed) printf(", ");
+ plist_print(v->array->array[i]);
+ comma_needed = TRUE;
+ }
+ printf("]"); break;
+ case kTAG_DICTIONARY:
+ comma_needed = FALSE;
+ printf("%p:[", v);
+ dict = v->dict;
+ while (dict) {
+ if (comma_needed) printf(", ");
+ printf("%p:", dict);
+ plist_print(dict->key);
+ printf("->");
+ plist_print(dict->value);
+ comma_needed = TRUE;
+ dict = dict->next;
+ }
+ printf("]"); break;
+ default:
+ printf("UNKNOWN tag=%x", v->tag);
+ break;
+ }
+}
+
+
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_mac/readbinaryplist.h b/libs/backends/wavesaudio/portmidi/src/pm_mac/readbinaryplist.h new file mode 100644 index 0000000000..577865996b --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_mac/readbinaryplist.h @@ -0,0 +1,88 @@ +/* readbinaryplist.h -- header to read preference files + + Roger B. Dannenberg, Jun 2008 +*/ + +#include <stdint.h> /* for uint8_t ... */ + +#ifndef TRUE + #define TRUE 1 + #define FALSE 0 +#endif + +#define MAX_KEY_SIZE 256 + +enum +{ + // Object tags (high nybble) + kTAG_SIMPLE = 0x00, // Null, true, false, filler, or invalid + kTAG_INT = 0x10, + kTAG_REAL = 0x20, + kTAG_DATE = 0x30, + kTAG_DATA = 0x40, + kTAG_ASCIISTRING = 0x50, + kTAG_UNICODESTRING = 0x60, + kTAG_UID = 0x80, + kTAG_ARRAY = 0xA0, + kTAG_DICTIONARY = 0xD0, + + // "simple" object values + kVALUE_NULL = 0x00, + kVALUE_FALSE = 0x08, + kVALUE_TRUE = 0x09, + kVALUE_FILLER = 0x0F, + + kVALUE_FULLDATETAG = 0x33 // Dates are tagged with a whole byte. +}; + + +typedef struct pldata_struct { + uint8_t *data; + size_t len; +} pldata_node, *pldata_ptr; + + +typedef struct array_struct { + struct value_struct **array; + uint64_t length; +} array_node, *array_ptr; + + +// a dict_node is a list of <key, value> pairs +typedef struct dict_struct { + struct value_struct *key; + struct value_struct *value; + struct dict_struct *next; +} dict_node, *dict_ptr; + + +// an value_node is a value with a tag telling the type +typedef struct value_struct { + int tag; + union { + int64_t integer; + uint64_t uinteger; + double real; + char *string; + pldata_ptr data; + array_ptr array; + struct dict_struct *dict; + }; +} value_node, *value_ptr; + + +value_ptr bplist_read_file(char *filename); +value_ptr bplist_read_user_pref(char *filename); +value_ptr bplist_read_system_pref(char *filename); +void bplist_free_data(); + +/*************** functions for accessing values ****************/ + +char *value_get_asciistring(value_ptr v); +value_ptr value_dict_lookup_using_string(value_ptr v, char *key); +value_ptr value_dict_lookup_using_path(value_ptr v, char *path); + +/*************** functions for debugging ***************/ + +void plist_print(value_ptr v); + diff --git a/libs/backends/wavesaudio/portmidi/src/pm_win/pmwin.c b/libs/backends/wavesaudio/portmidi/src/pm_win/pmwin.c new file mode 100644 index 0000000000..5b4dec63fc --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_win/pmwin.c @@ -0,0 +1,143 @@ +/* pmwin.c -- PortMidi os-dependent code */
+
+/* This file only needs to implement:
+ pm_init(), which calls various routines to register the
+ available midi devices,
+ Pm_GetDefaultInputDeviceID(), and
+ Pm_GetDefaultOutputDeviceID().
+ This file must
+ be separate from the main portmidi.c file because it is system
+ dependent, and it is separate from, say, pmwinmm.c, because it
+ might need to register devices for winmm, directx, and others.
+
+ */
+
+#include "stdlib.h"
+#include "portmidi.h"
+#include "pmutil.h"
+#include "pminternal.h"
+#include "pmwinmm.h"
+#ifdef DEBUG
+#include "stdio.h"
+#endif
+#include <windows.h>
+
+/* pm_exit is called when the program exits.
+ It calls pm_term to make sure PortMidi is properly closed.
+ If DEBUG is on, we prompt for input to avoid losing error messages.
+ */
+static void pm_exit(void) {
+ pm_term();
+#ifdef DEBUG
+#define STRING_MAX 80
+ {
+ char line[STRING_MAX];
+ printf("Type ENTER...\n");
+ /* note, w/o this prompting, client console application can not see one
+ of its errors before closing. */
+ fgets(line, STRING_MAX, stdin);
+ }
+#endif
+}
+
+
+/* pm_init is the windows-dependent initialization.*/
+void pm_init(void)
+{
+ atexit(pm_exit);
+#ifdef DEBUG
+ printf("registered pm_exit with atexit()\n");
+#endif
+ pm_winmm_init();
+ /* initialize other APIs (DirectX?) here */
+}
+
+
+void pm_term(void) {
+ pm_winmm_term();
+}
+
+
+static PmDeviceID pm_get_default_device_id(int is_input, char *key) {
+ HKEY hkey;
+#define PATTERN_MAX 256
+ char pattern[PATTERN_MAX];
+ long pattern_max = PATTERN_MAX;
+ DWORD dwType;
+ /* Find first input or device -- this is the default. */
+ PmDeviceID id = pmNoDevice;
+ int i, j;
+ Pm_Initialize(); /* make sure descriptors exist! */
+ for (i = 0; i < pm_descriptor_index; i++) {
+ if (descriptors[i].pub.input == is_input) {
+ id = i;
+ break;
+ }
+ }
+ /* Look in registry for a default device name pattern. */
+ if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software", 0, KEY_READ, &hkey) !=
+ ERROR_SUCCESS) {
+ return id;
+ }
+ if (RegOpenKeyEx(hkey, "JavaSoft", 0, KEY_READ, &hkey) !=
+ ERROR_SUCCESS) {
+ return id;
+ }
+ if (RegOpenKeyEx(hkey, "Prefs", 0, KEY_READ, &hkey) !=
+ ERROR_SUCCESS) {
+ return id;
+ }
+ if (RegOpenKeyEx(hkey, "/Port/Midi", 0, KEY_READ, &hkey) !=
+ ERROR_SUCCESS) {
+ return id;
+ }
+ if (RegQueryValueEx(hkey, key, NULL, &dwType, pattern, &pattern_max) !=
+ ERROR_SUCCESS) {
+ return id;
+ }
+
+ /* decode pattern: upper case encoded with "/" prefix */
+ i = j = 0;
+ while (pattern[i]) {
+ if (pattern[i] == '/' && pattern[i + 1]) {
+ pattern[j++] = toupper(pattern[++i]);
+ } else {
+ pattern[j++] = tolower(pattern[i]);
+ }
+ i++;
+ }
+ pattern[j] = 0; /* end of string */
+
+ /* now pattern is the string from the registry; search for match */
+ i = pm_find_default_device(pattern, is_input);
+ if (i != pmNoDevice) {
+ id = i;
+ }
+ return id;
+}
+
+
+PmDeviceID Pm_GetDefaultInputDeviceID() {
+ return pm_get_default_device_id(TRUE,
+ "/P/M_/R/E/C/O/M/M/E/N/D/E/D_/I/N/P/U/T_/D/E/V/I/C/E");
+}
+
+
+PmDeviceID Pm_GetDefaultOutputDeviceID() {
+ return pm_get_default_device_id(FALSE,
+ "/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");
+}
+
+
+#include "stdio.h"
+
+void *pm_alloc(size_t s) {
+ return malloc(s);
+}
+
+
+void pm_free(void *ptr) {
+ free(ptr);
+}
+
+
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_win/pmwinmm.c b/libs/backends/wavesaudio/portmidi/src/pm_win/pmwinmm.c new file mode 100644 index 0000000000..ab66f80dc1 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_win/pmwinmm.c @@ -0,0 +1,1464 @@ +/* pmwinmm.c -- system specific definitions */
+
+#ifdef _MSC_VER
+ #pragma warning(disable: 4133) // stop warnings about implicit typecasts
+#endif
+
+#ifndef _WIN32_WINNT
+ /* without this define, InitializeCriticalSectionAndSpinCount is
+ * undefined. This version level means "Windows 2000 and higher"
+ */
+ #define _WIN32_WINNT 0x0500
+#endif
+
+#include "windows.h"
+#include "mmsystem.h"
+#include "portmidi.h"
+#include "pmutil.h"
+#include "pminternal.h"
+#include "pmwinmm.h"
+#include <string.h>
+#include "porttime.h"
+
+/* asserts used to verify portMidi code logic is sound; later may want
+ something more graceful */
+#include <assert.h>
+#ifdef DEBUG
+/* this printf stuff really important for debugging client app w/host errors.
+ probably want to do something else besides read/write from/to console
+ for portability, however */
+#define STRING_MAX 80
+#include "stdio.h"
+#endif
+
+#define streql(x, y) (strcmp(x, y) == 0)
+
+#define MIDI_SYSEX 0xf0
+#define MIDI_EOX 0xf7
+
+/* callback routines */
+static void CALLBACK winmm_in_callback(HMIDIIN hMidiIn,
+ WORD wMsg, DWORD dwInstance,
+ DWORD dwParam1, DWORD dwParam2);
+static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
+ DWORD dwInstance, DWORD dwParam1,
+ DWORD dwParam2);
+#ifdef USE_SYSEX_BUFFERS
+static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg,
+ DWORD dwInstance, DWORD dwParam1,
+ DWORD dwParam2);
+#endif
+
+extern pm_fns_node pm_winmm_in_dictionary;
+extern pm_fns_node pm_winmm_out_dictionary;
+
+static void winmm_out_delete(PmInternal *midi); /* forward reference */
+
+/*
+A note about buffers: WinMM seems to hold onto buffers longer than
+one would expect, e.g. when I tried using 2 small buffers to send
+long sysex messages, at some point WinMM held both buffers. This problem
+was fixed by making buffers bigger. Therefore, it seems that there should
+be enough buffer space to hold a whole sysex message.
+
+The bufferSize passed into Pm_OpenInput (passed into here as buffer_len)
+will be used to estimate the largest sysex message (= buffer_len * 4 bytes).
+Call that the max_sysex_len = buffer_len * 4.
+
+For simple midi output (latency == 0), allocate 3 buffers, each with half
+the size of max_sysex_len, but each at least 256 bytes.
+
+For stream output, there will already be enough space in very short
+buffers, so use them, but make sure there are at least 16.
+
+For input, use many small buffers rather than 2 large ones so that when
+there are short sysex messages arriving frequently (as in control surfaces)
+there will be more free buffers to fill. Use max_sysex_len / 64 buffers,
+but at least 16, of size 64 bytes each.
+
+The following constants help to represent these design parameters:
+*/
+#define NUM_SIMPLE_SYSEX_BUFFERS 3
+#define MIN_SIMPLE_SYSEX_LEN 256
+
+#define MIN_STREAM_BUFFERS 16
+#define STREAM_BUFFER_LEN 24
+
+#define INPUT_SYSEX_LEN 64
+#define MIN_INPUT_BUFFERS 16
+
+/* if we run out of space for output (assume this is due to a sysex msg,
+ expand by up to NUM_EXPANSION_BUFFERS in increments of EXPANSION_BUFFER_LEN
+ */
+#define NUM_EXPANSION_BUFFERS 128
+#define EXPANSION_BUFFER_LEN 1024
+
+/* A sysex buffer has 3 DWORDS as a header plus the actual message size */
+#define MIDIHDR_SYSEX_BUFFER_LENGTH(x) ((x) + sizeof(long)*3)
+/* A MIDIHDR with a sysex message is the buffer length plus the header size */
+#define MIDIHDR_SYSEX_SIZE(x) (MIDIHDR_SYSEX_BUFFER_LENGTH(x) + sizeof(MIDIHDR))
+#ifdef USE_SYSEX_BUFFERS
+/* Size of a MIDIHDR with a buffer contaning multiple MIDIEVENT structures */
+#define MIDIHDR_SIZE(x) ((x) + sizeof(MIDIHDR))
+#endif
+
+/*
+==============================================================================
+win32 mmedia system specific structure passed to midi callbacks
+==============================================================================
+*/
+
+/* global winmm device info */
+MIDIINCAPS *midi_in_caps = NULL;
+MIDIINCAPS midi_in_mapper_caps;
+UINT midi_num_inputs = 0;
+MIDIOUTCAPS *midi_out_caps = NULL;
+MIDIOUTCAPS midi_out_mapper_caps;
+UINT midi_num_outputs = 0;
+
+/* per device info */
+typedef struct midiwinmm_struct {
+ union {
+ HMIDISTRM stream; /* windows handle for stream */
+ HMIDIOUT out; /* windows handle for out calls */
+ HMIDIIN in; /* windows handle for in calls */
+ } handle;
+
+ /* midi output messages are sent in these buffers, which are allocated
+ * in a round-robin fashion, using next_buffer as an index
+ */
+ LPMIDIHDR *buffers; /* pool of buffers for midi in or out data */
+ int max_buffers; /* length of buffers array */
+ int buffers_expanded; /* buffers array expanded for extra msgs? */
+ int num_buffers; /* how many buffers allocated in buffers array */
+ int next_buffer; /* index of next buffer to send */
+ HANDLE buffer_signal; /* used to wait for buffer to become free */
+#ifdef USE_SYSEX_BUFFERS
+ /* sysex buffers will be allocated only when
+ * a sysex message is sent. The size of the buffer is fixed.
+ */
+ LPMIDIHDR sysex_buffers[NUM_SYSEX_BUFFERS]; /* pool of buffers for sysex data */
+ int next_sysex_buffer; /* index of next sysexbuffer to send */
+#endif
+ unsigned long last_time; /* last output time */
+ int first_message; /* flag: treat first message differently */
+ int sysex_mode; /* middle of sending sysex */
+ unsigned long sysex_word; /* accumulate data when receiving sysex */
+ unsigned int sysex_byte_count; /* count how many received */
+ LPMIDIHDR hdr; /* the message accumulating sysex to send */
+ unsigned long sync_time; /* when did we last determine delta? */
+ long delta; /* difference between stream time and
+ real time */
+ int error; /* host error from doing port midi call */
+ CRITICAL_SECTION lock; /* prevents reentrant callbacks (input only) */
+} midiwinmm_node, *midiwinmm_type;
+
+
+/*
+=============================================================================
+general MIDI device queries
+=============================================================================
+*/
+static void pm_winmm_general_inputs()
+{
+ UINT i;
+ WORD wRtn;
+ midi_num_inputs = midiInGetNumDevs();
+ midi_in_caps = (MIDIINCAPS *) pm_alloc(sizeof(MIDIINCAPS) *
+ midi_num_inputs);
+ if (midi_in_caps == NULL) {
+ /* if you can't open a particular system-level midi interface
+ * (such as winmm), we just consider that system or API to be
+ * unavailable and move on without reporting an error.
+ */
+ return;
+ }
+
+ for (i = 0; i < midi_num_inputs; i++) {
+ wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) & midi_in_caps[i],
+ sizeof(MIDIINCAPS));
+ if (wRtn == MMSYSERR_NOERROR) {
+ /* ignore errors here -- if pm_descriptor_max is exceeded, some
+ devices will not be accessible. */
+ pm_add_device("MMSystem", midi_in_caps[i].szPname, TRUE,
+ (void *) i, &pm_winmm_in_dictionary);
+ }
+ }
+}
+
+
+static void pm_winmm_mapper_input()
+{
+ WORD wRtn;
+ /* Note: if MIDIMAPPER opened as input (documentation implies you
+ can, but current system fails to retrieve input mapper
+ capabilities) then you still should retrieve some formof
+ setup info. */
+ wRtn = midiInGetDevCaps((UINT) MIDIMAPPER,
+ (LPMIDIINCAPS) & midi_in_mapper_caps,
+ sizeof(MIDIINCAPS));
+ if (wRtn == MMSYSERR_NOERROR) {
+ pm_add_device("MMSystem", midi_in_mapper_caps.szPname, TRUE,
+ (void *) MIDIMAPPER, &pm_winmm_in_dictionary);
+ }
+}
+
+
+static void pm_winmm_general_outputs()
+{
+ UINT i;
+ DWORD wRtn;
+ midi_num_outputs = midiOutGetNumDevs();
+ midi_out_caps = pm_alloc( sizeof(MIDIOUTCAPS) * midi_num_outputs );
+
+ if (midi_out_caps == NULL) {
+ /* no error is reported -- see pm_winmm_general_inputs */
+ return ;
+ }
+
+ for (i = 0; i < midi_num_outputs; i++) {
+ wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) & midi_out_caps[i],
+ sizeof(MIDIOUTCAPS));
+ if (wRtn == MMSYSERR_NOERROR) {
+ pm_add_device("MMSystem", midi_out_caps[i].szPname, FALSE,
+ (void *) i, &pm_winmm_out_dictionary);
+ }
+ }
+}
+
+
+static void pm_winmm_mapper_output()
+{
+ WORD wRtn;
+ /* Note: if MIDIMAPPER opened as output (pseudo MIDI device
+ maps device independent messages into device dependant ones,
+ via NT midimapper program) you still should get some setup info */
+ wRtn = midiOutGetDevCaps((UINT) MIDIMAPPER, (LPMIDIOUTCAPS)
+ & midi_out_mapper_caps, sizeof(MIDIOUTCAPS));
+ if (wRtn == MMSYSERR_NOERROR) {
+ pm_add_device("MMSystem", midi_out_mapper_caps.szPname, FALSE,
+ (void *) MIDIMAPPER, &pm_winmm_out_dictionary);
+ }
+}
+
+
+/*
+=========================================================================================
+host error handling
+=========================================================================================
+*/
+static unsigned int winmm_has_host_error(PmInternal * midi)
+{
+ midiwinmm_type m = (midiwinmm_type)midi->descriptor;
+ return m->error;
+}
+
+
+/* str_copy_len -- like strcat, but won't overrun the destination string */
+/*
+ * returns length of resulting string
+ */
+static int str_copy_len(char *dst, char *src, int len)
+{
+ strncpy(dst, src, len);
+ /* just in case suffex is greater then len, terminate with zero */
+ dst[len - 1] = 0;
+ return strlen(dst);
+}
+
+
+static void winmm_get_host_error(PmInternal * midi, char * msg, UINT len)
+{
+ /* precondition: midi != NULL */
+ midiwinmm_node * m = (midiwinmm_node *) midi->descriptor;
+ char *hdr1 = "Host error: ";
+ char *hdr2 = "Host callback error: ";
+
+ msg[0] = 0; /* initialize result string to empty */
+
+ if (descriptors[midi->device_id].pub.input) {
+ /* input and output use different winmm API calls */
+ if (m) { /* make sure there is an open device to examine */
+ if (m->error != MMSYSERR_NOERROR) {
+ int n = str_copy_len(msg, hdr1, len);
+ /* read and record host error */
+ int err = midiInGetErrorText(m->error, msg + n, len - n);
+ assert(err == MMSYSERR_NOERROR);
+ m->error = MMSYSERR_NOERROR;
+ }
+ }
+ } else { /* output port */
+ if (m) {
+ if (m->error != MMSYSERR_NOERROR) {
+ int n = str_copy_len(msg, hdr1, len);
+ int err = midiOutGetErrorText(m->error, msg + n, len - n);
+ assert(err == MMSYSERR_NOERROR);
+ m->error = MMSYSERR_NOERROR;
+ }
+ }
+ }
+}
+
+
+/*
+=============================================================================
+buffer handling
+=============================================================================
+*/
+static MIDIHDR *allocate_buffer(long data_size)
+{
+ LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
+ MIDIEVENT *evt;
+ if (!hdr) return NULL;
+ evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
+ hdr->lpData = (LPSTR) evt;
+ hdr->dwBufferLength = MIDIHDR_SYSEX_BUFFER_LENGTH(data_size);
+ hdr->dwBytesRecorded = 0;
+ hdr->dwFlags = 0;
+ hdr->dwUser = hdr->dwBufferLength;
+ return hdr;
+}
+
+#ifdef USE_SYSEX_BUFFERS
+static MIDIHDR *allocate_sysex_buffer(long data_size)
+{
+ /* we're actually allocating more than data_size because the buffer
+ * will include the MIDIEVENT header in addition to the data
+ */
+ LPMIDIHDR hdr = (LPMIDIHDR) pm_alloc(MIDIHDR_SYSEX_SIZE(data_size));
+ MIDIEVENT *evt;
+ if (!hdr) return NULL;
+ evt = (MIDIEVENT *) (hdr + 1); /* place MIDIEVENT after header */
+ hdr->lpData = (LPSTR) evt;
+ hdr->dwFlags = 0;
+ hdr->dwUser = 0;
+ return hdr;
+}
+#endif
+
+static PmError allocate_buffers(midiwinmm_type m, long data_size, long count)
+{
+ int i;
+ /* buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
+ m->num_buffers = 0; /* in case no memory can be allocated */
+ m->buffers = (LPMIDIHDR *) pm_alloc(sizeof(LPMIDIHDR) * count);
+ if (!m->buffers) return pmInsufficientMemory;
+ m->max_buffers = count;
+ for (i = 0; i < count; i++) {
+ LPMIDIHDR hdr = allocate_buffer(data_size);
+ if (!hdr) { /* free everything allocated so far and return */
+ for (i = i - 1; i >= 0; i--) pm_free(m->buffers[i]);
+ pm_free(m->buffers);
+ m->max_buffers = 0;
+ return pmInsufficientMemory;
+ }
+ m->buffers[i] = hdr; /* this may be NULL if allocation fails */
+ }
+ m->num_buffers = count;
+ return pmNoError;
+}
+
+#ifdef USE_SYSEX_BUFFERS
+static PmError allocate_sysex_buffers(midiwinmm_type m, long data_size)
+{
+ PmError rslt = pmNoError;
+ /* sysex_buffers is an array of count pointers to MIDIHDR/MIDIEVENT struct */
+ int i;
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
+ LPMIDIHDR hdr = allocate_sysex_buffer(data_size);
+
+ if (!hdr) rslt = pmInsufficientMemory;
+ m->sysex_buffers[i] = hdr; /* this may be NULL if allocation fails */
+ hdr->dwFlags = 0; /* mark as free */
+ }
+ return rslt;
+}
+#endif
+
+#ifdef USE_SYSEX_BUFFERS
+static LPMIDIHDR get_free_sysex_buffer(PmInternal *midi)
+{
+ LPMIDIHDR r = NULL;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ if (!m->sysex_buffers[0]) {
+ if (allocate_sysex_buffers(m, SYSEX_BYTES_PER_BUFFER)) {
+ return NULL;
+ }
+ }
+ /* busy wait until we find a free buffer */
+ while (TRUE) {
+ int i;
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
+ /* cycle through buffers, modulo NUM_SYSEX_BUFFERS */
+ m->next_sysex_buffer++;
+ if (m->next_sysex_buffer >= NUM_SYSEX_BUFFERS) m->next_sysex_buffer = 0;
+ r = m->sysex_buffers[m->next_sysex_buffer];
+ if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_sysex_buffer;
+ }
+ /* after scanning every buffer and not finding anything, block */
+ if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
+#ifdef DEBUG
+ printf("PortMidi warning: get_free_sysex_buffer() wait timed out after 1000ms\n");
+#endif
+ }
+ }
+found_sysex_buffer:
+ r->dwBytesRecorded = 0;
+ r->dwBufferLength = 0; /* changed to correct value later */
+ return r;
+}
+#endif
+
+static LPMIDIHDR get_free_output_buffer(PmInternal *midi)
+{
+ LPMIDIHDR r = NULL;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ while (TRUE) {
+ int i;
+ for (i = 0; i < m->num_buffers; i++) {
+ /* cycle through buffers, modulo m->num_buffers */
+ m->next_buffer++;
+ if (m->next_buffer >= m->num_buffers) m->next_buffer = 0;
+ r = m->buffers[m->next_buffer];
+ if ((r->dwFlags & MHDR_PREPARED) == 0) goto found_buffer;
+ }
+ /* after scanning every buffer and not finding anything, block */
+ if (WaitForSingleObject(m->buffer_signal, 1000) == WAIT_TIMEOUT) {
+#ifdef DEBUG
+ printf("PortMidi warning: get_free_output_buffer() wait timed out after 1000ms\n");
+#endif
+ /* if we're trying to send a sysex message, maybe the
+ * message is too big and we need more message buffers.
+ * Expand the buffer pool by 128KB using 1024-byte buffers.
+ */
+ /* first, expand the buffers array if necessary */
+ if (!m->buffers_expanded) {
+ LPMIDIHDR *new_buffers = (LPMIDIHDR *) pm_alloc(
+ (m->num_buffers + NUM_EXPANSION_BUFFERS) *
+ sizeof(LPMIDIHDR));
+ /* if no memory, we could return a no-memory error, but user
+ * probably will be unprepared to deal with it. Maybe the
+ * MIDI driver is temporarily hung so we should just wait.
+ * I don't know the right answer, but waiting is easier.
+ */
+ if (!new_buffers) continue;
+ /* copy buffers to new_buffers and replace buffers */
+ memcpy(new_buffers, m->buffers,
+ m->num_buffers * sizeof(LPMIDIHDR));
+ pm_free(m->buffers);
+ m->buffers = new_buffers;
+ m->max_buffers = m->num_buffers + NUM_EXPANSION_BUFFERS;
+ m->buffers_expanded = TRUE;
+ }
+ /* next, add one buffer and return it */
+ if (m->num_buffers < m->max_buffers) {
+ r = allocate_buffer(EXPANSION_BUFFER_LEN);
+ /* again, if there's no memory, we may not really be
+ * dead -- maybe the system is temporarily hung and
+ * we can just wait longer for a message buffer */
+ if (!r) continue;
+ m->buffers[m->num_buffers++] = r;
+ goto found_buffer; /* break out of 2 loops */
+ }
+ /* else, we've allocated all NUM_EXPANSION_BUFFERS buffers,
+ * and we have no free buffers to send. We'll just keep
+ * polling to see if any buffers show up.
+ */
+ }
+ }
+found_buffer:
+ r->dwBytesRecorded = 0;
+ /* actual buffer length is saved in dwUser field */
+ r->dwBufferLength = (DWORD) r->dwUser;
+ return r;
+}
+
+#ifdef EXPANDING_SYSEX_BUFFERS
+note: this is not working code, but might be useful if you want
+ to grow sysex buffers.
+static PmError resize_sysex_buffer(PmInternal *midi, long old_size, long new_size)
+{
+ LPMIDIHDR big;
+ int i;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ /* buffer must be smaller than 64k, but be also a multiple of 4 */
+ if (new_size > 65520) {
+ if (old_size >= 65520)
+ return pmBufferMaxSize;
+ else
+ new_size = 65520;
+ }
+ /* allocate a bigger message */
+ big = allocate_sysex_buffer(new_size);
+ /* printf("expand to %d bytes\n", new_size);*/
+ if (!big) return pmInsufficientMemory;
+ m->error = midiOutPrepareHeader(m->handle.out, big, sizeof(MIDIHDR));
+ if (m->error) {
+ pm_free(big);
+ return pmHostError;
+ }
+ /* make sure we're not going to overwrite any memory */
+ assert(old_size <= new_size);
+ memcpy(big->lpData, m->hdr->lpData, old_size);
+ /* keep track of how many sysex bytes are in message so far */
+ big->dwBytesRecorded = m->hdr->dwBytesRecorded;
+ big->dwBufferLength = new_size;
+ /* find which buffer this was, and replace it */
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
+ if (m->sysex_buffers[i] == m->hdr) {
+ m->sysex_buffers[i] = big;
+ m->sysex_buffer_size[i] = new_size;
+ pm_free(m->hdr);
+ m->hdr = big;
+ break;
+ }
+ }
+ assert(i != NUM_SYSEX_BUFFERS);
+
+ return pmNoError;
+}
+#endif
+
+/*
+=========================================================================================
+begin midi input implementation
+=========================================================================================
+*/
+
+
+static PmError allocate_input_buffer(HMIDIIN h, long buffer_len)
+{
+ LPMIDIHDR hdr = allocate_buffer(buffer_len);
+ if (!hdr) return pmInsufficientMemory;
+ pm_hosterror = midiInPrepareHeader(h, hdr, sizeof(MIDIHDR));
+ if (pm_hosterror) {
+ pm_free(hdr);
+ return pm_hosterror;
+ }
+ pm_hosterror = midiInAddBuffer(h, hdr, sizeof(MIDIHDR));
+ return pm_hosterror;
+}
+
+
+static PmError winmm_in_open(PmInternal *midi, void *driverInfo)
+{
+ DWORD dwDevice;
+ int i = midi->device_id;
+ int max_sysex_len = midi->buffer_len * 4;
+ int num_input_buffers = max_sysex_len / INPUT_SYSEX_LEN;
+ midiwinmm_type m;
+
+ dwDevice = (DWORD) descriptors[i].descriptor;
+
+ /* create system dependent device data */
+ m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
+ midi->descriptor = m;
+ if (!m) goto no_memory;
+ m->handle.in = NULL;
+ m->buffers = NULL; /* not used for input */
+ m->num_buffers = 0; /* not used for input */
+ m->max_buffers = FALSE; /* not used for input */
+ m->buffers_expanded = 0; /* not used for input */
+ m->next_buffer = 0; /* not used for input */
+ m->buffer_signal = 0; /* not used for input */
+#ifdef USE_SYSEX_BUFFERS
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++)
+ m->sysex_buffers[i] = NULL; /* not used for input */
+ m->next_sysex_buffer = 0; /* not used for input */
+#endif
+ m->last_time = 0;
+ m->first_message = TRUE; /* not used for input */
+ m->sysex_mode = FALSE;
+ m->sysex_word = 0;
+ m->sysex_byte_count = 0;
+ m->hdr = NULL; /* not used for input */
+ m->sync_time = 0;
+ m->delta = 0;
+ m->error = MMSYSERR_NOERROR;
+ /* 4000 is based on Windows documentation -- that's the value used in the
+ memory manager. It's small enough that it should not hurt performance even
+ if it's not optimal.
+ */
+ InitializeCriticalSectionAndSpinCount(&m->lock, 4000);
+ /* open device */
+ pm_hosterror = midiInOpen(
+ &(m->handle.in), /* input device handle */
+ dwDevice, /* device ID */
+ (DWORD_PTR) winmm_in_callback, /* callback address */
+ (DWORD_PTR) midi, /* callback instance data */
+ CALLBACK_FUNCTION); /* callback is a procedure */
+ if (pm_hosterror) goto free_descriptor;
+
+ if (num_input_buffers < MIN_INPUT_BUFFERS)
+ num_input_buffers = MIN_INPUT_BUFFERS;
+ for (i = 0; i < num_input_buffers; i++) {
+ if (allocate_input_buffer(m->handle.in, INPUT_SYSEX_LEN)) {
+ /* either pm_hosterror was set, or the proper return code
+ is pmInsufficientMemory */
+ goto close_device;
+ }
+ }
+ /* start device */
+ pm_hosterror = midiInStart(m->handle.in);
+ if (pm_hosterror) goto reset_device;
+ return pmNoError;
+
+ /* undo steps leading up to the detected error */
+reset_device:
+ /* ignore return code (we already have an error to report) */
+ midiInReset(m->handle.in);
+close_device:
+ midiInClose(m->handle.in); /* ignore return code */
+free_descriptor:
+ midi->descriptor = NULL;
+ pm_free(m);
+no_memory:
+ if (pm_hosterror) {
+ int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
+ PM_HOST_ERROR_MSG_LEN);
+ assert(err == MMSYSERR_NOERROR);
+ return pmHostError;
+ }
+ /* if !pm_hosterror, then the error must be pmInsufficientMemory */
+ return pmInsufficientMemory;
+ /* note: if we return an error code, the device will be
+ closed and memory will be freed. It's up to the caller
+ to free the parameter midi */
+}
+
+static PmError winmm_in_poll(PmInternal *midi) {
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ return m->error;
+}
+
+
+
+/* winmm_in_close -- close an open midi input device */
+/*
+ * assume midi is non-null (checked by caller)
+ */
+static PmError winmm_in_close(PmInternal *midi)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ if (!m) return pmBadPtr;
+ /* device to close */
+ if (pm_hosterror = midiInStop(m->handle.in)) {
+ midiInReset(m->handle.in); /* try to reset and close port */
+ midiInClose(m->handle.in);
+ } else if (pm_hosterror = midiInReset(m->handle.in)) {
+ midiInClose(m->handle.in); /* best effort to close midi port */
+ } else {
+ pm_hosterror = midiInClose(m->handle.in);
+ }
+ midi->descriptor = NULL;
+ DeleteCriticalSection(&m->lock);
+ pm_free(m); /* delete */
+ if (pm_hosterror) {
+ int err = midiInGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
+ PM_HOST_ERROR_MSG_LEN);
+ assert(err == MMSYSERR_NOERROR);
+ return pmHostError;
+ }
+ return pmNoError;
+}
+
+
+/* Callback function executed via midiInput SW interrupt (via midiInOpen). */
+static void FAR PASCAL winmm_in_callback(
+ HMIDIIN hMidiIn, /* midiInput device Handle */
+ WORD wMsg, /* midi msg */
+ DWORD dwInstance, /* application data */
+ DWORD dwParam1, /* MIDI data */
+ DWORD dwParam2) /* device timestamp (wrt most recent midiInStart) */
+{
+ static int entry = 0;
+ PmInternal *midi = (PmInternal *) dwInstance;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+
+ /* NOTE: we do not just EnterCriticalSection() here because an
+ * MIM_CLOSE message arrives when the port is closed, but then
+ * the m->lock has been destroyed.
+ */
+
+ switch (wMsg) {
+ case MIM_DATA: {
+ /* if this callback is reentered with data, we're in trouble.
+ * It's hard to imagine that Microsoft would allow callbacks
+ * to be reentrant -- isn't the model that this is like a
+ * hardware interrupt? -- but I've seen reentrant behavior
+ * using a debugger, so it happens.
+ */
+ long new_driver_time;
+ EnterCriticalSection(&m->lock);
+
+ /* dwParam1 is MIDI data received, packed into DWORD w/ 1st byte of
+ message LOB;
+ dwParam2 is time message received by input device driver, specified
+ in [ms] from when midiInStart called.
+ each message is expanded to include the status byte */
+
+ new_driver_time = dwParam2;
+
+ if ((dwParam1 & 0x80) == 0) {
+ /* not a status byte -- ignore it. This happened running the
+ sysex.c test under Win2K with MidiMan USB 1x1 interface,
+ but I can't reproduce it. -RBD
+ */
+ /* printf("non-status byte found\n"); */
+ } else { /* data to process */
+ PmEvent event;
+ if (midi->time_proc)
+ dwParam2 = (*midi->time_proc)(midi->time_info);
+ event.timestamp = dwParam2;
+ event.message = dwParam1;
+ pm_read_short(midi, &event);
+ }
+ LeaveCriticalSection(&m->lock);
+ break;
+ }
+ case MIM_LONGDATA: {
+ MIDIHDR *lpMidiHdr = (MIDIHDR *) dwParam1;
+ unsigned char *data = (unsigned char *) lpMidiHdr->lpData;
+ unsigned int processed = 0;
+ int remaining = lpMidiHdr->dwBytesRecorded;
+
+ EnterCriticalSection(&m->lock);
+ /* printf("midi_in_callback -- lpMidiHdr %x, %d bytes, %2x...\n",
+ lpMidiHdr, lpMidiHdr->dwBytesRecorded, *data); */
+ if (midi->time_proc)
+ dwParam2 = (*midi->time_proc)(midi->time_info);
+ /* can there be more than one message in one buffer? */
+ /* assume yes and iterate through them */
+ while (remaining > 0) {
+ unsigned int amt = pm_read_bytes(midi, data + processed,
+ remaining, dwParam2);
+ remaining -= amt;
+ processed += amt;
+ }
+
+ /* when a device is closed, the pending MIM_LONGDATA buffers are
+ returned to this callback with dwBytesRecorded == 0. In this
+ case, we do not want to send them back to the interface (if
+ we do, the interface will not close, and Windows OS may hang). */
+ if (lpMidiHdr->dwBytesRecorded > 0) {
+ MMRESULT rslt;
+ lpMidiHdr->dwBytesRecorded = 0;
+ lpMidiHdr->dwFlags = 0;
+
+ /* note: no error checking -- can this actually fail? */
+ rslt = midiInPrepareHeader(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
+ assert(rslt == MMSYSERR_NOERROR);
+ /* note: I don't think this can fail except possibly for
+ * MMSYSERR_NOMEM, but the pain of reporting this
+ * unlikely but probably catastrophic error does not seem
+ * worth it.
+ */
+ rslt = midiInAddBuffer(hMidiIn, lpMidiHdr, sizeof(MIDIHDR));
+ assert(rslt == MMSYSERR_NOERROR);
+ LeaveCriticalSection(&m->lock);
+ } else {
+ midiInUnprepareHeader(hMidiIn,lpMidiHdr,sizeof(MIDIHDR));
+ LeaveCriticalSection(&m->lock);
+ pm_free(lpMidiHdr);
+ }
+ break;
+ }
+ case MIM_OPEN:
+ break;
+ case MIM_CLOSE:
+ break;
+ case MIM_ERROR:
+ /* printf("MIM_ERROR\n"); */
+ break;
+ case MIM_LONGERROR:
+ /* printf("MIM_LONGERROR\n"); */
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+=========================================================================================
+begin midi output implementation
+=========================================================================================
+*/
+
+/* begin helper routines used by midiOutStream interface */
+
+/* add_to_buffer -- adds timestamped short msg to buffer, returns fullp */
+static int add_to_buffer(midiwinmm_type m, LPMIDIHDR hdr,
+ unsigned long delta, unsigned long msg)
+{
+ unsigned long *ptr = (unsigned long *)
+ (hdr->lpData + hdr->dwBytesRecorded);
+ *ptr++ = delta; /* dwDeltaTime */
+ *ptr++ = 0; /* dwStream */
+ *ptr++ = msg; /* dwEvent */
+ hdr->dwBytesRecorded += 3 * sizeof(long);
+ /* if the addition of three more words (a message) would extend beyond
+ the buffer length, then return TRUE (full)
+ */
+ return hdr->dwBytesRecorded + 3 * sizeof(long) > hdr->dwBufferLength;
+}
+
+
+static PmTimestamp pm_time_get(midiwinmm_type m)
+{
+ MMTIME mmtime;
+ MMRESULT wRtn;
+ mmtime.wType = TIME_TICKS;
+ mmtime.u.ticks = 0;
+ wRtn = midiStreamPosition(m->handle.stream, &mmtime, sizeof(mmtime));
+ assert(wRtn == MMSYSERR_NOERROR);
+ return mmtime.u.ticks;
+}
+
+
+/* end helper routines used by midiOutStream interface */
+
+
+static PmError winmm_out_open(PmInternal *midi, void *driverInfo)
+{
+ DWORD dwDevice;
+ int i = midi->device_id;
+ midiwinmm_type m;
+ MIDIPROPTEMPO propdata;
+ MIDIPROPTIMEDIV divdata;
+ int max_sysex_len = midi->buffer_len * 4;
+ int output_buffer_len;
+ int num_buffers;
+ dwDevice = (DWORD) descriptors[i].descriptor;
+
+ /* create system dependent device data */
+ m = (midiwinmm_type) pm_alloc(sizeof(midiwinmm_node)); /* create */
+ midi->descriptor = m;
+ if (!m) goto no_memory;
+ m->handle.out = NULL;
+ m->buffers = NULL;
+ m->num_buffers = 0;
+ m->max_buffers = 0;
+ m->buffers_expanded = FALSE;
+ m->next_buffer = 0;
+#ifdef USE_SYSEX_BUFFERS
+ m->sysex_buffers[0] = NULL;
+ m->sysex_buffers[1] = NULL;
+ m->next_sysex_buffer = 0;
+#endif
+ m->last_time = 0;
+ m->first_message = TRUE; /* we treat first message as special case */
+ m->sysex_mode = FALSE;
+ m->sysex_word = 0;
+ m->sysex_byte_count = 0;
+ m->hdr = NULL;
+ m->sync_time = 0;
+ m->delta = 0;
+ m->error = MMSYSERR_NOERROR;
+
+ /* create a signal */
+ m->buffer_signal = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ /* this should only fail when there are very serious problems */
+ assert(m->buffer_signal);
+
+ /* open device */
+ if (midi->latency == 0) {
+ /* use simple midi out calls */
+ pm_hosterror = midiOutOpen(
+ (LPHMIDIOUT) & m->handle.out, /* device Handle */
+ dwDevice, /* device ID */
+ /* note: same callback fn as for StreamOpen: */
+ (DWORD_PTR) winmm_streamout_callback, /* callback fn */
+ (DWORD_PTR) midi, /* callback instance data */
+ CALLBACK_FUNCTION); /* callback type */
+ } else {
+ /* use stream-based midi output (schedulable in future) */
+ pm_hosterror = midiStreamOpen(
+ &m->handle.stream, /* device Handle */
+ (LPUINT) & dwDevice, /* device ID pointer */
+ 1, /* reserved, must be 1 */
+ (DWORD_PTR) winmm_streamout_callback,
+ (DWORD_PTR) midi, /* callback instance data */
+ CALLBACK_FUNCTION);
+ }
+ if (pm_hosterror != MMSYSERR_NOERROR) {
+ goto free_descriptor;
+ }
+
+ if (midi->latency == 0) {
+ num_buffers = NUM_SIMPLE_SYSEX_BUFFERS;
+ output_buffer_len = max_sysex_len / num_buffers;
+ if (output_buffer_len < MIN_SIMPLE_SYSEX_LEN)
+ output_buffer_len = MIN_SIMPLE_SYSEX_LEN;
+ } else {
+ long dur = 0;
+ num_buffers = max(midi->buffer_len, midi->latency / 2);
+ if (num_buffers < MIN_STREAM_BUFFERS)
+ num_buffers = MIN_STREAM_BUFFERS;
+ output_buffer_len = STREAM_BUFFER_LEN;
+
+ propdata.cbStruct = sizeof(MIDIPROPTEMPO);
+ propdata.dwTempo = 480000; /* microseconds per quarter */
+ pm_hosterror = midiStreamProperty(m->handle.stream,
+ (LPBYTE) & propdata,
+ MIDIPROP_SET | MIDIPROP_TEMPO);
+ if (pm_hosterror) goto close_device;
+
+ divdata.cbStruct = sizeof(MIDIPROPTEMPO);
+ divdata.dwTimeDiv = 480; /* divisions per quarter */
+ pm_hosterror = midiStreamProperty(m->handle.stream,
+ (LPBYTE) & divdata,
+ MIDIPROP_SET | MIDIPROP_TIMEDIV);
+ if (pm_hosterror) goto close_device;
+ }
+ /* allocate buffers */
+ if (allocate_buffers(m, output_buffer_len, num_buffers))
+ goto free_buffers;
+ /* start device */
+ if (midi->latency != 0) {
+ pm_hosterror = midiStreamRestart(m->handle.stream);
+ if (pm_hosterror != MMSYSERR_NOERROR) goto free_buffers;
+ }
+ return pmNoError;
+
+free_buffers:
+ /* buffers are freed below by winmm_out_delete */
+close_device:
+ midiOutClose(m->handle.out);
+free_descriptor:
+ midi->descriptor = NULL;
+ winmm_out_delete(midi); /* frees buffers and m */
+no_memory:
+ if (pm_hosterror) {
+ int err = midiOutGetErrorText(pm_hosterror, (char *) pm_hosterror_text,
+ PM_HOST_ERROR_MSG_LEN);
+ assert(err == MMSYSERR_NOERROR);
+ return pmHostError;
+ }
+ return pmInsufficientMemory;
+}
+
+
+/* winmm_out_delete -- carefully free data associated with midi */
+/**/
+static void winmm_out_delete(PmInternal *midi)
+{
+ int i;
+ /* delete system dependent device data */
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ if (m) {
+ if (m->buffer_signal) {
+ /* don't report errors -- better not to stop cleanup */
+ CloseHandle(m->buffer_signal);
+ }
+ /* if using stream output, free buffers */
+ for (i = 0; i < m->num_buffers; i++) {
+ if (m->buffers[i]) pm_free(m->buffers[i]);
+ }
+ m->num_buffers = 0;
+ pm_free(m->buffers);
+ m->max_buffers = 0;
+#ifdef USE_SYSEX_BUFFERS
+ /* free sysex buffers */
+ for (i = 0; i < NUM_SYSEX_BUFFERS; i++) {
+ if (m->sysex_buffers[i]) pm_free(m->sysex_buffers[i]);
+ }
+#endif
+ }
+ midi->descriptor = NULL;
+ pm_free(m); /* delete */
+}
+
+
+/* see comments for winmm_in_close */
+static PmError winmm_out_close(PmInternal *midi)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ if (m->handle.out) {
+ /* device to close */
+ if (midi->latency == 0) {
+ pm_hosterror = midiOutClose(m->handle.out);
+ } else {
+ pm_hosterror = midiStreamClose(m->handle.stream);
+ }
+ /* regardless of outcome, free memory */
+ winmm_out_delete(midi);
+ }
+ if (pm_hosterror) {
+ int err = midiOutGetErrorText(pm_hosterror,
+ (char *) pm_hosterror_text,
+ PM_HOST_ERROR_MSG_LEN);
+ assert(err == MMSYSERR_NOERROR);
+ return pmHostError;
+ }
+ return pmNoError;
+}
+
+
+static PmError winmm_out_abort(PmInternal *midi)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ m->error = MMSYSERR_NOERROR;
+
+ /* only stop output streams */
+ if (midi->latency > 0) {
+ m->error = midiStreamStop(m->handle.stream);
+ }
+ return m->error ? pmHostError : pmNoError;
+}
+
+
+static PmError winmm_write_flush(PmInternal *midi, PmTimestamp timestamp)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ assert(m);
+ if (m->hdr) {
+ m->error = midiOutPrepareHeader(m->handle.out, m->hdr,
+ sizeof(MIDIHDR));
+ if (m->error) {
+ /* do not send message */
+ } else if (midi->latency == 0) {
+ /* As pointed out by Nigel Brown, 20Sep06, dwBytesRecorded
+ * should be zero. This is set in get_free_sysex_buffer().
+ * The msg length goes in dwBufferLength in spite of what
+ * Microsoft documentation says (or doesn't say). */
+ m->hdr->dwBufferLength = m->hdr->dwBytesRecorded;
+ m->hdr->dwBytesRecorded = 0;
+ m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
+ } else {
+ m->error = midiStreamOut(m->handle.stream, m->hdr,
+ sizeof(MIDIHDR));
+ }
+ midi->fill_base = NULL;
+ m->hdr = NULL;
+ if (m->error) {
+ m->hdr->dwFlags = 0; /* release the buffer */
+ return pmHostError;
+ }
+ }
+ return pmNoError;
+}
+
+
+
+#ifdef GARBAGE
+static PmError winmm_write_sysex_byte(PmInternal *midi, unsigned char byte)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ unsigned char *msg_buffer;
+
+ /* at the beginning of sysex, m->hdr is NULL */
+ if (!m->hdr) { /* allocate a buffer if none allocated yet */
+ m->hdr = get_free_output_buffer(midi);
+ if (!m->hdr) return pmInsufficientMemory;
+ m->sysex_byte_count = 0;
+ }
+ /* figure out where to write byte */
+ msg_buffer = (unsigned char *) (m->hdr->lpData);
+ assert(m->hdr->lpData == (char *) (m->hdr + 1));
+
+ /* check for overflow */
+ if (m->sysex_byte_count >= m->hdr->dwBufferLength) {
+ /* allocate a bigger message -- double it every time */
+ LPMIDIHDR big = allocate_buffer(m->sysex_byte_count * 2);
+ /* printf("expand to %d bytes\n", m->sysex_byte_count * 2); */
+ if (!big) return pmInsufficientMemory;
+ m->error = midiOutPrepareHeader(m->handle.out, big,
+ sizeof(MIDIHDR));
+ if (m->error) {
+ m->hdr = NULL;
+ return pmHostError;
+ }
+ memcpy(big->lpData, msg_buffer, m->sysex_byte_count);
+ msg_buffer = (unsigned char *) (big->lpData);
+ if (m->buffers[0] == m->hdr) {
+ m->buffers[0] = big;
+ pm_free(m->hdr);
+ /* printf("freed m->hdr\n"); */
+ } else if (m->buffers[1] == m->hdr) {
+ m->buffers[1] = big;
+ pm_free(m->hdr);
+ /* printf("freed m->hdr\n"); */
+ }
+ m->hdr = big;
+ }
+
+ /* append byte to message */
+ msg_buffer[m->sysex_byte_count++] = byte;
+
+ /* see if we have a complete message */
+ if (byte == MIDI_EOX) {
+ m->hdr->dwBytesRecorded = m->sysex_byte_count;
+ /*
+ { int i; int len = m->hdr->dwBytesRecorded;
+ printf("OutLongMsg %d ", len);
+ for (i = 0; i < len; i++) {
+ printf("%2x ", msg_buffer[i]);
+ }
+ }
+ */
+ m->error = midiOutLongMsg(m->handle.out, m->hdr, sizeof(MIDIHDR));
+ m->hdr = NULL; /* stop using this message buffer */
+ if (m->error) return pmHostError;
+ }
+ return pmNoError;
+}
+#endif
+
+
+static PmError winmm_write_short(PmInternal *midi, PmEvent *event)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ PmError rslt = pmNoError;
+ assert(m);
+
+ if (midi->latency == 0) { /* use midiOut interface, ignore timestamps */
+ m->error = midiOutShortMsg(m->handle.out, event->message);
+ if (m->error) rslt = pmHostError;
+ } else { /* use midiStream interface -- pass data through buffers */
+ unsigned long when = event->timestamp;
+ unsigned long delta;
+ int full;
+ if (when == 0) when = midi->now;
+ /* when is in real_time; translate to intended stream time */
+ when = when + m->delta + midi->latency;
+ /* make sure we don't go backward in time */
+ if (when < m->last_time) when = m->last_time;
+ delta = when - m->last_time;
+ m->last_time = when;
+ /* before we insert any data, we must have a buffer */
+ if (m->hdr == NULL) {
+ /* stream interface: buffers allocated when stream is opened */
+ m->hdr = get_free_output_buffer(midi);
+ }
+ full = add_to_buffer(m, m->hdr, delta, event->message);
+ if (full) rslt = winmm_write_flush(midi, when);
+ }
+ return rslt;
+}
+
+#define winmm_begin_sysex winmm_write_flush
+#ifndef winmm_begin_sysex
+static PmError winmm_begin_sysex(PmInternal *midi, PmTimestamp timestamp)
+{
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ PmError rslt = pmNoError;
+
+ if (midi->latency == 0) {
+ /* do nothing -- it's handled in winmm_write_byte */
+ } else {
+ /* sysex expects an empty sysex buffer, so send whatever is here */
+ rslt = winmm_write_flush(midi);
+ }
+ return rslt;
+}
+#endif
+
+static PmError winmm_end_sysex(PmInternal *midi, PmTimestamp timestamp)
+{
+ /* could check for callback_error here, but I haven't checked
+ * what happens if we exit early and don't finish the sysex msg
+ * and clean up
+ */
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ PmError rslt = pmNoError;
+ LPMIDIHDR hdr = m->hdr;
+ if (!hdr) return rslt; /* something bad happened earlier,
+ do not report an error because it would have been
+ reported (at least) once already */
+ /* a(n old) version of MIDI YOKE requires a zero byte after
+ * the sysex message, but do not increment dwBytesRecorded: */
+ hdr->lpData[hdr->dwBytesRecorded] = 0;
+ if (midi->latency == 0) {
+#ifdef DEBUG_PRINT_BEFORE_SENDING_SYSEX
+ /* DEBUG CODE: */
+ { int i; int len = m->hdr->dwBufferLength;
+ printf("OutLongMsg %d ", len);
+ for (i = 0; i < len; i++) {
+ printf("%2x ", (unsigned char) (m->hdr->lpData[i]));
+ }
+ }
+#endif
+ } else {
+ /* Using stream interface. There are accumulated bytes in m->hdr
+ to send using midiStreamOut
+ */
+ /* add bytes recorded to MIDIEVENT length, but don't
+ count the MIDIEVENT data (3 longs) */
+ MIDIEVENT *evt = (MIDIEVENT *) (hdr->lpData);
+ evt->dwEvent += hdr->dwBytesRecorded - 3 * sizeof(long);
+ /* round up BytesRecorded to multiple of 4 */
+ hdr->dwBytesRecorded = (hdr->dwBytesRecorded + 3) & ~3;
+ }
+ rslt = winmm_write_flush(midi, timestamp);
+ return rslt;
+}
+
+
+static PmError winmm_write_byte(PmInternal *midi, unsigned char byte,
+ PmTimestamp timestamp)
+{
+ /* write a sysex byte */
+ PmError rslt = pmNoError;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ LPMIDIHDR hdr = m->hdr;
+ unsigned char *msg_buffer;
+ assert(m);
+ if (!hdr) {
+ m->hdr = hdr = get_free_output_buffer(midi);
+ assert(hdr);
+ midi->fill_base = (unsigned char *) m->hdr->lpData;
+ midi->fill_offset_ptr = &(hdr->dwBytesRecorded);
+ /* when buffer fills, Pm_WriteSysEx will revert to calling
+ * pmwin_write_byte, which expect to have space, so leave
+ * one byte free for pmwin_write_byte. Leave another byte
+ * of space for zero after message to make early version of
+ * MIDI YOKE driver happy -- therefore dwBufferLength - 2 */
+ midi->fill_length = hdr->dwBufferLength - 2;
+ if (midi->latency != 0) {
+ unsigned long when = (unsigned long) timestamp;
+ unsigned long delta;
+ unsigned long *ptr;
+ if (when == 0) when = midi->now;
+ /* when is in real_time; translate to intended stream time */
+ when = when + m->delta + midi->latency;
+ /* make sure we don't go backward in time */
+ if (when < m->last_time) when = m->last_time;
+ delta = when - m->last_time;
+ m->last_time = when;
+
+ ptr = (unsigned long *) hdr->lpData;
+ *ptr++ = delta;
+ *ptr++ = 0;
+ *ptr = MEVT_F_LONG;
+ hdr->dwBytesRecorded = 3 * sizeof(long);
+ /* data will be added at an offset of dwBytesRecorded ... */
+ }
+ }
+ /* add the data byte */
+ msg_buffer = (unsigned char *) (hdr->lpData);
+ msg_buffer[hdr->dwBytesRecorded++] = byte;
+
+ /* see if buffer is full, leave one byte extra for pad */
+ if (hdr->dwBytesRecorded >= hdr->dwBufferLength - 1) {
+ /* write what we've got and continue */
+ rslt = winmm_end_sysex(midi, timestamp);
+ }
+ return rslt;
+}
+
+#ifdef EXPANDING_SYSEX_BUFFERS
+note: this code is here as an aid in case you want sysex buffers
+ to expand to hold large messages completely. If so, you
+ will want to change SYSEX_BYTES_PER_BUFFER above to some
+ variable that remembers the buffer size. A good place to
+ put this value would be in the hdr->dwUser field.
+
+ rslt = resize_sysex_buffer(midi, m->sysex_byte_count,
+ m->sysex_byte_count * 2);
+
+ if (rslt == pmBufferMaxSize) /* if the buffer can't be resized */
+#endif
+#ifdef EXPANDING_SYSEX_BUFFERS
+ int bytesRecorded = hdr->dwBytesRecorded; /* this field gets wiped out, so we'll save it */
+ rslt = resize_sysex_buffer(midi, bytesRecorded, 2 * bytesRecorded);
+ hdr->dwBytesRecorded = bytesRecorded;
+
+ if (rslt == pmBufferMaxSize) /* if buffer can't be resized */
+#endif
+
+
+
+static PmTimestamp winmm_synchronize(PmInternal *midi)
+{
+ midiwinmm_type m;
+ unsigned long pm_stream_time_2;
+ unsigned long real_time;
+ unsigned long pm_stream_time;
+
+ /* only synchronize if we are using stream interface */
+ if (midi->latency == 0) return 0;
+
+ /* figure out the time */
+ m = (midiwinmm_type) midi->descriptor;
+ pm_stream_time_2 = pm_time_get(m);
+
+ do {
+ /* read real_time between two reads of stream time */
+ pm_stream_time = pm_stream_time_2;
+ real_time = (*midi->time_proc)(midi->time_info);
+ pm_stream_time_2 = pm_time_get(m);
+ /* repeat if more than 1ms elapsed */
+ } while (pm_stream_time_2 > pm_stream_time + 1);
+ m->delta = pm_stream_time - real_time;
+ m->sync_time = real_time;
+ return real_time;
+}
+
+#ifdef USE_SYSEX_BUFFERS
+/* winmm_out_callback -- recycle sysex buffers */
+static void CALLBACK winmm_out_callback(HMIDIOUT hmo, UINT wMsg,
+ DWORD dwInstance, DWORD dwParam1,
+ DWORD dwParam2)
+{
+ PmInternal *midi = (PmInternal *) dwInstance;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
+ int err = 0; /* set to 0 so that no buffer match will also be an error */
+
+ /* Future optimization: eliminate UnprepareHeader calls -- they aren't
+ necessary; however, this code uses the prepared-flag to indicate which
+ buffers are free, so we need to do something to flag empty buffers if
+ we leave them prepared
+ */
+ /*
+ printf("out_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
+ hdr, wMsg, MOM_DONE);
+ */
+ if (wMsg == MOM_DONE) {
+ MMRESULT ret = midiOutUnprepareHeader(m->handle.out, hdr,
+ sizeof(MIDIHDR));
+ assert(ret == MMSYSERR_NOERROR);
+ }
+ /* notify waiting sender that a buffer is available */
+ err = SetEvent(m->buffer_signal);
+ assert(err); /* false -> error */
+}
+#endif
+
+/* winmm_streamout_callback -- unprepare (free) buffer header */
+static void CALLBACK winmm_streamout_callback(HMIDIOUT hmo, UINT wMsg,
+ DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
+{
+ PmInternal *midi = (PmInternal *) dwInstance;
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ LPMIDIHDR hdr = (LPMIDIHDR) dwParam1;
+ int err;
+
+ /* Even if an error is pending, I think we should unprepare msgs and
+ signal their arrival
+ */
+ /* printf("streamout_callback: hdr %x, wMsg %x, MOM_DONE %x\n",
+ hdr, wMsg, MOM_DONE); */
+ if (wMsg == MOM_DONE) {
+ MMRESULT ret = midiOutUnprepareHeader(m->handle.out, hdr,
+ sizeof(MIDIHDR));
+ assert(ret == MMSYSERR_NOERROR);
+ }
+ /* signal client in case it is blocked waiting for buffer */
+ err = SetEvent(m->buffer_signal);
+ assert(err); /* false -> error */
+}
+
+
+/*
+=========================================================================================
+begin exported functions
+=========================================================================================
+*/
+
+#define winmm_in_abort pm_fail_fn
+pm_fns_node pm_winmm_in_dictionary = {
+ none_write_short,
+ none_sysex,
+ none_sysex,
+ none_write_byte,
+ none_write_short,
+ none_write_flush,
+ winmm_synchronize,
+ winmm_in_open,
+ winmm_in_abort,
+ winmm_in_close,
+ winmm_in_poll,
+ winmm_has_host_error,
+ winmm_get_host_error
+ };
+
+pm_fns_node pm_winmm_out_dictionary = {
+ winmm_write_short,
+ winmm_begin_sysex,
+ winmm_end_sysex,
+ winmm_write_byte,
+ winmm_write_short, /* short realtime message */
+ winmm_write_flush,
+ winmm_synchronize,
+ winmm_out_open,
+ winmm_out_abort,
+ winmm_out_close,
+ none_poll,
+ winmm_has_host_error,
+ winmm_get_host_error
+ };
+
+
+/* initialize winmm interface. Note that if there is something wrong
+ with winmm (e.g. it is not supported or installed), it is not an
+ error. We should simply return without having added any devices to
+ the table. Hence, no error code is returned. Furthermore, this init
+ code is called along with every other supported interface, so the
+ user would have a very hard time figuring out what hardware and API
+ generated the error. Finally, it would add complexity to pmwin.c to
+ remember where the error code came from in order to convert to text.
+ */
+void pm_winmm_init( void )
+{
+ pm_winmm_mapper_input();
+ pm_winmm_mapper_output();
+ pm_winmm_general_inputs();
+ pm_winmm_general_outputs();
+}
+
+
+/* no error codes are returned, even if errors are encountered, because
+ there is probably nothing the user could do (e.g. it would be an error
+ to retry.
+ */
+void pm_winmm_term( void )
+{
+ int i;
+#ifdef DEBUG
+ char msg[PM_HOST_ERROR_MSG_LEN];
+#endif
+ int doneAny = 0;
+#ifdef DEBUG
+ printf("pm_winmm_term called\n");
+#endif
+ for (i = 0; i < pm_descriptor_index; i++) {
+ PmInternal * midi = descriptors[i].internalDescriptor;
+ if (midi) {
+ midiwinmm_type m = (midiwinmm_type) midi->descriptor;
+ if (m->handle.out) {
+ /* close next open device*/
+#ifdef DEBUG
+ if (doneAny == 0) {
+ printf("begin closing open devices...\n");
+ doneAny = 1;
+ }
+ /* report any host errors; this EXTEREMELY useful when
+ trying to debug client app */
+ if (winmm_has_host_error(midi)) {
+ winmm_get_host_error(midi, msg, PM_HOST_ERROR_MSG_LEN);
+ printf("%s\n", msg);
+ }
+#endif
+ /* close all open ports */
+ (*midi->dictionary->close)(midi);
+ }
+ }
+ }
+ if (midi_in_caps) {
+ pm_free(midi_in_caps);
+ midi_in_caps = NULL;
+ }
+ if (midi_out_caps) {
+ pm_free(midi_out_caps);
+ midi_out_caps = NULL;
+ }
+#ifdef DEBUG
+ if (doneAny) {
+ printf("warning: devices were left open. They have been closed.\n");
+ }
+ printf("pm_winmm_term exiting\n");
+#endif
+ pm_descriptor_index = 0;
+}
diff --git a/libs/backends/wavesaudio/portmidi/src/pm_win/pmwinmm.h b/libs/backends/wavesaudio/portmidi/src/pm_win/pmwinmm.h new file mode 100644 index 0000000000..53c5fe2841 --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/pm_win/pmwinmm.h @@ -0,0 +1,5 @@ +/* midiwin32.h -- system-specific definitions */
+
+void pm_winmm_init( void );
+void pm_winmm_term( void );
+
diff --git a/libs/backends/wavesaudio/portmidi/src/porttime/ptmacosx_mach.c b/libs/backends/wavesaudio/portmidi/src/porttime/ptmacosx_mach.c new file mode 100644 index 0000000000..753f5832ef --- /dev/null +++ b/libs/backends/wavesaudio/portmidi/src/porttime/ptmacosx_mach.c @@ -0,0 +1,131 @@ +/* ptmacosx.c -- portable timer implementation for mac os x */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <CoreAudio/HostTime.h>
+
+#import <mach/mach.h>
+#import <mach/mach_error.h>
+#import <mach/mach_time.h>
+#import <mach/clock.h>
+#include <unistd.h>
+
+#include "porttime.h"
+#include "sys/time.h"
+#include "pthread.h"
+
+#define NSEC_PER_MSEC 1000000
+#define THREAD_IMPORTANCE 30
+
+static int time_started_flag = FALSE;
+static UInt64 start_time;
+static pthread_t pt_thread_pid;
+
+/* note that this is static data -- we only need one copy */
+typedef struct {
+ int id;
+ int resolution;
+ PtCallback *callback;
+ void *userData;
+} pt_callback_parameters;
+
+static int pt_callback_proc_id = 0;
+
+static void *Pt_CallbackProc(void *p)
+{
+ pt_callback_parameters *parameters = (pt_callback_parameters *) p;
+ int mytime = 1;
+
+ kern_return_t error;
+ thread_extended_policy_data_t extendedPolicy;
+ thread_precedence_policy_data_t precedencePolicy;
+
+ extendedPolicy.timeshare = 0;
+ error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY,
+ (thread_policy_t)&extendedPolicy,
+ THREAD_EXTENDED_POLICY_COUNT);
+ if (error != KERN_SUCCESS) {
+ mach_error("Couldn't set thread timeshare policy", error);
+ }
+
+ precedencePolicy.importance = THREAD_IMPORTANCE;
+ error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY,
+ (thread_policy_t)&precedencePolicy,
+ THREAD_PRECEDENCE_POLICY_COUNT);
+ if (error != KERN_SUCCESS) {
+ mach_error("Couldn't set thread precedence policy", error);
+ }
+
+
+ /* to kill a process, just increment the pt_callback_proc_id */
+ /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */
+ while (pt_callback_proc_id == parameters->id) {
+ /* wait for a multiple of resolution ms */
+ UInt64 wait_time;
+ int delay = mytime++ * parameters->resolution - Pt_Time();
+ PtTimestamp timestamp;
+ if (delay < 0) delay = 0;
+ wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC);
+ wait_time += AudioGetCurrentHostTime();
+ error = mach_wait_until(wait_time);
+ timestamp = Pt_Time();
+ (*(parameters->callback))(timestamp, parameters->userData);
+ }
+ free(parameters);
+ return NULL;
+}
+
+
+PtError Pt_Start(int resolution, PtCallback *callback, void *userData)
+{
+ if (time_started_flag) return ptAlreadyStarted;
+ start_time = AudioGetCurrentHostTime();
+
+ if (callback) {
+ int res;
+ pt_callback_parameters *parms;
+
+ parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters));
+ if (!parms) return ptInsufficientMemory;
+ parms->id = pt_callback_proc_id;
+ parms->resolution = resolution;
+ parms->callback = callback;
+ parms->userData = userData;
+ res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms);
+ if (res != 0) return ptHostError;
+ }
+
+ time_started_flag = TRUE;
+ return ptNoError;
+}
+
+
+PtError Pt_Stop()
+{
+ /* printf("Pt_Stop called\n"); */
+ pt_callback_proc_id++;
+ pthread_join(pt_thread_pid, NULL);
+ time_started_flag = FALSE;
+ return ptNoError;
+}
+
+
+int Pt_Started()
+{
+ return time_started_flag;
+}
+
+
+PtTimestamp Pt_Time()
+{
+ UInt64 clock_time, nsec_time;
+ clock_time = AudioGetCurrentHostTime() - start_time;
+ nsec_time = AudioConvertHostTimeToNanos(clock_time);
+ return (PtTimestamp)(nsec_time / NSEC_PER_MSEC);
+}
+
+
+void Pt_Sleep(int32_t duration)
+{
+ usleep(duration * 1000);
+}
diff --git a/libs/backends/wavesaudio/waves_audiobackend.cc b/libs/backends/wavesaudio/waves_audiobackend.cc new file mode 100644 index 0000000000..fbadc4c713 --- /dev/null +++ b/libs/backends/wavesaudio/waves_audiobackend.cc @@ -0,0 +1,1203 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_audiobackend.h"
+#include "waves_audioport.h"
+#include "waves_midiport.h"
+
+using namespace ARDOUR;
+
+void WavesAudioBackend::AudioDeviceManagerNotification (NotificationReason reason, void* parameter)
+{
+ switch (reason) {
+ case WCMRAudioDeviceManagerClient::DeviceDebugInfo:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceDebugInfo -- " << (char*)parameter << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::BufferSizeChanged:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::BufferSizeChanged" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::RequestReset:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::RequestReset" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::RequestResync:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::RequestResync" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::SamplingRateChanged:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::SamplingRateChanged: " << (int64_t)parameter << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::DeviceDroppedSamples:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceDroppedSamples" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::DeviceStoppedStreaming:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceStoppedStreaming" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::DeviceConnectionLost:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceConnectionLost" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::DeviceListChanged:
+ std::cout << "------------------------------- WCMRAudioDeviceManagerClient::DeviceListChanged" << std::endl;
+ break;
+ case WCMRAudioDeviceManagerClient::AudioCallback:
+ if (parameter) {
+ AudioCallbackData* audio_callback_data = (AudioCallbackData*)parameter;
+ _audio_device_callback (
+ audio_callback_data->acdInputBuffer,
+ audio_callback_data->acdOutputBuffer,
+ audio_callback_data->acdFrames,
+ audio_callback_data->acdSampleTime,
+ audio_callback_data->acdCycleStartTimeNanos
+ );
+ }
+ break;
+
+ default:
+ break;
+ };
+}
+
+
+WavesAudioBackend::WavesAudioBackend (AudioEngine& e)
+ : AudioBackend (e)
+ , _audio_device_manager (this)
+ , _midi_device_manager (*this)
+ , _device (NULL)
+ , _sample_format (FormatFloat)
+ , _interleaved (true)
+ , _input_channels (0)
+ , _max_input_channels (0)
+ , _output_channels (0)
+ , _max_output_channels (0)
+ , _sample_rate (0)
+ , _buffer_size (0)
+ , _systemic_input_latency (0)
+ , _systemic_output_latency (0)
+ , _call_thread_init_callback (false)
+ , _use_midi (false)
+ , _sample_time_at_cycle_start (0)
+ , _freewheeling (false)
+ , _freewheel_thread_active (false)
+ , _audio_cycle_period_nanos (0)
+ , _dsp_load_accumulator (0)
+ , _dsp_load_history_length(0)
+{
+}
+
+
+WavesAudioBackend::~WavesAudioBackend ()
+{
+}
+
+std::string
+WavesAudioBackend::name () const
+{
+#ifdef __MACOS__
+ return std::string ("CoreAudio");
+#elif _WINDOWS
+ return std::string ("ASIO");
+#endif
+}
+
+
+bool
+WavesAudioBackend::is_realtime () const
+{
+ return true;
+}
+
+
+bool
+WavesAudioBackend::requires_driver_selection () const
+{
+ return false;
+}
+
+
+std::vector<std::string>
+WavesAudioBackend::enumerate_drivers () const
+{
+ // this backend does not suppose driver selection
+ assert (false);
+
+ return std::vector<std::string> ();
+}
+
+
+int
+WavesAudioBackend::set_driver (const std::string& /*drivername*/)
+{
+ //Waves audio backend does not suppose driver selection
+ assert (false);
+
+ return -1;
+}
+
+
+std::vector<AudioBackend::DeviceStatus>
+WavesAudioBackend::enumerate_devices () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::enumerate_devices (): " << std::endl;
+
+ std::vector<DeviceStatus> devicesStatus;
+ const WCMRAudioDeviceList& devices = _audio_device_manager.Devices ();
+
+ for (WCMRAudioDeviceListConstIter deviceIter = devices.begin (); deviceIter != devices.end (); ++deviceIter) {
+ /* COMMENTED DBG LOGS */ std::cout << "\t Device found: " << (*deviceIter)->DeviceName () << std::endl;
+ devicesStatus.push_back (DeviceStatus ((*deviceIter)->DeviceName (), true));
+ }
+
+ return devicesStatus;
+}
+
+
+std::vector<float>
+WavesAudioBackend::available_sample_rates (const std::string& device_name) const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::available_sample_rates (): [" << device_name << "]" << std::endl;
+
+ std::vector<int> sr;
+
+ WCMRAudioDevice * device = _audio_device_manager.GetDeviceByName (device_name);
+
+ if (!device) {
+ std::cerr << "WavesAudioBackend::available_sample_rates (): Failed to find device [" << device_name << "]" << std::endl;
+ return std::vector<float> ();
+ }
+
+ sr = device->SamplingRates ();
+ /* COMMENTED DBG LOGS */ std::cout << "\tFound " << sr.size () << " sample rates for " << device->DeviceName () << ":";
+
+ std::vector<float> sample_rates (sr.begin (), sr.end ());
+
+ /* COMMENTED DBG LOGS */ for (std::vector<float>::iterator i = sample_rates.begin (); i != sample_rates.end (); ++i) std::cout << " " << *i; std::cout << std::endl;
+
+ return sample_rates;
+}
+
+
+float WavesAudioBackend::default_sample_rate () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::default_sample_rate ():" << std::endl;
+ return AudioBackend::default_sample_rate ();
+}
+
+
+std::vector<uint32_t>
+WavesAudioBackend::available_buffer_sizes (const std::string& device_name) const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::available_buffer_sizes (): [" << device_name << "]" << std::endl;
+
+ WCMRAudioDevice * device = _audio_device_manager.GetDeviceByName (device_name);
+ if (!device) {
+ std::cerr << "WavesAudioBackend::available_buffer_sizes (): Failed to find device [" << device_name << "]" << std::endl;
+ return std::vector<uint32_t> ();
+ }
+
+ std::vector<uint32_t> buffer_sizes (device->BufferSizes ().begin (), device->BufferSizes ().end ());
+
+ /* COMMENTED DBG LOGS */ std::cout << "\tFound " << buffer_sizes.size () << " buffer sizes for " << device->DeviceName () << ":";
+ /* COMMENTED DBG LOGS */ for (std::vector<uint32_t>::const_iterator i = buffer_sizes.begin (); i != buffer_sizes.end (); ++i) std::cout << " " << *i; std::cout << std::endl;
+
+ return buffer_sizes;
+}
+
+
+uint32_t
+WavesAudioBackend::available_input_channel_count (const std::string& device_name) const
+{
+
+ WCMRAudioDevice * device = _audio_device_manager.GetDeviceByName (device_name);
+
+ if (!device) {
+ std::cerr << "WavesAudioBackend::available_input_channel_count (): Failed to find device [" << device_name << "]" << std::endl;
+ return 0;
+ }
+
+ uint32_t num_of_input_channels = device->InputChannels ().size ();
+
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::available_input_channel_count (): " << num_of_input_channels << std::endl;
+ return num_of_input_channels;
+}
+
+
+uint32_t
+WavesAudioBackend::available_output_channel_count (const std::string& device_name) const
+{
+ std::vector<std::string> output_channels;
+
+ WCMRAudioDevice * device = _audio_device_manager.GetDeviceByName (device_name);
+ if (!device) {
+ std::cerr << "WavesAudioBackend::available_output_channel_count (): Failed to find device [" << device_name << "]" << std::endl;
+ return 0;
+ }
+
+ uint32_t num_of_output_channels = device->OutputChannels ().size ();
+
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::available_output_channel_count (): " << num_of_output_channels << std::endl;
+
+ return num_of_output_channels;
+}
+
+
+bool
+WavesAudioBackend::can_change_sample_rate_when_running () const
+{
+ // VERIFY IT CAREFULLY
+ return true;
+}
+
+
+bool
+WavesAudioBackend::can_change_buffer_size_when_running () const
+{
+ // VERIFY IT CAREFULLY
+ return true;
+}
+
+
+int
+WavesAudioBackend::set_device_name (const std::string& device_name)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_device_name (): " << device_name << std::endl;
+
+ if (_ports.size ()) {
+ std::cerr << "WavesAudioBackend::set_device_name (): There are unregistered ports left after [" << (_device ? _device->DeviceName () : std::string ("<NULL>")) << "]!" << std::endl;
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ std::cerr << "\t[" << _ports[i]->name () << "]!" << std::endl;
+ }
+ return -1;
+ }
+
+ WCMRAudioDevice * device = _audio_device_manager.GetDeviceByName (device_name);
+
+ if (!device) {
+ std::cerr << "WavesAudioBackend::set_device_name (): Failed to find device [" << device_name << "]!" << std::endl;
+ return -1;
+ }
+
+ WTErr retVal;
+ if (_device) {
+ retVal = _device->SetActive (false);
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_device_name (): [" << _device->DeviceName () << "]->SetActive (false) failed!" << std::endl;
+ return -1;
+ }
+ }
+
+ _device = NULL;
+
+ retVal = device->SetActive (true);
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_device_name (): [" << device->DeviceName () << "]->SetActive () failed!" << std::endl;
+ return -1;
+ }
+
+ _device = device;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_sample_rate (float sample_rate)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_sample_rate (): " << sample_rate << std::endl;
+
+ WTErr retVal = eNoErr;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::set_sample_rate (): No device is set!" << std::endl;
+ return -1;
+ }
+
+
+ bool device_needs_restart = _device->Streaming ();
+
+ if (device_needs_restart) {
+ retVal = _device->SetStreaming (false);
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->_device->SetStreaming (false);"<< std::endl;
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_sample_rate (): [" << _device->DeviceName () << "]->SetStreaming (false) failed (" << retVal << ") !" << std::endl;
+ return -1;
+ }
+ }
+
+ retVal = _device->SetCurrentSamplingRate ((int)sample_rate);
+
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_sample_rate (): [" << _device->DeviceName() << "]->SetCurrentSamplingRate ((int)" << sample_rate << ") failed (" << retVal << ") !" << std::endl;
+ return -1;
+ }
+
+ _sample_rate = sample_rate;
+ _init_dsp_load_history();
+ engine.sample_rate_change (sample_rate);
+
+ if (device_needs_restart) {
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetStreaming (true);"<< std::endl;
+ _call_thread_init_callback = true;
+ retVal = _device->SetStreaming (true);
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_sample_rate (): [" << _device->DeviceName () << "]->SetStreaming (true) failed (" << retVal << ") !" << std::endl;
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_buffer_size (uint32_t buffer_size)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_buffer_size (): " << buffer_size << std::endl;
+
+ WTErr retVal = eNoErr;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::set_buffer_size (): No device is set!" << std::endl;
+ return -1;
+ }
+
+ bool device_needs_restart = _device->Streaming ();
+
+ if (device_needs_restart) {
+ retVal = _device->SetStreaming (false);
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetStreaming (false);"<< std::endl;
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_buffer_size (): [" << _device->DeviceName () << "]->SetStreaming (false) failed (" << retVal << ") !" << std::endl;
+ return -1;
+ }
+ }
+
+ retVal = _device->SetCurrentBufferSize (buffer_size);
+
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_sample_rate (): [" << _device->DeviceName() << "]->SetCurrentBufferSize (" << buffer_size << ") failed (" << retVal << ") !" << std::endl;
+ return -1;
+ }
+
+ _buffer_size = buffer_size;
+ _init_dsp_load_history();
+ engine.buffer_size_change (buffer_size);
+
+ if (device_needs_restart) {
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t[" << _device->DeviceName() << "]->SetStreaming (true);"<< std::endl;
+ _call_thread_init_callback = true;
+ retVal = _device->SetStreaming (true);
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::set_buffer_size (): [" << _device->DeviceName () << "]->SetStreaming (true) failed (" << retVal << ") !" << std::endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_sample_format (SampleFormat sample_format)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_sample_format (): " << sample_format << std::endl;
+
+ _sample_format = sample_format;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_interleaved (bool yn)
+{
+ /*you can ignore them totally*/
+ _interleaved = yn;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_input_channels (uint32_t input_channels)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_input_channels (): " << input_channels << std::endl;
+
+ _input_channels = input_channels;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_output_channels (uint32_t output_channels)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_output_channels (): " << output_channels << std::endl;
+
+ _output_channels = output_channels;
+ return 0;
+}
+
+
+std::string
+WavesAudioBackend::device_name () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::device_name (): " << _device->DeviceName () << std::endl;
+ if (!_device) {
+ return "";
+ }
+ return _device->DeviceName ();
+}
+
+
+float
+WavesAudioBackend::sample_rate () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::sample_rate (): " << std::endl;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::sample_rate (): No device is set!" << std::endl;
+ return -1;
+ }
+
+ int sample_rate = _device->CurrentSamplingRate ();
+
+ /* COMMENTED DBG LOGS */ std::cout << "\t[" << _device->DeviceName () << "]->CurrentSamplingRate () returned " << sample_rate << std::endl;
+
+ return (float)sample_rate;
+}
+
+
+uint32_t
+WavesAudioBackend::buffer_size () const
+{
+
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::buffer_size (): " << std::endl;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::buffer_size (): No device is set!" << std::endl;
+ return 0;
+ }
+
+ int size = _device->CurrentBufferSize ();
+
+ /* COMMENTED DBG LOGS */ std::cout << "\t[" << _device->DeviceName () << "]->CurrentBufferSize () returned " << size << std::endl;
+
+ return (uint32_t)size;
+}
+
+
+SampleFormat
+WavesAudioBackend::sample_format () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::sample_format ()" << std::endl;
+ return _sample_format;
+}
+
+
+bool
+WavesAudioBackend::interleaved () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::interleaved ()" << std::endl;
+
+ return _interleaved;
+}
+
+
+uint32_t
+WavesAudioBackend::input_channels () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::input_channels ()" << std::endl;
+
+ return _input_channels;
+}
+
+
+uint32_t
+WavesAudioBackend::output_channels () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::output_channels ()" << std::endl;
+
+ return _output_channels;
+}
+
+
+std::string
+WavesAudioBackend::control_app_name () const
+{
+ std::string app_name = "";
+
+ if (_device && !dynamic_cast<WCMRNativeAudioNoneDevice*> (_device)) {
+ app_name = "PortAudioMayKnowIt";
+ }
+
+ return app_name;
+}
+
+
+void
+WavesAudioBackend::launch_control_app ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::launch_control_app ()" << std::endl;
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::launch_control_app (): No device is set!" << std::endl;
+ return;
+ }
+
+ WTErr err = _device->ShowConfigPanel (NULL);
+
+ if (eNoErr != err) {
+ std::cerr << "WavesAudioBackend::launch_control_app (): [" << _device->DeviceName () << "]->ShowConfigPanel () failed (" << err << ")!" << std::endl;
+ }
+
+ /* COMMENTED DBG LOGS */ else std::cout << "WavesAudioBackend::launch_control_app (): [" << _device->DeviceName () << "]->ShowConfigPanel () successfully launched!" << std::endl;
+}
+
+
+int
+WavesAudioBackend::_start (bool for_latency_measurement)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_start ()" << std::endl;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::_start (): No device is set!" << std::endl;
+ return -1;
+ }
+
+ if (_register_system_audio_ports () != 0) {
+ std::cerr << "WavesAudioBackend::_start (): _register_system_audio_ports () failed!" << std::endl;
+ return -1;
+ }
+
+ if (_use_midi) {
+ if (_midi_device_manager.start () != 0) {
+ std::cerr << "WavesAudioBackend::_start (): _midi_device_manager.start () failed!" << std::endl;
+ return -1;
+ }
+ if (_register_system_midi_ports () != 0) {
+ std::cerr << "WavesAudioBackend::_start (): _register_system_midi_ports () failed!" << std::endl;
+ return -1;
+ }
+ }
+
+ if (engine.reestablish_ports () != 0) {
+ std::cerr << "WavesAudioBackend::_start (): engine.reestablish_ports () failed!" << std::endl;
+ }
+
+ manager.registration_callback ();
+
+ _call_thread_init_callback = true;
+ WTErr retVal = _device->SetStreaming (true);
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::_start (): [" << _device->DeviceName () << "]->SetStreaming () failed!" << std::endl;
+ return -1;
+ }
+
+ if (_use_midi) {
+ if (_midi_device_manager.stream (true)) {
+ std::cerr << "WavesAudioBackend::_start (): _midi_device_manager.stream (true) failed!" << std::endl;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+void
+WavesAudioBackend::_audio_device_callback (const float* input_buffer,
+ float* output_buffer,
+ unsigned long nframes,
+ pframes_t sample_time,
+ uint64_t cycle_start_time_nanos)
+{
+ uint64_t dsp_start_time_nanos = __get_time_nanos();
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::_audio_device_callback ():" << _device->DeviceName () << std::endl;
+ _sample_time_at_cycle_start = sample_time;
+ _cycle_start_time_nanos = cycle_start_time_nanos;
+
+ if (_buffer_size != nframes) {
+ std::cout << _buffer_size << "!=" << nframes << std::endl;
+ return;
+ }
+
+ _read_audio_data_from_device (input_buffer, nframes);
+ _read_midi_data_from_devices ();
+
+ if (_call_thread_init_callback) {
+ _call_thread_init_callback = false;
+ /* COMMENTED DBG LOGS */ std::cout << "\tAudioEngine::thread_init_callback() invoked for " << std::hex << pthread_self() << std::dec << " !" << std::endl;
+ AudioEngine::thread_init_callback (this);
+ }
+
+ engine.process_callback (nframes);
+
+ _write_audio_data_to_device (output_buffer, nframes);
+ _write_midi_data_to_devices (nframes);
+
+ uint64_t dsp_end_time_nanos = __get_time_nanos();
+
+ _dsp_load_accumulator -= *_dsp_load_history.begin();
+ _dsp_load_history.pop_front();
+ uint64_t dsp_load_nanos = dsp_end_time_nanos - dsp_start_time_nanos;
+ _dsp_load_accumulator += dsp_load_nanos;
+ _dsp_load_history.push_back(dsp_load_nanos);
+
+ return;
+}
+
+
+int
+WavesAudioBackend::stop ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::stop ()" << std::endl;
+
+ WTErr retVal = eNoErr;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::stop (): No device is set!" << std::endl;
+ return -1;
+ }
+
+ /* COMMENTED DBG LOGS */ std::cout << "\t[" << _device->DeviceName () << "]" << std::endl;
+
+ retVal = _device->SetStreaming (false);
+ if (retVal != eNoErr) {
+ std::cerr << "WavesAudioBackend::stop (): [" << _device->DeviceName () << "]->SetStreaming () failed!" << std::endl;
+ return -1;
+ }
+
+ _midi_device_manager.stop ();
+
+ _unregister_system_audio_ports ();
+ _unregister_system_midi_ports ();
+ return 0;
+}
+
+
+int
+WavesAudioBackend::freewheel (bool start_stop)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::freewheel (" << start_stop << "):" << std::endl;
+
+ if (start_stop != _freewheeling) {
+ if (start_stop == true) {
+ WTErr retval = _device->SetStreaming (false);
+ if (retval != eNoErr) {
+ std::cerr << "WavesAudioBackend::freewheel (): [" << _device->DeviceName () << "]->SetStreaming () failed!" << std::endl;
+ return -1;
+ }
+ _call_thread_init_callback = true;
+ _freewheel_thread ();
+ engine.freewheel_callback (start_stop);
+ }
+ else {
+ _freewheel_thread_active = false; // stop _freewheel_thread ()
+ engine.freewheel_callback (start_stop);
+ _call_thread_init_callback = true;
+ WTErr retval = _device->SetStreaming (true);
+ if (retval != eNoErr) {
+ std::cerr << "WavesAudioBackend::freewheel (): [" << _device->DeviceName () << "]->SetStreaming () failed!" << std::endl;
+ return -1;
+ }
+ }
+ _freewheeling = start_stop;
+ }
+ // already doing what has been asked for
+ return 0;
+}
+
+
+void
+WavesAudioBackend::_freewheel_thread ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_freewheel_thread ():" << std::endl;
+ if (!_freewheel_thread_active) { // Lets create it
+
+ /* COMMENTED DBG LOGS */ std::cout << "\tCreating the thread _freewheel_thread () . . ." << std::endl;
+ pthread_attr_t attributes;
+ pthread_t thread_id;
+
+ ThreadData* thread_data = new ThreadData (this, boost::bind (&WavesAudioBackend::_freewheel_thread, this), __thread_stack_size ());
+
+ if (pthread_attr_init (&attributes)) {
+ std::cerr << "WavesAudioBackend::freewheel_thread (): pthread_attr_init () failed!" << std::endl;
+ return;
+ }
+
+ if (pthread_attr_setstacksize (&attributes, __thread_stack_size ())) {
+ std::cerr << "WavesAudioBackend::freewheel_thread (): pthread_attr_setstacksize () failed!" << std::endl;
+ return;
+ }
+
+ _freewheel_thread_active = false;
+ if ((pthread_create (&thread_id, &attributes, __start_process_thread, thread_data))) {
+ _freewheel_thread_active = true;
+ std::cerr << "WavesAudioBackend::freewheel_thread (): pthread_create () failed!" << std::endl;
+ return;
+ }
+
+ /* COMMENTED DBG LOGS */ std::cout << "\t. . . _freewheel_thread () complete." << std::endl;
+ return;
+ }
+
+ if (_call_thread_init_callback) {
+ _call_thread_init_callback = false;
+ AudioEngine::thread_init_callback (this);
+ }
+
+ while (_freewheel_thread_active) {
+ engine.process_callback (_buffer_size);
+ }
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_freewheel_thread (): FINISHED" << std::endl;
+ return;
+}
+
+
+float
+WavesAudioBackend::dsp_load () const
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::dsp_load (): " << std::endl;
+
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::cpu_load (): No device is set!" << std::endl;
+ return 0;
+ }
+
+ float average_dsp_load = (float)_dsp_load_accumulator/_dsp_load_history_length;
+
+ return ( average_dsp_load / _audio_cycle_period_nanos)*100.0;
+}
+
+
+void
+WavesAudioBackend::_init_dsp_load_history()
+{
+ if((_sample_rate <= 0.0) || (_buffer_size <= 0.0)) {
+ return;
+ }
+
+ _audio_cycle_period_nanos = ((uint64_t)1000000000L * _buffer_size) / _sample_rate;
+
+ _dsp_load_accumulator = 0;
+
+ _dsp_load_history_length = (_sample_rate + _buffer_size - 1) / _buffer_size;
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t_dsp_load_history_length = " << _dsp_load_history_length << std::endl;
+ _dsp_load_history = std::list<uint64_t>(_dsp_load_history_length, 0);
+}
+
+
+void
+WavesAudioBackend::transport_start ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::transport_start (): " << std::endl;
+}
+
+
+void
+WavesAudioBackend::transport_stop ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::transport_stop (): " << std::endl;
+}
+
+
+TransportState
+WavesAudioBackend::transport_state () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::transport_state (): " << std::endl;
+ return TransportStopped;
+}
+
+
+void
+WavesAudioBackend::transport_locate (framepos_t pos)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::transport_locate (" << pos << "): " << std::endl;
+}
+
+
+framepos_t
+WavesAudioBackend::transport_frame () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::transport_frame (): " << std::endl;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_time_master (bool yn)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_time_master (): " << yn << std::endl;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::usecs_per_cycle () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::usecs_per_cycle (): " << std::endl;
+ return (1000000 * _sample_rate) / _buffer_size;
+}
+
+
+size_t
+WavesAudioBackend::raw_buffer_size (DataType data_type)
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::raw_buffer_size (" << data_type.to_string () << "): " << std::endl;
+ switch (data_type) {
+ case DataType::AUDIO:
+ return WavesAudioPort::MAX_BUFFER_SIZE_BYTES;
+ break;
+
+ case DataType::MIDI:
+ return WavesMidiPort::MAX_BUFFER_SIZE_BYTES;
+ break;
+
+ default:
+ std::cerr << "WavesAudioBackend::raw_buffer_size (): unexpected data type (" << (uint32_t)data_type <<")!" << std::endl;
+ break;
+ }
+ return 0;
+}
+
+
+pframes_t
+WavesAudioBackend::sample_time ()
+{
+ // WARNING: This is approximate calculation. Implementation of accurate calculation is pending.
+ // http://kokkinizita.linuxaudio.org/papers/usingdll.pdf
+
+ return _sample_time_at_cycle_start + ((__get_time_nanos () - _cycle_start_time_nanos)*_sample_rate)/1000000000L;
+}
+
+
+uint64_t
+WavesAudioBackend::__get_time_nanos ()
+{
+#ifdef __MACOS__
+ // here we exploit the time counting API which is used by the WCMRCoreAudioDeviceManager. However,
+ // the API should be a part of WCMRCoreAudioDeviceManager to give a chance of being tied to the
+ // audio device transport timeß.
+ return AudioConvertHostTimeToNanos (AudioGetCurrentHostTime ());
+
+#elif _WINDOWS
+ LARGE_INTEGER Count;
+ QueryPerformanceCounter (&Count);
+ return uint64_t ((Count.QuadPart * 1000000000L / __performance_counter_frequency));
+#endif
+}
+
+
+pframes_t
+WavesAudioBackend::sample_time_at_cycle_start ()
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::sample_time_at_cycle_start (): " << _sample_time_at_cycle_start << std::endl;
+ return _sample_time_at_cycle_start;
+}
+
+
+pframes_t
+WavesAudioBackend::samples_since_cycle_start ()
+{
+ pframes_t diff_sample_time;
+ diff_sample_time = sample_time () - _sample_time_at_cycle_start;
+ /* COMMENTED DBG LOGS */ std::cout << "samples_since_cycle_start: " << diff_sample_time << std::endl;
+
+ return diff_sample_time;
+}
+
+
+bool
+WavesAudioBackend::get_sync_offset (pframes_t& /*offset*/) const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "get_sync_offset: false" << std::endl;
+
+ return false;
+}
+
+
+int
+WavesAudioBackend::create_process_thread (boost::function<void ()> func)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::create_process_thread ():" << std::endl;
+ int retVal;
+ pthread_attr_t attributes;
+ size_t stacksize_aligned;
+ pthread_t thread_id;
+
+ // Align stacksize to PTHREAD_STACK_MIN.
+ stacksize_aligned = __thread_stack_size ();
+
+ ThreadData* td = new ThreadData (this, func, stacksize_aligned);
+
+ if ((retVal = pthread_attr_init (&attributes))) {
+ std::cerr << "Cannot set thread attr init res = " << retVal << endmsg;
+ return -1;
+ }
+
+ if ((retVal = pthread_attr_setstacksize (&attributes, stacksize_aligned))) {
+ std::cerr << "Cannot set thread stack size (" << stacksize_aligned << ") res = " << retVal << endmsg;
+ return -1;
+ }
+
+ if ((retVal = pthread_create (&thread_id, &attributes, __start_process_thread, td))) {
+ std::cerr << "Cannot create thread res = " << retVal << endmsg;
+ return -1;
+ }
+
+ _backend_threads.push_back (thread_id);
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t\t. . . thread " << std::hex << thread_id << " has been created" << std::endl;
+
+ return 0;
+}
+
+
+void*
+WavesAudioBackend::__start_process_thread (void* arg)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::__start_process_thread ():" << std::endl;
+ ThreadData* td = reinterpret_cast<ThreadData*> (arg);
+ boost::function<void ()> f = td->f;
+ delete td;
+ f ();
+ return 0;
+}
+
+
+int
+WavesAudioBackend::join_process_threads ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::join_process_thread ()" << std::endl;
+ int ret = 0;
+
+ for (std::vector<pthread_t>::const_iterator i = _backend_threads.begin ();
+ i != _backend_threads.end ();
+ ++i) {
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t\tstopping thread " << std::hex << *i << std::dec << "...\n";
+
+ void* status;
+ if (pthread_join (*i, &status) != 0) {
+ std::cerr << "AudioEngine: cannot stop process thread !" << std::endl;
+ ret += -1;
+ }
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t\t\t...done" << std::endl;
+ }
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t\tall threads finished..." << std::endl;
+ _backend_threads.clear ();
+ /* COMMENTED DBG LOGS */ std::cout << "\t\t\tthread list cleared..." << std::endl;
+
+ return ret;
+}
+
+
+bool
+WavesAudioBackend::in_process_thread ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::in_process_thread ()" << std::endl;
+ for (std::vector<pthread_t>::const_iterator i = _backend_threads.begin ();
+ i != _backend_threads.end (); i++) {
+ if (pthread_equal (*i, pthread_self ()) != 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+size_t
+WavesAudioBackend::__thread_stack_size ()
+{
+ // Align stacksize to PTHREAD_STACK_MIN.
+#if defined (__MACOS__)
+ return (((thread_stack_size () - 1) / PTHREAD_STACK_MIN) + 1) * PTHREAD_STACK_MIN;
+#elif defined (_WINDOWS)
+ return thread_stack_size ();
+#endif
+}
+
+
+uint32_t
+WavesAudioBackend::process_thread_count ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::process_thread_count (): returns " << _backend_threads.size () << std::endl;
+ return _backend_threads.size ();
+}
+
+
+void
+WavesAudioBackend::_read_audio_data_from_device (const float* input_buffer, pframes_t nframes)
+{
+#if defined(_WINDOWS)
+ const float **buffer = (const float**)input_buffer;
+ size_t copied_bytes = nframes*sizeof(float*);
+
+ for(std::vector<WavesAudioPort*>::iterator it = _physical_audio_inputs.begin ();
+ it != _physical_audio_inputs.end();
+ ++it)
+ {
+ memcpy((*it)->buffer(), *buffer, copied_bytes);
+ ++buffer;
+ }
+#else
+ std::vector<WavesAudioPort*>::iterator it = _physical_audio_inputs.begin ();
+
+ // Well, let's de-interleave here:
+ const Sample* source = input_buffer;
+
+ for (uint32_t chann_cnt = 0; (chann_cnt < _max_input_channels) && (it != _physical_audio_inputs.end ()); ++chann_cnt, ++source, ++it) {
+ const Sample* src = source;
+ Sample* tgt = (*it)->buffer ();
+
+ for (uint32_t frame = 0; frame < nframes; ++frame, src += _max_input_channels, ++tgt) {
+ *tgt = *src;
+ }
+ }
+#endif
+}
+
+void
+WavesAudioBackend::_write_audio_data_to_device (float* output_buffer, pframes_t nframes)
+{
+#if defined(_WnonononoINDOWS)
+ float **buffer = (float**)output_buffer;
+ size_t copied_bytes = nframes*sizeof(float);
+ int i = 0;
+ for(std::vector<WavesAudioPort*>::iterator it = _physical_audio_outputs.begin ();
+ it != _physical_audio_outputs.end();
+ ++it)
+ {
+ memcpy(*buffer, (*it)->buffer(), copied_bytes);
+ //*buffer = (*it)->buffer();
+ buffer++;
+ }
+#else
+ // Well, let's interleave here:
+ std::vector<WavesAudioPort*>::iterator it = _physical_audio_outputs.begin ();
+ Sample* target = output_buffer;
+
+ for (uint32_t chann_cnt = 0;
+ (chann_cnt < _max_output_channels) && (it != _physical_audio_outputs.end ());
+ ++chann_cnt, ++target, ++it) {
+ const Sample* src = (Sample*) ((*it)->get_buffer (nframes));
+ Sample* tgt = target;
+ for (uint32_t frame = 0; frame < nframes; ++frame, tgt += _max_output_channels, ++src) {
+ *tgt = *src;
+ }
+ }
+#endif
+}
+
+
+static boost::shared_ptr<WavesAudioBackend> __instance;
+
+
+boost::shared_ptr<AudioBackend>
+WavesAudioBackend::__waves_backend_factory (AudioEngine& e)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::__waves_backend_factory ():" << std::endl;
+ if (!__instance) {
+ __instance.reset (new WavesAudioBackend (e));
+ }
+ return __instance;
+}
+
+
+#if defined(_WINDOWS)
+
+uint64_t WavesAudioBackend::__performance_counter_frequency;
+
+#endif
+
+int
+WavesAudioBackend::__instantiate (const std::string& arg1, const std::string& arg2)
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::__instantiate ():" << "[" << arg1 << "], [" << arg2 << "]" << std::endl;
+ __instantiated_name = arg1;
+#if defined(_WINDOWS)
+
+ LARGE_INTEGER Frequency;
+ QueryPerformanceFrequency(&Frequency);
+ __performance_counter_frequency = Frequency.QuadPart;
+ std::cout << "__performance_counter_frequency:" << __performance_counter_frequency << std::endl;
+
+#endif
+ return 0;
+}
+
+
+int
+WavesAudioBackend::__deinstantiate ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::__deinstantiate ():" << std::endl;
+ __instance.reset ();
+ return 0;
+}
+
+
+bool
+WavesAudioBackend::__already_configured ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::__already_configured ():" << std::endl;
+ return false;
+}
+
+
+void*
+WavesAudioBackend::private_handle () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WHY DO CALL IT: WavesAudioBackend::private_handle: " << std::endl;
+ return NULL;
+}
+
+
+bool
+WavesAudioBackend::available () const
+{
+ // COMMENTED SECONDARY DBG LOGS */// std::cout << "WavesAudioBackend::available: " << std::endl;
+ return true;
+}
+
+
+const std::string&
+WavesAudioBackend::my_name () const
+{
+ // COMMENTED SECONDARY DBG LOGS */// std::cout << "WavesAudioBackend::my_name: " << _port_prefix_name << std::endl;
+ return __instantiated_name;
+}
+
+
+bool
+WavesAudioBackend::can_monitor_input () const
+{
+ /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::can_monitor_input: " << std::endl;
+ return false;
+}
+
+std::string WavesAudioBackend::__instantiated_name;
+
+AudioBackendInfo WavesAudioBackend::__backend_info = {
+#ifdef __MACOS__
+ "CoreAudio",
+#elif _WINDOWS
+ "ASIO",
+#endif
+ __instantiate,
+ WavesAudioBackend::__deinstantiate,
+ WavesAudioBackend::__waves_backend_factory,
+ WavesAudioBackend::__already_configured,
+};
+
+
+extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor ()
+{
+ /* COMMENTED DBG LOGS */ std::cout << "waves_backend.dll : ARDOUR::AudioBackendInfo* descriptor (): " << std::endl;
+ return &WavesAudioBackend::backend_info ();
+}
diff --git a/libs/backends/wavesaudio/waves_audiobackend.h b/libs/backends/wavesaudio/waves_audiobackend.h new file mode 100644 index 0000000000..7b56656295 --- /dev/null +++ b/libs/backends/wavesaudio/waves_audiobackend.h @@ -0,0 +1,379 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libardour_waves_audiobackend_h__ +#define __libardour_waves_audiobackend_h__ + +#include <string> +#include <vector> +#include <list> + +#include <stdint.h> +#include <stdlib.h> + +#include <boost/function.hpp> + +#include "ardour/types.h" +#include "ardour/audio_backend.h" + +#include "waves_midi_device_manager.h" + +#ifdef __MACOS__ + +#include <WCMRCoreAudioDeviceManager.h> + +class ArdourAudioDeviceManager : public WCMRCoreAudioDeviceManager +{ + public: + ArdourAudioDeviceManager (WCMRAudioDeviceManagerClient *client) : WCMRCoreAudioDeviceManager (client, eFullDuplexDevices, true, eCABS_Simple, false) {}; +}; + +#elif defined (_WINDOWS) + +#include <WCMRPortAudioDeviceManager.h> + +class ArdourAudioDeviceManager : public WCMRPortAudioDeviceManager +{ + public: + ArdourAudioDeviceManager (WCMRAudioDeviceManagerClient *client) : WCMRPortAudioDeviceManager (client, eFullDuplexDevices, paASIO) {}; +}; + +#endif + +namespace ARDOUR { + +class AudioEngine; +class PortEngine; +class PortManager; +class WavesAudioBackend; +class WavesDataPort; +class WavesAudioPort; +class WavesMidiPort; + + + class WavesAudioBackend : public AudioBackend, WCMRAudioDeviceManagerClient +{ + public: + WavesAudioBackend (AudioEngine& e); + virtual ~WavesAudioBackend (); + + /* AUDIOBACKEND API */ + + virtual std::string name () const; + + virtual bool is_realtime () const; + + virtual bool requires_driver_selection () const; + + virtual std::vector<std::string> enumerate_drivers () const; + + virtual int set_driver (const std::string& /*drivername*/); + + virtual std::vector<DeviceStatus> enumerate_devices () const; + + virtual std::vector<float> available_sample_rates (const std::string& device) const; + + virtual float default_sample_rate () const; + + virtual std::vector<uint32_t> available_buffer_sizes (const std::string& device) const; + + virtual uint32_t available_input_channel_count (const std::string& device) const; + + virtual uint32_t available_output_channel_count (const std::string& device) const; + + virtual bool can_change_sample_rate_when_running () const; + + virtual bool can_change_buffer_size_when_running () const; + + virtual int set_device_name (const std::string& name); + + virtual int set_sample_rate (float); + + virtual int set_buffer_size (uint32_t); + + virtual int set_sample_format (SampleFormat); + + virtual int set_interleaved (bool yn); + + virtual int set_input_channels (uint32_t); + + virtual int set_output_channels (uint32_t); + + virtual int set_systemic_input_latency (uint32_t); + + virtual int set_systemic_output_latency (uint32_t); + + virtual std::string device_name () const; + + virtual float sample_rate () const; + + virtual uint32_t buffer_size () const; + + virtual SampleFormat sample_format () const; + + virtual bool interleaved () const; + + virtual uint32_t input_channels () const; + + virtual uint32_t output_channels () const; + + virtual uint32_t systemic_input_latency () const; + + virtual uint32_t systemic_output_latency () const; + + virtual std::string control_app_name () const; + + virtual void launch_control_app (); + + virtual std::vector<std::string> enumerate_midi_options () const; + + virtual int set_midi_option (const std::string& option); + + virtual std::string midi_option () const; + + virtual int _start (bool for_latency_measurement); + + virtual int stop (); + + virtual int freewheel (bool start_stop); + + virtual float dsp_load () const ; + + virtual void transport_start (); + + virtual void transport_stop (); + + virtual TransportState transport_state () const; + + virtual void transport_locate (framepos_t pos); + + virtual framepos_t transport_frame () const; + + virtual int set_time_master (bool yn); + + virtual int usecs_per_cycle () const; + + virtual size_t raw_buffer_size (DataType data_type); + + virtual pframes_t sample_time (); + + virtual pframes_t sample_time_at_cycle_start (); + + virtual pframes_t samples_since_cycle_start (); + + virtual bool get_sync_offset (pframes_t& offset) const; + + virtual int create_process_thread (boost::function<void ()> func); + + virtual int join_process_threads (); + + virtual bool in_process_thread (); + + virtual uint32_t process_thread_count (); + + virtual void update_latencies (); + + virtual bool speed_and_position (double& speed, framepos_t& position) { + speed = 0.0; + position = 0; + return false; + } + + /* PORTENGINE API */ + + virtual void* private_handle () const; + + virtual const std::string& my_name () const; + + virtual bool available () const; + + virtual uint32_t port_name_size () const; + + virtual int set_port_name (PortHandle port_handle, const std::string& port_name); + + virtual std::string get_port_name (PortHandle port_handle ) const; + + virtual PortHandle get_port_by_name (const std::string& port_name) const; + + virtual int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>& port_handles) const; + + virtual DataType port_data_type (PortHandle port_handle) const; + + virtual PortHandle register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags); + + virtual void unregister_port (PortHandle port_handle); + + virtual int connect (const std::string& src, const std::string& dst); + + virtual int disconnect (const std::string& src, const std::string& dst); + + virtual int connect (PortHandle port_handle, const std::string& port_name); + + virtual int disconnect (PortHandle port_handle, const std::string& port_name); + + virtual int disconnect_all (PortHandle port_handle); + + virtual bool connected (PortHandle port_handle, bool process_callback_safe); + + virtual bool connected_to (PortHandle port_handle, const std::string& port_name, bool process_callback_safe); + + virtual bool physically_connected (PortHandle port_handle, bool process_callback_safe); + + virtual int get_connections (PortHandle port_handle, std::vector<std::string>&, bool process_callback_safe); + + virtual int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index); + + virtual int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size); + + virtual uint32_t get_midi_event_count (void* port_buffer); + + virtual void midi_clear (void* port_buffer); + + virtual bool can_monitor_input () const; + + virtual int request_input_monitoring (PortHandle port_handle, bool); + + virtual int ensure_input_monitoring (PortHandle port_handle, bool); + + virtual bool monitoring_input (PortHandle port_handle); + + virtual void set_latency_range (PortHandle port_handle, bool for_playback, LatencyRange); + + virtual LatencyRange get_latency_range (PortHandle port_handle, bool for_playback); + + virtual bool port_is_physical (PortHandle port_handle) const; + + virtual void get_physical_outputs (DataType type, std::vector<std::string>& port_names); + + virtual void get_physical_inputs (DataType type, std::vector<std::string>& port_names); + + virtual ChanCount n_physical_outputs () const; + + virtual ChanCount n_physical_inputs () const; + + virtual void* get_buffer (PortHandle port_handle, pframes_t frames); + + static AudioBackendInfo& backend_info () { return __backend_info; } + + virtual void AudioDeviceManagerNotification (NotificationReason reason, void* pParam); + + private: + //ArdourAudioDeviceManagerClient _audio_device_manager_client; + ArdourAudioDeviceManager _audio_device_manager; + WavesMidiDeviceManager _midi_device_manager; + + WCMRAudioDevice *_device; + SampleFormat _sample_format; + bool _interleaved; + static std::string __instantiated_name; + uint32_t _input_channels; + uint32_t _max_input_channels; + uint32_t _output_channels; + uint32_t _max_output_channels; + float _sample_rate; + uint32_t _buffer_size; + uint32_t _systemic_input_latency; + uint32_t _systemic_output_latency; + bool _call_thread_init_callback; + std::vector<pthread_t> _backend_threads; + static const size_t __max_raw_midi_buffer_size; + + static const std::vector<std::string> __available_midi_options; + bool _use_midi; + + struct ThreadData { + WavesAudioBackend* engine; + boost::function<void ()> f; + size_t stacksize; + + ThreadData (WavesAudioBackend* e, boost::function<void ()> fp, size_t stacksz) + : engine (e) , f (fp) , stacksize (stacksz) {} + }; + + static boost::shared_ptr<AudioBackend> __waves_backend_factory (AudioEngine& e); + static int __instantiate (const std::string& arg1, const std::string& arg2); + static int __deinstantiate (); + static bool __already_configured (); + + static void* __start_process_thread (void*); + static uint64_t __get_time_nanos (); + + static size_t __thread_stack_size (); + + void _audio_device_callback (const float* input_audio_buffer, + float* output_buffer, + unsigned long nframes, + pframes_t sample_time, + uint64_t cycle_start_time_nanos); + + void _changed_midi_devices (); + + int _register_system_audio_ports (); + int _register_system_midi_ports (); + + int _read_midi_data_from_devices (); + int _write_midi_data_to_devices (pframes_t); + + pframes_t _ms_to_sample_time (int32_t time_ms) const; + int32_t _sample_time_to_ms (pframes_t sample_time) const ; + + void _read_audio_data_from_device (const float* input_buffer, pframes_t nframes); + void _write_audio_data_to_device (float* output_buffer, pframes_t nframes); + + void _unregister_system_audio_ports (); + void _unregister_system_midi_ports (); + + WavesDataPort* _register_port (const std::string& port_name, ARDOUR::DataType type, ARDOUR::PortFlags flags); + inline bool _registered (PortHandle port_handle) const + { + return std::find (_ports.begin (), _ports.end (), (WavesDataPort*)port_handle) != _ports.end (); + } + + WavesDataPort* _find_port (const std::string& port_name) const; + void _freewheel_thread (); + + std::vector<WavesAudioPort*> _physical_audio_inputs; + std::vector<WavesAudioPort*> _physical_audio_outputs; + std::vector<WavesMidiPort*> _physical_midi_inputs; + std::vector<WavesMidiPort*> _physical_midi_outputs; + std::vector<WavesDataPort*> _ports; + static AudioBackendInfo __backend_info; + +#if defined (_WINDOWS) + static uint64_t __performance_counter_frequency; +#endif + uint64_t _cycle_start_time_nanos; + pframes_t _sample_time_at_cycle_start; + + bool _freewheeling; + bool _freewheel_thread_active; + + friend class WavesMidiDeviceManager; + + std::list<uint64_t> _dsp_load_history; + size_t _dsp_load_history_length; + uint64_t _dsp_load_accumulator; + float _audio_cycle_period_nanos; + void _init_dsp_load_history(); +}; + +} // namespace + +#endif /* __libardour_waves_audiobackend_h__ */ + diff --git a/libs/backends/wavesaudio/waves_audiobackend.latency.cc b/libs/backends/wavesaudio/waves_audiobackend.latency.cc new file mode 100644 index 0000000000..c0d2fcd315 --- /dev/null +++ b/libs/backends/wavesaudio/waves_audiobackend.latency.cc @@ -0,0 +1,90 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_dataport.h"
+#include "waves_audiobackend.h"
+
+using namespace ARDOUR;
+
+
+int
+WavesAudioBackend::set_systemic_input_latency (uint32_t systemic_input_latency)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_systemic_input_latency (): " << systemic_input_latency << std::endl;
+
+ _systemic_input_latency = systemic_input_latency;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::set_systemic_output_latency (uint32_t systemic_output_latency)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_systemic_output_latency (): " << systemic_output_latency << std::endl;
+
+ _systemic_output_latency = systemic_output_latency;
+ return 0;
+}
+
+uint32_t
+WavesAudioBackend::systemic_input_latency () const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::systemic_input_latency ()" << std::endl;
+
+ return _systemic_input_latency;
+}
+
+
+uint32_t
+WavesAudioBackend::systemic_output_latency () const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::systemic_output_latency ()" << std::endl;
+
+ return _systemic_output_latency;
+}
+
+
+void
+WavesAudioBackend::update_latencies ()
+{
+ // COMMENTED DBG LOGS */ std::cout << "update_latencies:" << std::endl;
+}
+
+
+void
+WavesAudioBackend::set_latency_range (PortHandle port_handle, bool for_playback, LatencyRange latency_range)
+{
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::set_latency_range (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return;
+ }
+ ((WavesDataPort*)port_handle)->set_latency_range (latency_range, for_playback);
+}
+
+
+LatencyRange
+WavesAudioBackend::get_latency_range (PortHandle port_handle, bool for_playback)
+{
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::get_latency_range (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ LatencyRange lr = {0,0};
+ return lr;
+ }
+ return ((WavesDataPort*)port_handle)->latency_range (for_playback);
+}
diff --git a/libs/backends/wavesaudio/waves_audiobackend.midi.cc b/libs/backends/wavesaudio/waves_audiobackend.midi.cc new file mode 100644 index 0000000000..94c674d073 --- /dev/null +++ b/libs/backends/wavesaudio/waves_audiobackend.midi.cc @@ -0,0 +1,354 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <boost/assign/list_of.hpp>
+
+#include "waves_audiobackend.h"
+#include "waves_midiport.h"
+#include "waves_midi_event.h"
+#include "waves_midi_buffer.h"
+
+using namespace ARDOUR;
+
+#ifdef __MACOS__
+
+const std::vector<std::string> WavesAudioBackend::__available_midi_options = boost::assign::list_of ("None") ("CoreMIDI");
+
+#elif _WINDOWS
+
+const std::vector<std::string> WavesAudioBackend::__available_midi_options = boost::assign::list_of ("None") ("Multimedia Extensions");
+
+#endif
+
+
+std::vector<std::string>
+WavesAudioBackend::enumerate_midi_options () const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::enumerate_midi_options ()" << std::endl;
+ return __available_midi_options;
+}
+
+
+int
+WavesAudioBackend::set_midi_option (const std::string& option)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_midi_option ( " << option << " )" << std::endl;
+ if (option == __available_midi_options[0]) {
+ _use_midi = false;
+ // COMMENTED DBG LOGS */ std::cout << "\tNO MIDI system used)" << std::endl;
+ }
+ else if (option == __available_midi_options[1]) {
+ _use_midi = true;
+ // COMMENTED DBG LOGS */ std::cout << "\tNO MIDI system used)" << std::endl;
+ }
+ else {
+ std::cerr << "WavesAudioBackend::set_midi_option (): Invalid MIDI option!" << std::endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+std::string
+WavesAudioBackend::midi_option () const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::midi_option ():" << std::endl;
+ return * (__available_midi_options.begin () + (_use_midi?1:0));
+}
+
+
+int
+WavesAudioBackend::midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buffer, void* port_buffer, uint32_t event_index)
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::midi_event_get ():" << std::endl;
+
+ if (buffer == NULL) {
+ std::cerr << "WavesAudioBackend::midi_event_get () : NULL in the 'buffer' argument!\n";
+ return -1;
+ }
+
+ if (port_buffer == NULL) {
+ std::cerr << "WavesAudioBackend::midi_event_get () : NULL in the 'port_buffer' argument!\n";
+ return -1;
+ }
+
+ WavesMidiBuffer& source = * (WavesMidiBuffer*)port_buffer;
+
+ if (event_index >= source.size ()) {
+ std::cerr << "WavesAudioBackend::midi_event_get () : 'event_index' is out of the number of events stored in 'port_buffer'!\n";
+ return -1;
+ }
+
+ WavesMidiEvent* waves_midi_event = source[event_index];
+
+ timestamp = waves_midi_event->timestamp ();
+ size = waves_midi_event->size ();
+ *buffer = waves_midi_event->data ();
+
+ return 0;
+}
+
+
+int
+WavesAudioBackend::midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size)
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::midi_event_put ():" << std::endl;
+ if (buffer == NULL) {
+ std::cerr << "WavesAudioBackend::midi_event_put () : NULL in the 'buffer' argument!\n";
+ return -1;
+ }
+
+ if (port_buffer == NULL) {
+ std::cerr << "WavesAudioBackend::midi_event_put () : NULL in the 'port_buffer' argument!\n";
+ return -1;
+ }
+
+ WavesMidiBuffer& target = * (WavesMidiBuffer*)port_buffer;
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "\t [" << target.name () << "]"<< std::endl;
+
+ if (target.size () && (pframes_t)target.back ()->timestamp () > timestamp) {
+ std::cerr << "WavesAudioBackend::midi_event_put (): The MIDI Event to put is a bit late!" << std::endl;
+ std::cerr << "\tprev timestamp is " << (pframes_t)target.back ()->timestamp () << " as the current one is " << timestamp << std::endl;
+ return -1;
+ }
+
+ target.push_back (new WavesMidiEvent (timestamp, buffer, size));
+ return 0;
+}
+
+
+uint32_t
+WavesAudioBackend::get_midi_event_count (void* port_buffer)
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::get_midi_event_count (): " << std::endl;
+
+ if (port_buffer == NULL) {
+ std::cerr << "WavesAudioBackend::get_midi_event_count () : NULL in the 'port_buffer' argument!\n";
+ return -1;
+ }
+
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "\tcount = " << (* (WavesMidiBuffer*)port_buffer).size () << std::endl;
+
+ return (* (WavesMidiBuffer*)port_buffer).size ();
+}
+
+
+void
+WavesAudioBackend::midi_clear (void* port_buffer)
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::midi_clear (): " << std::endl;
+ if (port_buffer == NULL) {
+ std::cerr << "WavesAudioBackend::midi_clear () : NULL in the 'port_buffer' argument!\n";
+ return;
+ }
+
+ (* (WavesMidiBuffer*)port_buffer).clear ();
+}
+
+
+void
+WavesAudioBackend::_changed_midi_devices ()
+{
+ if (_midi_device_manager.stream (false)) {
+ std::cerr << "WavesAudioBackend::_changed_midi_devices (): _midi_device_manager.stream (false) failed!" << std::endl;
+ return;
+ }
+
+ _midi_device_manager.stop ();
+
+ if (_midi_device_manager.start () != 0) {
+ std::cerr << "WavesAudioBackend::_changed_midi_devices (): _midi_device_manager.start () failed!" << std::endl;
+ return;
+ }
+
+ if (_register_system_midi_ports () != 0) {
+ std::cerr << "WavesAudioBackend::_changed_midi_devices (): _register_system_midi_ports () failed!" << std::endl;
+ return;
+ }
+
+ manager.registration_callback ();
+
+ if (_midi_device_manager.stream (true)) {
+ std::cerr << "WavesAudioBackend::_changed_midi_devices (): _midi_device_manager.stream (true) failed!" << std::endl;
+ return;
+ }
+}
+
+
+void
+WavesAudioBackend::_unregister_system_midi_ports ()
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_unregister_system_midi_ports ()" << std::endl;
+ std::vector<WavesMidiPort*> physical_midi_ports = _physical_midi_inputs;
+ physical_midi_ports.insert (physical_midi_ports.begin (), _physical_midi_outputs.begin (), _physical_midi_outputs.end ());
+
+ for (std::vector<WavesMidiPort*>::const_iterator it = physical_midi_ports.begin (); it != physical_midi_ports.end (); ++it) {
+ std::vector<WavesDataPort*>::iterator port_iterator = std::find (_ports.begin (), _ports.end (), *it);
+ if (port_iterator == _ports.end ()) {
+ std::cerr << "WavesAudioBackend::_unregister_system_midi_ports (): Failed to find port [" << (*it)->name () << "]!" << std::endl;
+ }
+ else
+ _ports.erase (port_iterator);
+ delete *it;
+ }
+ _physical_midi_inputs.clear ();
+ _physical_midi_outputs.clear ();
+}
+
+
+int
+WavesAudioBackend::_register_system_midi_ports ()
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_register_system_midi_ports ()" << std::endl;
+
+ LatencyRange lr = {0,0};
+ lr.min = lr.max = _buffer_size;
+
+ for (size_t i = 0; i<_ports.size ();) {
+ WavesMidiPort* midi_port = dynamic_cast<WavesMidiPort*> (_ports[i]);
+ if (!midi_port || !midi_port->is_physical () || !midi_port->is_terminal ()) {
+ ++i;
+ continue;
+ }
+
+ if ((midi_port->is_input () && !midi_port->midi_device ()->is_output ()) ||
+ (midi_port->is_output () && !midi_port->midi_device ()->is_input ())) {
+ disconnect_all (midi_port);
+ unregister_port (midi_port);
+ continue; // to be here for further additions in the end of this loop
+ }
+
+ ++i;
+ }
+
+ const std::vector<WavesMidiDevice *>& devices = _midi_device_manager.devices ();
+
+ for (std::vector<WavesMidiDevice*>::const_iterator it = devices.begin (); it != devices.end (); ++it) {
+ if ((*it)->is_input ()) {
+ std::string port_name = "system_midi:" + (*it)->name () + " capture";
+ WavesDataPort* port = _find_port (port_name);
+ WavesMidiPort* midi_port = dynamic_cast<WavesMidiPort*> (port);
+ if (midi_port && (midi_port->type () != DataType::MIDI ||
+ midi_port->midi_device () != *it ||
+ !midi_port->is_output () ||
+ !midi_port->is_physical () ||
+ !midi_port->is_terminal ())) {
+ std::cerr << "WavesAudioBackend::_register_system_midi_ports (): the port [" << midi_port->name () << "] is inconsystently constructed!" << std::endl;
+ disconnect_all (midi_port);
+ unregister_port (midi_port);
+ port = NULL;
+ }
+
+ if (port == NULL) {
+ port = _register_port ( port_name, DataType::MIDI , static_cast<ARDOUR::PortFlags> (IsOutput | IsPhysical | IsTerminal));
+ if (port == NULL) {
+ return -1;
+ }
+ ((WavesMidiPort*)port)->set_midi_device (*it);
+ }
+ port->set_latency_range (lr, false);
+ }
+
+ if ((*it)->is_output ()) {
+ std::string port_name = "system_midi:" + (*it)->name () + " playback";
+ WavesDataPort* port = _find_port (port_name);
+ WavesMidiPort* midi_port = dynamic_cast<WavesMidiPort*> (port);
+ if (midi_port && (midi_port->type () != DataType::MIDI ||
+ midi_port->midi_device () != *it ||
+ !midi_port->is_input () ||
+ !midi_port->is_physical () ||
+ !midi_port->is_terminal ())) {
+ std::cerr << "WavesAudioBackend::_register_system_midi_ports (): the port [" << midi_port->name () << "] is inconsystently constructed!" << std::endl;
+ disconnect_all (midi_port);
+ unregister_port (midi_port);
+ }
+
+ if (port == NULL) {
+ port = _register_port (port_name,
+ DataType::MIDI,
+ static_cast<ARDOUR::PortFlags> (IsInput | IsPhysical | IsTerminal));
+ if (port == NULL) {
+ return -1;
+ }
+ }
+
+ ((WavesMidiPort*)port)->set_midi_device ((*it));
+ port->set_latency_range (lr, true);
+ }
+ }
+
+ return 0;
+}
+
+
+int
+WavesAudioBackend::_read_midi_data_from_devices ()
+{
+ // COMMENTED FREQUENT DBG LOGS */ std::cout << "WavesAudioBackend::_read_midi_data_from_devices ():" << std::endl;
+ if (!_midi_device_manager.is_streaming ())
+ return 0;
+
+ _midi_device_manager.do_read ();
+
+ for (std::vector<WavesMidiPort*>::iterator it = _physical_midi_inputs.begin (); it != _physical_midi_inputs.end (); ++it) {
+ WavesMidiDevice* midi_device = (*it)->midi_device ();
+
+ WavesMidiBuffer& waves_midi_buffer = (*it)->buffer ();
+ waves_midi_buffer.clear ();
+
+ while (WavesMidiEvent *waves_midi_event = midi_device->dequeue_input_waves_midi_event ()) {
+ int32_t timestamp_st = _buffer_size - (_sample_time_at_cycle_start - waves_midi_event->timestamp ());
+
+ if (timestamp_st < 0) {
+ timestamp_st = 0;
+ }
+ else if (timestamp_st >= (int32_t)_buffer_size) {
+ timestamp_st = _buffer_size - 1;
+ }
+ waves_midi_event->set_timestamp (timestamp_st);
+ waves_midi_buffer.push_back (waves_midi_event);
+ }
+ }
+ return 0;
+}
+
+
+int
+WavesAudioBackend::_write_midi_data_to_devices (pframes_t nframes)
+{
+ if (!_midi_device_manager.is_streaming ())
+ return 0;
+
+ for (std::vector<WavesMidiPort*>::iterator it = _physical_midi_outputs.begin (); it != _physical_midi_outputs.end (); ++it) {
+ WavesMidiDevice* midi_device = (*it)->midi_device ();
+ WavesMidiBuffer &waves_midi_buffer = * (WavesMidiBuffer*) (*it)->get_buffer (nframes);
+
+ for (WavesMidiBufferIterator it = waves_midi_buffer.begin (); it != waves_midi_buffer.end ();) {
+ WavesMidiEvent* waves_midi_event = *it;
+
+ waves_midi_buffer.erase (it);
+
+ waves_midi_event->set_timestamp (_sample_time_at_cycle_start + waves_midi_event->timestamp () + nframes);
+ midi_device->enqueue_output_waves_midi_event (waves_midi_event);
+ }
+ }
+ _midi_device_manager.do_write ();
+ return 0;
+}
diff --git a/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc b/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc new file mode 100644 index 0000000000..6225468864 --- /dev/null +++ b/libs/backends/wavesaudio/waves_audiobackend.port_engine.cc @@ -0,0 +1,654 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_audiobackend.h"
+#include "waves_audioport.h"
+#include "waves_midiport.h"
+#include "waves_midi_event.h"
+
+using namespace ARDOUR;
+
+uint32_t
+WavesAudioBackend::port_name_size () const
+{
+ return 256+64;
+}
+
+int
+WavesAudioBackend::set_port_name (PortHandle port_handle, const std::string& port_name)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::set_port_name (): [" << std::hex << port_handle << std::dec << "], [" << port_name << "]" << std::endl;
+
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::set_port_name (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return -1;
+ }
+
+ return ((WavesAudioPort*)port_handle)->set_name (__instantiated_name + ":" + port_name);
+}
+
+
+std::string
+WavesAudioBackend::get_port_name (PortHandle port_handle) const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_port_name (): [" << std::hex << port_handle << std::dec << "]" << std::endl;
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::get_port_name (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return std::string ();
+ }
+ // COMMENTED DBG LOGS */ else std::cout << "\t[" << ((WavesAudioPort*)port_handle)->name () << "]" << std::endl;
+
+ return ((WavesAudioPort*)port_handle)->name ();
+}
+
+
+PortEngine::PortHandle
+WavesAudioBackend::get_port_by_name (const std::string& port_name) const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_port_by_name (): [" << port_name << "]" << std::endl;
+
+ PortHandle port_handle = (PortHandle)_find_port (port_name);
+ if (!port_handle) {
+ std::cerr << "WavesAudioBackend::get_port_by_name (): Failed to find port [" << port_name << "]!" << std::endl;
+ }
+
+ return port_handle;
+}
+
+
+WavesDataPort*
+WavesAudioBackend::_find_port (const std::string& port_name) const
+{
+ for (std::vector<WavesDataPort*>::const_iterator it = _ports.begin (); it != _ports.end (); ++it) {
+ if ((*it)->name () == port_name) {
+ return *it;
+ }
+ }
+
+ return NULL;
+}
+
+
+int
+WavesAudioBackend::get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>& port_names) const
+{
+
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_ports (): \n\tPattern: [" << port_name_pattern << "]\n\tType: " << type << "\n\tFlags: " << flags << endl;
+
+ unsigned found_ports =0;
+
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ WavesDataPort* port = _ports[i];
+
+ if ((port->type () == type) && (port->flags () & flags)) {
+ port_names.push_back (port->name ());
+ found_ports++;
+ }
+ }
+ return found_ports;
+}
+
+
+DataType
+WavesAudioBackend::port_data_type (PortHandle port_handle) const
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::port_data_type" << std::endl;
+
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::port_data_type (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return DataType::NIL;
+ }
+
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::port_data_type: " << endl;
+
+ return ((WavesAudioPort*)port_handle)->type ();
+}
+
+
+PortEngine::PortHandle
+WavesAudioBackend::register_port (const std::string& shortname, ARDOUR::DataType type, ARDOUR::PortFlags flags)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::register_port (): " << type.to_string () << " [" << shortname << "]" << std::endl;
+
+ if (shortname.size () == 0) {
+ std::cerr << "WavesAudioBackend::register_port (): Invalid (empty) port name!" << std::endl;
+ return NULL;
+ }
+
+ if (flags & IsPhysical) {
+ std::cerr << "WavesAudioBackend::register_port (): Unexpected attribute for port [" << shortname << "]! The port must not be physical!";
+ return NULL;
+ }
+
+ return (PortEngine::PortHandle)_register_port (__instantiated_name + ":" + shortname, type, flags);
+}
+
+
+WavesDataPort*
+WavesAudioBackend::_register_port (const std::string& port_name, ARDOUR::DataType type, ARDOUR::PortFlags flags)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::_register_port (): [" << port_name << "]" << std::endl;
+
+ if (_find_port (port_name) != NULL) {
+ std::cerr << "WavesAudioBackend::register_port () : Port [" << port_name << "] is already registered!" << std::endl;
+ return NULL;
+ }
+
+ WavesDataPort* port = NULL;
+ switch (type) {
+ case ARDOUR::DataType::AUDIO: {
+ WavesAudioPort* audio_port = new WavesAudioPort (port_name, flags);
+ if (flags & IsPhysical)
+ {
+ if (flags & IsOutput)
+ {
+ _physical_audio_inputs.push_back (audio_port);
+ // COMMENTED DBG LOGS */ std::cout << "\t\t" << port_name << " added to physical AUDIO Inputs !" << std::endl;
+ }
+ else if (flags & IsInput)
+ {
+ _physical_audio_outputs.push_back (audio_port);
+ // COMMENTED DBG LOGS */ std::cout << "\t\t" << port_name << " added to physical AUDIO Outputs !" << std::endl;
+ }
+ }
+ port = audio_port;
+ } break;
+ case ARDOUR::DataType::MIDI: {
+ WavesMidiPort* midi_port = new WavesMidiPort (port_name, flags);
+ if (flags & IsPhysical)
+ {
+ if (flags & IsOutput)
+ {
+ _physical_midi_inputs.push_back (midi_port);
+ // COMMENTED DBG LOGS */ std::cout << "\t\t" << port_name << " added to physical MIDI Inputs !" << std::endl;
+ }
+ else if (flags & IsInput)
+ {
+ _physical_midi_outputs.push_back (midi_port);
+ // COMMENTED DBG LOGS */ std::cout << "\t\t" << port_name << " added to physical MIDI Outputs !" << std::endl;
+ }
+ }
+ port = midi_port;
+ } break;
+ default:
+ std::cerr << "WavesAudioBackend::register_port () : Invalid data type (" << (uint32_t)type << ") applied to port [" << port_name << "]!" << std::endl;
+ return NULL;
+ }
+
+ _ports.push_back (port);
+
+ return port;
+}
+
+
+void
+WavesAudioBackend::unregister_port (PortHandle port_handle)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::unregister_port ():" << std::hex << port_handle << std::dec << std::endl;
+
+ // so far we suppose all disconnections will be done prior to unregistering.
+ WavesDataPort* port = (WavesDataPort*)port_handle;
+ std::vector<WavesDataPort*>::iterator port_iterator = std::find (_ports.begin (), _ports.end (), (WavesDataPort*)port_handle);
+ if (port_iterator == _ports.end ()) {
+ std::cerr << "WavesAudioBackend::unregister_port (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return;
+ }
+ // COMMENTED DBG LOGS */ std::cout << "\t[" << ((WavesDataPort*)port_handle)->name () << "]" << std::endl;
+
+ _ports.erase (port_iterator);
+
+ if (port->is_physical ()) {
+ if (port->is_output ()) {
+ switch (port->type ()) {
+ case ARDOUR::DataType::AUDIO: {
+ std::vector<WavesAudioPort*>::iterator audio_port_iterator = std::find (_physical_audio_inputs.begin (), _physical_audio_inputs.end (), port);
+ if (audio_port_iterator == _physical_audio_inputs.end ()) {
+ std::cerr << "WavesAudioBackend::unregister_port (): Failed to find port [" << port->name () << "] in the list of registered physical audio inputs!" << std::endl;
+ return;
+ }
+ _physical_audio_inputs.erase (audio_port_iterator);
+ }
+ break;
+ case ARDOUR::DataType::MIDI: {
+ std::vector<WavesMidiPort*>::iterator midi_port_iterator = std::find (_physical_midi_inputs.begin (), _physical_midi_inputs.end (), port);
+ if (midi_port_iterator == _physical_midi_inputs.end ()) {
+ std::cerr << "WavesAudioBackend::unregister_port (): Failed to find port [" << port->name () << "] in the list of registered physical midi inputs!" << std::endl;
+ return;
+ }
+ _physical_midi_inputs.erase (midi_port_iterator);
+ }
+ break;
+ default:
+ std::cerr << "WavesAudioBackend::unregister_port (): Invalid type (" << port->type () << " applied to [" << port->name () << "]!" << std::endl;
+ break;
+ }
+ }
+ else if (port->flags () & IsInput) {
+ switch (port->type ()) {
+ case ARDOUR::DataType::AUDIO: {
+ std::vector<WavesAudioPort*>::iterator audio_port_iterator = std::find (_physical_audio_outputs.begin (), _physical_audio_outputs.end (), port);
+ if (audio_port_iterator == _physical_audio_outputs.end ())
+ {
+ std::cerr << "WavesAudioBackend::unregister_port: Failed to find port [" << port->name () << std::dec << "] in the list of registered physical audio outputs!\n";
+ return;
+ }
+ _physical_audio_outputs.erase (audio_port_iterator);
+ }
+ break;
+ case ARDOUR::DataType::MIDI: {
+
+ std::vector<WavesMidiPort*>::iterator midi_port_iterator = std::find (_physical_midi_outputs.begin (), _physical_midi_outputs.end (), port);
+ if (midi_port_iterator == _physical_midi_outputs.end ())
+ {
+ std::cerr << "WavesAudioBackend::unregister_port: Failed to find port [" << port->name () << std::dec << "] in the list of registered physical midi outputs!\n";
+ return;
+ }
+ _physical_midi_outputs.erase (midi_port_iterator);
+ }
+ break;
+ default:
+ std::cerr << "WavesAudioBackend::unregister_port (): Invalid type (" << port->type () << " applied to [" << port->name () << "]!" << std::endl;
+ break;
+ }
+ }
+ }
+
+ delete port;
+}
+
+
+int
+WavesAudioBackend::connect (const std::string& src_port_name, const std::string& dst_port_name)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::connect (" << src_port_name << ", " << dst_port_name << "):" << std::endl;
+
+ WavesDataPort* src_port = _find_port (src_port_name);
+ if (src_port == NULL) {
+ std::cerr << "WavesAudioBackend::connect: Failed to find source port " << src_port_name << " !" << std::endl;
+ return -1;
+ }
+
+ WavesDataPort* dst_port = _find_port (dst_port_name);
+ if (dst_port == NULL) {
+ std::cerr << "WavesAudioBackend::connect: Failed to find destination port " << dst_port_name << " !" << std::endl;
+ return -1;
+ }
+
+ // COMMENTED DBG LOGS */ std::cout << "\t\t (" << src_port << ", " << dst_port << "):" << std::endl;
+ return src_port->connect (dst_port);
+}
+
+
+int
+WavesAudioBackend::connect (PortHandle src_port_handle, const std::string& dst_port_name)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::connect ():" << std::endl;
+ if (!_registered (src_port_handle)) {
+ std::cerr << "WavesAudioBackend::connect: Failed to find source port [" << std::hex << src_port_handle << std::dec << "]!" << std::endl;
+ return -1;
+ }
+
+ // COMMENTED DBG LOGS */ std::cout << "\t[" << std::hex << src_port_handle << std::dec << "]" << std::endl;
+ // COMMENTED DBG LOGS */ std::cout << "\t[" << dst_port_name << "]" << std::endl;
+
+ WavesDataPort* dst_port = _find_port (dst_port_name);
+ if (dst_port == NULL) {
+ std::cerr << "WavesAudioBackend::connect (): Failed to find destination port [" << dst_port_name << "]!" << std::endl;
+ return -1;
+ }
+
+ return ((WavesDataPort*)src_port_handle)->connect (dst_port);
+}
+
+
+int
+WavesAudioBackend::disconnect (PortHandle src_port_handle, const std::string& dst_port_name)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::disconnect (" << src_port_handle << ", " << dst_port_name << "):" << std::endl;
+ if (!_registered (src_port_handle)) {
+ std::cerr << "WavesAudioBackend::disconnect (): Failed to find source port [" << std::hex << src_port_handle << std::dec << "]!" << std::endl;
+ return -1;
+ }
+
+ // COMMENTED DBG LOGS */ std::cout << "\t[" << std::hex << src_port_handle << std::dec << "]" << std::endl;
+ // COMMENTED DBG LOGS */ std::cout << "\t[" << dst_port_name << "]" << std::endl;
+
+ WavesDataPort* dst_port = _find_port (dst_port_name);
+ if (dst_port == NULL) {
+ std::cerr << "WavesAudioBackend::disconnect (): Failed to find destination port [" << dst_port_name << "]!" << std::endl;
+ return -1;
+ }
+
+ return ((WavesDataPort*)src_port_handle)->disconnect (dst_port);
+}
+
+
+int
+WavesAudioBackend::disconnect_all (PortHandle port_handle)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::disconnect_all ():" << std::endl;
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::disconnect_all : Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return -1;
+ }
+
+ ((WavesDataPort*)port_handle)->disconnect_all ();
+
+ return 0;
+}
+
+
+int
+WavesAudioBackend::disconnect (const std::string& src_port_name, const std::string& dst_port_name)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::disconnect (" << src_port_name << ", " << dst_port_name << "):" << std::endl;
+
+ WavesDataPort* src_port = _find_port (src_port_name);
+ if (src_port == NULL) {
+ std::cerr << "WavesAudioBackend::disconnect : Failed to find source port!\n";
+ return -1;
+ }
+
+ WavesDataPort* dst_port = _find_port (dst_port_name);
+ if (dst_port == NULL) {
+ std::cerr << "WavesAudioBackend::disconnect : Failed to find destination port!\n";
+ return -1;
+ }
+
+ return dst_port->disconnect (src_port);
+}
+
+
+bool
+WavesAudioBackend::connected (PortHandle port_handle, bool process_callback_safe)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::connected ():" << std::endl;
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::connected (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return false;
+ }
+
+ return ((WavesDataPort*)port_handle)->is_connected ();
+}
+
+
+bool
+WavesAudioBackend::connected_to (PortHandle src_port_handle, const std::string& dst_port_name, bool process_callback_safe)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::connected_to (" << src_port_handle << ", " << dst_port_name << ")" << std::endl;
+
+ if (!_registered (src_port_handle)) {
+ std::cerr << "WavesAudioBackend::connected_to : Failed to find source port!" << std::endl;
+ return false;
+ }
+
+ WavesDataPort* dst_port = _find_port (dst_port_name);
+ if (dst_port == NULL) {
+ std::cerr << "WavesAudioBackend::connected_to : Failed to find destination port!" << std::endl;
+ return -1;
+ }
+ // COMMENTED DBG LOGS */ std::cout << "\t return " << ((((WavesDataPort*)src_port_handle)->is_connected (dst_port)) ? "YES":"NO") << ", " << dst_port_name << ")" << std::endl;
+ return ((WavesDataPort*)src_port_handle)->is_connected (dst_port);
+}
+
+
+bool
+WavesAudioBackend::physically_connected (PortHandle port_handle, bool process_callback_safe)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::physically_connected ():" << std::endl;
+
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::physically_connected (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return false;
+ }
+
+ return ((WavesDataPort*)port_handle)->is_physically_connected ();
+}
+
+
+int
+WavesAudioBackend::get_connections (PortHandle port_handle, std::vector<std::string>& names, bool process_callback_safe)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_connections ()" << std::endl;
+
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::get_connections (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return -1;
+ }
+
+ if (names.size ()) {
+ std::cerr << "WavesAudioBackend::get_connections () : Parameter 'names' is not empty!\n";
+ return -1;
+ }
+
+ const std::vector<WavesDataPort*>& connected_ports = ((WavesDataPort*)port_handle)->get_connections ();
+
+ for (std::vector<WavesDataPort*>::const_iterator it = connected_ports.begin (); it != connected_ports.end (); ++it) {
+ names.push_back ((*it)->name ());
+ }
+
+ return (int)names.size ();
+}
+
+
+int
+WavesAudioBackend::request_input_monitoring (PortHandle, bool)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::request_input_monitoring: " << std::endl;
+ return 0;
+}
+
+
+int
+WavesAudioBackend::ensure_input_monitoring (PortHandle, bool)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::ensure_input_monitoring: " << std::endl;
+ return 0;
+}
+
+
+bool
+WavesAudioBackend::monitoring_input (PortHandle)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::monitoring_input: " << std::endl;
+ return false;
+}
+
+
+bool
+WavesAudioBackend::port_is_physical (PortHandle port_handle) const
+{
+
+ if (!_registered (port_handle)) {
+ std::cerr << "WavesAudioBackend::port_is_physical (): Failed to find port [" << std::hex << port_handle << std::dec << "]!" << std::endl;
+ return -1;
+ }
+
+ return (((WavesAudioPort*)port_handle)->flags () & IsPhysical) != 0;
+}
+
+
+void
+WavesAudioBackend::get_physical_outputs (DataType type, std::vector<std::string>& names)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_physical_outputs ():" << std::endl << "\tdatatype = " << type << std::endl;
+
+ switch (type) {
+ case ARDOUR::DataType::AUDIO: {
+ for (std::vector<WavesAudioPort*>::iterator it = _physical_audio_outputs.begin (); it != _physical_audio_outputs.end (); ++it) {
+ // COMMENTED DBG LOGS */ std::cout << "\t" << (*it)->name () << std::endl;
+ names.push_back ((*it)->name ());
+ }
+ } break;
+ case ARDOUR::DataType::MIDI: {
+ for (std::vector<WavesMidiPort*>::iterator it = _physical_midi_outputs.begin (); it != _physical_midi_outputs.end (); ++it) {
+ // COMMENTED DBG LOGS */ std::cout << "\t" << (*it)->name () << std::endl;
+ names.push_back ((*it)->name ());
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+
+void
+WavesAudioBackend::get_physical_inputs (DataType type, std::vector<std::string>& names)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::get_physical_inputs ():" << std::endl << "\tdatatype = " << type << std::endl;
+ switch (type) {
+ case ARDOUR::DataType::AUDIO: {
+ for (std::vector<WavesAudioPort*>::iterator it = _physical_audio_inputs.begin (); it != _physical_audio_inputs.end (); ++it) {
+ // COMMENTED DBG LOGS */ std::cout << "\t" << (*it)->name () << std::endl;
+ names.push_back ((*it)->name ());
+ }
+ } break;
+ case ARDOUR::DataType::MIDI: {
+ for (std::vector<WavesMidiPort*>::iterator it = _physical_midi_inputs.begin (); it != _physical_midi_inputs.end (); ++it) {
+ // COMMENTED DBG LOGS */ std::cout << "\t" << (*it)->name () << std::endl;
+ names.push_back ((*it)->name ());
+ }
+ } break;
+ default:
+ break;
+ }
+}
+
+
+ChanCount
+WavesAudioBackend::n_physical_outputs () const
+{
+ ChanCount chan_count;
+ chan_count.set (DataType::AUDIO, _physical_audio_outputs.size ());
+ chan_count.set (DataType::MIDI, _physical_midi_outputs.size ());
+
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::n_physical_outputs ():" << std::endl << "\ttotal = " << chan_count.n_total () << std::endl;
+
+ return chan_count;
+}
+
+
+ChanCount
+WavesAudioBackend::n_physical_inputs () const
+{
+ ChanCount chan_count;
+ chan_count.set (DataType::AUDIO, _physical_audio_inputs.size ());
+ chan_count.set (DataType::MIDI, _physical_midi_inputs.size ());
+
+ // COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::n_physical_outputs ():" << std::endl << "\ttotal = " << chan_count.n_total () << std::endl;
+
+ return chan_count;
+}
+
+
+void*
+WavesAudioBackend::get_buffer (PortHandle port_handle, pframes_t nframes)
+{
+ // Here we would check if the port is registered. However, we will not do it as
+ // it's relatively VERY SLOW operation. So let's count on consistency
+ // of the caller as get_buffer normally is called hundreds of "kilotimes" per second.
+
+ if (port_handle == NULL) {
+ std::cerr << "WavesAudioBackend::get_buffer : Invalid port handler <NULL>!" << std::endl;
+ return NULL;
+ }
+
+ return ((WavesAudioPort*)port_handle)->get_buffer (nframes);
+}
+
+
+int
+WavesAudioBackend::_register_system_audio_ports ()
+{
+ if (!_device) {
+ std::cerr << "WavesAudioBackend::_register_system_audio_ports (): No device is set!" << std::endl;
+ return -1;
+ }
+
+ std::vector<std::string> input_channels = _device->InputChannels ();
+ _max_input_channels = input_channels.size ();
+
+ uint32_t channels = (_input_channels ? _input_channels : input_channels.size ());
+ uint32_t port_number = 0;
+
+ LatencyRange lr = {0,0};
+
+ // Get latency for capture
+ lr.min = lr.max = _device->GetLatency (false) + _device->CurrentBufferSize () + _systemic_input_latency;
+ for (std::vector<std::string>::iterator it = input_channels.begin ();
+ (port_number < channels) && (it != input_channels.end ());
+ ++it) {
+ std::ostringstream port_name;
+ port_name << "capture_" << ++port_number;
+
+ WavesDataPort* port = _register_port ("system:" + port_name.str (), DataType::AUDIO , static_cast<PortFlags> (IsOutput | IsPhysical | IsTerminal));
+ if (port == NULL) {
+ std::cerr << "WavesAudioBackend::_create_system_audio_ports (): Failed registering port [" << port_name << "] for [" << _device->DeviceName () << "]" << std::endl;
+ return-1;
+ }
+ set_latency_range (port, false, lr);
+ }
+
+ std::vector<std::string> output_channels = _device->OutputChannels ();
+ _max_output_channels = output_channels.size ();
+ channels = (_output_channels ? _output_channels : _max_output_channels);
+ port_number = 0;
+
+ // Get latency for playback
+ lr.min = lr.max = _device->GetLatency (true) + _device->CurrentBufferSize () + _systemic_output_latency;
+
+ for (std::vector<std::string>::iterator it = output_channels.begin ();
+ (port_number < channels) && (it != output_channels.end ());
+ ++it) {
+ std::ostringstream port_name;
+ port_name << "playback_" << ++port_number;
+ WavesDataPort* port = _register_port ("system:" + port_name.str (), DataType::AUDIO , static_cast<PortFlags> (IsInput| IsPhysical | IsTerminal));
+ if (port == NULL) {
+ std::cerr << "WavesAudioBackend::_create_system_audio_ports (): Failed registering port ]" << port_name << "] for [" << _device->DeviceName () << "]" << std::endl;
+ return-1;
+ }
+ set_latency_range (port, true, lr);
+ }
+
+ return 0;
+}
+
+
+void
+WavesAudioBackend::_unregister_system_audio_ports ()
+{
+ std::vector<WavesAudioPort*> physical_audio_ports = _physical_audio_inputs;
+ physical_audio_ports.insert (physical_audio_ports.begin (), _physical_audio_outputs.begin (), _physical_audio_outputs.end ());
+
+ for (std::vector<WavesAudioPort*>::const_iterator it = physical_audio_ports.begin (); it != physical_audio_ports.end (); ++it) {
+ std::vector<WavesDataPort*>::iterator port_iterator = std::find (_ports.begin (), _ports.end (), *it);
+ if (port_iterator == _ports.end ()) {
+ std::cerr << "WavesAudioBackend::_unregister_system_audio_ports (): Failed to find port [" << (*it)->name () << "]!" << std::endl;
+ }
+ else {
+ _ports.erase (port_iterator);
+ }
+ delete *it;
+ }
+
+ _physical_audio_inputs.clear ();
+ _physical_audio_outputs.clear ();
+}
+
+
diff --git a/libs/backends/wavesaudio/waves_audioport.cc b/libs/backends/wavesaudio/waves_audioport.cc new file mode 100644 index 0000000000..62bacdbcea --- /dev/null +++ b/libs/backends/wavesaudio/waves_audioport.cc @@ -0,0 +1,62 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_audioport.h"
+
+using namespace ARDOUR;
+
+WavesAudioPort::WavesAudioPort (const std::string& port_name, PortFlags flags)
+ : WavesDataPort (port_name, flags)
+{
+ memset (_buffer, 0, sizeof (_buffer));
+}
+
+
+void* WavesAudioPort::get_buffer (pframes_t nframes)
+{
+ if (is_input ()) {
+
+ std::vector<WavesDataPort*>::const_iterator it = get_connections ().begin ();
+
+ if (it != get_connections ().end ()) {
+ /* In fact, the static casting to (const WavesAudioPort*) is not that safe.
+ * However, mixing the buffers is assumed in the time critical conditions.
+ * Base class WavesDataPort takes is supposed to provide enough consistentcy
+ * of the connections.
+ */
+ for (memcpy (_buffer, ((const WavesAudioPort*)*it)->const_buffer (), nframes * sizeof (Sample)), ++it;
+ it != get_connections ().end ();
+ ++it) {
+ Sample* tgt = buffer ();
+ const Sample* src = ((const WavesAudioPort*)*it)->const_buffer ();
+ for (uint32_t frame = 0; frame < nframes; ++frame, ++tgt, ++src) {
+ *tgt += *src;
+ }
+ }
+ }
+ }
+ return _buffer;
+}
+
+
+void
+WavesAudioPort::_wipe_buffer()
+{
+ memset (_buffer, 0, sizeof (_buffer));
+}
diff --git a/libs/backends/wavesaudio/waves_audioport.h b/libs/backends/wavesaudio/waves_audioport.h new file mode 100644 index 0000000000..a0f878bee5 --- /dev/null +++ b/libs/backends/wavesaudio/waves_audioport.h @@ -0,0 +1,58 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __libardour_waves_audioport_h__
+#define __libardour_waves_audioport_h__
+
+#include "memory.h"
+#include "waves_dataport.h"
+
+namespace ARDOUR {
+
+class WavesAudioPort : public WavesDataPort {
+
+public:
+ enum BufferSize {
+ MAX_BUFFER_SIZE_SAMPLES = 8192,
+ MAX_BUFFER_SIZE_BYTES = sizeof (Sample) * MAX_BUFFER_SIZE_SAMPLES
+ };
+
+ WavesAudioPort (const std::string& port_name, PortFlags flags);
+
+ virtual ~WavesAudioPort () { };
+
+ virtual DataType type () const { return DataType::AUDIO; };
+
+ inline Sample* buffer () { return _buffer; }
+ inline const Sample* const_buffer () const { return _buffer; }
+
+ virtual void* get_buffer (pframes_t nframes);
+
+protected:
+ virtual void _wipe_buffer();
+
+private:
+
+ Sample _buffer[MAX_BUFFER_SIZE_SAMPLES];
+};
+
+} // namespace
+
+#endif /* __libardour_waves_audioport_h__ */
+
diff --git a/libs/backends/wavesaudio/waves_dataport.cc b/libs/backends/wavesaudio/waves_dataport.cc new file mode 100644 index 0000000000..84f4efaf6c --- /dev/null +++ b/libs/backends/wavesaudio/waves_dataport.cc @@ -0,0 +1,142 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_dataport.h"
+
+using namespace ARDOUR;
+
+WavesDataPort::WavesDataPort (const std::string& inport_name, PortFlags inflags)
+ : _name (inport_name)
+ , _flags (inflags)
+{
+ _capture_latency_range.min =
+ _capture_latency_range.max =
+ _playback_latency_range.min =
+ _playback_latency_range.max = 0;
+}
+
+
+WavesDataPort::~WavesDataPort ()
+{
+ disconnect_all ();
+}
+
+
+int WavesDataPort::connect (WavesDataPort *port)
+{
+ if (!port) {
+ std::cerr << "WavesDataPort::connect (): invalid (null) port to connect to!" << std::endl;
+ return -1;
+ }
+
+ if (type () != port->type ()) {
+ std::cerr << "WavesDataPort::connect (): wrong type of the port to connect to!" << std::endl;
+ return -1;
+ }
+
+ if (is_output () && port->is_output ()) {
+ std::cerr << "WavesDataPort::connect (): attempt to connect output port to output port!" << std::endl;
+ return -1;
+ }
+
+ if (is_input () && port->is_input ()) {
+ std::cerr << "WavesDataPort::connect (): attempt to connect input port to input port!" << std::endl;
+ return -1;
+ }
+
+ if (this == port) {
+ std::cerr << "WavesDataPort::connect (): attempt to connect port to itself!" << std::endl;
+ return -1;
+ }
+
+ if (is_connected (port)) {
+ std::cerr << "WavesDataPort::connect (): the ports are already connected!" << std::endl;
+ return -1;
+ }
+
+ _connect (port, true);
+ return 0;
+}
+
+
+void WavesDataPort::_connect (WavesDataPort *port, bool api_call)
+{
+ _connections.push_back (port);
+ if (api_call) {
+ port->_connect (this, false);
+ }
+}
+
+
+int WavesDataPort::disconnect (WavesDataPort *port)
+{
+ if (port == NULL) {
+ std::cerr << "WavesDataPort::disconnect (): invalid (null) port to disconnect from!" << std::endl;
+ return -1;
+ }
+
+ if (!is_connected (port)) {
+ std::cerr << "WavesDataPort::disconnect (): the ports are not connected!" << std::endl;
+ return -1;
+ }
+
+ _disconnect (port, true);
+
+ return 0;
+}
+
+
+void WavesDataPort::_disconnect (WavesDataPort *port, bool api_call)
+{
+ std::vector<WavesDataPort*>::iterator it = std::find (_connections.begin (), _connections.end (), port);
+
+ if (it != _connections.end ()) { // actually, it's supposed to be always true.
+ _connections.erase (it);
+ }
+
+ if (api_call) {
+ port->_disconnect (this, false);
+ }
+
+ if (is_input() && _connections.empty())
+ {
+ _wipe_buffer();
+ }
+}
+
+
+void WavesDataPort::disconnect_all ()
+{
+ while (!_connections.empty ()) {
+ _connections.back ()->_disconnect (this, false);
+ _connections.pop_back ();
+ }
+}
+
+
+bool WavesDataPort::is_physically_connected () const
+{
+ for (std::vector<WavesDataPort*>::const_iterator it = _connections.begin (); it != _connections.end (); ++it) {
+ if ((*it)->is_physical ()) {
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/libs/backends/wavesaudio/waves_dataport.h b/libs/backends/wavesaudio/waves_dataport.h new file mode 100644 index 0000000000..fd8dd8090b --- /dev/null +++ b/libs/backends/wavesaudio/waves_dataport.h @@ -0,0 +1,115 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __libardour_waves_dataport_h__
+#define __libardour_waves_dataport_h__
+
+#include "ardour/types.h"
+#include "memory.h"
+
+namespace ARDOUR {
+
+class WavesDataPort {
+public:
+
+ virtual ~WavesDataPort ();
+
+ inline const std::string& name () const
+ {
+ return _name;
+ }
+
+ int set_name (const std::string &name)
+ {
+ _name = name;
+ return 0;
+ }
+
+ virtual DataType type () const = 0;
+
+ inline PortFlags flags () const
+ {
+ return _flags;
+ }
+
+ inline bool is_input () { return flags () & IsInput; }
+ inline bool is_output () { return flags () & IsOutput; }
+ inline bool is_physical () { return flags () & IsPhysical; }
+ inline bool is_terminal () { return flags () & IsTerminal; }
+ inline operator void* () { return (void*)this; }
+
+ inline const LatencyRange& latency_range (bool for_playback) const
+ {
+ return for_playback ? _playback_latency_range : _capture_latency_range;
+ }
+
+ inline void set_latency_range (const LatencyRange &latency_range, bool for_playback)
+ {
+ if (for_playback)
+ {
+ _playback_latency_range = latency_range;
+ }
+ else
+ {
+ _capture_latency_range = latency_range;
+ }
+ }
+
+ int connect (WavesDataPort *port);
+
+ int disconnect (WavesDataPort *port);
+
+ void disconnect_all ();
+
+ bool inline is_connected (const WavesDataPort *port) const
+ {
+ return std::find (_connections.begin (), _connections.end (), port) != _connections.end ();
+ }
+
+ bool inline is_connected () const
+ {
+ return _connections.size () != 0;
+ }
+
+ bool is_physically_connected () const;
+
+ inline const std::vector<WavesDataPort *>& get_connections () const { return _connections; }
+
+ virtual void* get_buffer (pframes_t nframes) = 0;
+
+protected:
+ WavesDataPort (const std::string& inport_name, PortFlags inflags);
+ virtual void _wipe_buffer() = 0;
+
+private:
+
+ std::string _name;
+ const PortFlags _flags;
+ LatencyRange _capture_latency_range;
+ LatencyRange _playback_latency_range;
+ std::vector<WavesDataPort*> _connections;
+
+ void _connect (WavesDataPort* port, bool api_call);
+ void _disconnect (WavesDataPort* port, bool api_call);
+};
+
+} // namespace
+
+#endif /* __libardour_waves_dataport_h__ */
+
diff --git a/libs/backends/wavesaudio/waves_midi_buffer.cc b/libs/backends/wavesaudio/waves_midi_buffer.cc new file mode 100644 index 0000000000..24527de0e5 --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_buffer.cc @@ -0,0 +1,50 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_midi_buffer.h"
+#include "waves_midi_event.h"
+
+using namespace ARDOUR;
+
+WavesMidiBuffer::WavesMidiBuffer (std::string name)
+ : std::vector<WavesMidiEvent*> ()
+ , _name (name)
+{
+}
+
+WavesMidiBuffer::~WavesMidiBuffer ()
+{
+ clear ();
+}
+
+void WavesMidiBuffer::clear ()
+{
+ for (WavesMidiBufferIterator it = begin (); it != end (); ++it)
+ delete *it;
+
+ std::vector<WavesMidiEvent*>::clear ();
+}
+
+WavesMidiBuffer& WavesMidiBuffer::operator += (const WavesMidiBuffer& source)
+{
+ for (WavesMidiBufferConstIterator it = source.begin (); it != source.end (); ++it) {
+ push_back (new WavesMidiEvent (**it));
+ }
+ return *this;
+}
diff --git a/libs/backends/wavesaudio/waves_midi_buffer.h b/libs/backends/wavesaudio/waves_midi_buffer.h new file mode 100644 index 0000000000..b1f6e90c36 --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_buffer.h @@ -0,0 +1,47 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+#ifndef __libardour_waves_midi_buffer_h__
+#define __libardour_waves_midi_buffer_h__
+
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class WavesMidiEvent;
+
+class WavesMidiBuffer : public std::vector<WavesMidiEvent*>
+{
+public:
+ WavesMidiBuffer (std::string name);
+ ~WavesMidiBuffer ();
+ void clear ();
+ WavesMidiBuffer& operator += (const WavesMidiBuffer& source);
+
+ inline const std::string name () { return _name; } // for DBG purpouses;
+
+private:
+ const std::string _name;
+};
+
+typedef std::vector<WavesMidiEvent*>::iterator WavesMidiBufferIterator;
+typedef std::vector<WavesMidiEvent*>::const_iterator WavesMidiBufferConstIterator;
+
+} // namespace
+
+#endif /* __libardour_waves_midi_buffer_h__ */
diff --git a/libs/backends/wavesaudio/waves_midi_device.cc b/libs/backends/wavesaudio/waves_midi_device.cc new file mode 100644 index 0000000000..aa305955a6 --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_device.cc @@ -0,0 +1,268 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_midi_device.h"
+#include "waves_midi_event.h"
+
+// use non-zero latency because we want output to be timestapmed
+#define LATENCY 0
+
+#define QUEUE_LENGTH 1024
+
+using namespace ARDOUR;
+
+WavesMidiDevice::WavesMidiDevice (const std::string& device_name)
+ : _pm_input_id (pmNoDevice)
+ , _pm_output_id (pmNoDevice)
+ , _name (device_name)
+ , _input_queue (NULL)
+ , _output_queue (NULL)
+ , _input_pm_stream (NULL)
+ , _output_pm_stream (NULL)
+ , _incomplete_waves_midi_event (NULL)
+{
+ validate ();
+}
+
+WavesMidiDevice::~WavesMidiDevice ()
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::~WavesMidiDevice ():" << name () << std::endl;
+ close ();
+}
+
+void
+WavesMidiDevice::validate ()
+{
+ _pm_input_id =
+ _pm_output_id = pmNoDevice;
+ int count = Pm_CountDevices ();
+
+ for (int i = 0; i < count; i++) {
+
+ const PmDeviceInfo* pm_device_info = Pm_GetDeviceInfo (i);
+
+ if (pm_device_info == NULL) {
+ continue;
+ }
+ if (name () == pm_device_info->name) {
+ if (pm_device_info->input){
+ _pm_input_id = i;
+ }
+ if (pm_device_info->output){
+ _pm_output_id = i;
+ }
+ }
+ }
+}
+
+int
+WavesMidiDevice::open (PmTimeProcPtr time_proc, void* time_info)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::open ():" << name () << std::endl;
+
+ if (is_input () && !_input_pm_stream) {
+ if (pmNoError != Pm_OpenInput (&_input_pm_stream,
+ _pm_input_id,
+ NULL,
+ 1024,
+ time_proc,
+ time_info)) {
+ std::cerr << "WavesMidiDevice::open (): Pm_OpenInput () failed for " << _pm_input_id << "-[" << name () << "]!" << std::endl;
+ _input_pm_stream = NULL;
+ _pm_input_id = pmNoDevice;
+ return -1;
+ }
+ _input_queue = Pm_QueueCreate (QUEUE_LENGTH, sizeof (const WavesMidiEvent*));
+ if (NULL == _input_queue) {
+ std::cerr << "WavesMidiDevice::open (): _input_queue = Pm_QueueCreate () failed for " << _pm_input_id << "-[" << name () << "]!" << std::endl;
+ close ();
+ return -1;
+ }
+ }
+
+ if (is_output () && !_output_pm_stream) {
+ if (pmNoError != Pm_OpenOutput (&_output_pm_stream,
+ _pm_output_id,
+ NULL,
+ 1024,
+ time_proc,
+ time_info,
+ LATENCY)) {
+ std::cerr << "WavesMidiDevice::open (): Pm_OpenOutput () failed for " << _pm_output_id << "-[" << name () << "]!" << std::endl;
+ _output_pm_stream = NULL;
+ _pm_output_id = pmNoDevice;
+ return -1;
+ }
+ _output_queue = Pm_QueueCreate (QUEUE_LENGTH, sizeof (const WavesMidiEvent*));
+ if (NULL == _output_queue) {
+ std::cerr << "WavesMidiDevice::open (): _output_queue = Pm_QueueCreate () failed for " << _pm_output_id << "-[" << name () << "]!" << std::endl;
+ close ();
+ return -1;
+ }
+ }
+ return 0;
+}
+
+
+void
+WavesMidiDevice::close ()
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::close ():" << name () << std::endl;
+ WavesMidiEvent *waves_midi_event;
+
+ if (_input_pm_stream) {
+ Pm_Close (_input_pm_stream);
+ while (1 == Pm_Dequeue (_input_queue, &waves_midi_event)) {
+ delete waves_midi_event;
+ }
+
+ Pm_QueueDestroy (_input_queue);
+ _input_queue = NULL;
+ _input_pm_stream = NULL;
+ _pm_input_id = pmNoDevice;
+ }
+
+
+ if ( _output_pm_stream ) {
+ Pm_Close (_output_pm_stream);
+ while (1 == Pm_Dequeue (_output_queue, &waves_midi_event)) {
+ delete waves_midi_event;
+ }
+ Pm_QueueDestroy (_output_queue);
+ _output_queue = NULL;
+ _output_pm_stream = NULL;
+ _pm_output_id = pmNoDevice;
+ }
+}
+
+void
+WavesMidiDevice::do_io ()
+{
+ read_midi ();
+ write_midi ();
+}
+
+void
+WavesMidiDevice::read_midi ()
+{
+ if (NULL == _input_pm_stream) {
+ return;
+ }
+
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_read_midi (): " << _pm_device_id << "-[" << name () << "]" << std::endl;
+
+ while (Pm_Poll (_input_pm_stream) > 0) {
+ PmEvent pm_event; // just one message at a time
+ int result = Pm_Read (_input_pm_stream, &pm_event, 1);
+ if (result < 0) {
+ std::cerr << "WavesMidiDevice::_read_midi (): Pm_Read () failed (" << result << ") for [" << name () << "]!" << std::endl;
+ break;
+ }
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_read_midi (): " << _pm_device_id << "-[" << name () << "] evt-tm:" << pm_event.timestamp << std::endl;
+ if (_incomplete_waves_midi_event == NULL ) {
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_read_midi (): " << _pm_device_id << "-[" << name () << "] : new _incomplete_waves_midi_event" << std::endl;
+ _incomplete_waves_midi_event = new WavesMidiEvent (pm_event.timestamp);
+ }
+
+ WavesMidiEvent *nested_pm_event = _incomplete_waves_midi_event->append_data (pm_event);
+ if (nested_pm_event) {
+ Pm_Enqueue (_input_queue, &nested_pm_event);
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_read_midi (): " << _pm_device_id << "-[" << name () << "] : Pm_Enqueue (_input_queue, nested_pm_event)" << std::endl;
+ }
+ switch ( _incomplete_waves_midi_event->state ()) {
+ case WavesMidiEvent::BROKEN:
+ delete _incomplete_waves_midi_event;
+ _incomplete_waves_midi_event = NULL;
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_read_midi (): " << _pm_device_id << "-[" << name () << "] : case WavesMidiEvent::BROKEN:" << std::endl;
+ break;
+ case WavesMidiEvent::COMPLETE:
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_read_midi (): " << _pm_device_id << "-[" << name () << "] : Pm_Enqueue (_input_queue, _incomplete_waves_midi_event); " << std::hex << (void*)_incomplete_waves_midi_event << std::dec << std::endl;
+ Pm_Enqueue (_input_queue, &_incomplete_waves_midi_event);
+ _incomplete_waves_midi_event = NULL;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+void
+WavesMidiDevice::write_midi ()
+{
+ if (NULL == _output_pm_stream) {
+ return;
+ }
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_write_midi (): " << _pm_device_id << "-[" << name () << "]" << std::endl;
+
+ PmError err;
+ WavesMidiEvent *waves_midi_event;
+
+ while (1 == Pm_Dequeue (_output_queue, &waves_midi_event)) {
+ if (waves_midi_event->sysex ()) {
+ // LATENCY compensation
+ err = Pm_WriteSysEx (_output_pm_stream, waves_midi_event->timestamp () - LATENCY, waves_midi_event->data ());
+ if (0 > err) {
+ std::cout << "WavesMidiDevice::write_event_to_device (): [" << name () << "] Pm_WriteSysEx () failed (" << err << ")!" << std::endl;
+ };
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_write_midi (): SYSEX used, ev->tm:" << waves_midi_event->timestamp () - LATENCY << std::endl;
+ }
+ else
+ {
+ err = Pm_WriteShort (_output_pm_stream, waves_midi_event->timestamp () - LATENCY, * (PmMessage*)waves_midi_event->data ());
+ if (0 > err) {
+ std::cout << "WavesMidiDevice::write_event_to_device (): [" << name () << "] Pm_WriteShort () failed (" << err << ")!" << std::endl;
+ }
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::_write_midi (): SHORTMSG used, ev->tm:" << waves_midi_event->timestamp () - LATENCY << std::endl;
+ }
+ delete waves_midi_event;
+ }
+ return;
+}
+
+int
+WavesMidiDevice::enqueue_output_waves_midi_event (const WavesMidiEvent* waves_midi_event)
+{
+ // COMMENTED DBG LOGS */ std::cout << "WavesMidiDevice::enqueue_output_waves_midi_event (): " << _pm_device_id << "-[" << name () << "]" << std::endl;
+
+ if (waves_midi_event == NULL) {
+ std::cerr << "WavesMidiDevice::put_event_to_callback (): 'waves_midi_event' is NULL!" << std::endl;
+ return -1;
+ }
+
+ PmError err = Pm_Enqueue (_output_queue, &waves_midi_event);
+
+ if (0 > err) {
+ std::cerr << "WavesMidiDevice::put_event_to_callback (): Pm_Enqueue () failed (" << err << ")!" << std::endl;
+ return -1;
+ };
+
+ return 0;
+}
+
+WavesMidiEvent*
+WavesMidiDevice::dequeue_input_waves_midi_event ()
+{
+ WavesMidiEvent* waves_midi_event;
+ if (Pm_Dequeue (_input_queue, &waves_midi_event) == 1) {
+ return waves_midi_event;
+ }
+ return NULL;
+}
+
diff --git a/libs/backends/wavesaudio/waves_midi_device.h b/libs/backends/wavesaudio/waves_midi_device.h new file mode 100644 index 0000000000..a8b734736d --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_device.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libardour_waves_midi_device_h__ +#define __libardour_waves_midi_device_h__ + +#include <portmidi/portmidi.h> +#include <portmidi/pmutil.h> +#include <portmidi/porttime.h> + +#include "ardour/types.h" + +namespace ARDOUR { + +class WavesMidiEvent; + +class WavesMidiDevice { +public: + WavesMidiDevice (const std::string& name); + ~WavesMidiDevice (); + + inline const std::string& name () const { return _name; } + + int open (PmTimeProcPtr time_proc, void* time_info); + void close (); + void do_io (); + void read_midi (); + void write_midi (); + + int enqueue_output_waves_midi_event (const WavesMidiEvent* waves_midi_event); + WavesMidiEvent* dequeue_input_waves_midi_event (); + + inline bool is_input () const { return _pm_input_id != pmNoDevice; }; + inline bool is_output () const { return _pm_output_id != pmNoDevice; }; + void validate (); + +private: + + + PmDeviceID _pm_input_id; + PmDeviceID _pm_output_id; + const std::string _name; + + /* shared queues */ + PmQueue* _input_queue; + PmQueue* _output_queue; + + PmStream* _input_pm_stream; + PmStream* _output_pm_stream; + WavesMidiEvent *_incomplete_waves_midi_event; +}; + +} // namespace + +#endif /* __libardour_waves_midi_device_h__ */ + diff --git a/libs/backends/wavesaudio/waves_midi_device_manager.cc b/libs/backends/wavesaudio/waves_midi_device_manager.cc new file mode 100644 index 0000000000..84667a2dbd --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_device_manager.cc @@ -0,0 +1,242 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "waves_midi_device_manager.h" +#include "waves_audiobackend.h" + +#ifdef __WINDOWS__ + +#include "windows.h" +#include "mmsystem.h" + +#elif __MACOS__ + +#include <CoreMIDI/MIDIServices.h> + +#define midiInGetNumDevs MIDIGetNumberOfSources +#define midiOutGetNumDevs MIDIGetNumberOfDestinations + +#endif + +using namespace ARDOUR; + +WavesMidiDeviceManager::WavesMidiDeviceManager (WavesAudioBackend& audiobackend) + : _active (false) + , _streaming (false) + , _input_device_count (0) + , _output_device_count (0) + , _audiobackend (audiobackend) +{ +} + + +WavesMidiDeviceManager::~WavesMidiDeviceManager () +{ +} + + +int +WavesMidiDeviceManager::start () +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiDeviceManager::stream ():" << std::endl; + if ( _active == true ) { + return -1; + } + + if (Pm_Initialize () != pmNoError) { + return -1; + } + + _create_devices (); + + _input_device_count = midiInGetNumDevs (); + _output_device_count = midiOutGetNumDevs (); + + _active = true; + + return 0; +} + + +int +WavesMidiDeviceManager::stream (bool yn) +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiDeviceManager::stream ():" << std::endl; + if (!_active) { + std::cerr << "WavesMidiDeviceManager::stream (): the midi device manager is not started up !" << std::endl; + return -1; + } + + if (_streaming == yn) { + return 0; + } + + if (yn) { + if ( Pt_Start (1, __portmidi_callback, this) != ptNoError) { + std::cerr << "WavesMidiDeviceManager::stream (): Pt_Start () failed!" << std::endl; + return -1; + } + } + else { + if (Pt_Stop () != ptNoError) { + std::cerr << "WavesMidiDeviceManager::stream (): Pt_Stop () failed!" << std::endl; + return -1; + } + } + + _streaming = yn; + return 0; +} + + +int +WavesMidiDeviceManager::stop () +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiDeviceManager::stop ():" << std::endl; + + if ( _active == false ) + return 0; + + stream (false); + + _close_devices (); + _active = false; + + if (Pm_Terminate () != pmNoError) { + std::cerr << "WavesMidiDeviceManager::stop (): Pt_Terminate () failed!" << std::endl; + return -1; + } + + return 0; +} + +void +WavesMidiDeviceManager::__portmidi_callback (PtTimestamp timestamp, void * userData) +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiDeviceManager::__portmidi_callback ():" << std::endl; + WavesMidiDeviceManager *dm = (WavesMidiDeviceManager *)userData; + + if (dm == NULL) { + return; + } + + dm->_portmidi_callback (timestamp); +} + +void +WavesMidiDeviceManager::_portmidi_callback (PtTimestamp timestamp) +{ + if ((!_active) || (!_streaming)) { + return; + } + + if ((_input_device_count != midiInGetNumDevs ()) || (_output_device_count != midiOutGetNumDevs ())) { + _audiobackend._changed_midi_devices (); + return; + } +} + +void WavesMidiDeviceManager::do_read () +{ + for (std::vector<WavesMidiDevice *>::const_iterator it = _devices.begin (); it != _devices.end (); ++it) { + (*it)->read_midi (); + } +} + + +void WavesMidiDeviceManager::do_write () +{ + for (std::vector<WavesMidiDevice *>::const_iterator it = _devices.begin (); it != _devices.end (); ++it) { + (*it)->write_midi (); + } +} + + +PmTimestamp +WavesMidiDeviceManager::__get_time_ms (void *time_info) +{ + return ((WavesAudioBackend*)time_info)->sample_time (); +} + + +WavesMidiDevice* WavesMidiDeviceManager::_get_device (const std::string& name) +{ + for (size_t i = 0; i < _devices.size (); i++) { + if (name == _devices[i]->name ()) { + return _devices[i]; + } + } + return NULL; +} + + +int +WavesMidiDeviceManager::_create_devices () +{ + int count = Pm_CountDevices (); + + for (int i = 0; i < count; i++) { + + const PmDeviceInfo* pm_device_info = Pm_GetDeviceInfo (i); + + if (pm_device_info == NULL) { + std::cerr << "WavesMidiDeviceManager::_create_devices (): Pm_GetDeviceInfo (" << i << ") failed!" << std::endl; + continue; + } + + WavesMidiDevice *device = _get_device (pm_device_info->name); + if (device) { + device->validate (); + } + else + { + device = new WavesMidiDevice (pm_device_info->name); + _devices.push_back (device); + } + + if (device->open (__get_time_ms, (void*)&_audiobackend)) { + std::cerr << "WavesMidiDeviceManager::_create_devices (): [" << device->name () << "]->open () failed!" << std::endl; + } + } + + return 0; +} + + +int +WavesMidiDeviceManager::_delete_devices () +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiDeviceManager::_delete_devices ():" << std::endl; + while (!_devices.empty ()) { + WavesMidiDevice * device = _devices.back (); + _devices.pop_back (); + delete device; + } + return 0; +} + + +void +WavesMidiDeviceManager::_close_devices () +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiDeviceManager::_delete_devices ():" << std::endl; + for (size_t i = 0; i < _devices.size (); i++) { + _devices[i]->close (); + } +} diff --git a/libs/backends/wavesaudio/waves_midi_device_manager.h b/libs/backends/wavesaudio/waves_midi_device_manager.h new file mode 100644 index 0000000000..75a2757f90 --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_device_manager.h @@ -0,0 +1,77 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifndef __libardour_waves_midi_device_manager_h__ +#define __libardour_waves_midi_device_manager_h__ + +#include "waves_midi_device.h" + +namespace ARDOUR { + +class WavesAudioBackend; + +class WavesMidiDeviceManager { +public: + WavesMidiDeviceManager (WavesAudioBackend& audiobackend); + ~WavesMidiDeviceManager (); + + inline const std::vector<WavesMidiDevice *>& devices () const + { + return _devices; + } + + int start (); + int stop (); + int stream (bool yn); + int is_streaming () { return _streaming; } + void do_read (); + void do_write (); + +private: + + int _create_devices (); + void _close_devices (); + + int _delete_devices (); + static void __portmidi_callback (PtTimestamp timestamp, void * userData); + void _portmidi_callback (PtTimestamp timestamp); + /** __get_time_ms is given to Pm_Open functions (see WavesMidiDevice.cc) + * to provide the time in milliseconds using the time of audio + * transport. + * time_info is a pointer on the backend instance, which agregates the + * audio and miditransports. It's not checked for correctness to consume + * no time. + */ + static PmTimestamp __get_time_ms (void *time_info); + + WavesMidiDevice* _get_device (const std::string& name); + + std::vector<WavesMidiDevice*> _devices; // Vector for midi devices + bool _active; + bool _streaming; + + size_t _input_device_count; + size_t _output_device_count; + WavesAudioBackend& _audiobackend; +}; + +} // namespace + +#endif /* __libardour_waves_midi_device_manager_h__ */ + diff --git a/libs/backends/wavesaudio/waves_midi_event.cc b/libs/backends/wavesaudio/waves_midi_event.cc new file mode 100644 index 0000000000..532555c958 --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_event.cc @@ -0,0 +1,161 @@ +/* + Copyright (C) 2014 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "memory.h" +#include "waves_midi_event.h" + +using namespace ARDOUR; + +WavesMidiEvent::WavesMidiEvent (PmTimestamp timestamp) + : _size (0) + , _timestamp (timestamp) + , _data (NULL) + , _state (INCOMPLETE) +{ + +} + + +WavesMidiEvent::WavesMidiEvent (PmTimestamp timestamp, const uint8_t* data, size_t datalen) + : _size (datalen) + , _timestamp (timestamp) + , _data (data && datalen ? new uint8_t[ (datalen < sizeof (PmMessage)) ? sizeof (PmMessage) : datalen] : NULL) + , _state (data && datalen ? COMPLETE : BROKEN) +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiEvent::WavesMidiEvent (const WavesMidiEvent& source) : Size=" << _size << "---" << datalen << std::endl; + if (_state == COMPLETE) { + // COMMENTED DBG LOGS */ std::cout << "\t\t\t Allocated Size=" << ((datalen < sizeof (PmMessage)) ? sizeof (PmMessage) : datalen) << std::endl; + memcpy (_data, data, datalen); + } +} + + +WavesMidiEvent::WavesMidiEvent (const WavesMidiEvent& source) + : _size (source.size ()) + , _timestamp (source.timestamp ()) + , _data ((source.size () && source.const_data ()) ? new uint8_t[ (source.size () < sizeof (PmMessage)) ? sizeof (PmMessage) : source.size ()] : NULL) + , _state (source.state () ) +{ + // COMMENTED DBG LOGS */ std::cout << "WavesMidiEvent::WavesMidiEvent (const WavesMidiEvent& source) : Size=" << _size << "---" << source.size () << std::endl; + // COMMENTED DBG LOGS */ std::cout << "\t\t\t Allocated Size=" << ((source.size () < sizeof (PmMessage)) ? sizeof (PmMessage) : source.size ()) << std::endl; + if (_data && source.const_data ()) { + memcpy (_data, source.const_data (), source.size ()); + } +} + + +WavesMidiEvent::~WavesMidiEvent () +{ + delete _data; +} + + +WavesMidiEvent *WavesMidiEvent::append_data (const PmEvent &midi_event) +{ + switch ( _state ) { + case INCOMPLETE: + break; + default: + // COMMENTED DBG LOGS */ std::cout << "WavesMidiEvent::append_data (): NO case INCOMPLETE" << std::endl; + _state = BROKEN; + return NULL; + } + + size_t message_size = _midi_message_size (midi_event.message); + uint8_t message_status = Pm_MessageStatus (midi_event.message); + + if (_data == NULL) { // This is a first event to add + bool sysex = (message_status == SYSEX); + _data = new unsigned char [sysex ? PM_DEFAULT_SYSEX_BUFFER_SIZE : sizeof (PmMessage)]; + if (!sysex) + { + // COMMENTED DBG LOGS */ std::cout << "WavesMidiEvent::append_data (): SHORT MSG" << std::endl; + * (PmMessage*)_data = 0; + switch (message_size) { + case 1: + case 3: + _size = message_size; + // COMMENTED DBG LOGS */ std::cout << "WavesMidiEvent::append_data (): size = " << _size << std::endl; + break; + default: + // COMMENTED DBG LOGS */ std::cout << "WavesMidiEvent::append_data (): WRONG MESSAGE SIZE (" << message_size << ") in the message: "; + // COMMENTED DBG LOGS */ std::cout << std::hex << (int) ((unsigned char*)&midi_event)[0] << " " << (int) ((unsigned char*)&midi_event)[1] << " " << (int) ((unsigned char*)&midi_event)[2] << " " << (int) ((unsigned char*)&midi_event)[3] << std::dec << std::endl; + _state = BROKEN; + return NULL; + } + // COMMENTED DBG LOGS */ std::cout << "\t size = " << _size << std::endl; + memcpy (_data, &midi_event.message, _size); + // COMMENTED DBG LOGS */ std::cout << "\t\t size = " << _size << std::endl; + _state = COMPLETE; + // COMMENTED DBG LOGS */ std::cout << "\t\t\t size = " << _size << std::endl; + return NULL; + } + } + + // Now let's parse to sysex msg + if (message_status >= REAL_TIME_FIRST) { // Nested Real Time MIDI event + WavesMidiEvent *waves_midi_message = new WavesMidiEvent (midi_event.timestamp); + waves_midi_message->append_data (midi_event); + return waves_midi_message; + } + + if (message_status >= STATUS_FIRST && (message_status != EOX) && _size) { // Certainly it's a broken SYSEX case + WavesMidiEvent *waves_midi_message = new WavesMidiEvent (midi_event.timestamp); + waves_midi_message->append_data (midi_event); + return waves_midi_message; + } + + const uint8_t* source_data ((uint8_t*)&midi_event.message); + + for (size_t i = 0; i < sizeof (midi_event.message); ++i) { + _data[_size] = source_data[i]; + _size++; + + if (source_data[i] == EOX) { // Ended SYSEX message + _state = COMPLETE; + return NULL; + } + } + return NULL; +} + + +size_t WavesMidiEvent::_midi_message_size (PmMessage midi_message) +{ + static int high_lengths[] = { + 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ + 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ + }; + + static int low_lengths[] = { + 1, 2, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf7 */ + 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf8 through 0xff */ + }; + + int midi_message_status = Pm_MessageStatus (midi_message); + + if (midi_message_status < STATUS_FIRST) { + return sizeof (midi_message); + } + + int high = midi_message_status >> 4; + int low = midi_message_status & 0xF; + + return (high != 0xF) ? high_lengths[high] : low_lengths[low]; +} diff --git a/libs/backends/wavesaudio/waves_midi_event.h b/libs/backends/wavesaudio/waves_midi_event.h new file mode 100644 index 0000000000..9015a2ce81 --- /dev/null +++ b/libs/backends/wavesaudio/waves_midi_event.h @@ -0,0 +1,75 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __libardour_waves_midi_event_h__
+#define __libardour_waves_midi_event_h__
+
+#include <stdlib.h>
+#include <portmidi/portmidi.h>
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class WavesMidiEvent
+{
+public:
+ enum State {
+ INCOMPLETE,
+ BROKEN,
+ COMPLETE
+ };
+
+ WavesMidiEvent (PmTimestamp timestamp);
+ WavesMidiEvent (PmTimestamp timestamp, const uint8_t* data, size_t datalen);
+ WavesMidiEvent (const WavesMidiEvent& source);
+ ~WavesMidiEvent ();
+
+ WavesMidiEvent *append_data (const PmEvent &midi_event);
+
+ inline State state () const { return _state; };
+ inline size_t size () const { return _size; };
+ inline PmTimestamp timestamp () const { return _timestamp; };
+ inline void set_timestamp (PmTimestamp time_stamp) { _timestamp = time_stamp; };
+ inline const unsigned char* const_data () const { return _data; };
+ inline unsigned char* data () { return _data; };
+ inline bool operator< (const WavesMidiEvent &other) const { return timestamp () < other.timestamp (); };
+ inline bool sysex () const { return _data && (*_data == SYSEX); };
+
+private:
+
+ enum
+ {
+ SYSEX = 0xF0,
+ EOX = 0xF7,
+ REAL_TIME_FIRST = 0xF8,
+ STATUS_FIRST = 0x80
+ };
+
+ size_t _size;
+ PmTimestamp _timestamp;
+ uint8_t *_data;
+ State _state;
+
+ static size_t _midi_message_size (PmMessage midi_message);
+};
+
+
+} // namespace
+
+#endif /* __libardour_waves_midi_event_h__ */
diff --git a/libs/backends/wavesaudio/waves_midiport.cc b/libs/backends/wavesaudio/waves_midiport.cc new file mode 100644 index 0000000000..8a77776c5e --- /dev/null +++ b/libs/backends/wavesaudio/waves_midiport.cc @@ -0,0 +1,61 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "waves_midiport.h"
+#include "waves_midi_event.h"
+
+using namespace ARDOUR;
+
+WavesMidiPort::WavesMidiPort (const std::string& port_name, PortFlags flags)
+ : WavesDataPort (port_name, flags)
+ , _midi_device (NULL)
+ , _waves_midi_buffer (port_name)
+{
+}
+
+void*
+WavesMidiPort::get_buffer (pframes_t nframes)
+{
+ if (is_input ()) {
+ std::vector<WavesDataPort*>::const_iterator cit = get_connections ().begin ();
+ if (cit != get_connections ().end ()) {
+ _waves_midi_buffer.clear ();
+ WavesMidiBuffer& target = _waves_midi_buffer;
+
+ do {
+ /* In fact, the static casting to (const WavesMidiPort*) is not that safe.
+ * However, mixing the buffers is assumed in the time critical conditions.
+ * Base class WavesDataPort is supposed to provide enough consistentcy
+ * of the connections.
+ */
+ target += ((const WavesMidiPort*)*cit)->const_buffer ();
+ }while((++cit) != get_connections ().end ());
+
+ std::sort (target.begin (), target.end ());
+ }
+ }
+
+ return &_waves_midi_buffer;
+}
+
+void
+WavesMidiPort::_wipe_buffer()
+{
+ _waves_midi_buffer.clear ();
+}
diff --git a/libs/backends/wavesaudio/waves_midiport.h b/libs/backends/wavesaudio/waves_midiport.h new file mode 100644 index 0000000000..6df1c2b04a --- /dev/null +++ b/libs/backends/wavesaudio/waves_midiport.h @@ -0,0 +1,64 @@ +/*
+ Copyright (C) 2014 Waves Audio Ltd.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef __libardour_waves_midiport_h__
+#define __libardour_waves_midiport_h__
+
+#include "waves_dataport.h"
+#include "waves_midi_buffer.h"
+
+namespace ARDOUR {
+
+class WavesMidiEvent;
+class WavesMidiDevice;
+class WavesMidiEvent;
+
+class WavesMidiPort : public WavesDataPort {
+public:
+ enum BufferSize {
+ // This value has nothing to do with reality as buffer of MIDI Port is not a flat array.
+ // It's an iterated list.
+ MAX_BUFFER_SIZE_BYTES = 8192
+ };
+
+ WavesMidiPort (const std::string& port_name, PortFlags flags);
+ virtual ~WavesMidiPort (){};
+
+ virtual DataType type () const { return DataType::MIDI; };
+
+ virtual void* get_buffer (pframes_t nframes);
+
+ inline WavesMidiBuffer& buffer () { return _waves_midi_buffer; }
+ inline const WavesMidiBuffer& const_buffer () const { return _waves_midi_buffer; }
+
+ inline void set_midi_device (WavesMidiDevice* midi_device) { _midi_device = midi_device; };
+ inline WavesMidiDevice* midi_device () const { return _midi_device; };
+
+protected:
+ virtual void _wipe_buffer();
+
+private:
+ WavesMidiDevice * _midi_device;
+ WavesMidiBuffer _waves_midi_buffer;
+};
+
+} // namespace
+
+#endif /* __libardour_waves_midiport_h__ */
+
diff --git a/libs/backends/wavesaudio/wavesapi/BasicTypes/WCFourCC.h b/libs/backends/wavesaudio/wavesapi/BasicTypes/WCFourCC.h new file mode 100644 index 0000000000..165acc3295 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/BasicTypes/WCFourCC.h @@ -0,0 +1,212 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WCFourCC_h__ + #define __WCFourCC_h__ + +/* Copy to include +#include "BasicTypes/WCFourCC.h" +*/ + +//#include "BasicTypes/WTByteOrder.h" +#include "WCFixedString.h" + + +// These are preprocessor macros rather than inline functions because most compilers can't +// resolve functions at compile-time. +#if _BYTEORDER_BIG_ENDIAN==1 + #define FOURCC_BIG(a, b, c, d) ((uint32_t(a)<<24)|(uint32_t(b)<<16)|(uint32_t(c)<< 8)|(uint32_t(d)<< 0)) + #define FOURCC_LITTLE(a, b, c, d) ((uint32_t(a)<< 0)|(uint32_t(b)<< 8)|(uint32_t(c)<<16)|(uint32_t(d)<<24)) + #define FOURCC_COMPILER(a, b, c, d) FOURCC_BIG(a,b,c,d) +#elif _BYTEORDER_BIG_ENDIAN==0 + #define FOURCC_BIG(a, b, c, d) ((uint32_t(a)<< 0)|(uint32_t(b)<< 8)|(uint32_t(c)<<16)|(uint32_t(d)<<24)) + #define FOURCC_LITTLE(a, b, c, d) ((uint32_t(a)<<24)|(uint32_t(b)<<16)|(uint32_t(c)<< 8)|(uint32_t(d)<< 0)) + #define FOURCC_COMPILER(a, b, c, d) FOURCC_LITTLE(a,b,c,d) +#else + #error _BYTEORDER_BIG_ENDIAN not defined proparly +#endif // _BYTEORDER_HPP_BIG_ENDIAN + +typedef uint32_t WTFourCharCode; + +#ifndef kEnableWCFourCCDebug + #define kEnableWCFourCCDebug 0 // set to 1 to enable debug members +#endif + + +class WCFourCC +{ +private: + template<class _iter> + static WTFourCharCode stored_from_iter(_iter& i) + { + return s_stored_byte_order==wvNS::wvBO::byte_order_big_endian ? FOURCC_BIG(i[0], i[1], i[2], i[3]) : FOURCC_LITTLE(i[0], i[1], i[2], i[3]); + } + +public: + + // static const WCFourCC kDefaultFourCC_prv; + + static WCFourCC kDefaultFourCC_prv() { return WCFourCC(); } + + // change this line will change the byte order in which WCFourCC keeps the four char code + static const wvNS::wvBO::byte_order_type s_stored_byte_order = wvNS::wvBO::compiler_byte_order; + + WCFourCC(const char a, const char b, const char c, const char d) : + m_stored_value(s_stored_byte_order==wvNS::wvBO::compiler_byte_order ? FOURCC_BIG(a,b,c,d) : FOURCC_LITTLE(a,b,c,d)) + { +#if kEnableWCFourCCDebug == 1 + m_c_str_stored_value[sizeof(WTFourCharCode)] = '\0'; +#endif + } + + WCFourCC() : + m_stored_value(FOURCC_BIG('?','?','?','?')) // since the four chars are the same, there is no need to choose between big & little + { +#if kEnableWCFourCCDebug == 1 + m_c_str_stored_value[sizeof(WTFourCharCode)] = '\0'; +#endif + } + + WCFourCC(const WTFourCharCode in_fourCharCode, const wvNS::wvBO::byte_order_type in_byteOrder = wvNS::wvBO::compiler_byte_order) : + m_stored_value(in_byteOrder==s_stored_byte_order ? in_fourCharCode : wvNS::wvBO::swap32(in_fourCharCode)) + { +#if kEnableWCFourCCDebug == 1 + m_c_str_stored_value[sizeof(WTFourCharCode)] = '\0'; +#endif + } + + explicit WCFourCC(const char* in_source_string) : + m_stored_value(stored_from_iter(in_source_string)) + { +#if kEnableWCFourCCDebug == 1 + m_c_str_stored_value[sizeof(WTFourCharCode)] = '\0'; +#endif + } + + explicit WCFourCC(const WCFixedStringBase& in_source_string) : + m_stored_value(stored_from_iter(in_source_string)) + { +#if kEnableWCFourCCDebug == 1 + m_c_str_stored_value[sizeof(WTFourCharCode)] = '\0'; +#endif + } + + WTFourCharCode GetAsSomeEndian(const wvNS::wvBO::byte_order_type in_byteOrder) const + { + return s_stored_byte_order==in_byteOrder ? m_stored_value : wvNS::wvBO::swap32(m_stored_value); + } + + WTFourCharCode GetAsBigEndian() const + { + return s_stored_byte_order==wvNS::wvBO::byte_order_big_endian ? m_stored_value : wvNS::wvBO::swap32(m_stored_value); + } + + WTFourCharCode GetAsLittleEndian() const + { + return s_stored_byte_order==wvNS::wvBO::byte_order_little_endian ? m_stored_value : wvNS::wvBO::swap32(m_stored_value); + } + + WTFourCharCode GetAsCompilerEndian() const + { + return s_stored_byte_order==wvNS::wvBO::compiler_byte_order ? m_stored_value : wvNS::wvBO::swap32(m_stored_value); + } + + WTFourCharCode GetAsStored() const + { + return m_stored_value; + } + + char operator[](const unsigned int in_character_index) const + { + return char(m_stored_value >> (8 * (s_stored_byte_order==wvNS::wvBO::compiler_byte_order ? 3-in_character_index : in_character_index))); + } + + char& operator[](const unsigned int in_character_index) + { + return reinterpret_cast<char*>(&m_stored_value)[s_stored_byte_order==wvNS::wvBO::byte_order_little_endian ? 3-in_character_index : in_character_index]; + } + + static size_t size() + { + return sizeof(WTFourCharCode); + } + + static size_t max_size() + { + return size(); + } + + static size_t capacity() + { + return size(); + } + + WCFixedString4 GetString() const + { + WCFixedString4 retVal; + retVal << operator[](0) << operator[](1) << operator[](2) << operator[](3); + + return retVal; + } + +#if kEnableWCFourCCDebug == 1 + const char* c_str() const + { + return m_c_str_stored_value; + } +#endif + +protected: + +private: +#if kEnableWCFourCCDebug == 1 + union + { +#endif + WTFourCharCode m_stored_value; +#if kEnableWCFourCCDebug == 1 + char m_c_str_stored_value[sizeof(WTFourCharCode)+1]; + }; +#endif + + WCFourCC& operator=(const WTFourCharCode); // we want initialization from literal to be dome through the constructor +}; + +inline bool operator<(const WCFourCC in_left, const WCFourCC in_right) +{ + return in_left.GetAsSomeEndian(WCFourCC::s_stored_byte_order) < in_right.GetAsSomeEndian(WCFourCC::s_stored_byte_order); +} +inline bool operator==(const WCFourCC in_left, const WCFourCC in_right) +{ + return in_left.GetAsSomeEndian(WCFourCC::s_stored_byte_order) == in_right.GetAsSomeEndian(WCFourCC::s_stored_byte_order); +} + +inline bool operator!=(const WCFourCC in_left, const WCFourCC in_right) +{ + return ! operator==(in_left, in_right); +} + + +#define kDefaultFourCC WCFourCC::kDefaultFourCC_prv() + +static const WCFourCC kZeroFourCC(0, wvNS::wvBO::compiler_byte_order); + +#endif //#if !defined(__WCFourCC_h__) + + + diff --git a/libs/backends/wavesaudio/wavesapi/BasicTypes/WTByteOrder.h b/libs/backends/wavesaudio/wavesapi/BasicTypes/WTByteOrder.h new file mode 100644 index 0000000000..7d7fd8c1a9 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/BasicTypes/WTByteOrder.h @@ -0,0 +1,222 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#if !defined(__WTByteOrder_h__) +#define __WTByteOrder_h__ + +/* Copy to include +#include "BasicTypes/WTByteOrder.h" +*/ + +#include "WavesPublicAPI/wstdint.h" +#include "BasicTypes/WUDefines.h" + +// Stuff concerning little/big endian and the conversion between them. +// most of the code here was copied from NetShell with some modifications +// Written by Udi on Nov-2005 +// Adjusted to Cross platform by Shai Mar-2006 + +// Macros to determine endian. __BIG_ENDIAN__ & __LITTLE_ENDIAN__ should come from the compiler. +// We try to set the macro _BYTEORDER_BIG_ENDIAN to 1 if big-endian or to 0 if little-endian. + +// if the compiler properly has set either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ +#if defined(__BIG_ENDIAN__) || defined(__LITTLE_ENDIAN__) +#if defined(__BIG_ENDIAN__) && defined(__LITTLE_ENDIAN__) //if both defined, check them as booleans +#if __BIG_ENDIAN__ && !__LITTLE_ENDIAN__ +#define _BYTEORDER_BIG_ENDIAN 1 +#elif !__BIG_ENDIAN__ && __LITTLE_ENDIAN__ +#define _BYTEORDER_BIG_ENDIAN 0 +#else +#error I am confused. Is this big-endian or little-endian? +#endif // stupid compiler defines both __LITTLE_ENDIAN__ and __BIG_ENDIAN__ +#elif defined(__BIG_ENDIAN__) +#define _BYTEORDER_BIG_ENDIAN 1 +#else +#define _BYTEORDER_BIG_ENDIAN 0 +#endif // big/little switch +#else // if the compiler proparly has NOT set either __BIG_ENDIAN__ or __LITTLE_ENDIAN__ +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx for all preprocessor defs. _M_X64: 64 bit. _M_IA64: Itanium 64bit +#if defined(__i386__) || defined(__i386) || defined(_M_IX86) || defined(__INTEL__) || defined(__x86_64__) || defined(_M_X64) || defined(_M_IA64) +#define _BYTEORDER_BIG_ENDIAN 0 +#elif defined(_M_PPC) || defined(__POWERPC__ ) || defined(__ppc__) +#define _BYTEORDER_BIG_ENDIAN 1 +#else +#error Cannot detect compiler byte-order. Please add a test for your compiler appropriate symbol to this header file. +#endif // symbol search +#endif // standard preprocessor symbol found + +// code to determine which assembly code we can use +#if defined(_MSC_VER) && defined(_M_IX86) +#define _BYTEORDER_ASM_MSVC_I386 1 // Windows +#elif defined(__GNUC__) && defined(__i386__) +#define _BYTEORDER_ASM_GNUC_I386 1 // Linux, or MacOS with MacIntel on Xcode +#define _BYTEORDER_ASM_NONE 1 // Currently we have no assebley for GNU i386, so use the C version +#elif defined(__GNUC__) && defined(__POWERPC__) +#define _BYTEORDER_ASM_GNUC_PPC 1 // MacOS with PPC on Xcode +#define _BYTEORDER_ASM_NONE 1 // Currently we have no assebley for GNU PPC, so use the C version +#else +#define _BYTEORDER_ASM_NONE 1 // don't know the compiler and processor, use C implementation +#endif + +namespace wvNS { + +namespace wvBO // namespace Waves::ByteOrder +{ + typedef int byte_order_type; // we use int rather than enum because some compilers cannot resolve enum constants at compile-time. There are only two options anyway :-) + static const byte_order_type byte_order_little_endian = 0; + static const byte_order_type byte_order_big_endian = 1; + + + // We try to use this static const rather than preprocessor symbols in our code wherever possible. +#if _BYTEORDER_BIG_ENDIAN == 1 + static const byte_order_type compiler_byte_order = byte_order_big_endian; +#else + static const byte_order_type compiler_byte_order = byte_order_little_endian; +#endif + + + //--------------------------------------------------------------------------------- + // swap functions - best if implemented in inline assembly code + // The following are very slow swappers when compiled, do not use in loops +#if _BYTEORDER_ASM_MSVC_I386 + + // assembly implementation for Intel386 on Visual Studio + inline uint16_t swap16(uint16_t x) + { + __asm MOV AX,x; + __asm XCHG AL,AH; + __asm MOV x,AX; + return x; + } + + inline uint32_t swap32(uint32_t x) + { + __asm MOV EAX,x; + __asm BSWAP EAX; + __asm MOV x,EAX; + return x; + } + inline uint64_t swap64(uint64_t x) // TODO: To be replaced + { + return + ((x>>7*8)&0xFF)<<0*8 | ((x>>6*8)&0xFF)<<1*8 | ((x>>5*8)&0xFF)<<2*8 | ((x>>4*8)&0xFF)<<3*8 | + ((x>>3*8)&0xFF)<<4*8 | ((x>>2*8)&0xFF)<<5*8 | ((x>>1*8)&0xFF)<<6*8 | ((x>>0*8)&0xFF)<<7*8 ; + } + + /* the ASM code for swap64 does not compile + inline uint64_t swap64(uint64_t x) + { + __asm MOV EBX, OFFSET x; + __asm MOV EAX, [EBX]; + __asm MOV EDX, [EBX+4]; + __asm BSWAP EAX; + __asm BSWAP EDX; + __asm MOV [EBX],EDX; + __asm MOV [EBX+4],EAX; + return x; + } + */ +#endif // _BYTEORDER_ASM_MSVC_I386 + +#if _BYTEORDER_ASM_GNUC_I386 + // assembly implementation for Intel386 on GCC (Linux) + // TODO +#endif // _BYTEORDER_ASM_GNUC_I386 + +#if _BYTEORDER_ASM_GNUC_PPC + // assembly implementation for PowerPC on GCC (XCode) + // TODO +#endif // _BYTEORDER_ASM_GNUC_PPC + +#if _BYTEORDER_ASM_NONE + inline uint16_t swap16(uint16_t x) { return (x>>8) | ((x&0xFF)<<8); } + inline uint32_t swap32(uint32_t x) { return (x&0xFF)<<24 | (x&0xFF00)<<8 | (x&0xFF0000)>>8 | (x&0xFF000000)>>24; } + inline uint64_t swap64(uint64_t x) + { + return + ((x>>7*8)&0xFF)<<0*8 | ((x>>6*8)&0xFF)<<1*8 | ((x>>5*8)&0xFF)<<2*8 | ((x>>4*8)&0xFF)<<3*8 | + ((x>>3*8)&0xFF)<<4*8 | ((x>>2*8)&0xFF)<<5*8 | ((x>>1*8)&0xFF)<<6*8 | ((x>>0*8)&0xFF)<<7*8 ; + } +#endif // _BYTEORDER_ASM_NONE + + + + + //--------------------------------------------------------------------------------- + + // order conversion functions + // may want to overload for float and double as well. + // overload for signed ints is ambiguous and should be done only if no other choice exists. + // - - - - - - - - - - - - - - - - - - - - + inline uint16_t compiler_to_big_16(uint16_t x) + { + return compiler_byte_order==byte_order_big_endian ? x : swap16(x); + } + inline uint16_t big_to_compiler_16(uint16_t x) + { + return compiler_byte_order==byte_order_big_endian ? x : swap16(x); + } + inline uint16_t compiler_to_little_16(uint16_t x) + { + return compiler_byte_order==byte_order_little_endian ? x : swap16(x); + } + inline uint16_t little_to_compiler_16(uint16_t x) + { + return compiler_byte_order==byte_order_little_endian ? x : swap16(x); + } + // - - - - - - - - - - - - - - - - - - - - + inline uint32_t compiler_to_big_32(uint32_t x) + { + return compiler_byte_order==byte_order_big_endian ? x : swap32(x); + } + inline uint32_t big_to_compiler_32(uint32_t x) + { + return compiler_byte_order==byte_order_big_endian ? x : swap32(x); + } + inline uint32_t compiler_to_little_32(uint32_t x) + { + return compiler_byte_order==byte_order_little_endian ? x : swap32(x); + } + inline uint32_t little_to_compiler_32(uint32_t x) + { + return compiler_byte_order==byte_order_little_endian ? x : swap32(x); + } + // - - - - - - - - - - - - - - - - - - - - + inline uint64_t compiler_to_big_64(uint64_t x) + { + return compiler_byte_order==byte_order_big_endian ? x : swap64(x); + } + inline uint64_t big_to_compiler_64(uint64_t x) + { + return compiler_byte_order==byte_order_big_endian ? x : swap64(x); + } + inline uint64_t compiler_to_little_64(uint64_t x) + { + return compiler_byte_order==byte_order_little_endian ? x : swap64(x); + } + inline uint64_t little_to_compiler_64(uint64_t x) + { + return compiler_byte_order==byte_order_little_endian ? x : swap64(x); + } + +} // namespace wvBO + +} // namespace wvNS { + +#endif // #if !defined(__WTByteOrder_h__) + diff --git a/libs/backends/wavesaudio/wavesapi/BasicTypes/WUComPtr.h b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUComPtr.h new file mode 100644 index 0000000000..6193da38bb --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUComPtr.h @@ -0,0 +1,117 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WUComPtr_h__ +#define __WUComPtr_h__ + +/* Copy to include +#include "BasicTypes/WUComPtr.h" +*/ + +#include "WavesPublicAPI/wstdint.h" + +typedef int32_t wvComPtr[2]; + +// ConvertDPtr has the exact format of a vfp callback function, but it is a local function, native only. +// It converts a pointer in either 32 bits or 64 bits to a place-holder of 64 bits in coefs/states/external memory. +// pData is expected to point to a pre-allocate space enough for storing a pointer (posibly two single-precision coefs). +// Since pointers are not transferable between hardwares, at preset time no need for a shell callback. +// We keep this as a cALGORITHM for compatibility with the rest of the convert functions +//================================================================================ +inline uint32_t vfpConvertDPtr(const void* InPointer, void* pData) +//================================================================================ +{ + uint64_t *pL = (uint64_t *)pData; + *pL = (uint64_t)InPointer; + return (uint32_t)sizeof(uint64_t); +} + + +/* +{ + // data in that struct must be the same type of the Coefs/States type! + int32_t LSW; // Least significant word + int32_t MSW; // Most significant word +}; + +inline wvComPtr PackToComPtr(const intptr_t in_PtrToPack) +// take ptr that hosted in intptr_t type +// and pack it to wvComPtr container type (MSW and LSW of 32bit each) +{ + wvComPtr retVal; + int64_t t_PtrToPack = static_cast<int64_t>(in_PtrToPack); + // This packing is xPlatform coding for x32 and x64 + // #ifdef for x64 - intptr_t is 64 bit + retVal.LSW = static_cast<int32_t>(t_PtrToPack & intptr_t(0xFFFFFFFF)); + retVal.MSW = (static_cast<int32_t>(t_PtrToPack>>32)); + + // #ifdef for x32 - intptr_t is 32 bit +// retVal.LSW = int32_t(in_PtrToPack); +// retVal.MSW = 0; + + return retVal; +} + +inline intptr_t UnpackComPtr( const wvComPtr in_ComPtrToUnpack) +// take wvComPtr with MSW and LSW of 32bit each +// and unpack it to intptr_t type +{ + intptr_t retVal; + + // This unpacking is xPlatform coding for x32 and x64 + // #ifdef for x64 - intptr_t is 64 bit so use intptr_t instead of int64_t + int64_t PtrAt64 = static_cast<int64_t>(in_ComPtrToUnpack.MSW); + PtrAt64 <<= 32; + PtrAt64 |= static_cast<int64_t>(in_ComPtrToUnpack.LSW); + retVal = static_cast<intptr_t>(PtrAt64); + + + // #ifdef for x32 - intptr_t is 32 bit +// retVal = static_cast<intptr_t>(retVal.LSW); + + return retVal; +} + + +////////////////////////////////////////////////////////////////////////// +inline uint32_t ComPtr_to_DSP( const intptr_t PtrToConvert, char* pDataStruct ) +{ + + *(reinterpret_cast<wvComPtr *>(pDataStruct)) = PackToComPtr(PtrToConvert); + + return uint32_t(sizeof(wvComPtr)); +} +////////////////////////////////////////////////////////////////////////// + +////////////////////////////////////////////////////////////////////////// +inline uint32_t DSP_to_ComPtr( const char* pDataStruct, intptr_t *ThePtr) +// pDataStruct is pointing to wvComPtr in the Coefs/States +// the function reconstruct the pointer into ThePtr +{ + + *ThePtr = UnpackComPtr(*(reinterpret_cast<const wvComPtr *>(pDataStruct))); + + return uint32_t(sizeof(wvComPtr)); +} +////////////////////////////////////////////////////////////////////////// +*/ + +#endif //#if !defined(__WUComPtr_h__) + + + diff --git a/libs/backends/wavesaudio/wavesapi/BasicTypes/WUDefines.h b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUDefines.h new file mode 100644 index 0000000000..ea5c840d97 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUDefines.h @@ -0,0 +1,175 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WUDefines_h__ + #define __WUDefines_h__ + +/*Copy to include +#include "BasicTypes/WUDefines.h" +*/ + +#include "1.0/WavesPublicAPI_Defines.h" + +// When changing wvNS value also do the same change in Objective_C_MangledNames.h +// because CWSAUCocoaViewFactoryAsString is hard coded there +#define wvNS wvWavesV9_3 +#ifdef __MACOS__ + #define ObjCNameSpace(__className__) wvWavesV9_3_ ## __className__ +#endif + +#ifdef INSIDE_NETSHELL + #define DllExport +#else + #define DllExport WPAPI_DllExport +#endif + +#define __CDECL __WPAPI_CDECL +#define __STDCALL __WPAPI_STDCALL + + +#ifndef NULL + #define NULL (0) +#endif + +#ifndef nil + #define nil NULL +#endif + +#define PASCAL_MAC_ONLY #error do not use PASCAL_MAC_ONLY. See defintions in WavesFTT.h for replacment. +#define CALLCON #error do not use CALLCON. See defintions in WavesFTT.h for replacment. +#define FUNCEXP #error do not use FUNCEXP. See defintions in WavesFTT.h for replacment. + +#define WUNUSED_PARAM(__SOME_UNUSED_PARAM__) ((void)__SOME_UNUSED_PARAM__) + +#ifdef __MACOS__ + const char* const OS_NAME = "Mac"; + + #define WIN_ONLY(__Something_only_for_windows__) + #define MAC_ONLY(__Something_only_for_mac__) __Something_only_for_mac__ + + #if defined(i386) || defined(__i386) || defined(__i386__) + #define kNumArchBits 32 + #endif + #if defined(__x86_64) || defined(__x86_64__) + #define kNumArchBits 64 + #endif + + #if (__i386 || __x86_64) && !defined(__LITTLE_ENDIAN__) + #define __LITTLE_ENDIAN__ + #endif + #if !(__i386 || __x86_64) && !defined(__BIG_ENDIAN__) + #define __BIG_ENDIAN__ + #endif + #ifdef __GNUC__ + #define STD_EXCEPT_WIN std + #define FAR + #define PASCAL + // #define HINSTANCE void* + #define WINAPI + + #else + + #define DllExport_WinOnly + #define STD_EXCEPT_WIN std + #define FAR + #define PASCAL // windows' pascal + #define HINSTANCE void* + #define WINAPI + + #endif + #define THROW_SPEC(THROW_OBJ) throw (THROW_OBJ) + + #define WUNUSED_PARAM_ON_MAC(__SOME_UNUSED_PARAM__) WUNUSED_PARAM(__SOME_UNUSED_PARAM__) + #define WUNUSED_PARAM_ON_WIN(__SOME_UNUSED_PARAM__) +#endif + + +#ifdef _WINDOWS + const char* const OS_NAME = "Win"; + + #define WIN_ONLY(__Something_only_for_windows__) __Something_only_for_windows__ + #define MAC_ONLY(__Something_only_for_mac__) + + #if defined(_M_X64) + #define kNumArchBits 64 + #else // not sure what are the VisualStudio macros for 32 bits + #define kNumArchBits 32 + #endif + + #define DllExport_WinOnly DllExport // help solve window specific link errors + #define STD_EXCEPT_WIN + + #if !defined(__MINGW64__) + #define round(x) (floor(x+0.5)) + #endif + + #define __LITTLE_ENDIAN__ + #define THROW_SPEC(THROW_OBJ) throw (...) + + #define WUNUSED_PARAM_ON_MAC(__SOME_UNUSED_PARAM__) + #define WUNUSED_PARAM_ON_WIN(__SOME_UNUSED_PARAM__) WUNUSED_PARAM(__SOME_UNUSED_PARAM__) + +#endif + +#ifdef __linux__ + const char* const OS_NAME = "Linux"; + + #define WIN_ONLY(__Something_only_for_windows__) + #define MAC_ONLY(__Something_only_for_mac__) + + #define DllExport_WinOnly + #define STD_EXCEPT_WIN std + #define FAR + #define PASCAL + // #define HINSTANCE void* + #define WINAPI + #if __i386 && !defined(__LITTLE_ENDIAN__) + #define __LITTLE_ENDIAN__ + #endif + #if !__i386 && !defined(__BIG_ENDIAN__) + #define __BIG_ENDIAN__ + #endif + #define THROW_SPEC(THROW_OBJ) throw (THROW_OBJ) + + #if defined(__x86_64) || defined(__LP64__) + #error "64 bit not suported yet on linux" + #else + #define kNumArchBits 32 + #endif +#endif + +#ifndef _WU_DECL + #define _WU_DECL __CDECL // the default is calling model is cdecl, but you can also set this macro from the outside to something different +#endif + +#ifndef _XML_DECL + #define _XML_DECL __CDECL // the default is calling model is cdecl, but you can also set this macro from the outside to something different +#endif + +#ifndef kNumArchBits + #error Macro kNumArchBits was not defined +#endif + +#if kNumArchBits == 64 + const char* const kNumArchBits_c_str = "64"; +#endif +#if kNumArchBits == 32 + const char* const kNumArchBits_c_str = "32"; +#endif + +#endif //__WUDefines_h__ diff --git a/libs/backends/wavesaudio/wavesapi/BasicTypes/WUMathConsts.h b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUMathConsts.h new file mode 100644 index 0000000000..6f51bfa3f6 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUMathConsts.h @@ -0,0 +1,47 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WUMathConsts_h__ + #define __WUMathConsts_h__ + +/* Copy to include: +#include "BasicTypes/WUMathConsts.h" +*/ + +const float kfPI = 3.1415926535898f; // PI, single precision +const double kdPI = 3.1415926535897932384626433832795; // PI, double precision + +const float kf2PI = 6.2831853071796f; // 2*PI +const double kd2PI = 6.283185307179586476925286766559; // 2*PI + +const float kfhalfPI = 1.5707963267949f; // 0.5*PI +const double kdhalfPI = 1.57079632679489661923; // 0.5*PI + +const double kdLn2 = 0.69314718055994530942; // natural log(2.0) +const double kdOneOverLn2 = 1.4426950408889634073599246810019; // natural (1.0/log(2.0)) - for multiply log() to get it as with base 2 + +const double kdLog2 = 0.301029995663981; // log10(2.0) +const double kdOneOverLog2 = 3.321928094887363; // (1.0/log10(2.0)) - for multiply log() to get it as with base 2 + +const double kdExponent = 2.718281828459045235360287471352; // e + +const double kdSqrt2 = 1.41421356237309504880; // sqrt(2) + + + +#endif //__WUMathConsts_h__ diff --git a/libs/backends/wavesaudio/wavesapi/BasicTypes/WUTypes.h b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUTypes.h new file mode 100644 index 0000000000..c28547eddb --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/BasicTypes/WUTypes.h @@ -0,0 +1,265 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WUTypes_h__ + #define __WUTypes_h__ + +/* Copy to include: +#include "BasicTypes/WUTypes.h" +*/ + +#include "WavesPublicAPI/WTErr.h" +#include "WavesPublicAPI/wstdint.h" +#include "BasicTypes/WUDefines.h" +#include "BasicTypes/WCFourCC.h" // declares WTFourCharCode & WCFourCC +#include "BasicTypes/WUComPtr.h" // Communication Ptr for x64 compatibility +#include "WCFixedString.h" +#include <ctime> +#include <vector> +/******************************************************************************** + Atoms +*********************************************************************************/ + +#define WTSInt64 "WTSInt64 is obsolete, please use int64_t instead"; +#define WTUInt64 "WTUInt64 is obsolete, please use uint64_t instead"; +#define WTSInt32 "WTSInt32 is obsolete, please use int32_t instead"; +#define WTUInt32 "WTUInt32 is obsolete, please use uint32_t instead"; +#define WTSInt16 "WTSInt16 is obsolete, please use int16_t instead"; +#define WTUInt16 "WTUInt16 is obsolete, please use uint16_t instead"; +#define WTSInt8 "WTSInt8 is obsolete, please use int8_t instead"; +#define WTUInt8 "WTUInt8 is obsolete, please use uint8_t instead"; +#define WTFloat32 "WTFloat32 is obsolete, please use float instead"; +#define WTByte "WTByte is obsolete, please use uint8_t instead"; + +/******************************************************************************** + Consts +*********************************************************************************/ +//#define PI 3.1415926535897 // ... Was moved to WUMathConsts.h under the name kPI +const uint32_t kDefaultCircleSlices = 100; + + +/******************************************************************************** + Utilities +*********************************************************************************/ + +// SCOPED_ENUM is a macro that defines an enum inside a class with a given name, thus declaring the enum values +// inside a named scope. This allows declaring: +// SCOPED_ENUM(SomeType) +// { +// Val1, +// Val2, +// Val3 +// } +// SCOPED_ENUM_END +// And then you can reference SomeType::Val1, SomeType::Val2, SomeType::Val3 for the various values, unlike +// a regular enum on which Val1, Val2 and Val3 would become global names. +// Additionally, you get SomeType::Type to specify the type of the whole enum in case you want to transfer it to +// a function. +// Don't forget to close the enum with SCOPED_ENUM_END, otherwise you'll get bogus compilation errors. +// This requirement can probably be removed some day, but it will make the SCOPED_ENUM macro much less readable... +#define SCOPED_ENUM(name) \ +class name \ +{ \ +public: enum Type + +#define SCOPED_ENUM_END ;}; + + +//******************************************************************************** +// Files + +//! file (and resource container) opening permissions +// Note: When opening with eFMWriteOnly on existing file, writing to the file will append, not overwrite, Shai, 9/8/2007. +enum WEPermitions{ eFMReadOnly, eFMWriteOnly, eFMReadWrite}; + +// File cursor positions +enum WEPositionMode{eFMFileBegin, eFMFileCurrent, eFMFileEnd}; + +// File creation types +enum WECreateFlags { + eFMCreateFile_DontOverrideIfAlreadyExists, // Create a new file , If the file exists leaves the existing data intact + eFMCreateFile_FailIfAlreadyExists, // Attempt to create a new file, if file already exists - fail. + eFMCreateFile_OverrideIfAlreadyExists // Create a new file , If the file exists, overwrite the file and clear the existing data +}; + + +enum WEFoldersDomain{ + eSystemDomain, + eLocalDomain, + eUserDomain, + + eNumberOfFoldersDomains +}; +enum WEArchBits{ + e32Bits, + e64Bits, + eNumberOfArchBits +}; + +enum WESystemFolders{ + eSystemFolder, + eDesktopFolder, + ePreferencesFolder, + eWavesPreferencesFolder, //deprecated use eWavesPreferencesFolder2 + eTemporaryFolder, + eTrashFolder, + eCurrentFolder, + eRootFolder, + eLibrariesFolder, + eAudioComponentsFolder, // MacOS only + eCacheFolder, + eWavesCacheFolder, + eAppDataFolder, + eWavesAppDataFolder, + eSharedUserDataFolder, + eWavesSharedUserDataFolder, + eWavesScanViewFolder, + + eWavesPreferencesFolder2, // Mac: "/Users/username/Library/Preferences/Waves Audio" + // Win: "C:\Users\username\AppData\Roaming\Waves Audio\Preferences" + + eNumberOfSystemFolders +}; + +//******************************************************************************** +// Process + +#ifdef __MACOS__ + typedef uint32_t WTProcessID; // actually pid_t which is __darwin_pid_t which is __uint32_t +#endif +#ifdef _WINDOWS + typedef int WTProcessID; +#endif +#ifdef __linux__ + typedef uint32_t WTProcessID; +#endif + +enum WEManagerInitOptions +{ + eUnknown_ManagerInitOption, + eMacOS_Carbon_Runtime, + eMacOS_Cocoa_Runtime, + eLinuxOS_gtk_Runtime, + eLinuxOS_X_Runtime, + eWindowsOS_GoodOld_Runtime, // good old windows API + eWindowsOS_DotNET_Runtime, + eVerticalFliped_Graphics, + eInit_RM, + eInit_GMConfig, + eInit_PVM, + eInit_UM, + eInit_BKG +}; +#ifdef __MACOS__ + #if __LP64__ || NS_BUILD_32_LIKE_64 // in 64bit (or when NS_BUILD_32_LIKE_64 is specified) we decline Carbon implementation. + const WEManagerInitOptions eDefaultRuntime = eMacOS_Cocoa_Runtime; + #else + const WEManagerInitOptions eDefaultRuntime = eMacOS_Carbon_Runtime; + #endif +#endif +#ifdef _WINDOWS + const WEManagerInitOptions eDefaultRuntime = eWindowsOS_GoodOld_Runtime; +#endif +#ifdef __linux__ + const WEManagerInitOptions eDefaultRuntime = eLinuxOS_gtk_Runtime; +#endif + + +//******************************************************************************** +// Files + +const uint32_t kMaxPathLength = 1023; // maximum length of a path +const uint32_t kMaxFileNameLength = 255; // maximum length of a file name including extension +typedef WCFixedString<kMaxPathLength> WTPathString; +typedef WCFixedString<kMaxFileNameLength> WTFileNameString; + +typedef uint64_t WTFileSize; +const WTFileSize kIllegalFileSize = (WTFileSize)-1; + +typedef off_t WTFileOffset; + +typedef std::time_t WTFileTime; +const WTFileTime kIllegalFileTime = (WTFileTime)-1; + +typedef struct WTPathType* WTPathRef; // represents a path, path need not exists +typedef struct WTOpenFileType* WTOpenFileRef; // represents a real, open file +typedef struct WTNativeDLLRefType* WTNativeDLLRef; // define WTNativeDLLRef as a unique type CFBundleRef on Mac, HINSTANCE on Windows +const WTNativeDLLRef kIllegalNativeDLLRef = 0; +//******************************************************************************** +// Resources + +const size_t kMaxResTypeLength = 31; +typedef WCFixedString31 WTResType; +typedef short WTResID; +const WTResID kIllegalResID = -1; + + +typedef struct WTResContainerType* WTResContainerRef; +typedef struct WTResourceType* WTResRef; +const WTResContainerRef kIllegalContainerRef = 0; +const WTResRef kIllegalResourceRef = 0; + +#ifdef __MACOS__ + typedef struct WTNativeResourceType* WTNativeResourceRef; // for use when need to have access to the native resource without going though resource manager caching anf conversion. + const WTNativeResourceRef kIllegalNativeResourceRef = 0; +#endif +#ifdef _WINDOWS + typedef struct WTNativeResourceType* WTNativeResourceRef; //HGLOBAL // for use when need to have access to the native resource without going though resource manager caching anf conversion. + const WTNativeResourceRef kIllegalNativeResourceRef = 0; +#endif +#ifdef __linux__ +typedef void* WTNativeResourceRef; // WTOpenFileRef // for use when need to have access to the native resource without going though resource manager caching anf conversion. + const WTNativeResourceRef kIllegalNativeResourceRef = 0; +#endif + +//******************************************************************************** +// OpenGL + +typedef struct WCOGLContext* WCOGLContextRef; +typedef struct WCOGLTexture* WCOGLTextureRef; +typedef struct WSPluginView* WCPluginViewRef; +typedef struct WSMenu* WCMenuRef; +typedef struct WCPluginNativeView* WCPluginNativeViewRef; + +const WCOGLContextRef kIllegalOGLContextRef = 0; +const WCOGLTextureRef kIllegalOGLTextureRef = 0; +const WCPluginViewRef kIllegalPluginViewRef = 0; +const WCMenuRef kIllegalWCMenuRef = 0; + +const intptr_t kIllegalTexturesMaster = -1; + + +typedef unsigned int WTTextureRef; +const WTTextureRef kIllegalTextureRef = 0; + +// type for storing pointer to functions. Used to avoid warning such as "C++ forbids conversion between pointer to function and pointer to object" +typedef void (*DUMMY_FUNC_PTR)(void); + +// type for a generic callback function with one parameter +typedef intptr_t (*CALLBACK_1_PARAM_FUNC_PTR)(intptr_t); + +////////////////////////////////////////////////////////////// +// Timer +typedef intptr_t WTTimerRef; +const WTTimerRef kIllegalTimerRef = 0; +typedef void (*WTTimerCallback)(intptr_t); + +// generic type for OS native pointer +typedef void* WTPtr; + +#endif //__WUTypes_h__ diff --git a/libs/backends/wavesaudio/wavesapi/akupara/basics.hpp b/libs/backends/wavesaudio/wavesapi/akupara/basics.hpp new file mode 100644 index 0000000000..a25e0dd89d --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/akupara/basics.hpp @@ -0,0 +1,53 @@ +/* + * basics.hpp + * Akupara + * + * Created by Udi on 12/19/06. + * Copyright 2006 __MyCompanyName__. All rights reserved. + * + */ +#if !defined(_AKUPARA_BASICS_HPP__INCLUDED_) +#define _AKUPARA_BASICS_HPP__INCLUDED_ + +#include "WavesPublicAPI/wstdint.h" + +namespace Akupara +{ + // The ultimate nothingness + // This is useful for writing constructors that nullify their object, and for testing nullness + struct null_type + { + null_type() {} + null_type(const null_type *) {} // this allows 0 to be implicitly converted to null_type + }; + inline null_type null() { return null_type(); } + + + // This is a byte, guaranteed to be unsigned regardless of your compiler's char signedness + typedef uint8_t byte_type; + + + // derive from this if your class needs to be noncopyable + class noncopyable_type + { + private: + noncopyable_type(const noncopyable_type &); + noncopyable_type &operator=(const noncopyable_type &); + public: + noncopyable_type() {} + }; + + +} // namespace Akupara + + +#if defined(__GNUC__) +#define AKUPARA_EXPECT_FALSE(x) __builtin_expect(x,false) +#define AKUPARA_EXPECT_TRUE(x) __builtin_expect(x,true ) +#else +#define AKUPARA_EXPECT_FALSE(x) x +#define AKUPARA_EXPECT_TRUE(x) x +#endif // __GNUC__ + + +#endif // _AKUPARA_BASICS_HPP__INCLUDED_ diff --git a/libs/backends/wavesaudio/wavesapi/akupara/compiletime_functions.hpp b/libs/backends/wavesaudio/wavesapi/akupara/compiletime_functions.hpp new file mode 100644 index 0000000000..85eee8fcee --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/akupara/compiletime_functions.hpp @@ -0,0 +1,205 @@ +/* +* compiletime_functions.hpp +* Akupara +* +* Created by Udi on 12/19/06. +* +*/ +#if !defined(_AKUPARA_COMPILETIME_FUNCTIONS_HPP__INCLUDED_) +#define _AKUPARA_COMPILETIME_FUNCTIONS_HPP__INCLUDED_ + +//#include "WavesPublicAPIs/wstdint.h" + +namespace Akupara +{ + // For templates that "return" a value, use template_name<arguments>::value + // For templates that "return" a type, use template_name<arguments>::type + + + // Integer log2 functions + //------------------------------------------------------------------------ + template<unsigned int n> + struct compiletime_bit_count_to_represent { static const unsigned int value = 1+compiletime_bit_count_to_represent<(n>>1)>::value; }; + + template<> + struct compiletime_bit_count_to_represent<0> { static const unsigned int value = 0; }; + //------------------------------------------------------------------------ + template<unsigned int n> + struct compiletime_log2_ceiling { static const unsigned int value=compiletime_bit_count_to_represent<n-1>::value; }; + + template<> + struct compiletime_log2_ceiling<0> {}; // no value for 0 argument + //------------------------------------------------------------------------ + template<unsigned int n> + struct compiletime_log2_floor { static const unsigned int value=compiletime_bit_count_to_represent<n>::value-1; }; + + template<> + struct compiletime_log2_floor<0> {}; // no value for 0 argument + //------------------------------------------------------------------------ + + + + // Assertion - accessing 'value' will generate a compile-time error if the argument evaluates to false + //------------------------------------------------------------------------ + template<bool> + struct compiletime_assert; + + template<> + struct compiletime_assert<true> { static const bool value=true; }; + + template<> + struct compiletime_assert<false> {}; // no value member for false assertion -> compile time error + //------------------------------------------------------------------------ + + + // Select type - selects one of two types based on a boolean + //------------------------------------------------------------------------ + template<bool, typename, typename> + struct compiletime_select_type; + + template<typename _true_type, typename _false_type> + struct compiletime_select_type<true, _true_type, _false_type> { typedef _true_type type; }; + + template<typename _true_type, typename _false_type> + struct compiletime_select_type<false, _true_type, _false_type> { typedef _false_type type; }; + //------------------------------------------------------------------------ + + + + + + // Integer types by byte count + //------------------------------------------------------------------------ + namespace detail + { + template<unsigned int _size, bool _signed> + struct integer_with_byte_count_base; + + template<> + struct integer_with_byte_count_base<1,true> { typedef int8_t type; }; + + template<> + struct integer_with_byte_count_base<2,true> { typedef int16_t type; }; + + template<> + struct integer_with_byte_count_base<4,true> { typedef int32_t type; }; + + template<> + struct integer_with_byte_count_base<8,true> { typedef int64_t type; }; + + template<> + struct integer_with_byte_count_base<1,false> { typedef uint8_t type; }; + + template<> + struct integer_with_byte_count_base<2,false> { typedef uint16_t type; }; + + template<> + struct integer_with_byte_count_base<4,false> { typedef uint32_t type; }; + + template<> + struct integer_with_byte_count_base<8,false> { typedef uint64_t type; }; + } // namespace detail + //------------------------------------------------------------------------ + template<unsigned int _size, bool _signed=true> + struct integer_with_byte_count : public detail::integer_with_byte_count_base<_size,_signed> + { + typedef typename detail::integer_with_byte_count_base<_size,_signed>::type type; // not required but makes the statement below less messy + static const bool s_correct_size = compiletime_assert<sizeof(type)==_size>::value; // if you get a compilation error here then integer_with_byte_count is not defined correctly + }; + //------------------------------------------------------------------------ + template<unsigned int _size> + struct signed_integer_with_byte_count : public integer_with_byte_count<_size,true> {}; + + template<unsigned int _size> + struct unsigned_integer_with_byte_count : public integer_with_byte_count<_size,false> {}; + //------------------------------------------------------------------------ + + + + // The following are TR1 compatible, until we get decent TR1 library support on all platforms + //------------------------------------------------------------------------ + template<typename _T, _T _v> + struct integral_constant + { + static const _T value = _v; + typedef _T value_type; + typedef integral_constant<_T, _v> type; + }; // struct integral_constant + typedef integral_constant<bool, false> false_type; + typedef integral_constant<bool, true > true_type; + //------------------------------------------------------------------------ + template<typename _T, typename _U> struct is_same : public false_type {}; + template<typename _T> struct is_same<_T,_T> : public true_type {}; + //------------------------------------------------------------------------ + + + + // These are NOT TR1 but make use of some TR1 stuff + //------------------------------------------------------------------------ + namespace detail + { + struct no_type; // if you end up getting this type, it means that you asked for something that doesn't exist + template<unsigned int _pair_index> struct signed_unsigned_pair; +#define AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(index, base_type_name) \ + template<> struct signed_unsigned_pair<index> { typedef signed base_type_name signed_type; typedef unsigned base_type_name unsigned_type; }; +#define AKUPARA_SIGNED_UNSIGNED_FLOAT_PAIR(index, type_name) \ + template<> struct signed_unsigned_pair<index> { typedef type_name signed_type; typedef no_type unsigned_type; }; + AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(1, char ) + AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(2, short ) + AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(3, int ) + + //AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(4, int32_t )// 64BitConversion + template<> + struct + signed_unsigned_pair<4> + { + typedef int32_t signed_type; + typedef uint32_t unsigned_type; + }; + + + AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(5, long long) + AKUPARA_SIGNED_UNSIGNED_FLOAT_PAIR (6, float ) + AKUPARA_SIGNED_UNSIGNED_FLOAT_PAIR (7, double ) + AKUPARA_SIGNED_UNSIGNED_FLOAT_PAIR (8, long double) + const unsigned int k_signed_unsigned_pair_count = 8; + + // eliminate the no_type type + template<typename _T> struct filtered_type { typedef _T type; }; + template<> struct filtered_type<no_type> {}; // no type defined + + // search for _T in signed type list + template<unsigned int _index, typename _T> struct find_in_signed_type_list_from_index + { + static const unsigned int value = is_same< _T, typename signed_unsigned_pair<_index>::signed_type >::value ? _index : find_in_signed_type_list_from_index<_index-1,_T>::value; + }; + template<typename _T> struct find_in_signed_type_list_from_index<0, _T> { static const unsigned int value = 0; }; + template<typename _T> struct find_in_signed_type_list : public find_in_signed_type_list_from_index<k_signed_unsigned_pair_count, _T> {}; + + // search for _T in unsigned type list + template<unsigned int _index, typename _T> struct find_in_unsigned_type_list_from_index + { + static const unsigned int value = is_same< _T, typename signed_unsigned_pair<_index>::unsigned_type >::value ? _index : find_in_unsigned_type_list_from_index<_index-1,_T>::value; + }; + template<typename _T> struct find_in_unsigned_type_list_from_index<0, _T> { static const unsigned int value = 0; }; + template<typename _T> struct find_in_unsigned_type_list : public find_in_unsigned_type_list_from_index<k_signed_unsigned_pair_count, _T> {}; + + template<bool _is_signed, bool _is_unsigned, typename _T> struct equivalent_signed_type; + template<typename _T> struct equivalent_signed_type <true, false, _T> { typedef _T type; }; + template<typename _T> struct equivalent_signed_type <false, true, _T> { typedef typename filtered_type< typename signed_unsigned_pair< find_in_unsigned_type_list<_T>::value >::signed_type >::type type; }; + + template<bool _is_signed, bool _is_unsigned, typename _T> struct equivalent_unsigned_type; + template<typename _T> struct equivalent_unsigned_type<true, false, _T> { typedef typename filtered_type< typename signed_unsigned_pair< find_in_signed_type_list<_T>::value >::unsigned_type >::type type; }; + template<typename _T> struct equivalent_unsigned_type<false, true, _T> { typedef _T type; }; + } // namespace detail + //------------------------------------------------------------------------ + template<typename _T> struct is_signed { static const bool value = detail::find_in_signed_type_list <_T>::value != 0; }; + template<typename _T> struct is_unsigned { static const bool value = detail::find_in_unsigned_type_list<_T>::value != 0; }; + //------------------------------------------------------------------------ + template<typename _T> struct equivalent_signed_type : public detail::equivalent_signed_type < is_signed<_T>::value, is_unsigned<_T>::value, _T > {}; + template<typename _T> struct equivalent_unsigned_type : public detail::equivalent_unsigned_type< is_signed<_T>::value, is_unsigned<_T>::value, _T > {}; + //------------------------------------------------------------------------ + +} // namespace Akupara + +#endif // _AKUPARA_COMPILETIME_FUNCTIONS_HPP__INCLUDED_ diff --git a/libs/backends/wavesaudio/wavesapi/akupara/threading/atomic_ops.hpp b/libs/backends/wavesaudio/wavesapi/akupara/threading/atomic_ops.hpp new file mode 100644 index 0000000000..e87f548a2b --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/akupara/threading/atomic_ops.hpp @@ -0,0 +1,388 @@ +/* +* Akupara/threading/atomic_ops.hpp +* +* +* Created by Udi Barzilai on 06/06. +* Copyright 2006 __MyCompanyName__. All rights reserved. +* +*/ +#if !defined(_AKUPARA_THREADING_ATOMIC_OPS_HPP__INCLUDED_) +#define _AKUPARA_THREADING_ATOMIC_OPS_HPP__INCLUDED_ + +#include "Akupara/basics.hpp" // for EXPECT macro +#include "Akupara/compiletime_functions.hpp" // for TR1 stuff, signed/unsigned stuff + +namespace Akupara +{ + namespace threading + { + namespace atomic + { + namespace machine + { + // Machine capabilities + // The following templates are specialized by the machine-specific headers to indicate + // the capabilities of the machine being compiled for. A true 'value' member for a given + // byte count means that there is an implementation of the corresponding atomic operation. + //------------------------------------- + template<unsigned int _byte_count> struct implements_load : public false_type {}; // simple assignment from memory (assumes naturally aligned address) + template<unsigned int _byte_count> struct implements_store : public false_type {}; // simple assignment to memory (assumes naturally aligned address) + template<unsigned int _byte_count> struct implements_CAS : public false_type {}; // compare_and_store() + template<unsigned int _byte_count> struct implements_LL_SC : public false_type {}; // load_linked(), store_conditional() + template<unsigned int _byte_count> struct implements_add : public false_type {}; // add(), subtract() + template<unsigned int _byte_count> struct implements_fetch_and_add : public false_type {}; // fetch_and_add(), fetch_and_subtract() + template<unsigned int _byte_count> struct implements_add_and_fetch : public false_type {}; // add_and_fetch(), subtract_and_fetch() + //------------------------------------- + + + //------------------------------------- + // functions in this namespace may or may not be implemented, for any integer types, as specified by the machine capabilities templates above + template<typename _integer_type> bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store); + + template<typename _integer_type> _integer_type load_linked(volatile _integer_type * operand_address); + template<typename _integer_type> bool store_conditional(volatile _integer_type * operand_address, const _integer_type & value_to_store); + + template<typename _integer_type> void add(volatile _integer_type * operand_address, const _integer_type & addend); + template<typename _integer_type> void subtract(volatile _integer_type * operand_address, const _integer_type & subtrahend); + + template<typename _integer_type> _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend); + template<typename _integer_type> _integer_type fetch_and_subtract(volatile _integer_type * operand_address, const _integer_type & subtrahend); + + template<typename _integer_type> _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend); + template<typename _integer_type> _integer_type subtract_and_fetch(volatile _integer_type * operand_address, const _integer_type & subtrahend); + + void memory_barrier_read(); + void memory_barrier_write(); + void memory_barrier_readwrite(); + //------------------------------------- + + } // namespace machine + } // namespace atomic + } // namespace threading +} // namespace Akupara + +// Include the machine-specific implementations; these only implement the templates above for some of the _signed_ integer types +#if defined(__GNUC__) && defined(__POWERPC__) +#include "atomic_ops_gcc_ppc.hpp" +#endif // defined(__GNUC__) && defined(__POWERPC__) + +#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) +#include "atomic_ops_gcc_x86.hpp" +#endif // defined(__GNUC__) && defined(__i386__) + +#if defined(_MSC_VER) && defined(_M_IX86) +#include "atomic_ops_msvc_x86.hpp" +#endif // defined(_MSC_VER) && defined(_M_IX86) + +#if defined(_MSC_VER) && defined(_M_X64) +#include "atomic_ops_msvc_x86_64.hpp" +#endif // defined(_MSC_VER) && defined(_M_X64) + +namespace Akupara +{ + namespace threading + { + namespace atomic + { + + + // Select the most convenient atomic integer type based on the machine's ability to load/store atomically + // The definition below selects that largest atomically accessible integer up to the size of int + //---------------------------------------------------------------------------------------- + namespace detail + { + template<unsigned int _byte_count> + struct largest_atomic_byte_count_upto + { + static const unsigned int value = + machine::implements_load<_byte_count>::value && machine::implements_store<_byte_count>::value ? +_byte_count : + largest_atomic_byte_count_upto<_byte_count/2>::value; + }; + + template<> + struct largest_atomic_byte_count_upto<0> { static const unsigned int value = 0; }; + + const unsigned int k_byte_count_best_atomic = largest_atomic_byte_count_upto<sizeof(int)>::value; + } + typedef signed_integer_with_byte_count< detail::k_byte_count_best_atomic >::type signed_integer_type; + typedef unsigned_integer_with_byte_count< detail::k_byte_count_best_atomic >::type unsigned_integer_type; + typedef signed_integer_type integer_type; + //---------------------------------------------------------------------------------------- + + //---------------------------------------------------------------------------------------- + // These need to be implemented by all machines + using machine::memory_barrier_read; + using machine::memory_barrier_write; + using machine::memory_barrier_readwrite; + //---------------------------------------------------------------------------------------- + + //---------------------------------------------------------------------------------------- + // These may or may not be implemented, but if they aren't, we can't help much + using machine::load_linked; + using machine::store_conditional; + //---------------------------------------------------------------------------------------- + + + //---------------------------------------------------------------------------------------- + // CAS implementation + namespace detail + { + template< + typename _integer_type, + bool _implements_CAS = machine::implements_CAS <sizeof(_integer_type)>::value, + bool _implements_LL_SC = machine::implements_LL_SC<sizeof(_integer_type)>::value> + struct implementation_CAS + { + static const bool s_exists = false; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for native CAS support + template<typename _integer_type, bool _implements_LL_SC> + struct implementation_CAS<_integer_type, true, _implements_LL_SC> + { + static const bool s_exists = true; + static inline bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store) + { + return machine::compare_and_store(operand_address, expected_value, value_to_store); + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for cases with no CAS but with LL/SC + template<typename _integer_type> + struct implementation_CAS<_integer_type, false, true> + { + static const bool s_exists = true; + static inline bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store) + { + while (machine::load_linked(operand_address) == expected_value) + if (AKUPARA_EXPECT_TRUE(machine::store_conditional(operand_address, value_to_store))) + return true; + return false; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + } // namespace detail + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + template<typename _integer_type> + inline bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store) + { + // if your compiler can't find the function to call here then there is no implementation available for your machine + return detail::implementation_CAS<_integer_type>::compare_and_store(operand_address, expected_value, value_to_store); + } + //---------------------------------------------------------------------------------------- + + + + + + //---------------------------------------------------------------------------------------- + // fetch_and_add + namespace detail + { + template< + typename _integer_type, + bool _0 = machine::implements_fetch_and_add<sizeof(_integer_type)>::value, + bool _1 = machine::implements_add_and_fetch<sizeof(_integer_type)>::value, + bool _2 = machine::implements_LL_SC <sizeof(_integer_type)>::value, + bool _3 = machine::implements_CAS <sizeof(_integer_type)>::value> + struct implementation_FAA + { + static const bool s_exists = false; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for native support + template<typename _integer_type, bool _1, bool _2, bool _3> + struct implementation_FAA<_integer_type, true, _1, _2, _3> + { + static const bool s_exists = true; + static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend) + { + return machine::fetch_and_add(operand_address, addend); + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization using add_and_fetch + template<typename _integer_type, bool _2, bool _3> + struct implementation_FAA<_integer_type, false, true, _2, _3> + { + static const bool s_exists = true; + static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend) + { + return machine::add_and_fetch(operand_address, addend) - addend; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization using LL/SC + template<typename _integer_type, bool _3> + struct implementation_FAA<_integer_type, false, false, true, _3> + { + static const bool s_exists = true; + static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend) + { + _integer_type old_value; + do + old_value = machine::load_linked(operand_address); + while (!machine::store_conditional(operand_address, old_value+addend)); + return old_value; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization using CAS + template<typename _integer_type> + struct implementation_FAA<_integer_type, false, false, false, true> + { + static const bool s_exists = true; + static inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend) + { + _integer_type old_value; + do + old_value = *operand_address; + while (!machine::compare_and_store(operand_address, old_value, old_value+addend)); + return old_value; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + } // namespace detail + template<typename _integer_type> + inline _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend) + { + // if your compiler can't find the function to call here then there is no implementation available for your machine + return detail::implementation_FAA<_integer_type>::fetch_and_add(operand_address, addend); + } + //---------------------------------------------------------------------------------------- + + + + + //---------------------------------------------------------------------------------------- + // add_and_fetch + namespace detail + { + template< + typename _integer_type, + bool _0 = machine::implements_add_and_fetch<sizeof(_integer_type)>::value, + bool _1 = machine::implements_fetch_and_add<sizeof(_integer_type)>::value, + bool _2 = machine::implements_LL_SC <sizeof(_integer_type)>::value, + bool _3 = machine::implements_CAS <sizeof(_integer_type)>::value> + struct implementation_AAF + { + static const bool s_exists = false; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for native support + template<typename _integer_type, bool _1, bool _2, bool _3> + struct implementation_AAF<_integer_type, true, _1, _2, _3> + { + static const bool s_exists = true; + static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend) + { + return machine::add_and_fetch(operand_address, addend); + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization using add_and_fetch + template<typename _integer_type, bool _2, bool _3> + struct implementation_AAF<_integer_type, false, true, _2, _3> + { + static const bool s_exists = true; + static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend) + { + return machine::fetch_and_add(operand_address, addend) + addend; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization using LL/SC + template<typename _integer_type, bool _3> + struct implementation_AAF<_integer_type, false, false, true, _3> + { + static const bool s_exists = true; + static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend) + { + _integer_type new_value; + do + new_value = machine::load_linked(operand_address)+addend; + while (!machine::store_conditional(operand_address, new_value)); + return new_value; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization using CAS + template<typename _integer_type> + struct implementation_AAF<_integer_type, false, false, false, true> + { + static const bool s_exists = true; + static inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend) + { + _integer_type old_value, new_value; + do + old_value = *operand_address, new_value = old_value + addend; + while (!machine::compare_and_store(operand_address, old_value, new_value)); + return new_value; + } + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + } // namespace detail + template<typename _integer_type> + inline _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend) + { + // if your compiler can't find the function to call here then there is no implementation available for your machine + return detail::implementation_AAF<_integer_type>::add_and_fetch(operand_address, addend); + } + //---------------------------------------------------------------------------------------- + + + + //---------------------------------------------------------------------------------------- + // add + template<typename _integer_type> + inline void add(volatile _integer_type * operand_address, const _integer_type & addend) + { + if (machine::implements_add<sizeof(_integer_type)>::value) + machine::add(operand_address, addend); + else if (machine::implements_fetch_and_add<sizeof(_integer_type)>::value) + machine::fetch_and_add(operand_address, addend); + else if (machine::implements_add_and_fetch<sizeof(_integer_type)>::value) + machine::add_and_fetch(operand_address, addend); + else + fetch_and_add(operand_address, addend); // this will simulate using CAS or LL/SC (or it will fail the compilation if neither is available) + } + //---------------------------------------------------------------------------------------- + + + + //---------------------------------------------------------------------------------------- + // TODO: this is where we add implementations for: + // - functions not implemented by the machine + // - functions that take unsigned types (routed to call the signed versions with appropriate conversions) + // For now we add nothing, so developers will need to stick to what their machine can do, and use signed + // integers only. + using machine::subtract; + using machine::subtract_and_fetch; + using machine::fetch_and_subtract; + //---------------------------------------------------------------------------------------- + + + + //--------------------------------------------------------------------- + template<class _base_type, unsigned int _bytes_per_cache_line=machine::k_bytes_per_cache_line> + struct pad_to_cache_line : public _base_type + { + private: + typedef pad_to_cache_line this_type; + typedef _base_type base_type; + public: + static const unsigned int s_bytes_per_cache_line = _bytes_per_cache_line; + private: + int m_padding[(s_bytes_per_cache_line - sizeof(base_type))/sizeof(int)]; + public: + pad_to_cache_line() {} + template<typename _arg_type> pad_to_cache_line(_arg_type arg) : base_type(arg) {} + }; + //--------------------------------------------------------------------- + + } // namespace atomic + } // namespace threading +} // namespace Akupara + +#endif // _AKUPARA_THREADING_ATOMIC_OPS_HPP__INCLUDED_ diff --git a/libs/backends/wavesaudio/wavesapi/akupara/threading/atomic_ops_gcc_x86.hpp b/libs/backends/wavesaudio/wavesapi/akupara/threading/atomic_ops_gcc_x86.hpp new file mode 100644 index 0000000000..3039433bcf --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/akupara/threading/atomic_ops_gcc_x86.hpp @@ -0,0 +1,201 @@ +/* + * Akupara/threading/atomic_ops_gcc_x86.hpp + * + * + * Created by Udi Barzilai on 06/06. + * Copyright 2006 __MyCompanyName__. All rights reserved. + * + */ +#if !defined(_AKUPARA_THREADING_ATOMIC_OPS_GCC_X86_HPP__INCLUDED_) +# define _AKUPARA_THREADING_ATOMIC_OPS_GCC_X86_HPP__INCLUDED_ +# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)) + +namespace Akupara +{ + namespace threading + { + namespace atomic + { + namespace machine + { + const unsigned int k_bytes_per_cache_line = 64; // this is true for P4 & K8 + + + // Flags for operations supported by this machine + //------------------------------------- + template<> struct implements_load <4> : public true_type {}; + template<> struct implements_store <4> : public true_type {}; + template<> struct implements_CAS <4> : public true_type {}; + template<> struct implements_CAS <8> : public true_type {}; + template<> struct implements_add <4> : public true_type {}; + template<> struct implements_fetch_and_add<4> : public true_type {}; + //------------------------------------- + + + + // CAS + //-------------------------------------------------------------------------------- + template<> + inline bool compare_and_store<int64_t>(volatile int64_t * p, const int64_t & x, const int64_t & y) + { + register int32_t evh=int32_t(x>>32), evl=int32_t(x); + register const int32_t nvh=int32_t(y>>32), nvl=int32_t(y); + register bool result; + __asm__ __volatile__ ( + "# CAS64\n" + " lock \n" + " cmpxchg8b %[location] \n" + " sete %[result] \n" + : [location] "+m" (*p), [result] "=qm" (result), [expected_value_high] "+d" (evh), [expected_value_low] "+a" (evl) + : [new_value_high] "c" (nvh), [new_value_low] "b" (nvl) + : "cc" + ); + return result; + } + //-------------------------------------------------------------------------------- + template<> + inline bool compare_and_store<int32_t>(volatile int32_t *p, const int32_t & x, const int32_t & y) + { + register int32_t expected_value = x; + register bool result; + __asm__ __volatile__ ( + "# CAS32\n" + " lock \n" + " cmpxchgl %[new_value],%[operand] \n" + " sete %[result] \n" + : [operand] "+m" (*p), [result] "=qm" (result), [expected_value] "+a" (expected_value) + : [new_value] "r" (y) + : "cc" + ); + return result; + } + //-------------------------------------------------------------------------------- + + + + + // Atomic add/sub + //-------------------------------------------------------------------------------- + inline void increment(volatile int32_t * operand_address) + { + __asm__ __volatile__ ( + "# atomic_increment_32\n" + " lock; \n" + " incl %[operand]; \n" + : [operand] "+m" (*operand_address) + : + : "cc" + ); + } + //-------------------------------------------------------------------------------- + inline void decrement(volatile int32_t * operand_address) + { + __asm__ __volatile__ ( + "# atomic_decrement_32\n" + " lock; \n" + " decl %[operand]; \n" + : [operand] "+m" (*operand_address) + : + : "cc" + ); + } + //-------------------------------------------------------------------------------- + template<> + inline void add<int32_t>(volatile int32_t * operand_address, const int32_t & addend) + { + if (__builtin_constant_p(addend) && addend==1) + increment(operand_address); + else if (__builtin_constant_p(addend) && addend==-1) + decrement(operand_address); + else + __asm__ __volatile__ ( + "# atomic_add_32 \n" + " lock \n" + " addl %[addend], %[operand] \n" + : [operand] "+m" (*operand_address) + : [addend] "ir" (addend) + : "cc" + ); + } + //-------------------------------------------------------------------------------- + template<> + inline void subtract<int32_t>(volatile int32_t * operand_address, const int32_t & subtrahend) + { + if (__builtin_constant_p(subtrahend) && subtrahend==1) + decrement(operand_address); + else if (__builtin_constant_p(subtrahend) && subtrahend==-1) + increment(operand_address); + else + __asm__ __volatile__ ( + "# atomic_subtract_32 \n" + " lock \n" + " subl %[subtrahend], %[operand] \n" + : [operand] "+m" (*operand_address) + : [subtrahend] "ir" (subtrahend) + : "cc" + ); + } + //-------------------------------------------------------------------------------- + + + + // Atomic fetch and add/sub + //-------------------------------------------------------------------------------- + template<> + inline int32_t fetch_and_add<int32_t>(volatile int32_t * operand_address, const int32_t & addend) + { + register int32_t addend_and_fetched = addend; + __asm__ __volatile__ ( + "# atomic_fetch_and_add_32 \n" + " lock; \n" + " xaddl %[addend], %[operand]; \n" + : [operand] "+m" (*operand_address), [addend] "+r" (addend_and_fetched) + : + : "cc" + ); + return addend_and_fetched; + } + //-------------------------------------------------------------------------------- + template<> + inline int32_t fetch_and_subtract<int32_t>(volatile int32_t * operand_address, const int32_t & subtrahend) + { + return fetch_and_add(operand_address, -subtrahend); + } + //-------------------------------------------------------------------------------- + + + + + // Memory barriers + //-------------------------------------------------------------------------------- + inline void memory_barrier_readwrite() + { + #if _AKUPARA_X86_SSE_NOT_AVAILABLE + __asm__ __volatile__ (" lock; addl $0,0(%%esp); # memory_barrier_readwrite" : : : "memory"); + #else + __asm__ __volatile__ (" mfence; # memory_barrier_readwrite" : : : "memory"); + #endif // _LOCKFREE_ATOMIC_OPS_X86_LFENCE_NOT_AVAILABLE + } + //-------------------------------------------------------------------------------- + inline void memory_barrier_read() + { + #if _AKUPARA_X86_SSE_NOT_AVAILABLE + __asm__ __volatile__ (" lock; addl $0,0(%%esp); # memory_barrier_read" : : : "memory"); + #else + __asm__ __volatile__ (" lfence; # memory_barrier_read" : : : "memory"); + #endif // _LOCKFREE_ATOMIC_OPS_X86_LFENCE_NOT_AVAILABLE + } + //-------------------------------------------------------------------------------- + inline void memory_barrier_write() + { + __asm__ __volatile__ (" sfence; # memory_barrier_write" : : : "memory"); + } + //-------------------------------------------------------------------------------- + + } // namespace machine + } // namespace atomic + } // namespace threading +} // namespace Akupara + +# endif // defined(__GNUC__) && defined(__i386__) +#endif // _AKUPARA_THREADING_ATOMIC_OPS_GCC_X86_HPP__INCLUDED_ diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/IncludeWindows.h b/libs/backends/wavesaudio/wavesapi/devicemanager/IncludeWindows.h new file mode 100644 index 0000000000..5dd8f0ed50 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/IncludeWindows.h @@ -0,0 +1,49 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __IncludeWindows_h__
+#define __IncludeWindows_h__
+
+#ifdef _WINDOWS
+
+/* Copy to include
+#include "IncludeWindows.h"
+*/
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0601 // Windows 7
+#endif
+
+#ifndef WINVER
+#define WINVER 0x0601 // Windows 7
+#endif
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifndef NOMINMAX
+#define NOMINMAX // DO NOT REMOVE NOMINMAX - DOING SO CAUSES CONFLICTS WITH STD INCLUDES (<limits> ...)
+#endif
+
+#include <WinSock2.h>
+#include <Windows.h>
+#include <objbase.h>
+#endif // #if _WINDOWS
+#endif // #ifndef __IncludeWindows_h__
+
diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp new file mode 100644 index 0000000000..ae5ef3a923 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.cpp @@ -0,0 +1,743 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +//---------------------------------------------------------------------------------- +// +// +//! \file WCMRAudioDeviceManager.cpp +//! +//! WCMRAudioDeviceManager and related class declarations +//! +//---------------------------------------------------------------------------------*/ +#include "WCMRAudioDeviceManager.h" + + + + + + +//********************************************************************************************** +// WCMRAudioDevice::WCMRAudioDevice +// +//! Constructor for the audio device. The derived classes will need to do more actual work, such +//! as determining supported sampling rates, buffer sizes, and channel counts. Connection +//! and streaming will also be provided by the derived implementations. +//! +//! \param *pManager : The audio device manager that's managing this device. +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRAudioDevice::WCMRAudioDevice (WCMRAudioDeviceManager *pManager) +{ + m_pMyManager = pManager; + m_DeviceName = "Unknown"; + + m_ConnectionStatus = DeviceDisconnected; + m_IsActive = false; + m_IsStreaming = false; + + m_CurrentSamplingRate = -1; + m_CurrentBufferSize = 0; + + m_LeftMonitorChannel = -1; + m_RightMonitorChannel = -1; + m_MonitorGain = 1.0f; + + +} + + + +//********************************************************************************************** +// WCMRAudioDevice::~WCMRAudioDevice +// +//! Destructor for the audio device. It release all the connections that were created. +//! +//! \param none +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRAudioDevice::~WCMRAudioDevice () +{ + AUTO_FUNC_DEBUG; + try + { + } + catch (...) + { + //destructors should absorb exceptions, no harm in logging though!! + DEBUG_MSG ("Exception during destructor"); + } +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::DeviceName +// +//! Retrieves Device's name. +//! +//! \param none +//! +//! \return The device name. +//! +//********************************************************************************************** +const std::string& WCMRAudioDevice::DeviceName () const +{ + return (m_DeviceName); + +} + + + +//********************************************************************************************** +// WCMRAudioDevice::InputChannels +// +//! Retrieves Input Channel information. Note that the list may be changed at run-time. +//! +//! \param none +//! +//! \return A vector with Input Channel Names. +//! +//********************************************************************************************** +const std::vector<std::string>& WCMRAudioDevice::InputChannels () +{ + return (m_InputChannels); + +} + + + +//********************************************************************************************** +// WCMRAudioDevice::OutputChannels +// +//! Retrieves Output Channel Information. Note that the list may be changed at run-time. +//! +//! \param none +//! +//! \return A vector with Output Channel Names. +//! +//********************************************************************************************** +const std::vector<std::string>& WCMRAudioDevice::OutputChannels () +{ + return (m_OutputChannels); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::SamplingRates +// +//! Retrieves supported sampling rate information. +//! +//! \param none +//! +//! \return A vector with supported sampling rates. +//! +//********************************************************************************************** +const std::vector<int>& WCMRAudioDevice::SamplingRates () +{ + return (m_SamplingRates); +} + + + +//********************************************************************************************** +// WCMRAudioDevice::CurrentSamplingRate +// +//! The device's current sampling rate. This may be overridden, if the device needs to +//! query the driver for the current rate. +//! +//! \param none +//! +//! \return The device's current sampling rate. -1 on error. +//! +//********************************************************************************************** +int WCMRAudioDevice::CurrentSamplingRate () +{ + return (m_CurrentSamplingRate); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::SetCurrentSamplingRate +// +//! Change the sampling rate to be used by the device. This will most likely be overridden, +//! the base class simply updates the member variable. +//! +//! \param newRate : The rate to use (samples per sec). +//! +//! \return eNoErr always. The derived classes may return error codes. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SetCurrentSamplingRate (int newRate) +{ + //changes the status. + m_CurrentSamplingRate = newRate; + return (eNoErr); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::BufferSizes +// +//! Retrieves supported buffer size information. +//! +//! \param none +//! +//! \return A vector with supported buffer sizes. +//! +//********************************************************************************************** +const std::vector<int>& WCMRAudioDevice::BufferSizes () +{ + return (m_BufferSizes); +} + + + +//********************************************************************************************** +// WCMRAudioDevice::CurrentBufferSize +// +//! The device's current buffer size in use. This may be overridden, if the device needs to +//! query the driver for the current size. +//! +//! \param none +//! +//! \return The device's current buffer size. 0 on error. +//! +//********************************************************************************************** +int WCMRAudioDevice::CurrentBufferSize () +{ + return (m_CurrentBufferSize); +} + +//********************************************************************************************** +// WCMRAudioDevice::CurrentBlockSize +// +//! Device's block size we use for holding the audio samples. +//! Usually this is equal to the buffer size, but in some cases the buffer size holds additional +//! data other then the audio buffers, like frames info in SG, so it can be overriden +//! +//! \param none +//! +//! \return The device's current block size. 0 on error. +//! +//********************************************************************************************** +int WCMRAudioDevice::CurrentBlockSize() +{ + // By default - return the buffer size + return CurrentBufferSize(); +} + + +//********************************************************************************************** +// WCMRAudioDevice::SetCurrentBufferSize +// +//! Change the buffer size to be used by the device. This will most likely be overridden, +//! the base class simply updates the member variable. +//! +//! \param newSize : The buffer size to use (in sample-frames) +//! +//! \return eNoErr always. The derived classes may return error codes. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SetCurrentBufferSize (int newSize) +{ + //This will most likely be overridden, the base class simply + //changes the member. + m_CurrentBufferSize = newSize; + return (eNoErr); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::ConnectionStatus +// +//! Retrieves the device's current connection status. This will most likely be overridden, +//! in case some driver communication is required to query the status. +//! +//! \param none +//! +//! \return A ConnectionStates value. +//! +//********************************************************************************************** +WCMRAudioDevice::ConnectionStates WCMRAudioDevice::ConnectionStatus () +{ + return (m_ConnectionStatus); + +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::Active +// +//! Retrieves Device activation status. +//! +//! \param none +//! +//! \return true if device is active, false otherwise. +//! +//********************************************************************************************** +bool WCMRAudioDevice::Active () +{ + return (m_IsActive); + +} + + + +//********************************************************************************************** +// WCMRAudioDevice::SetActive +// +//! Sets the device's activation status. +//! +//! \param newState : Should be true to activate, false to deactivate. This roughly corresponds +//! to opening and closing the device handle/stream/audio unit. +//! +//! \return eNoErr always, the derived classes may return appropriate error code. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SetActive (bool newState) +{ + //This will most likely be overridden, the base class simply + //changes the member. + m_IsActive = newState; + return (eNoErr); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::Streaming +// +//! Retrieves Device streaming status. +//! +//! \param none +//! +//! \return true if device is streaming, false otherwise. +//! +//********************************************************************************************** +bool WCMRAudioDevice::Streaming () +{ + return (m_IsStreaming); +} + + + +//********************************************************************************************** +// WCMRAudioDevice::SetStreaming +// +//! Sets the device's streaming status. +//! +//! \param newState : Should be true to start streaming, false to stop streaming. This roughly +//! corresponds to calling Start/Stop on the lower level interface. +//! +//! \return eNoErr always, the derived classes may return appropriate error code. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SetStreaming (bool newState) +{ + //This will most likely be overridden, the base class simply + //changes the member. + m_IsStreaming = newState; + return (eNoErr); +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// IsProcessActive - returns true if process code is running. +// A normal audio device should return the Streaming() value +/////////////////////////////////////////////////////////////////////////////////////////////////////// +bool WCMRAudioDevice::IsProcessActive() +{ + return Streaming(); +} + + + + + +//********************************************************************************************** +// WCMRAudioDevice::DoIdle +// +//! A place for doing idle time processing. The derived classes will probably do something +//! meaningful. +//! +//! \param none +//! +//! \return eNoErr always. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::DoIdle () +{ + //We don't need to do anything here... + //the derived classes may want to use this however. + return (eNoErr); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::InputLevels +// +//! Retrieve current input levels. +//! +//! \param none +//! +//! \return A vector (the same size as input channels list) that contains current input levels. +//! +//********************************************************************************************** +const std::vector<float>& WCMRAudioDevice::InputLevels () +{ + //The derived classes may override if they need to query + //the driver for the levels. + return (m_InputLevels); +} + + + +//********************************************************************************************** +// WCMRAudioDevice::OutputLevels +// +//! Retrieve current output levels. +//! +//! \param none +//! +//! \return A vector (the same size as output channels list) that contains current output levels. +//! +//********************************************************************************************** +const std::vector<float>& WCMRAudioDevice::OutputLevels () +{ + //The derived classes may override if they need to query + //the driver for the levels. + return (m_OutputLevels); +} + + + +//********************************************************************************************** +// WCMRAudioDevice::GetMonitorInfo +// +//! Retrieves current monitoring information. +//! +//! \param *pLeftChannel : Pointer to receive left monitor channel index. +//! \param *pRightChannel : Pointer to receive right monitor channel index. +//! \param *pGain : Pointer to receive the gain (linear) to be applied. +//! +//! \return Nothing. +//! +//********************************************************************************************** +void WCMRAudioDevice::GetMonitorInfo (int *pLeftChannel, int *pRightChannel, float *pGain) +{ + if (pLeftChannel) + *pLeftChannel = m_LeftMonitorChannel; + if (pRightChannel) + *pRightChannel = m_RightMonitorChannel; + if (pGain) + *pGain = m_MonitorGain; + return; +} + + + +//********************************************************************************************** +// WCMRAudioDevice::SetMonitorChannels +// +//! Used to set the channels to be used for monitoring. +//! +//! \param leftChannel : Left monitor channel index. +//! \param rightChannel : Right monitor channel index. +//! +//! \return eNoErr always, the derived classes may return appropriate errors. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SetMonitorChannels (int leftChannel, int rightChannel) +{ + //This will most likely be overridden, the base class simply + //changes the member. + m_LeftMonitorChannel = leftChannel; + m_RightMonitorChannel = rightChannel; + return (eNoErr); +} + + + +//********************************************************************************************** +// WCMRAudioDevice::SetMonitorGain +// +//! Used to set monitor gain (or atten). +//! +//! \param newGain : The new gain or atten. value to use. Specified as a linear multiplier (not dB) +//! +//! \return eNoErr always, the derived classes may return appropriate errors. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SetMonitorGain (float newGain) +{ + //This will most likely be overridden, the base class simply + //changes the member. + m_MonitorGain = newGain; + return (eNoErr); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::ShowConfigPanel +// +//! Used to show device specific config/control panel. Some interfaces may not support it. +//! Some interfaces may require the device to be active before it can display a panel. +//! +//! \param pParam : A device/interface specific parameter - optional. +//! +//! \return eNoErr always, the derived classes may return errors. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::ShowConfigPanel (void *WCUNUSEDPARAM(pParam)) +{ + //This will most likely be overridden... + return (eNoErr); +} + + +//********************************************************************************************** +// WCMRAudioDevice::SendCustomCommand +// +//! Used to Send a custom command to the audiodevice. Some interfaces may require the device +//! to be active before it can do anything in this. +//! +//! \param customCommand : A device/interface specific command. +//! \param pCommandParam : A device/interface/command specific parameter - optional. +//! +//! \return eNoErr always, the derived classes may return errors. +//! +//********************************************************************************************** +WTErr WCMRAudioDevice::SendCustomCommand (int WCUNUSEDPARAM(customCommand), void *WCUNUSEDPARAM(pCommandParam)) +{ + //This will most likely be overridden... + return (eNoErr); +} + +//********************************************************************************************** +// WCMRAudioDevice::GetLatency +// +//! Get Latency for device. +//! +//! Use 'kAudioDevicePropertyLatency' and 'kAudioDevicePropertySafetyOffset' + GetStreamLatencies +//! +//! \param isInput : Return latency for the input if isInput is true, otherwise the output latency +//! wiil be returned. +//! \return Latency in samples. +//! +//********************************************************************************************** +uint32_t WCMRAudioDevice::GetLatency (bool isInput) +{ + //This will most likely be overridden... + return 0; +} + +//********************************************************************************************** +// WCMRAudioDeviceManager::WCMRAudioDeviceManager +// +//! The constructuor, most of the work will be done in the derived class' constructor. +//! +//! \param *pTheClient : +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRAudioDeviceManager::WCMRAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient, eAudioDeviceFilter eCurAudioDeviceFilter) + : m_pTheClient (pTheClient) + , m_eAudioDeviceFilter(eCurAudioDeviceFilter) +{ + //The derived classes will do lot more init! + return; +} + + + +//********************************************************************************************** +// WCMRAudioDeviceManager::~WCMRAudioDeviceManager +// +//! It clears the device list, releasing each of the device. +//! +//! \param none +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRAudioDeviceManager::~WCMRAudioDeviceManager() +{ + AUTO_FUNC_DEBUG; + + try + { + //Need to call release on our devices, and erase them from list + std::vector<WCMRAudioDevice*>::iterator deviceIter; + while (m_Devices.size()) + { + WCMRAudioDevice *pDeviceToRelease = m_Devices.back(); + m_Devices.pop_back(); + if (pDeviceToRelease) + SAFE_RELEASE (pDeviceToRelease); + } + + //The derived classes may want to do additional de-int! + } + catch (...) + { + //destructors should absorb exceptions, no harm in logging though!! + DEBUG_MSG ("Exception during destructor"); + } +} + + + + +//********************************************************************************************** +// WCMRAudioDeviceManager::DoIdle_Private +// +//! Used for idle time processing. This calls each device's DoIdle so that it can perform it's own idle processing. +//! +//! \param none +//! +//! \return noErr if no devices have returned an error. An error code if any of the devices returned error. +//! +//********************************************************************************************** +WTErr WCMRAudioDeviceManager::DoIdle_Private() +{ + WTErr retVal = eNoErr; + + //Need to call DoIdle of all our devices... + std::vector<WCMRAudioDevice*>::iterator deviceIter; + for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); deviceIter++) + { + WTErr thisDeviceErr = (*deviceIter)->DoIdle(); + + if (thisDeviceErr != eNoErr) + retVal = thisDeviceErr; + } + + return (retVal); +} + + + + +//********************************************************************************************** +// WCMRAudioDeviceManager::Devices_Private +// +//! Retrieve list of devices managed by this manager. +//! +//! \param none +//! +//! \return A vector containing the list of devices. +//! +//********************************************************************************************** +const WCMRAudioDeviceList& WCMRAudioDeviceManager::Devices_Private() const +{ + return (m_Devices); +} + + + +//********************************************************************************************** +// *WCMRAudioDeviceManager::GetDeviceByName_Private +// +//! Locates a device based on device name. +//! +//! \param nameToMatch : Device to look for. +//! +//! \return Pointer to the device object if found, NULL otherwise. +//! +//********************************************************************************************** +WCMRAudioDevice *WCMRAudioDeviceManager::GetDeviceByName_Private(const std::string& nameToMatch) const +{ + //Need to check all our devices... + WCMRAudioDevice *pRetVal = NULL; + + WCMRAudioDeviceListConstIter deviceIter; + for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); deviceIter++) + { + if ((*deviceIter)->DeviceName() == nameToMatch) + { + pRetVal = *deviceIter; + break; + } + } + + return (pRetVal); +} + +//********************************************************************************************** +// *WCMRAudioDeviceManager::GetDefaultDevice +// +//! Locates a device based on device name. +//! +//! \param nameToMatch : Device to look for. +//! +//! \return Pointer to the device object if found, NULL otherwise. +//! +//********************************************************************************************** +WCMRAudioDevice *WCMRAudioDeviceManager::GetDefaultDevice_Private() +{ + //Need to check all our devices... + WCMRAudioDevice *pRetVal = NULL; + + WCMRAudioDeviceListIter deviceIter = m_Devices.begin(); + if(deviceIter != m_Devices.end()) + { + pRetVal = *deviceIter; + } + return (pRetVal); +} + + + + +//********************************************************************************************** +// WCMRAudioDeviceManager::NotifyClient +// +//! A helper routine used to call the client for notification. +//! +//! \param forReason : The reason for notification. +//! \param *pParam : A parameter (if required) for notification. +//! +//! \return Nothing. +//! +//********************************************************************************************** +void WCMRAudioDeviceManager::NotifyClient (WCMRAudioDeviceManagerClient::NotificationReason forReason, void *pParam) +{ + if (m_pTheClient) + m_pTheClient->AudioDeviceManagerNotification (forReason, pParam); + return; +} diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h new file mode 100644 index 0000000000..0d6aa55dea --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRAudioDeviceManager.h @@ -0,0 +1,266 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +//---------------------------------------------------------------------------------- +// +// +//! \file WCMRAudioDeviceManager.h +//! +//! WCMRAudioDeviceManager and related class declarations +//! +//---------------------------------------------------------------------------------*/ +#ifndef __WCMRAudioDeviceManager_h_ + #define __WCMRAudioDeviceManager_h_ + +/* Copy to include +#include "WCMRAudioDeviceManager.h" +*/ + +#define AUTO_FUNC_DEBUG +#define DEBUG_MSG(a) +#define ASSERT_ERROR(a, b) +#define TRACE_MSG(a) + +#include <string> +#include <vector> +#include <map> +#include "WCRefManager.h" +#include "BasicTypes/WUTypes.h" +#include "WUErrors.h" + +#define WCUNUSEDPARAM(a) + +//forward decl. +class WCMRAudioConnection; +class WCMRAudioDevice; +class WCMRAudioDeviceManager; + +typedef std::vector<WCMRAudioDevice *> WCMRAudioDeviceList; ///< Vector for audio devices +typedef std::vector<WCMRAudioDevice *>::iterator WCMRAudioDeviceListIter; ///< Vector iterator for audio devices +typedef std::vector<WCMRAudioDevice *>::const_iterator WCMRAudioDeviceListConstIter; ///< Vector iterator for audio devices +typedef std::vector<WCMRAudioConnection *> WCMRAudioConnectionsList; ///< Vector for audio devices + + +/// for notification... A client must derive it's class from us. +class WCMRAudioDeviceManagerClient +{ + public: + enum NotificationReason + { + DeviceListChanged, + Dropout, + RequestReset, + RequestResync, + SamplingRateChanged, //param has new SR, or -1 if not known + SamplingRateChangedSilent, //To indicate sampling rate changed but no need to notify user + BufferSizeChanged, + ClockSourceChanged, + DeviceStoppedStreaming, + DeviceDroppedSamples, + DeviceConnectionLost, + DeviceGenericError, + DeviceStatusChanged, + DeviceStatisticsUpdated, + DeviceDebugInfo, //param has c string + DeviceProgressInfo, //param has c string + MIDIData, + MIDINodeUp, + MIDINodeDown, + DeviceSampleRateMisMatch, + SystemSamplingRateChangedInfoOnly, + LostClockSource, + IODeviceDisconnected, + ChannelCountModified, + MasterUp, + MasterDown, + AudioDropFound, + ReflasherEvent, + AGDeviceSamplingRateChangedInfoOnly, + IODeviceNameChanged, + SetDisplayNameFromIOModule, + IOMStateChanged, ///< This is used when IOM state is changed. + AudioCallback // VKamyshniy: param is AudioCallbackDataData* + }; + + WCMRAudioDeviceManagerClient () {} + virtual ~WCMRAudioDeviceManagerClient () {} + + // VKamyshniy: This is a structure to call the client's AudioDeviceManagerNotification + // every AudioCallback time + struct AudioCallbackData + { + const float *acdInputBuffer; + float *acdOutputBuffer; + size_t acdFrames; + uint32_t acdSampleTime; + uint64_t acdCycleStartTimeNanos; + }; + + virtual void AudioDeviceManagerNotification (NotificationReason WCUNUSEDPARAM(reason), void *WCUNUSEDPARAM(pParam)) {} +}; + + +class WCMRAudioDevice : public WCRefManager +{ +public: + + enum ConnectionStates + { + DeviceAvailable, + DeviceDisconnected, + DeviceError + }; + + WCMRAudioDevice (WCMRAudioDeviceManager *pManager);///<Constructor + virtual ~WCMRAudioDevice ();///<Destructor + + virtual const std::string& DeviceName() const;///<Name? + virtual const std::vector<std::string>& InputChannels();///<Current Input Channel List? - note that this may change with change in sampling rate. + virtual const std::vector<std::string>& OutputChannels();///<Current Output Channel List? - note that this may change with change in sampling rate. + + virtual const std::vector<int>& SamplingRates();///<Supported Sampling Rate List? + virtual int CurrentSamplingRate(); ///<Current Sampling rate.? + virtual WTErr SetCurrentSamplingRate(int newRate);///<Change Current Sampling Rate : This is a requset, might not be successful at run time! + + virtual const std::vector<int>& BufferSizes();///<Supported Buffer Size List? - note that this may change with change in sampling rate. + virtual int CurrentBufferSize();///<Current Buffer Size.? - note that this may change with change in sampling rate. + virtual WTErr SetCurrentBufferSize (int newSize);///<Change Current Buffer Size : This is a requset, might not be successful at run time! + + virtual int CurrentBlockSize(); + + virtual ConnectionStates ConnectionStatus();///< Connection Status - device available, gone, disconnected + + virtual bool Active();///<Active status - mainly significant for ASIO, as certain ops can only be performed on active devices! + virtual WTErr SetActive (bool newState);///<Prepare/Activate device. + + virtual bool Streaming();///<Streaming Status? + virtual WTErr SetStreaming (bool newState);///<Start/Stop Streaming - should reconnect connections when streaming starts! + + virtual bool IsProcessActive(); + + virtual WTErr DoIdle();///<Do Idle Processing + + virtual const std::vector<float>& InputLevels();///<Retrieve Input Levels (for VU display)? + + virtual const std::vector<float>& OutputLevels();///<Retrieve Output Levels (for VU display)? + + void GetMonitorInfo (int *pLeftChannel = NULL, int *pRightChannel = NULL, float *pGain = NULL);///<Retrieve current monitor channel pair and gain - optional, will not be available with AG + virtual WTErr SetMonitorChannels (int leftChannel, int rightChannel);///<Set monitor channels. - optional, will not be available with AG + virtual WTErr SetMonitorGain (float newGain);///<Set monitor gain. - optional, will not be available with AG + + virtual WTErr ShowConfigPanel (void *pParam);///< Show Control Panel - in case of ASIO this will work only with Active device! + virtual WTErr SendCustomCommand (int customCommand, void *pCommandParam); ///< Send a custom command to the audiodevice... + + virtual uint32_t GetLatency (bool isInput); ///Get latency. + +protected: + WCMRAudioDeviceManager *m_pMyManager; ///< The manager who's managing this device, can be used for sending notifications! + + std::string m_DeviceName; ///< Name of the device. + std::vector<std::string> m_InputChannels; ///< List of input channel names. + std::vector<std::string> m_OutputChannels; ///< List of output channel names. + std::vector<int> m_SamplingRates; ///< List of available sampling rates. + std::vector<int> m_BufferSizes; ///< List of available buffer sizes. + + int m_CurrentSamplingRate; ///< Currently selected sampling rate. + int m_CurrentBufferSize; ///< Currently selected buffer size. + + ConnectionStates m_ConnectionStatus; ///< Status of device connection + bool m_IsActive; ///< Flag for teh active status. + bool m_IsStreaming; ///< Flag for streaming status. + std::vector<float> m_InputLevels; ///< List of input levels. + std::vector<float> m_OutputLevels; ///< List of output levels. + + int m_LeftMonitorChannel; ///< The device channel to use for monitoring left channel data. + int m_RightMonitorChannel; ///< The device channel to use for monitoring right channel data. + float m_MonitorGain; ///< Amount of gain to apply for monitoring signal. +}; + +// This enum is for choosing filter for audio devices scan +typedef enum eAudioDeviceFilter +{ + eAllDevices = 0, // Choose all audio devices + eInputOnlyDevices, // Choose only input audio devices + eOutputOnlyDevices, // Choose only output audio devices + eFullDuplexDevices, // Choose audio devices that have both input and output channels on the same device + eMatchedDuplexDevices, // Match(aggregate) audio devices that have both input and output channels but are considered different audio devices (For mac) + eAudioDeviceFilterNum // Number of enums +} eAudioDeviceFilter; + +//! WCMRAudioDeviceManager +/*! The Audio Device Manager class */ +class WCMRAudioDeviceManager : public WCRefManager +{ +private://< Private version of class functions which will be called by class's public function after mutex lock acquistion. + WCMRAudioDevice* GetDefaultDevice_Private(); + WTErr DoIdle_Private(); + const WCMRAudioDeviceList& Devices_Private() const; + WCMRAudioDevice* GetDeviceByName_Private(const std::string & nameToMatch) const; + +public://< Public functions for the class. + WCMRAudioDevice* GetDefaultDevice() + { + //wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + return GetDefaultDevice_Private(); + } + + virtual WTErr DoIdle() + { + //wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + return DoIdle_Private(); + } + + const WCMRAudioDeviceList& Devices() const + { + //wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + return Devices_Private(); + } + + WCMRAudioDevice* GetDeviceByName(const std::string & nameToMatch) const + { + //wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + return GetDeviceByName_Private(nameToMatch); + } + +public: + + WCMRAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient, eAudioDeviceFilter eCurAudioDeviceFilter + ); ///< constructor + virtual ~WCMRAudioDeviceManager(void); ///< Destructor + + virtual WTErr UpdateDeviceList () = 0; //has to be overridden! + + + //This is primarily for use by WCMRAudioDevice and it's descendants... We could have made it + //protected and made WCMRAudioDevice a friend, and then in some way found a way to extend + //the friendship to WCMRAudioDevice's descendants, but that would require a lot of extra + //effort! + void NotifyClient (WCMRAudioDeviceManagerClient::NotificationReason forReason, void *pParam = NULL); + virtual void EnableVerboseLogging(bool /*bEnable*/, const std::string& /*logFilePath*/) { }; + +protected: + + //< NOTE : Mutex protection is commented, but wrapper classes are still there, in case they are required in future. + //wvNS::wvThread::ThreadMutex m_AudioDeviceManagerMutex; ///< Mutex for Audio device manager class function access. + WCMRAudioDeviceManagerClient *m_pTheClient; ///< The device manager's client, used to send notifications. + + WCMRAudioDeviceList m_Devices; ///< List of all relevant devices devices + eAudioDeviceFilter m_eAudioDeviceFilter; // filter of 'm_Devices' +}; + +#endif //#ifndef __WCMRAudioDeviceManager_h_ diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp new file mode 100644 index 0000000000..b66d2519ca --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp @@ -0,0 +1,2717 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +//---------------------------------------------------------------------------------- +// +// +//! \file WCMRCoreAudioDeviceManager.cpp +//! +//! WCMRCoreAudioDeviceManager and related class declarations +//! +//---------------------------------------------------------------------------------*/ +#include "WCMRCoreAudioDeviceManager.h" +#include <CoreServices/CoreServices.h> +#include "MiscUtils/safe_delete.h" +#include <sstream> +#include <syslog.h> + +// This flag is turned to 1, but it does not work with aggregated devices. +// due to problems with aggregated devices this flag is not functional there +#define ENABLE_DEVICE_CHANGE_LISTNER 1 + +#define PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS 10 +#define PROPERTY_CHANGE_TIMEOUT_SECONDS 5 +#define USE_IOCYCLE_TIMES 1 ///< Set this to 0 to use individual thread cpu measurement + +using namespace wvNS; +///< Supported Sample rates +static const double gAllSampleRates[] = +{ + 44100.0, 48000.0, 88200.0, 96000.0, -1 /* negative terminated list */ +}; + + +///< Default Supported Buffer Sizes. +static const int gAllBufferSizes[] = +{ + 32, 64, 96, 128, 192, 256, 512, 1024, 2048, -1 /* negative terminated list */ +}; + + +///< The default SR. +static const int DEFAULT_SR = 44100; +///< The default buffer size. +static const int DEFAULT_BUFFERSIZE = 128; + +///< Number of stalls to wait before notifying user... +static const int NUM_STALLS_FOR_NOTIFICATION = 2 * 50; // 2*50 corresponds to 2 * 50 x 42 ms idle timer - about 4 seconds. +static const int CHANGE_CHECK_COUNTER_PERIOD = 100; // 120 corresponds to 120 x 42 ms idle timer - about 4 seconds. + +#define AUHAL_OUTPUT_ELEMENT 0 +#define AUHAL_INPUT_ELEMENT 1 + +#include <sys/sysctl.h> + +static int getProcessorCount() +{ + int count = 1; + size_t size = sizeof(count); + + if (sysctlbyname("hw.ncpu", &count, &size, NULL, 0)) + return 1; + + //if something did not work, let's revert to a safe value... + if (count == 0) + count = 1; + + return count; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::WCMRCoreAudioDevice +// +//! Constructor for the audio device. Opens the PA device and gets information about the device. +//! such as determining supported sampling rates, buffer sizes, and channel counts. +//! +//! \param *pManager : The audio device manager that's managing this device. +//! \param deviceID : The port audio device ID. +//! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true. +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRCoreAudioDevice::WCMRCoreAudioDevice (WCMRCoreAudioDeviceManager *pManager, AudioDeviceID deviceID, bool useMultithreading, bool bNocopy) + : WCMRNativeAudioDevice (pManager, useMultithreading, bNocopy) + , m_SampleCountAtLastIdle (0) + , m_StalledSampleCounter(0) + , m_SampleCounter(0) + , m_BufferSizeChangeRequested (0) + , m_BufferSizeChangeReported (0) + , m_ResetRequested (0) + , m_ResetReported (0) + , m_ResyncRequested (0) + , m_ResyncReported (0) + , m_SRChangeRequested (0) + , m_SRChangeReported (0) + , m_ChangeCheckCounter(0) + , m_IOProcThreadPort (0) + , m_DropsDetected(0) + , m_DropsReported(0) + , m_IgnoreThisDrop(true) + , m_LastCPULog(0) +#if WV_USE_TONE_GEN + , m_pToneData(0) + , m_ToneDataSamples (0) + , m_NextSampleToUse (0) +#endif //WV_USE_TONE_GEN +{ + AUTO_FUNC_DEBUG; + UInt32 propSize = 0; + OSStatus err = kAudioHardwareNoError; + + //Update device info... + m_DeviceID = deviceID; + + m_CurrentSamplingRate = DEFAULT_SR; + m_CurrentBufferSize = DEFAULT_BUFFERSIZE; + m_StopRequested = true; + m_pInputData = NULL; + + m_CPUCount = getProcessorCount(); + m_LastCPULog = wvThread::now() - 10 * wvThread::ktdOneSecond; + + + + /* + @constant kAudioDevicePropertyNominalSampleRate + A Float64 that indicates the current nominal sample rate of the AudioDevice. + */ + Float64 currentNominalRate; + propSize = sizeof (currentNominalRate); + err = kAudioHardwareNoError; + if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate) != kAudioHardwareNoError) + err = AudioDeviceGetProperty(m_DeviceID, 0, 1, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate); + + if (err == kAudioHardwareNoError) + m_CurrentSamplingRate = (int)currentNominalRate; + + /* + @constant kAudioDevicePropertyBufferFrameSize + A UInt32 whose value indicates the number of frames in the IO buffers. + */ + + UInt32 bufferSize; + propSize = sizeof (bufferSize); + err = kAudioHardwareNoError; + if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferSize) != kAudioHardwareNoError) + err = AudioDeviceGetProperty(m_DeviceID, 0, 1, kAudioDevicePropertyBufferFrameSize, &propSize, &bufferSize); + + if (err == kAudioHardwareNoError) + m_CurrentBufferSize = (int)bufferSize; + + + UpdateDeviceInfo(true /*updateSRSupported*/, true /* updateBufferSizes */); + + //should use a valid current SR... + if (m_SamplingRates.size()) + { + //see if the current sr is present in the sr list, if not, use the first one! + std::vector<int>::iterator intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), m_CurrentSamplingRate); + if (intIter == m_SamplingRates.end()) + { + //not found... use the first one + m_CurrentSamplingRate = m_SamplingRates[0]; + } + } + + //should use a valid current buffer size + if (m_BufferSizes.size()) + { + //see if the current sr is present in the buffersize list, if not, use the first one! + std::vector<int>::iterator intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), m_CurrentBufferSize); + if (intIter == m_BufferSizes.end()) + { + //not found... use the first one + m_CurrentBufferSize = m_BufferSizes[0]; + } + } + + //build our input/output level lists + for (unsigned int currentChannel = 0; currentChannel < m_InputChannels.size(); currentChannel++) + { + m_InputLevels.push_back (0.0); + } + + //build our input/output level lists + for (unsigned int currentChannel = 0; currentChannel < m_OutputChannels.size(); currentChannel++) + { + m_OutputLevels.push_back (0.0); + } + +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::~WCMRCoreAudioDevice +// +//! Destructor for the audio device. The base release all the connections that were created, if +//! they have not been already destroyed! Here we simply stop streaming, and close device +//! handles if necessary. +//! +//! \param none +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRCoreAudioDevice::~WCMRCoreAudioDevice () +{ + AUTO_FUNC_DEBUG; + + try + { + //If device is streaming, need to stop it! + if (Streaming()) + { + SetStreaming (false); + } + + //If device is active (meaning stream is open) we need to close it. + if (Active()) + { + SetActive (false); + } + + } + catch (...) + { + //destructors should absorb exceptions, no harm in logging though!! + DEBUG_MSG ("Exception during destructor"); + } + +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::UpdateDeviceInfo +// +//! Updates Device Information about channels, sampling rates, buffer sizes. +//! +//! \param updateSRSupported : Is Sampling Rate support needs to be updated. +//! \param updateBufferSizes : Is buffer size support needs to be updated. +//! +//! \return WTErr. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::UpdateDeviceInfo (bool updateSRSupported, bool updateBufferSizes) +{ + AUTO_FUNC_DEBUG; + + WTErr retVal = eNoErr; + + // Update all devices parts regardless of errors + WTErr errName = UpdateDeviceName(); + WTErr errIn = UpdateDeviceInputs(); + WTErr errOut = UpdateDeviceOutputs(); + WTErr errSR = eNoErr; + WTErr errBS = eNoErr; + + if (updateSRSupported) + { + errSR = UpdateDeviceSampleRates(); + } + + //update SR list... This is done conditionally, because some devices may not like + //changing the SR later on, just to check on things. + if (updateBufferSizes) + { + errBS = UpdateDeviceBufferSizes(); + } + + if(errName != eNoErr || errIn != eNoErr || errOut != eNoErr || errSR != eNoErr || errBS != eNoErr) + { + retVal = eCoreAudioFailed; + } + + return retVal; +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::UpdateDeviceName +// +//! Updates Device name. +//! +//! Use 'kAudioDevicePropertyDeviceName' +//! +//! 1. Get property name size. +//! 2. Get property: name. +//! +//! \return WTErr. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::UpdateDeviceName() +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + + // Initiate name to unknown. + m_DeviceName = "Unknown"; + + //! 1. Get property name size. + err = AudioDeviceGetPropertyInfo(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL); + if (err == kAudioHardwareNoError) + { + //! 2. Get property: name. + char* deviceName = new char[propSize]; + err = AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceName, &propSize, deviceName); + if (err == kAudioHardwareNoError) + { + m_DeviceName = deviceName; + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device name. Device ID: " << m_DeviceID); + } + + delete [] deviceName; + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device name property size. Device ID: " << m_DeviceID); + } + + return retVal; +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::UpdateDeviceInputs +// +//! Updates Device Inputs. +//! +//! Use 'kAudioDevicePropertyStreamConfiguration' +//! This property returns the stream configuration of the device in an +//! AudioBufferList (with the buffer pointers set to NULL) which describes the +//! list of streams and the number of channels in each stream. This corresponds +//! to what will be passed into the IOProc. +//! +//! 1. Get property cannels input size. +//! 2. Get property: cannels input. +//! 3. Update input channels +//! +//! \return WTErr. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::UpdateDeviceInputs() +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + int maxInputChannels = 0; + + // 1. Get property cannels input size. + err = AudioDeviceGetPropertyInfo (m_DeviceID, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL); + if (err == kAudioHardwareNoError) + { + //! 2. Get property: cannels input. + + // Allocate size according to the property size. Note that this is a variable sized struct... + AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize); + + if (pStreamBuffers) + { + memset (pStreamBuffers, 0, propSize); + + // Get the Input channels + err = AudioDeviceGetProperty (m_DeviceID, 0, 1/* Input */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers); + if (err == kAudioHardwareNoError) + { + // Calculate the number of input channels + for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++) + { + maxInputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels; + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Input channels. Device Name: " << m_DeviceName.c_str()); + } + + free (pStreamBuffers); + } + else + { + retVal = eMemOutOfMemory; + DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str()); + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Input channels property size. Device Name: " << m_DeviceName.c_str()); + } + + // Update input channels + m_InputChannels.clear(); + for (int channel = 0; channel < maxInputChannels; channel++) + { + std::stringstream chNameStream; + //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces + chNameStream << "Input " << (channel+1); + m_InputChannels.push_back (chNameStream.str()); + } + + return retVal; +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::UpdateDeviceOutputs +// +//! Updates Device Outputs. +//! +//! Use 'kAudioDevicePropertyStreamConfiguration' +//! This property returns the stream configuration of the device in an +//! AudioBufferList (with the buffer pointers set to NULL) which describes the +//! list of streams and the number of channels in each stream. This corresponds +//! to what will be passed into the IOProc. +//! +//! 1. Get property cannels output size. +//! 2. Get property: cannels output. +//! 3. Update output channels +//! +//! \return Nothing. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::UpdateDeviceOutputs() +{ + AUTO_FUNC_DEBUG; + + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + int maxOutputChannels = 0; + + //! 1. Get property cannels output size. + err = AudioDeviceGetPropertyInfo (m_DeviceID, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, NULL); + if (err == kAudioHardwareNoError) + { + //! 2. Get property: cannels output. + + // Allocate size according to the property size. Note that this is a variable sized struct... + AudioBufferList *pStreamBuffers = (AudioBufferList *)malloc(propSize); + if (pStreamBuffers) + { + memset (pStreamBuffers, 0, propSize); + + // Get the Output channels + err = AudioDeviceGetProperty (m_DeviceID, 0, 0/* Output */, kAudioDevicePropertyStreamConfiguration, &propSize, pStreamBuffers); + if (err == kAudioHardwareNoError) + { + // Calculate the number of output channels + for (UInt32 streamIndex = 0; streamIndex < pStreamBuffers->mNumberBuffers; streamIndex++) + { + maxOutputChannels += pStreamBuffers->mBuffers[streamIndex].mNumberChannels; + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Output channels. Device Name: " << m_DeviceName.c_str()); + } + free (pStreamBuffers); + } + else + { + retVal = eMemOutOfMemory; + DEBUG_MSG("Faild to allocate memory. Device Name: " << m_DeviceName.c_str()); + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Output channels property size. Device Name: " << m_DeviceName.c_str()); + } + + // Update output channels + m_OutputChannels.clear(); + for (int channel = 0; channel < maxOutputChannels; channel++) + { + std::stringstream chNameStream; + //A better implementation would be to retrieve the names from ASIO or CoreAudio interfaces + chNameStream << "Output " << (channel+1); + m_OutputChannels.push_back (chNameStream.str()); + } + + return retVal; +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::UpdateDeviceSampleRates +// +//! Updates Device Sample rates. +//! +//! Use 'kAudioDevicePropertyAvailableNominalSampleRates' +//! +//! 1. Get sample rate property size. +//! 2. Get property: sample rates. +//! 3. Update sample rates +//! +//! \return Nothing. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::UpdateDeviceSampleRates() +{ + AUTO_FUNC_DEBUG; + + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + + m_SamplingRates.clear(); + + //! 1. Get sample rate property size. + err = AudioDeviceGetPropertyInfo(m_DeviceID, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, NULL); + if (err == kAudioHardwareNoError) + { + //! 2. Get property: cannels output. + + // Allocate size accrding to the number of audio values + int numRates = propSize / sizeof(AudioValueRange); + AudioValueRange* supportedRates = new AudioValueRange[numRates]; + + // Get sampling rates from Audio device + err = AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyAvailableNominalSampleRates, &propSize, supportedRates); + if (err == kAudioHardwareNoError) + { + //! 3. Update sample rates + + // now iterate through our standard SRs + for(int ourSR=0; gAllSampleRates[ourSR] > 0; ourSR++) + { + //check to see if our SR is in the supported rates... + for (int deviceSR = 0; deviceSR < numRates; deviceSR++) + { + if ((supportedRates[deviceSR].mMinimum <= gAllSampleRates[ourSR]) && + (supportedRates[deviceSR].mMaximum >= gAllSampleRates[ourSR])) + { + m_SamplingRates.push_back ((int)gAllSampleRates[ourSR]); + break; + } + } + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Sample rates. Device Name: " << m_DeviceName.c_str()); + } + + delete [] supportedRates; + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device Sample rates property size. Device Name: " << m_DeviceName.c_str()); + } + + return retVal; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::UpdateDeviceBufferSizes_Simple +// +// Use kAudioDevicePropertyBufferFrameSizeRange +// +// in case of 'eMatchedDuplexDevices' and a matching device exists return common device name +// in all other cases retur base class function implementation +// +// 1. Get buffer size range +// 2. Run on all ranges and add them to the list +// +// \return error code +// +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::UpdateDeviceBufferSizes () +{ + AUTO_FUNC_DEBUG; + + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + + // Clear buffer sizes + m_BufferSizes.clear(); + + // 1. Get buffer size range + AudioValueRange bufferSizesRange; + propSize = sizeof (AudioValueRange); + err = AudioDeviceGetProperty (m_DeviceID, 0, 0, kAudioDevicePropertyBufferFrameSizeRange, &propSize, &bufferSizesRange); + if(err == kAudioHardwareNoError) + { + // 2. Run on all ranges and add them to the list + for(int bsize=0; gAllBufferSizes[bsize] > 0; bsize++) + { + if ((bufferSizesRange.mMinimum <= gAllBufferSizes[bsize]) && (bufferSizesRange.mMaximum >= gAllBufferSizes[bsize])) + { + m_BufferSizes.push_back (gAllBufferSizes[bsize]); + } + } + + //if we didn't get a single hit, let's simply add the min. and the max... + if (m_BufferSizes.empty()) + { + m_BufferSizes.push_back ((int)bufferSizesRange.mMinimum); + m_BufferSizes.push_back ((int)bufferSizesRange.mMaximum); + } + } + else + { + retVal = eCoreAudioFailed; + DEBUG_MSG("Failed to get device buffer sizes range. Device Name: " << m_DeviceName.c_str()); + } + + return retVal; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::DeviceName +// +//! in case of 'eMatchedDuplexDevices' and a matching device exists return common device name +//! in all other cases retur base class function implementation +//! +//! \param none +//! +//! \return current device name +//! +//********************************************************************************************** +const std::string& WCMRCoreAudioDevice::DeviceName() const +{ + return WCMRAudioDevice::DeviceName(); +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::InputChannels +// +//! return base class function implementation +//! +//! \param none +//! +//! \return base class function implementation +//! +//********************************************************************************************** +const std::vector<std::string>& WCMRCoreAudioDevice::InputChannels() +{ + return WCMRAudioDevice::InputChannels(); +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::OutputChannels +// +//! in case of 'eMatchedDuplexDevices' return matching device output channel if there is one +//! in all other cases retur base class function implementation +//! +//! \param none +//! +//! \return list of output channels of current device +//! +//********************************************************************************************** +const std::vector<std::string>& WCMRCoreAudioDevice::OutputChannels() +{ + return WCMRAudioDevice::OutputChannels(); +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SamplingRates +// +//! in case of 'eMatchedDuplexDevices' and a matching device exists return common sample rate +//! in all other cases retur base class function implementation +//! +//! \param none +//! +//! \return current sample rate +//! +//********************************************************************************************** +const std::vector<int>& WCMRCoreAudioDevice::SamplingRates() +{ + return WCMRAudioDevice::SamplingRates(); +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::CurrentSamplingRate +// +//! The device's current sampling rate. This may be overridden, if the device needs to +//! query the driver for the current rate. +//! +//! \param none +//! +//! \return The device's current sampling rate. -1 on error. +//! +//********************************************************************************************** +int WCMRCoreAudioDevice::CurrentSamplingRate () +{ + AUTO_FUNC_DEBUG; + //ToDo: Perhaps for ASIO devices that are active, we should retrive the SR from the device... + UInt32 propSize = 0; + OSStatus err = kAudioHardwareNoError; + + Float64 currentNominalRate; + propSize = sizeof (currentNominalRate); + err = kAudioHardwareNoError; + if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate) != kAudioHardwareNoError) + err = AudioDeviceGetProperty(m_DeviceID, 0, 1, kAudioDevicePropertyNominalSampleRate, &propSize, ¤tNominalRate); + + if (err == kAudioHardwareNoError) + m_CurrentSamplingRate = (int)currentNominalRate; + else + { + DEBUG_MSG("Unable to get sampling rate!"); + } + + return (m_CurrentSamplingRate); +} + + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetCurrentSamplingRate +// +//! Change the sampling rate to be used by the device. +//! +//! \param newRate : The rate to use (samples per sec). +//! +//! \return eNoErr always. The derived classes may return error codes. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetCurrentSamplingRate (int newRate) +{ + AUTO_FUNC_DEBUG; + std::vector<int>::iterator intIter; + WTErr retVal = eNoErr; + + //changes the status. + int oldRate = CurrentSamplingRate(); + bool oldActive = Active(); + + //no change, nothing to do + if (oldRate == newRate) + goto Exit; + + //see if this is one of our supported rates... + intIter = find(m_SamplingRates.begin(), m_SamplingRates.end(), newRate); + if (intIter == m_SamplingRates.end()) + { + //Can't change, perhaps use an "invalid param" type of error + retVal = eCommandLineParameter; + goto Exit; + } + + if (Streaming()) + { + //Can't change, perhaps use an "in use" type of error + retVal = eGenericErr; + goto Exit; + } + + if (oldActive) + { + //Deactivate it for the change... + SetActive (false); + } + + retVal = SetAndCheckCurrentSamplingRate (newRate); + if(retVal == eNoErr) + { + retVal = UpdateDeviceInfo (false/*updateSRSupported*/, true/*updateBufferSizes*/); + } + + //reactivate it. + if (oldActive) + { + retVal = SetActive (true); + } + +Exit: + + return (retVal); + +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetAndCheckCurrentSamplingRate +// +//! Change the sampling rate to be used by the device. +//! +//! \param newRate : The rate to use (samples per sec). +//! +//! \return eNoErr always. The derived classes may return error codes. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetAndCheckCurrentSamplingRate (int newRate) +{ + AUTO_FUNC_DEBUG; + std::vector<int>::iterator intIter; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + + // 1. Set new sampling rate + Float64 newNominalRate = newRate; + propSize = sizeof (Float64); + err = AudioDeviceSetProperty(m_DeviceID, NULL, 0, 0, kAudioDevicePropertyNominalSampleRate, propSize, &newNominalRate); + + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Changed the Sampling Rate."); + + if (err != kAudioHardwareNoError) + { + retVal = eCoreAudioFailed; + DEBUG_MSG ("Unable to set SR! Device name: " << m_DeviceName.c_str()); + } + else + { + // 2. wait for the SR to actually change... + + // Set total time out time + int tryAgain = ((PROPERTY_CHANGE_TIMEOUT_SECONDS * 1000) / PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS) ; + int actualWait = 0; + Float64 actualSamplingRate = 0.0; + + // Run as ling as time out is not finished + while (tryAgain) + { + // Get current sampling rate + err = AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, &propSize, &actualSamplingRate); + if (err == kAudioHardwareNoError) + { + if (actualSamplingRate == newNominalRate) + { + //success, let's get out! + break; + } + } + else + { + //error reading rate, but let's not complain too much! + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Could not read Sampling Rate for verification."); + DEBUG_MSG ("Unable to get SR. Device name: " << m_DeviceName.c_str()); + } + + // oh well...there's always another millisecond... + wvThread::sleep_milliseconds (PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); + tryAgain--; + actualWait++; + } + + // If sample rate actually changed + if (tryAgain != 0) + { + // Update member with new rate + m_CurrentSamplingRate = newRate; + + char debugMsg[128]; + snprintf (debugMsg, sizeof(debugMsg), "Actual Wait for SR Change was %d milliseconds", actualWait * PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)debugMsg); + } + // If sample rate did not change after time out + else + { + // Update member with last read value + m_CurrentSamplingRate = static_cast<int>(actualSamplingRate); + + char debugMsg[128]; + snprintf (debugMsg, sizeof(debugMsg), "Unable to change SR, even after waiting for %d milliseconds", actualWait * PROPERTY_CHANGE_SLEEP_TIME_MILLISECONDS); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)debugMsg); + } + } + + return (retVal); +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::BufferSizes +// +//! in case of 'eMatchedDuplexDevices' and a matching device exists return common buffer sizes +//! in all other cases retur base class function implementation +//! +//! \param none +//! +//! \return current sample rate +//! +//********************************************************************************************** +const std::vector<int>& WCMRCoreAudioDevice::BufferSizes() +{ + return WCMRAudioDevice::BufferSizes(); +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::CurrentBufferSize +// +//! The device's current buffer size in use. This may be overridden, if the device needs to +//! query the driver for the current size. +//! +//! \param none +//! +//! \return The device's current buffer size. 0 on error. +//! +//********************************************************************************************** +int WCMRCoreAudioDevice::CurrentBufferSize () +{ + AUTO_FUNC_DEBUG; + + return (m_CurrentBufferSize); +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetCurrentBufferSize +// +//! Change the buffer size to be used by the device. This will most likely be overridden, +//! the base class simply updates the member variable. +//! +//! \param newSize : The buffer size to use (in sample-frames) +//! +//! \return eNoErr always. The derived classes may return error codes. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetCurrentBufferSize (int newSize) +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + std::vector<int>::iterator intIter; + + //changes the status. + int oldSize = CurrentBufferSize(); + bool oldActive = Active(); + + //same size, nothing to do. + if (oldSize == newSize) + goto Exit; + + if (Streaming()) + { + //Can't change, perhaps use an "in use" type of error + retVal = eGenericErr; + goto Exit; + } + + if (oldActive) + { + //Deactivate it for the change... + SetActive (false); + } + + // when audio device is inactive it is safe to set a working buffer size according to new buffer size + // if 'newSize' is not a valid buffer size, another valid buffer size will be set + retVal = SetWorkingBufferSize(newSize); + if(retVal != eNoErr) + { + DEBUG_MSG("Unable to set a working buffer size. Device Name: " << DeviceName().c_str()); + goto Exit; + } + + //reactivate it. + if (oldActive) + { + retVal = SetActive (true); + if(retVal != eNoErr) + { + DEBUG_MSG("Unable to activate device. Device Name: " << DeviceName().c_str()); + goto Exit; + } + } + +Exit: + + return (retVal); +} + +WTErr WCMRCoreAudioDevice::SetWorkingBufferSize(int newSize) +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + + // 1. Set new buffer size + err = SetBufferSizesByIO(newSize); + + // If there's no error it means this buffer size is supported + if(err == kAudioHardwareNoError) + { + m_CurrentBufferSize = newSize; + } + // If there was an error it means that this buffer size was not supported + else + { + // In case the new buffer size could not be set, set another working buffer size + + // Run on all buffer sizes: + + // Try setting buffer sizes that are bigger then selected buffer size first, + // Since bigger buffer sizes usually work safer + for(std::vector<int>::const_iterator iter = m_BufferSizes.begin();iter != m_BufferSizes.end();++iter) + { + int nCurBS = *iter; + + if(nCurBS > newSize) + { + // Try setting current buffer size + err = SetBufferSizesByIO(nCurBS); + + // in case buffer size is valid + if(err == kAudioHardwareNoError) + { + // Set current buffer size + m_CurrentBufferSize = nCurBS; + break; + } + } + } + + // If bigger buffer sizes failed, go to smaller buffer sizes + if(err != kAudioHardwareNoError) + { + for(std::vector<int>::const_iterator iter = m_BufferSizes.begin();iter != m_BufferSizes.end();++iter) + { + int nCurBS = *iter; + + if(nCurBS < newSize) + { + // Try setting current buffer size + err = SetBufferSizesByIO(*iter); + + // in case buffer size is valid + if(err == kAudioHardwareNoError) + { + // Set current buffer size + m_CurrentBufferSize = *iter; + break; + } + } + } + } + + // Check if a valid buffer size was found + if(err == kAudioHardwareNoError) + { + // Notify that a different sample rate is set + char debugMsg[256]; + snprintf (debugMsg, sizeof(debugMsg), "Could not set buffer size: %d, Set buffer size to: %d.", newSize, m_CurrentBufferSize); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)debugMsg); + } + // if there was no buffer size that could be set + else + { + // Set the parameter buffer size by default, set a debug message + m_CurrentBufferSize = newSize; + DEBUG_MSG("Unable to set any buffer size. Device Name: " << m_DeviceName.c_str()); + } + } + + return retVal; +} + +OSStatus WCMRCoreAudioDevice::SetBufferSizesByIO(int newSize) +{ + OSStatus err = kAudioHardwareNoError; + + // 1. Set new buffer size + UInt32 bufferSize = (UInt32)newSize; + UInt32 propSize = sizeof (UInt32); + + // Set new buffer size to input + if (!m_InputChannels.empty()) + { + err = AudioDeviceSetProperty(m_DeviceID, NULL, 0, 1, kAudioDevicePropertyBufferFrameSize, propSize, &bufferSize); + } + else + { + err = AudioDeviceSetProperty(m_DeviceID, NULL, 0, 0, kAudioDevicePropertyBufferFrameSize, propSize, &bufferSize); + } + + return err; +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::ConnectionStatus +// +//! Retrieves the device's current connection status. This will most likely be overridden, +//! in case some driver communication is required to query the status. +//! +//! \param none +//! +//! \return A ConnectionStates value. +//! +//********************************************************************************************** +WCMRCoreAudioDevice::ConnectionStates WCMRCoreAudioDevice::ConnectionStatus () +{ + AUTO_FUNC_DEBUG; + //ToDo: May want to do something more to extract the actual status! + return (m_ConnectionStatus); + +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::EnableAudioUnitIO +// +//! Sets up the AUHAL for IO, allowing changes to the devices to be used by the AudioUnit. +//! +//! \param none +//! +//! \return eNoErr on success, an error code on failure. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::EnableAudioUnitIO() +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + + UInt32 enableIO = 1; + if (!m_InputChannels.empty()) + { + /////////////// + //ENABLE IO (INPUT) + //You must enable the Audio Unit (AUHAL) for input + + //Enable input on the AUHAL + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, + AUHAL_INPUT_ELEMENT, + &enableIO, sizeof(enableIO)); + + if (err) + { + DEBUG_MSG("Couldn't Enable IO on input scope of input element, error = " << err); + retVal = eGenericErr; + goto Exit; + } + } + + //disable Output on the AUHAL if there's no output + if (m_OutputChannels.empty()) + enableIO = 0; + else + enableIO = 1; + + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, + AUHAL_OUTPUT_ELEMENT, + &enableIO, sizeof(enableIO)); + + if (err) + { + DEBUG_MSG("Couldn't Enable/Disable IO on output scope of output element, error = " << err); + retVal = eGenericErr; + goto Exit; + } + +Exit: + return retVal; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::EnableListeners +// +//! Sets up listeners to listen for Audio Device property changes, so that app can be notified. +//! +//! \param none +//! +//! \return eNoErr on success, an error code on failure. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::EnableListeners() +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + + //listner for SR change... + err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, + StaticPropertyChangeProc, this); + + if (err) + { + DEBUG_MSG("Couldn't Setup SR Property Listner, error = " << err); + retVal = eGenericErr; + goto Exit; + } + +#if ENABLE_DEVICE_CHANGE_LISTNER + { + //listner for device change... + err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged, + StaticPropertyChangeProc, this); + + if (err) + { + DEBUG_MSG("Couldn't Setup device change Property Listner, error = " << err); + retVal = eGenericErr; + goto Exit; + } + } +#endif //ENABLE_DEVICE_CHANGE_LISTNER + + //listner for dropouts... + err = AudioDeviceAddPropertyListener(m_DeviceID, 0, 0, kAudioDeviceProcessorOverload, + StaticPropertyChangeProc, this); + + if (err) + { + DEBUG_MSG("Couldn't Setup Processor Overload Property Listner, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + +Exit: + return retVal; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::DisableListeners +// +//! Undoes the work done by EnableListeners +//! +//! \param none +//! +//! \return eNoErr on success, an error code on failure. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::DisableListeners() +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + + //listner for SR change... + err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyNominalSampleRate, + StaticPropertyChangeProc); + + if (err) + { + DEBUG_MSG("Couldn't Cleanup SR Property Listner, error = " << err); + //not sure if we need to report this... + } + +#if ENABLE_DEVICE_CHANGE_LISTNER + { + err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDevicePropertyDeviceHasChanged, + StaticPropertyChangeProc); + + if (err) + { + DEBUG_MSG("Couldn't Cleanup device change Property Listner, error = " << err); + //not sure if we need to report this... + } + } +#endif //ENABLE_DEVICE_CHANGE_LISTNER + + err = AudioDeviceRemovePropertyListener(m_DeviceID, 0, 0, kAudioDeviceProcessorOverload, + StaticPropertyChangeProc); + + if (err) + { + DEBUG_MSG("Couldn't Cleanup device change Property Listner, error = " << err); + //not sure if we need to report this... + } + + + return retVal; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::StaticPropertyChangeProc +// +//! The property change function called (as a result of EnableListeners) when device properties change. +//! It calls upon the non-static PropertyChangeProc to do the work. +//! +//! \param inDevice : The audio device in question. +//! \param inChannel : The channel on which the property has change. +//! \param isInput : If the change is for Input. +//! \param inPropertyID : The property that has changed. +//! \param inClientData: What was passed when listener was enabled, in our case teh WCMRCoreAudioDevice object. +//! +//! \return 0 always. +//! +//********************************************************************************************** +OSStatus WCMRCoreAudioDevice::StaticPropertyChangeProc (AudioDeviceID /*inDevice*/, UInt32 /*inChannel*/, Boolean /*isInput*/, + AudioDevicePropertyID inPropertyID, void *inClientData) +{ + if (inClientData) + { + WCMRCoreAudioDevice* pCoreDevice = (WCMRCoreAudioDevice *)inClientData; + pCoreDevice->PropertyChangeProc (inPropertyID); + } + + return 0; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::PropertyChangeProc +// +//! The non-static property change proc. Gets called when properties change. Since this gets called +//! on an arbitrary thread, we simply update the request counters and return. +//! +//! \param none +//! +//! \return nothing. +//! +//********************************************************************************************** +void WCMRCoreAudioDevice::PropertyChangeProc (AudioDevicePropertyID inPropertyID) +{ + switch (inPropertyID) + { + case kAudioDevicePropertyNominalSampleRate: + m_SRChangeRequested++; + break; +#if ENABLE_DEVICE_CHANGE_LISTNER + case kAudioDevicePropertyDeviceHasChanged: + { + m_ResetRequested++; + } + break; +#endif //ENABLE_DEVICE_CHANGE_LISTNER + case kAudioDeviceProcessorOverload: + if (m_IgnoreThisDrop) + m_IgnoreThisDrop = false; //We'll ignore once, just once! + else + m_DropsDetected++; + break; + default: + break; + } +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetupAUHAL +// +//! Sets up the AUHAL AudioUnit for device IO. +//! +//! \param none +//! +//! \return eNoErr on success, an error code on failure. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetupAUHAL() +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + UInt32 propSize = 0; + Component comp; + ComponentDescription desc; + AudioStreamBasicDescription streamFormatToUse, auhalStreamFormat; + + //There are several different types of Audio Units. + //Some audio units serve as Outputs, Mixers, or DSP + //units. See AUComponent.h for listing + desc.componentType = kAudioUnitType_Output; + + //Every Component has a subType, which will give a clearer picture + //of what this components function will be. + desc.componentSubType = kAudioUnitSubType_HALOutput; + + //all Audio Units in AUComponent.h must use + //"kAudioUnitManufacturer_Apple" as the Manufacturer + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + //Finds a component that meets the desc spec's + comp = FindNextComponent(NULL, &desc); + if (comp == NULL) + { + DEBUG_MSG("Couldn't find AUHAL Component"); + retVal = eGenericErr; + goto Exit; + } + + //gains access to the services provided by the component + OpenAComponent(comp, &m_AUHALAudioUnit); + + + retVal = EnableAudioUnitIO(); + if (retVal != eNoErr) + goto Exit; + + //Now setup the device to use by the audio unit... + + //input + if (!m_InputChannels.empty()) + { + err = AudioUnitSetProperty(m_AUHALAudioUnit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT, + &m_DeviceID, sizeof(m_DeviceID)); + + if (err) + { + DEBUG_MSG("Couldn't Set the audio device property for Input Element Global scope, error = " << err); + retVal = eGenericErr; + goto Exit; + } + } + + //output + if (!m_OutputChannels.empty()) + { + err = AudioUnitSetProperty(m_AUHALAudioUnit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT, + &m_DeviceID, sizeof(m_DeviceID)); + + if (err) + { + DEBUG_MSG("Couldn't Set the audio device property for Output Element Global scope, error = " << err); + retVal = eGenericErr; + goto Exit; + } + } + + //also set Sample Rate... + { + retVal = SetAndCheckCurrentSamplingRate(m_CurrentSamplingRate); + if(retVal != eNoErr) + { + DEBUG_MSG ("Unable to set SR, error = " << err); + goto Exit; + } + } + + //now set the buffer size... + { + err = SetWorkingBufferSize(m_CurrentBufferSize); + if (err) + { + DEBUG_MSG("Couldn't Set the buffer size property, error = " << err); + //we don't really quit here..., just keep going even if this does not work, + //the AUHAL is supposed to take care of this by way of slicing... + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Could not set buffer size."); + + } + } + + //convertor quality + { + UInt32 quality = kAudioConverterQuality_Max; + propSize = sizeof (quality); + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_RenderQuality, kAudioUnitScope_Global, + AUHAL_OUTPUT_ELEMENT, + &quality, sizeof (quality)); + + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to set Convertor Quality, error = " << err); + retVal = eGenericErr; + goto Exit; + } + } + + memset (&auhalStreamFormat, 0, sizeof (auhalStreamFormat)); + propSize = sizeof (auhalStreamFormat); + err = AudioUnitGetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + AUHAL_INPUT_ELEMENT, + &auhalStreamFormat, &propSize); + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to get Input format, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + if (auhalStreamFormat.mSampleRate != (Float64)m_CurrentSamplingRate) + { + TRACE_MSG ("AUHAL's Input SR differs from expected SR, expected = " << m_CurrentSamplingRate << ", AUHAL's = " << (UInt32)auhalStreamFormat.mSampleRate); + } + + //format, and slice size... + memset (&streamFormatToUse, 0, sizeof (streamFormatToUse)); + streamFormatToUse.mFormatID = kAudioFormatLinearPCM; + streamFormatToUse.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; + streamFormatToUse.mFramesPerPacket = 1; + streamFormatToUse.mBitsPerChannel = sizeof (float) * 8; + streamFormatToUse.mSampleRate = auhalStreamFormat.mSampleRate; + + if (!m_InputChannels.empty()) + { + streamFormatToUse.mChannelsPerFrame = m_InputChannels.size(); + streamFormatToUse.mBytesPerFrame = sizeof (float)*streamFormatToUse.mChannelsPerFrame; + streamFormatToUse.mBytesPerPacket = streamFormatToUse.mBytesPerFrame; + propSize = sizeof (streamFormatToUse); + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, + AUHAL_INPUT_ELEMENT, + &streamFormatToUse, sizeof (streamFormatToUse)); + + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to set Input format, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + UInt32 bufferSize = m_CurrentBufferSize; + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Output, + AUHAL_INPUT_ELEMENT, + &bufferSize, sizeof (bufferSize)); + + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to set Input frames, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + } + + if (!m_OutputChannels.empty()) + { + err = AudioUnitGetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, + AUHAL_OUTPUT_ELEMENT, + &auhalStreamFormat, &propSize); + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to get Output format, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + if (auhalStreamFormat.mSampleRate != (Float64)m_CurrentSamplingRate) + { + TRACE_MSG ("AUHAL's Output SR differs from expected SR, expected = " << m_CurrentSamplingRate << ", AUHAL's = " << (UInt32)auhalStreamFormat.mSampleRate); + } + + + streamFormatToUse.mChannelsPerFrame = m_OutputChannels.size(); + streamFormatToUse.mBytesPerFrame = sizeof (float)*streamFormatToUse.mChannelsPerFrame; + streamFormatToUse.mBytesPerPacket = streamFormatToUse.mBytesPerFrame; + streamFormatToUse.mSampleRate = auhalStreamFormat.mSampleRate; + propSize = sizeof (streamFormatToUse); + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + AUHAL_OUTPUT_ELEMENT, + &streamFormatToUse, sizeof (streamFormatToUse)); + + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to set Output format, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + UInt32 bufferSize = m_CurrentBufferSize; + err = AudioUnitSetProperty(m_AUHALAudioUnit, + kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Input, + AUHAL_OUTPUT_ELEMENT, + &bufferSize, sizeof (bufferSize)); + + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to set Output frames, error = " << err); + retVal = eGenericErr; + goto Exit; + } + + } + + //setup callback (IOProc) + { + AURenderCallbackStruct renderCallback; + memset (&renderCallback, 0, sizeof (renderCallback)); + propSize = sizeof (renderCallback); + renderCallback.inputProc = StaticAudioIOProc; + renderCallback.inputProcRefCon = this; + + err = AudioUnitSetProperty(m_AUHALAudioUnit, + (m_OutputChannels.empty() ? (AudioUnitPropertyID)kAudioOutputUnitProperty_SetInputCallback : (AudioUnitPropertyID)kAudioUnitProperty_SetRenderCallback), + kAudioUnitScope_Output, + m_OutputChannels.empty() ? AUHAL_INPUT_ELEMENT : AUHAL_OUTPUT_ELEMENT, + &renderCallback, sizeof (renderCallback)); + + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to set callback, error = " << err); + retVal = eGenericErr; + goto Exit; + } + } + + retVal = EnableListeners(); + if (retVal != eNoErr) + goto Exit; + + //also prepare the buffer list for input... + if (!m_InputChannels.empty()) + { + + //now setup the buffer list. + memset (&m_InputAudioBufferList, 0, sizeof (m_InputAudioBufferList)); + m_InputAudioBufferList.mNumberBuffers = 1; + m_InputAudioBufferList.mBuffers[0].mNumberChannels = m_InputChannels.size(); + m_InputAudioBufferList.mBuffers[0].mDataByteSize = m_InputAudioBufferList.mBuffers[0].mNumberChannels * + m_CurrentBufferSize * sizeof(float); + //allocate the data buffer... + try + { + m_pInputData = new float[m_InputAudioBufferList.mBuffers[0].mNumberChannels * m_CurrentBufferSize]; + } + catch (...) + { + retVal = eMemNewFailed; + goto Exit; + } + + m_InputAudioBufferList.mBuffers[0].mData = m_pInputData; + + //zero it out... + memset (m_InputAudioBufferList.mBuffers[0].mData, 0, m_InputAudioBufferList.mBuffers[0].mDataByteSize); + + } + + //initialize the audio-unit now! + err = AudioUnitInitialize(m_AUHALAudioUnit); + if (err != kAudioHardwareNoError) + { + DEBUG_MSG ("Unable to Initialize AudioUnit = " << err); + retVal = eGenericErr; + goto Exit; + } + +Exit: + if (retVal != eNoErr) + TearDownAUHAL(); + + return retVal; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::TearDownAUHAL +// +//! Undoes the work done by SetupAUHAL +//! +//! \param none +//! +//! \return eNoErr on success, an error code on failure. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::TearDownAUHAL() +{ + WTErr retVal = eNoErr; + + if (m_AUHALAudioUnit) + { + DisableListeners (); + AudioUnitUninitialize(m_AUHALAudioUnit); + CloseComponent(m_AUHALAudioUnit); + m_AUHALAudioUnit = NULL; + } + + safe_delete_array(m_pInputData); + + return retVal; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetActive +// +//! Sets the device's activation status. Essentially, opens or closes the PA device. +//! If it's an ASIO device it may result in buffer size change in some cases. +//! +//! \param newState : Should be true to activate, false to deactivate. This roughly corresponds +//! to opening and closing the device handle/stream/audio unit. +//! +//! \return eNoErr on success, an error code otherwise. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetActive (bool newState) +{ + AUTO_FUNC_DEBUG; + + WTErr retVal = eNoErr; + + if (Active() == newState) + goto Exit; + + + if (newState) + { + + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Setting up AUHAL."); + retVal = SetupAUHAL(); + + if (retVal != eNoErr) + goto Exit; + + m_BufferSizeChangeRequested = 0; + m_BufferSizeChangeReported = 0; + m_ResetRequested = 0; + m_ResetReported = 0; + m_ResyncRequested = 0; + m_ResyncReported = 0; + m_SRChangeRequested = 0; + m_SRChangeReported = 0; + m_DropsDetected = 0; + m_DropsReported = 0; + m_IgnoreThisDrop = true; + } + else + { + if (Streaming()) + { + SetStreaming (false); + } + + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Tearing down AUHAL."); + retVal = TearDownAUHAL(); + if (retVal != eNoErr) + goto Exit; + + m_BufferSizeChangeRequested = 0; + m_BufferSizeChangeReported = 0; + m_ResetRequested = 0; + m_ResetReported = 0; + m_ResyncRequested = 0; + m_ResyncReported = 0; + m_SRChangeRequested = 0; + m_SRChangeReported = 0; + m_DropsDetected = 0; + m_DropsReported = 0; + m_IgnoreThisDrop = true; + + UpdateDeviceInfo(true /*updateSRSupported */, true /* updateBufferSizes#*/); + + } + + m_IsActive = newState; + +Exit: + return (retVal); +} + + +#if WV_USE_TONE_GEN +//********************************************************************************************** +// WCMRCoreAudioDevice::SetupToneGenerator +// +//! Sets up the Tone generator - only if a file /tmp/tonegen.txt is present. If the file is +//! present, it reads the value in the file and uses that as the frequency for the tone. This +//! code attempts to create an array of samples that would constitute an integral number of +//! cycles - for the currently active sampling rate. If tonegen is active, then the input +//! from the audio device is ignored, instead a data is supplied from the tone generator's +//! array - for all channels. The array is in m_pToneData, the size of the array is in +//! m_ToneDataSamples, and m_NextSampleToUse holds the index in the array from where +//! the next sample is going to be taken. +//! +//! +//! \return : Nothing +//! +//********************************************************************************************** +void WCMRCoreAudioDevice::SetupToneGenerator () +{ + safe_delete_array(m_pToneData); + m_ToneDataSamples = 0; + + //if tonegen exists? + FILE *toneGenHandle = fopen ("/tmp/tonegen.txt", "r"); + if (toneGenHandle) + { + int toneFreq = 0; + fscanf(toneGenHandle, "%d", &toneFreq); + if ((toneFreq <= 0) || (toneFreq > (m_CurrentSamplingRate/2))) + { + toneFreq = 1000; + } + + + m_ToneDataSamples = m_CurrentSamplingRate / toneFreq; + int toneDataSamplesFrac = m_CurrentSamplingRate % m_ToneDataSamples; + int powerOfTen = 1; + while (toneDataSamplesFrac) + { + m_ToneDataSamples = (uint32_t)((pow(10, powerOfTen) * m_CurrentSamplingRate) / toneFreq); + toneDataSamplesFrac = m_CurrentSamplingRate % m_ToneDataSamples; + powerOfTen++; + } + + //allocate + m_pToneData = new float_t[m_ToneDataSamples]; + + //fill with a -6dB Sine Tone + uint32_t numSamplesLeft = m_ToneDataSamples; + float_t *pNextSample = m_pToneData; + double phase = 0; + double phaseIncrement = (M_PI * 2.0 * toneFreq ) / ((double)m_CurrentSamplingRate); + while (numSamplesLeft) + { + *pNextSample = (float_t)(0.5 * sin(phase)); + phase += phaseIncrement; + pNextSample++; + numSamplesLeft--; + } + + m_NextSampleToUse = 0; + + fclose(toneGenHandle); + } +} +#endif //WV_USE_TONE_GEN + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetStreaming +// +//! Sets the device's streaming status. Calls PA's Start/Stop stream routines. +//! +//! \param newState : Should be true to start streaming, false to stop streaming. This roughly +//! corresponds to calling Start/Stop on the lower level interface. +//! +//! \return eNoErr always, the derived classes may return appropriate error code. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetStreaming (bool newState) +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + ComponentResult err = 0; + + if (Streaming () == newState) + goto Exit; + + if (newState) + { +#if WV_USE_TONE_GEN + SetupToneGenerator (); +#endif //WV_USE_TONE_GEN + + m_StopRequested = false; + m_SampleCountAtLastIdle = 0; + m_StalledSampleCounter = 0; + m_SampleCounter = 0; + m_IOProcThreadPort = 0; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Starting AUHAL."); + + if (m_UseMultithreading) + { + //set thread constraints... + unsigned int periodAndConstraintUS = (unsigned int)((1000000.0 * m_CurrentBufferSize) / m_CurrentSamplingRate); + unsigned int computationUS = (unsigned int)(0.8 * periodAndConstraintUS); //assuming we may want to use up to 80% CPU + //ErrandManager().SetRealTimeConstraintsForAllThreads (periodAndConstraintUS, computationUS, periodAndConstraintUS); + } + + err = AudioOutputUnitStart (m_AUHALAudioUnit); + + if(err) + { + DEBUG_MSG( "Failed to start AudioUnit, err " << err ); + retVal = eGenericErr; + goto Exit; + } + } + else + { + m_StopRequested = true; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDebugInfo, (void *)"Stopping AUHAL."); + err = AudioOutputUnitStop (m_AUHALAudioUnit); + if (!err) + { + if (!m_InputChannels.empty()); + { + err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_INPUT_ELEMENT); + } + if (!m_OutputChannels.empty()); + { + err = AudioUnitReset (m_AUHALAudioUnit, kAudioUnitScope_Global, AUHAL_OUTPUT_ELEMENT); + } + } + + if(err) + { + DEBUG_MSG( "Failed to stop AudioUnit " << err ); + retVal = eGenericErr; + goto Exit; + } + m_IOProcThreadPort = 0; + } + + // After units restart, reset request for reset and SR change + m_SRChangeReported = m_SRChangeRequested; + m_ResetReported = m_ResetRequested; + + m_IsStreaming = newState; + +Exit: + return (retVal); +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::DoIdle +// +//! A place for doing idle time processing. The other derived classes will probably do something +//! meaningful. +//! +//! \param none +//! +//! \return eNoErr always. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::DoIdle () +{ + if (m_BufferSizeChangeRequested != m_BufferSizeChangeReported) + { + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::BufferSizeChanged); + m_BufferSizeChangeReported = m_BufferSizeChangeRequested; + } + + if (m_ResetRequested != m_ResetReported) + { + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestReset); + m_ResetReported = m_ResetRequested; + } + + + if (m_ResyncRequested != m_ResyncReported) + { + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::RequestResync); + m_ResyncReported = m_ResyncRequested; + } + + if (m_SRChangeReported != m_SRChangeRequested) + { + m_SRChangeReported = m_SRChangeRequested; + int newSR = CurrentSamplingRate(); + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::SamplingRateChanged, (void *)newSR); + } + + if (m_DropsReported != m_DropsDetected) + { + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceDroppedSamples); + m_DropsReported = m_DropsDetected; + } + + + //Perhaps add checks to make sure a stream counter is incrementing if + //stream is supposed to be streaming! + if (Streaming()) + { + //latch the value + int64_t currentSampleCount = m_SampleCounter; + if (m_SampleCountAtLastIdle == currentSampleCount) + m_StalledSampleCounter++; + else + { + m_SampleCountAtLastIdle = (int)currentSampleCount; + m_StalledSampleCounter = 0; + } + + if (m_StalledSampleCounter > NUM_STALLS_FOR_NOTIFICATION) + { + m_StalledSampleCounter = 0; + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::DeviceStoppedStreaming, (void *)currentSampleCount); + } + } + + + return (eNoErr); +} + + + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetMonitorChannels +// +//! Used to set the channels to be used for monitoring. +//! +//! \param leftChannel : Left monitor channel index. +//! \param rightChannel : Right monitor channel index. +//! +//! \return eNoErr always, the derived classes may return appropriate errors. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetMonitorChannels (int leftChannel, int rightChannel) +{ + AUTO_FUNC_DEBUG; + //This will most likely be overridden, the base class simply + //changes the member. + m_LeftMonitorChannel = leftChannel; + m_RightMonitorChannel = rightChannel; + return (eNoErr); +} + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::SetMonitorGain +// +//! Used to set monitor gain (or atten). +//! +//! \param newGain : The new gain or atten. value to use. Specified as a linear multiplier (not dB) +//! +//! \return eNoErr always, the derived classes may return appropriate errors. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::SetMonitorGain (float newGain) +{ + AUTO_FUNC_DEBUG; + //This will most likely be overridden, the base class simply + //changes the member. + + + m_MonitorGain = newGain; + return (eNoErr); +} + + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::ShowConfigPanel +// +//! Used to show device specific config/control panel. Some interfaces may not support it. +//! Some interfaces may require the device to be active before it can display a panel. +//! +//! \param pParam : A device/interface specific parameter, should be the app window handle for ASIO. +//! +//! \return eNoErr always, the derived classes may return errors. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDevice::ShowConfigPanel (void */*pParam*/) +{ + AUTO_FUNC_DEBUG; + WTErr retVal = eNoErr; + + CFStringRef configAP; + UInt32 propSize = sizeof (configAP); + /* + @constant kAudioDevicePropertyConfigurationApplication + A CFString that contains the bundle ID for an application that provides a + GUI for configuring the AudioDevice. By default, the value of this property + is the bundle ID for Audio MIDI Setup. The caller is responsible for + releasing the returned CFObject. + */ + + if (AudioDeviceGetProperty(m_DeviceID, 0, 0, kAudioDevicePropertyConfigurationApplication, &propSize, &configAP) == kAudioHardwareNoError) + { + // get the FSRef of the config app + FSRef theAppFSRef; + OSStatus theError = LSFindApplicationForInfo(kLSUnknownCreator, configAP, NULL, &theAppFSRef, NULL); + if (!theError) + { + LSOpenFSRef(&theAppFSRef, NULL); + } + CFRelease (configAP); + } + + return (retVal); +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::StaticAudioIOProc +// +//! The AudioIOProc that gets called when the AudioUnit is ready with recorded audio, and wants to get audio. +//! This one simply calls the non-static member. +//! +//! \param inRefCon : What was passed when setting up the Callback (in our case a pointer to teh WCMRCoreAudioDevice object). +//! \param ioActionFlags : What actios has to be taken. +//! \param inTimeStamp: When the data will be played back. +//! \param inBusNumber : The AU element. +//! \param inNumberFrames: Number af Audio frames that are requested. +//! \param ioData : Where the playback data is to be placed. +//! +//! \return 0 always +//! +//********************************************************************************************** +OSStatus WCMRCoreAudioDevice::StaticAudioIOProc(void *inRefCon, AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + WCMRCoreAudioDevice *pMyDevice = (WCMRCoreAudioDevice *)inRefCon; + if (pMyDevice) + return pMyDevice->AudioIOProc (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData); + else + return 0; +} + + + + +//********************************************************************************************** +// WCMRCoreAudioDevice::AudioIOProc +// +//! The non-static AudioIOProc that gets called when the AudioUnit is ready with recorded audio, and wants to get audio. +//! We retrieve the recorded audio, and then do our processing, to generate audio to be played back. +//! +//! \param ioActionFlags : What actios has to be taken. +//! \param inTimeStamp: When the data will be played back. +//! \param inBusNumber : The AU element. +//! \param inNumberFrames: Number af Audio frames that are requested. +//! \param ioData : Where the playback data is to be placed. +//! +//! \return 0 always +//! +//********************************************************************************************** +OSStatus WCMRCoreAudioDevice::AudioIOProc(AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 /*inBusNumber*/, UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + UInt64 theStartTime = AudioGetCurrentHostTime(); + + OSStatus retVal = 0; + + if (m_StopRequested) + goto Exit; + + if (m_IOProcThreadPort == 0) + m_IOProcThreadPort = mach_thread_self (); + + //cannot really deal with it unless the number of frames are the same as our buffer size! + if (inNumberFrames != (UInt32)m_CurrentBufferSize) + goto Exit; + + //Retrieve the input data... + if (!m_InputChannels.empty()) + { + retVal = AudioUnitRender(m_AUHALAudioUnit, ioActionFlags, inTimeStamp, AUHAL_INPUT_ELEMENT, inNumberFrames, &m_InputAudioBufferList); + } + + //is this an input only device? + if (m_OutputChannels.empty()) + AudioCallback (NULL, inNumberFrames, (uint32_t)inTimeStamp->mSampleTime, theStartTime); + else if ((!m_OutputChannels.empty()) && (ioData->mBuffers[0].mNumberChannels == m_OutputChannels.size())) + AudioCallback ((float *)ioData->mBuffers[0].mData, inNumberFrames, (uint32_t)inTimeStamp->mSampleTime, theStartTime); + +Exit: + return retVal; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::AudioCallback +// +//! Here's where the actual audio processing happens. We call upon all the active connections' +//! sinks to provide data to us which can be put/mixed in the output buffer! Also, we make the +//! input data available to any sources that may call upon us during this time! +//! +//! \param *pOutputBuffer : Points to a buffer to receive playback data. For Input only devices, this will be NULL +//! \param framesPerBuffer : Number of sample frames in input and output buffers. Number of channels, +//! which are interleaved, is fixed at Device Open (Active) time. In this implementation, +//! the number of channels are fixed to use the maximum available. +//! +//! \return true +//! +//********************************************************************************************** +int WCMRCoreAudioDevice::AudioCallback (float *pOutputBuffer, unsigned long framesPerBuffer, uint32_t inSampleTime, uint64_t inCycleStartTime) +{ + struct WCMRAudioDeviceManagerClient::AudioCallbackData audioCallbackData = + { + m_pInputData, + pOutputBuffer, + framesPerBuffer, + inSampleTime, + AudioConvertHostTimeToNanos(inCycleStartTime) + }; + + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::AudioCallback, (void *)&audioCallbackData); + + m_SampleCounter += framesPerBuffer; + return m_StopRequested; +} + + +//********************************************************************************************** +// WCMRCoreAudioDevice::GetLatency +// +//! Get Latency for device. +//! +//! Use 'kAudioDevicePropertyLatency' and 'kAudioDevicePropertySafetyOffset' + GetStreamLatencies +//! +//! \param isInput : Return latency for the input if isInput is true, otherwise the output latency +//! wiil be returned. +//! \return Latency in samples. +//! +//********************************************************************************************** +uint32_t WCMRCoreAudioDevice::GetLatency(bool isInput) +{ + WTErr retVal = eNoErr; + OSStatus err = kAudioHardwareNoError; + + UInt32 propSize = sizeof(UInt32); + UInt32 value1 = 0; + UInt32 value2 = 0; + + UInt32 latency = 0; + std::vector<int> streamLatencies; + + + err = AudioDeviceGetProperty(m_DeviceID, 0, isInput, kAudioDevicePropertyLatency, &propSize, &value1); + if (err != kAudioHardwareNoError) + { + DEBUG_MSG("GetLatency kAudioDevicePropertyLatency err = " << err); + } + + err = AudioDeviceGetProperty(m_DeviceID, 0, isInput, kAudioDevicePropertySafetyOffset, &propSize, &value2); + if (err != kAudioHardwareNoError) + { + DEBUG_MSG("GetLatency kAudioDevicePropertySafetyOffset err = " << err); + } + + latency = value1 + value2; + + err = GetStreamLatency(m_DeviceID, isInput, streamLatencies); + if (err == kAudioHardwareNoError) + { + for ( int i = 0; i < streamLatencies.size(); i++) { + latency += streamLatencies[i]; + } + } + + return latency; +} + +//********************************************************************************************** +// WCMRCoreAudioDevice::GetStreamLatency +// +//! Get stream latency for device. +//! +//! \param deviceID : The audio device ID. +//! +//! \param isInput : Return latency for the input if isInput is true, otherwise the output latency +//! wiil be returned. +//********************************************************************************************** +OSStatus WCMRCoreAudioDevice::GetStreamLatency(AudioDeviceID device, bool isInput, std::vector<int>& latencies) +{ + OSStatus err = kAudioHardwareNoError; + UInt32 outSize1, outSize2, outSize3; + Boolean outWritable; + + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, &outWritable); + if (err == noErr) { + int stream_count = outSize1 / sizeof(UInt32); + AudioStreamID streamIDs[stream_count]; + AudioBufferList bufferList[stream_count]; + UInt32 streamLatency; + outSize2 = sizeof(UInt32); + + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreams, &outSize1, streamIDs); + if (err != noErr) { + DEBUG_MSG("GetStreamLatencies kAudioDevicePropertyStreams err = " << err); + return err; + } + + err = AudioDeviceGetPropertyInfo(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, &outWritable); + if (err != noErr) { + DEBUG_MSG("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = " << err); + return err; + } + + for (int i = 0; i < stream_count; i++) { + err = AudioStreamGetProperty(streamIDs[i], 0, kAudioStreamPropertyLatency, &outSize2, &streamLatency); + if (err != noErr) { + DEBUG_MSG("GetStreamLatencies kAudioStreamPropertyLatency err = " << err); + return err; + } + err = AudioDeviceGetProperty(device, 0, isInput, kAudioDevicePropertyStreamConfiguration, &outSize3, bufferList); + if (err != noErr) { + DEBUG_MSG("GetStreamLatencies kAudioDevicePropertyStreamConfiguration err = " << err); + return err; + } + latencies.push_back(streamLatency); + } + } + return err; +} + + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager +// +//! The constructuor, we initialize PA, and build the device list. +//! +//! \param *pTheClient : The manager's client object (which receives notifications). +//! \param useMultithreading : Whether to use multi-threading for audio processing. Default is true. +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRCoreAudioDeviceManager::WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient, eAudioDeviceFilter eCurAudioDeviceFilter + , bool useMultithreading, eCABS_Method eCABS_method, bool bNocopy) + : WCMRAudioDeviceManager (pTheClient, eCurAudioDeviceFilter + ) + , m_UpdateDeviceListRequested(0) + , m_UpdateDeviceListProcessed(0) + , m_UseMultithreading (useMultithreading) + , m_eCABS_Method(eCABS_method) + , m_bNoCopyAudioBuffer(bNocopy) +{ + AUTO_FUNC_DEBUG; + + //first of all, tell HAL to use it's own run loop, not to wait for our runloop to do + //it's dirty work... + //Essentially, this makes the HAL on Snow Leopard behave like Leopard. + //It's not yet (as of October 2009 documented), but the following discussion + //has the information provided by Jeff Moore @ Apple: + // http://lists.apple.com/archives/coreaudio-api/2009/Oct/msg00214.html + // + // As per Jeff's suggestion, opened an Apple Bug on this - ID# 7364011 + + CFRunLoopRef nullRunLoop = 0; + OSStatus err = AudioHardwareSetProperty (kAudioHardwarePropertyRunLoop, sizeof(CFRunLoopRef), &nullRunLoop); + + if (err != kAudioHardwareNoError) + { + syslog (LOG_NOTICE, "Unable to set RunLoop for Audio Hardware"); + } + + //add a listener to find out when devices change... + AudioHardwareAddPropertyListener (kAudioHardwarePropertyDevices, StaticPropertyChangeProc, this); + + //Always add the None device first... + m_Devices.push_back (new WCMRNativeAudioNoneDevice(this)); + + //prepare our initial list... + UpdateDeviceList_Private(); + + return; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::~WCMRCoreAudioDeviceManager +// +//! It clears the device list, releasing each of the device. +//! +//! \param none +//! +//! \return Nothing. +//! +//********************************************************************************************** +WCMRCoreAudioDeviceManager::~WCMRCoreAudioDeviceManager() +{ + AUTO_FUNC_DEBUG; + + try + { + AudioHardwareRemovePropertyListener (kAudioHardwarePropertyDevices, StaticPropertyChangeProc); + + //Note: We purposely release the device list here, instead of + //depending on the superclass to do it, as by the time the superclass' + //destructor executes, we will have called Pa_Terminate()! + + //Need to call release on our devices, and erase them from list + std::vector<WCMRAudioDevice*>::iterator deviceIter; + while (m_Devices.size()) + { + WCMRAudioDevice *pDeviceToRelease = m_Devices.back(); + m_Devices.pop_back(); + + SAFE_RELEASE (pDeviceToRelease); + } + + + //The derived classes may want to do additional de-int! + + } + catch (...) + { + //destructors should absorb exceptions, no harm in logging though!! + DEBUG_MSG ("Exception during destructor"); + } + +} + + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::StaticPropertyChangeProc +// +//! The property change listener for the Audio Device Manager. It calls upon (non-static) PropertyChangeProc +//! to do the actual work. +//! +//! \param iPropertyID : the property that has changed. +//! \param inClientData : What was supplied at init time. +//! +//! \return if parameters are incorrect, or the value returned by PropertyChangeProc. +//! +//********************************************************************************************** +OSStatus WCMRCoreAudioDeviceManager::StaticPropertyChangeProc (AudioHardwarePropertyID inPropertyID, void* inClientData) +{ + WCMRCoreAudioDeviceManager *pMyManager = (WCMRCoreAudioDeviceManager *)inClientData; + + if (pMyManager) + return pMyManager->PropertyChangeProc (inPropertyID); + + return 0; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::PropertyChangeProc +// +//! The property change listener for the Audio Device Manager. Currently we only listen for the +//! device list change (device arrival/removal, and accordingly cause an update to the device list. +//! Note that the actual update happens from the DoIdle() call to prevent multi-threading related issues. +//! +//! \param +//! +//! \return Nothing. +//! +//********************************************************************************************** +OSStatus WCMRCoreAudioDeviceManager::PropertyChangeProc (AudioHardwarePropertyID inPropertyID) +{ + OSStatus retVal = 0; + switch (inPropertyID) + { + case kAudioHardwarePropertyDevices: + m_UpdateDeviceListRequested++; + break; + default: + break; + } + + return retVal; +} + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::remove_pattern +// +//! remove a substring from a given string +//! +//! \param original_str - original string +//! \param pattern_str - pattern to find +//! \param return_str - the return string - without the pattern substring +//! +//! \return Nothing. +//! +//********************************************************************************************** +void WCMRCoreAudioDeviceManager::remove_pattern(const std::string& original_str, const std::string& pattern_str, std::string& return_str) +{ + char *orig_c_str = new char[original_str.size() + 1]; + char* strSavePtr; + strcpy(orig_c_str, original_str.c_str()); + char *p_splited_orig_str = strtok_r(orig_c_str," ", &strSavePtr); + + std::ostringstream stream_str; + while (p_splited_orig_str != 0) + { + int cmp_res = strcmp(p_splited_orig_str, pattern_str.c_str()); // might need Ignore case ( stricmp OR strcasecmp) + if ( cmp_res != 0) + stream_str << p_splited_orig_str << " "; + p_splited_orig_str = strtok_r(NULL," ", &strSavePtr); + } + delete[] orig_c_str; + return_str = stream_str.str(); +} + + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::UpdateDeviceList_Private +// +//! Updates the list of devices maintained by the manager. If devices have gone away, they are removed +//! if new devices have been connected, they are added to the list. +//! +//! \param none +//! +//! \return eNoErr on success, an error code on failure. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDeviceManager::UpdateDeviceList_Private() +{ + AUTO_FUNC_DEBUG; + + + WTErr retVal = eNoErr; + OSStatus osErr = noErr; + AudioDeviceID* deviceIDs = 0; + size_t reportedDeviceIndex = 0; + + openlog("WCMRCoreAudioDeviceManager", LOG_PID | LOG_CONS, LOG_USER); + + try + { + + // Define 2 vectors for input and output - only for eMatchedDuplexDevices case + WCMRAudioDeviceList adOnlyIn; + WCMRAudioDeviceList adOnlyOut; + + //Get device count... + UInt32 propSize = 0; + osErr = AudioHardwareGetPropertyInfo (kAudioHardwarePropertyDevices, &propSize, NULL); + ASSERT_ERROR(osErr, "AudioHardwareGetProperty 1"); + if (WUIsError(osErr)) + throw osErr; + + size_t numDevices = propSize / sizeof (AudioDeviceID); + deviceIDs = new AudioDeviceID[numDevices]; + + //retrieve the device IDs + propSize = numDevices * sizeof (AudioDeviceID); + osErr = AudioHardwareGetProperty (kAudioHardwarePropertyDevices, &propSize, deviceIDs); + ASSERT_ERROR(osErr, "Error while getting audio devices: AudioHardwareGetProperty 2"); + if (WUIsError(osErr)) + throw osErr; + + //first go through our list of devices, remove the ones that are no longer present... + std::vector<WCMRAudioDevice*>::iterator deviceIter; + for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); /*This is purposefully blank*/) + { + WCMRCoreAudioDevice *pDeviceToWorkUpon = dynamic_cast<WCMRCoreAudioDevice *>(*deviceIter); + + //it's possible that the device is actually not a core audio device - perhaps a none device... + if (!pDeviceToWorkUpon) + { + deviceIter++; + continue; + } + + AudioDeviceID myDeviceID = pDeviceToWorkUpon->DeviceID(); + bool deviceFound = false; + for (reportedDeviceIndex = 0; reportedDeviceIndex < numDevices; reportedDeviceIndex++) + { + if (myDeviceID == deviceIDs[reportedDeviceIndex]) + { + deviceFound = true; + break; + } + } + + if (!deviceFound) + { + //it's no longer there, need to remove it! + WCMRAudioDevice *pTheDeviceToErase = *deviceIter; + deviceIter = m_Devices.erase (deviceIter); + if (pTheDeviceToErase->Active()) + { + NotifyClient (WCMRAudioDeviceManagerClient::DeviceConnectionLost); + } + SAFE_RELEASE (pTheDeviceToErase); + } + else + deviceIter++; + } + + //now add the ones that are not there... + for (reportedDeviceIndex = 0; reportedDeviceIndex < numDevices; reportedDeviceIndex++) + { + bool deviceFound = false; + for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); deviceIter++) + { + WCMRCoreAudioDevice *pDeviceToWorkUpon = dynamic_cast<WCMRCoreAudioDevice *>(*deviceIter); + //it's possible that the device is actually not a core audio device - perhaps a none device... + if (!pDeviceToWorkUpon) + continue; + + if (pDeviceToWorkUpon->DeviceID() == deviceIDs[reportedDeviceIndex]) + { + deviceFound = true; + break; + } + } + + if (!deviceFound) + { + //add it to our list... + //build a device object... + WCMRCoreAudioDevice *pNewDevice = new WCMRCoreAudioDevice (this, deviceIDs[reportedDeviceIndex], m_UseMultithreading, m_bNoCopyAudioBuffer); + bool bDeleteNewDevice = true; + + if (pNewDevice) + { + + // Don't delete the new device by default, since most cases use it + bDeleteNewDevice = false; + + // Insert the new device to the device list according to its enum + switch(m_eAudioDeviceFilter) + { + case eInputOnlyDevices: + if ((int) pNewDevice->InputChannels().size() != 0) + { + m_Devices.push_back (pNewDevice); + } + else + { + // Delete unnecesarry device + bDeleteNewDevice = true; + } + break; + case eOutputOnlyDevices: + if ((int) pNewDevice->OutputChannels().size() != 0) + { + m_Devices.push_back (pNewDevice); + } + else + { + // Delete unnecesarry device + bDeleteNewDevice = true; + } + break; + case eFullDuplexDevices: + if ((int) pNewDevice->InputChannels().size() != 0 && (int) pNewDevice->OutputChannels().size() != 0) + { + m_Devices.push_back (pNewDevice); + } + else + { + // Delete unnecesarry device + bDeleteNewDevice = true; + } + break; + case eAllDevices: + default: + m_Devices.push_back (pNewDevice); + break; + } + } + + if(bDeleteNewDevice) + { + syslog (LOG_NOTICE, "%s rejected, In Channels = %d, Out Channels = %d\n", + pNewDevice->DeviceName().c_str(), (int) pNewDevice->InputChannels().size(), + (int) pNewDevice->OutputChannels().size()); + // In case of Input and Output both channels being Zero, we will release memory; since we created CoreAudioDevice but we are Not adding it in list. + SAFE_RELEASE(pNewDevice); + } + } + } + + + //If no devices were found, that's not a good thing! + if (m_Devices.empty()) + { + DEBUG_MSG ("No matching CoreAudio devices were found\n"); + } + + + m_UpdateDeviceListRequested = m_UpdateDeviceListProcessed = 0; + + } + catch (...) + { + if (WUNoError(retVal)) + retVal = eCoreAudioFailed; + } + + safe_delete_array(deviceIDs); + closelog(); + + return retVal; +} + + + +//********************************************************************************************** +// WCMRCoreAudioDeviceManager::DoIdle +// +//! Used for idle time processing. This calls each device's DoIdle so that it can perform it's own idle processing. +//! Also, if a device list change is detected, it updates the device list. +//! +//! \param none +//! +//! \return noErr if no devices have returned an error. An error code if any of the devices returned error. +//! +//********************************************************************************************** +WTErr WCMRCoreAudioDeviceManager::DoIdle() +{ + //WTErr retVal = eNoErr; + + { + //wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + + //If there's something specific to CoreAudio manager idle handling do it here... + if (m_UpdateDeviceListRequested != m_UpdateDeviceListProcessed) + { + m_UpdateDeviceListProcessed = m_UpdateDeviceListRequested; + UpdateDeviceList_Private(); + NotifyClient (WCMRAudioDeviceManagerClient::DeviceListChanged); + } + } + + //Note that the superclass is going to call all the devices' DoIdle() anyway... + return (WCMRAudioDeviceManager::DoIdle()); +} + diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h new file mode 100644 index 0000000000..96d2a9d70e --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRCoreAudioDeviceManager.h @@ -0,0 +1,220 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +//---------------------------------------------------------------------------------- +// +// +//! \file WCMRCoreAudioDeviceManager.h +//! +//! WCMRCoreAudioDeviceManager and related class declarations +//! +//---------------------------------------------------------------------------------*/ +#ifndef __WCMRCoreAudioDeviceManager_h_ + #define __WCMRCoreAudioDeviceManager_h_ + +#include "WCMRAudioDeviceManager.h" +#include "WCMRNativeAudio.h" +#include "Threads/WCThreadSafe.h" + +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <mach/mach.h> + +#include <CoreAudio/CoreAudio.h> + +//forward decl. +class WCMRCoreAudioDeviceManager; + +#define WV_USE_TONE_GEN 0 ///! Set this to 1 to use a tone generator for input. See description at SetupToneGenerator for details. + +// This enum is for choosing filter for audio devices scan +typedef enum eCABS_Method +{ + eCABS_Simple = 0, + eCABS_DestructiveCache, + eCABS_CacheOnDeviceSet, + eCABS_MethodNum // Must be last +} eCABS_Method; + +//! Manages a port audio device, providing information +//! about the device, and managing audio callbacks. +class WCMRCoreAudioDevice : public WCMRNativeAudioDevice +{ +public: + + WCMRCoreAudioDevice (WCMRCoreAudioDeviceManager *pManager, AudioDeviceID deviceID, bool useMultithreading = true, bool bNocopy = false);///<Constructor + virtual ~WCMRCoreAudioDevice ();///<Destructor + + virtual const std::string& DeviceName() const;///<Name? + virtual const std::vector<std::string>& InputChannels();///<Current Input Channel List? - note that this may change with change in sampling rate. + virtual const std::vector<std::string>& OutputChannels();///<Current Output Channel List? - note that this may change with change in sampling rate. + + + virtual const std::vector<int>& SamplingRates();///<Supported Sampling Rate List? + virtual int CurrentSamplingRate(); ///<Current Sampling rate.? + virtual WTErr SetCurrentSamplingRate(int newRate);///<Change Current Sampling Rate : This is a requset, might not be successful at run time! + + virtual const std::vector<int>& BufferSizes();///<Supported Buffer Size List? - note that this may change with change in sampling rate. + virtual int CurrentBufferSize();///<Current Buffer Size.? - note that this may change with change in sampling rate. + virtual WTErr SetCurrentBufferSize (int newSize);///<Change Current Buffer Size : This is a requset, might not be successful at run time! + + virtual ConnectionStates ConnectionStatus();///< Connection Status - device available, gone, disconnected + + virtual WTErr SetActive (bool newState);///<Prepare/Activate device. + virtual WTErr SetStreaming (bool newState);///<Start/Stop Streaming - should reconnect connections when streaming starts! + + virtual WTErr DoIdle();///<Do Idle Processing + + virtual WTErr SetMonitorChannels (int leftChannel, int rightChannel);///<Set monitor channels. - optional, will not be available with AG + virtual WTErr SetMonitorGain (float newGain);///<Set monitor gain. - optional, will not be available with AG + + virtual WTErr ShowConfigPanel (void *pParam);///< Show Control Panel - in case of ASIO this will work only with Active device! + + virtual int AudioCallback (float *pOutputBuffer, unsigned long framesPerBuffer, uint32_t inSampleTime, uint64_t inCycleStartTime); + + AudioDeviceID DeviceID () {return m_DeviceID;} + + virtual uint32_t GetLatency (bool isInput); ///< Get latency. + virtual OSStatus GetStreamLatency(AudioDeviceID deviceID, bool isInput, std::vector<int>& latencies); + + +protected: + + AudioDeviceID m_DeviceID; ///< The CoreAudio device id + bool m_StopRequested; ///< should be set to true when want to stop, set to false otherwise. + float *m_pInputData; ///< This is what came in with the most recent callback. + int m_SampleCounter; ///< The current running sample counter, updated by the audio callback. + int m_SampleCountAtLastIdle; ///< What was the sample count last time we checked... + int m_StalledSampleCounter; ///< The number of idle calls with same sample count detected + int m_ChangeCheckCounter; ///< The number of idle calls passed since we checked the buffer size change. + + wvNS::wvThread::timestamp m_LastCPULog; ///< The time when the last CPU details log was sent as a notification. +// unsigned int m_IOCyclesTimesTaken[MAX_IOCYCLE_TIMES]; ///< This stores the times taken by each IOCycle, in host-time units. +// int m_CurrentIOCycle; ///< The location in m_IOCyclesTymesTaken array, where the next cycle's value will go. +// int m_CyclesToAccumulate; ///< The number of cycles to accumulate the values for - maximum for last one second. +// unsigned int m_CyclePeriod; ///< The number of host time units for a cycle period - determined by buffer size and sampling rate + + + + AudioBufferList m_InputAudioBufferList; ///< The buffer list used to get AHHAL to render input to. + AudioUnit m_AUHALAudioUnit;///< The AUHAL AudioUnit + + int m_BufferSizeChangeRequested; + int m_BufferSizeChangeReported; + int m_ResetRequested; + int m_ResetReported; + int m_ResyncRequested; + int m_ResyncReported; + int m_SRChangeRequested; + int m_SRChangeReported; + + int m_DropsDetected; ///< Number of times audio drops have been detected so far. + int m_DropsReported; ///< Number of times audio drops have been reported so far to the client. + bool m_IgnoreThisDrop; ///< Allows disregarding the first drop + + thread_t m_IOProcThreadPort; ///< Thread handle to calculate CPU consumption. + int m_CPUCount; ///< Number of processors/core to normalize cpu consumption calculation. + +#if WV_USE_TONE_GEN + //The Tone Generator... + float_t *m_pToneData; + uint32_t m_ToneDataSamples; + uint32_t m_NextSampleToUse; +#endif //WV_USE_TONE_GEN + + WTErr UpdateDeviceInfo (bool updateSRSupported, bool updateBufferSizes); + WTErr UpdateDeviceName(); + WTErr UpdateDeviceInputs(); + WTErr UpdateDeviceOutputs(); + WTErr UpdateDeviceSampleRates(); + WTErr UpdateDeviceBufferSizes(); + WTErr SetWorkingBufferSize(int newSize); + OSStatus SetBufferSizesByIO(int newSize); + WTErr SetAndCheckCurrentSamplingRate (int newRate); + + WTErr EnableAudioUnitIO(); + WTErr virtual EnableListeners(); + WTErr virtual DisableListeners(); + WTErr SetupAUHAL(); + WTErr TearDownAUHAL(); + +#if WV_USE_TONE_GEN + void SetupToneGenerator (); +#endif //WV_USE_TONE_GEN + + static OSStatus StaticAudioIOProc(void *inRefCon, AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + OSStatus AudioIOProc(AudioUnitRenderActionFlags * ioActionFlags, + const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, + AudioBufferList *ioData); + + static OSStatus StaticPropertyChangeProc (AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, + AudioDevicePropertyID inPropertyID, void *inClientData); + void PropertyChangeProc (AudioDevicePropertyID inPropertyID); + +private: + +}; + + +//! WCMRCoreAudioDeviceManager +/*! The CoreAudio Device Manager class */ +class WCMRCoreAudioDeviceManager : public WCMRAudioDeviceManager +{ +public: + + WCMRCoreAudioDeviceManager(WCMRAudioDeviceManagerClient *pTheClient, eAudioDeviceFilter eCurAudioDeviceFilter, + bool useMultithreading = true, eCABS_Method eCABS_method = eCABS_Simple, bool bNocopy = false); ///< constructor + virtual ~WCMRCoreAudioDeviceManager(void); ///< Destructor + + + virtual WTErr UpdateDeviceList() //has to be overridden! + { + //wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + return UpdateDeviceList_Private(); + } + + virtual eCABS_Method GetBufferSizeMethod() + { + //wvNS::wvThread::ThreadMutex::lock theLock(m_AudioDeviceManagerMutex); + return GetBufferSizeMethod_Private(); + } + + virtual WTErr DoIdle(); + +private: + WTErr UpdateDeviceList_Private(); + eCABS_Method GetBufferSizeMethod_Private() { return m_eCABS_Method; } + +protected: + + int m_UpdateDeviceListRequested; ///< Number of times device list change has been detected. + int m_UpdateDeviceListProcessed; ///< Number of times device list change has been processed. + bool m_UseMultithreading; ///< Flag indicates whether to use multi-threading for audio processing. + bool m_bNoCopyAudioBuffer; + eCABS_Method m_eCABS_Method; // Type of core audio buffer size list method + + static OSStatus StaticPropertyChangeProc (AudioHardwarePropertyID inPropertyID, void* inClientData); + OSStatus PropertyChangeProc (AudioHardwarePropertyID inPropertyID); + + void remove_pattern(const std::string& original_str, const std::string& pattern_str, std::string& return_str); +}; + +#endif //#ifndef __WCMRCoreAudioDeviceManager_h_ diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp new file mode 100644 index 0000000000..0cba7ee851 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.cpp @@ -0,0 +1,270 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +//---------------------------------------------------------------------------------- +// +// +//! \file WCMRNativeAudio.cpp +//! +//! WCMRNativeAudioConnection and related class defienitions +//! +//---------------------------------------------------------------------------------*/ +#if defined(__MACOS__) +#include <CoreAudio/CoreAudio.h> +#endif + +#include "WCMRNativeAudio.h" +#include "MiscUtils/safe_delete.h" +#include <sstream> +#include <boost/assign/list_of.hpp> + +#define NONE_DEVICE_NAME "None" +#define NONE_DEVICE_INPUT_NAMES "Input " +#define NONE_DEVICE_OUTPUT_NAMES "Output " + +//********************************************************************************************** +// WCMRNativeAudioNoneDevice::WCMRNativeAudioNoneDevice +// +//! Constructor for the dummy "None" device. This constructor simply adds supported SRs, +//! buffer sizes, and channels, so that it may look like a real native device to +//! the applications. +//! +//! \param pManager : The managing device manager - simply passed on to the base class. +//! +//! +//********************************************************************************************** +WCMRNativeAudioNoneDevice::WCMRNativeAudioNoneDevice (WCMRAudioDeviceManager *pManager) + : WCMRNativeAudioDevice (pManager, false /*useMultiThreading*/) + , m_SilenceThread(0) +#if defined (_WINDOWS) + , _waitableTimerForUsleep (CreateWaitableTimer(NULL, TRUE, NULL)) +#endif +{ + m_DeviceName = NONE_DEVICE_NAME; + + m_SamplingRates = boost::assign::list_of (m_CurrentSamplingRate=44100)(48000)(88200)(96000); + + m_BufferSizes = boost::assign::list_of (32)(64)(128)(m_CurrentBufferSize=256)(512)(1024); + + for (int channel = 0; channel < __m_NumInputChannels; channel++) + { + std::stringstream name; + name << NONE_DEVICE_INPUT_NAMES; + name << (channel + 1); + m_InputChannels.push_back(name.str()); + } + + for (int channel = 0; channel < __m_NumOutputChannels; channel++) + { + std::stringstream name; + name << NONE_DEVICE_INPUT_NAMES; + name << (channel + 1); + m_OutputChannels.push_back(name.str()); + } + _m_inputBuffer = new float[__m_NumInputChannels * m_BufferSizes.back()]; + _m_outputBuffer = new float[__m_NumOutputChannels * m_BufferSizes.back()]; +} + + +WCMRNativeAudioNoneDevice::~WCMRNativeAudioNoneDevice () +{ +#if defined (_WINDOWS) + if(_waitableTimerForUsleep) { + CloseHandle(_waitableTimerForUsleep); + } +#endif +} + +WTErr WCMRNativeAudioNoneDevice::SetActive (bool newState) +{ + //This will most likely be overridden, the base class simply + //changes the member. + if (Active() == newState) + { + return (eNoErr); + } + + if (Active() && Streaming()) + { + SetStreaming(false); + } + return WCMRAudioDevice::SetActive(newState); +} + +WTErr WCMRNativeAudioNoneDevice::SetCurrentBufferSize (int newSize) +{ + + //changes the status. + int oldSize = CurrentBufferSize(); + bool oldActive = Active(); + + //same size, nothing to do. + if (oldSize == newSize) + return eNoErr; + + //see if this is one of our supported rates... + std::vector<int>::iterator intIter = find(m_BufferSizes.begin(), m_BufferSizes.end(), newSize); + if (intIter == m_BufferSizes.end()) + { + //Can't change, perhaps use an "invalid param" type of error + return eCommandLineParameter; + } + + if (Streaming()) + { + //Can't change, perhaps use an "in use" type of error + return eGenericErr; + } + + + return WCMRAudioDevice::SetCurrentBufferSize(newSize); +} + + +WTErr WCMRNativeAudioNoneDevice::SetStreaming (bool newState) +{ + if (Streaming() == newState) + { + return (eNoErr); + } + + WCMRAudioDevice::SetStreaming(newState); + if(Streaming()) + { + if (m_SilenceThread) + std::cerr << "\t\t\t\t\t !!!!!!!!!!!!!!! Warning: the inactive NONE-DEVICE was streaming!" << std::endl; + + pthread_attr_t attributes; + size_t stack_size = 100000; +#ifdef __MACOS__ + stack_size = (((stack_size - 1) / PTHREAD_STACK_MIN) + 1) * PTHREAD_STACK_MIN; +#endif + if (pthread_attr_init (&attributes)) { + std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_attr_init () failed!" << std::endl; + return eGenericErr; + } + + if (pthread_attr_setstacksize (&attributes, stack_size)) { + std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_attr_setstacksize () failed!" << std::endl; + return eGenericErr; + } + + if (pthread_create (&m_SilenceThread, &attributes, __SilenceThread, this)) { + m_SilenceThread = 0; + std::cerr << "WCMRNativeAudioNoneDevice::SetStreaming (): pthread_create () failed!" << std::endl; + return eGenericErr; + } + } + else + { + if (!m_SilenceThread) + { + std::cerr << "\t\t\t\t\t !!!!!!!!!!!!!!! Warning: the active NONE-DEVICE was NOT streaming!" << std::endl; + } + + while (m_SilenceThread) + { + _usleep(1); //now wait for ended thread; + } + } + + return eNoErr; +} + +void WCMRNativeAudioNoneDevice::_SilenceThread() +{ +#if defined(_WINDOWS) + float* theInpBuffers[__m_NumInputChannels]; + for(int i = 0; i < __m_NumInputChannels; ++i) + { + theInpBuffers[i] = _m_inputBuffer + m_BufferSizes.back() * i; + } +#else + float* theInpBuffers = _m_inputBuffer; +#endif + + uint32_t currentSampleTime = 0; + const size_t buffer_size = CurrentBufferSize(); + const uint64_t cyclePeriodNanos = (1000000000.0 * buffer_size) / CurrentSamplingRate(); + + struct WCMRAudioDeviceManagerClient::AudioCallbackData audioCallbackData = + { + (const float*)theInpBuffers, + _m_outputBuffer, + buffer_size, + 0, + 0 + }; + + audioCallbackData.acdCycleStartTimeNanos =__get_time_nanos(); + + // VERY ROUGH IMPLEMENTATION: + while(Streaming()) { + + uint64_t cycleEndTimeNanos = audioCallbackData.acdCycleStartTimeNanos + cyclePeriodNanos; + + m_pMyManager->NotifyClient (WCMRAudioDeviceManagerClient::AudioCallback, (void *)&audioCallbackData); + + currentSampleTime += buffer_size; + + int64_t timeToSleepUsecs = ((int64_t)cycleEndTimeNanos - (int64_t)__get_time_nanos())/1000; + + if (timeToSleepUsecs > 0) { + _usleep (timeToSleepUsecs); + } + audioCallbackData.acdCycleStartTimeNanos = cycleEndTimeNanos+1; + } + m_SilenceThread = 0; +} + +void* WCMRNativeAudioNoneDevice::__SilenceThread(void *This) +{ + ((WCMRNativeAudioNoneDevice*)This)->_SilenceThread(); + return 0; +} + +#if defined(_WINDOWS) +void WCMRNativeAudioNoneDevice::_usleep(uint64_t duration_usec) +{ + LARGE_INTEGER ft; + + ft.QuadPart = -(10*duration_usec); // Convert to 100 nanosecond interval, negative value indicates relative time + + SetWaitableTimer(_waitableTimerForUsleep, &ft, 0, NULL, NULL, 0); + WaitForSingleObject(_waitableTimerForUsleep, INFINITE); +} +#endif + +uint64_t +WCMRNativeAudioNoneDevice::__get_time_nanos () +{ +#ifdef __MACOS__ + // here we exploit the time counting API which is used by the WCMRCoreAudioDeviceManager. However, + // the API should be a part of WCMRCoreAudioDeviceManager to give a chance of being tied to the + // audio device transport timeß. + return AudioConvertHostTimeToNanos (AudioGetCurrentHostTime ()); + +#elif _WINDOWS + + LARGE_INTEGER Frequency, Count ; + + QueryPerformanceFrequency (&Frequency) ; + QueryPerformanceCounter (&Count); + return uint64_t ((Count.QuadPart * 1000000000.0 / Frequency.QuadPart)); +#endif +} diff --git a/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h new file mode 100644 index 0000000000..dc350ff6be --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/devicemanager/WCMRNativeAudio.h @@ -0,0 +1,90 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +//---------------------------------------------------------------------------------- +// +// +//! \file WCMRNativeAudio.h +//! +//! WCMRNativeAudio and related class declarations +//! +//---------------------------------------------------------------------------------*/ +#ifndef __WCMRNativeAudio_h_ + #define __WCMRNativeAudio_h_ + +#if defined(_WINDOWS) +#include "windows.h" +#endif +#include "pthread.h" +#include "WCRefManager.h" +#include "WCMRAudioDeviceManager.h" + +class WCMRNativeAudioDevice; //forward + + + +class WCMRNativeAudioDevice : public WCMRAudioDevice +{ +public: + + WCMRNativeAudioDevice (WCMRAudioDeviceManager *pManager, bool useMultithreading = true, bool bNoCopy = false) : WCMRAudioDevice (pManager), + m_UseMultithreading (useMultithreading), + m_bNoCopyAudioBuffer(bNoCopy) + {} + virtual ~WCMRNativeAudioDevice () {} + +protected: + bool m_UseMultithreading; + bool m_bNoCopyAudioBuffer; ///< This flag determines whether the audio callback performs a copy of audio, or the source/sink perform the copy. It should be true to let source/sink do the copies. + + +}; + + +//! A dummy device to allow apps to choose "None" in case no real device connection is required. +class WCMRNativeAudioNoneDevice : public WCMRNativeAudioDevice +{ +public: + WCMRNativeAudioNoneDevice (WCMRAudioDeviceManager *pManager); + virtual ~WCMRNativeAudioNoneDevice (); + virtual WTErr SetActive (bool newState);///<Prepare/Activate device. + virtual WTErr SetStreaming (bool newState);///<Start/Stop Streaming - should reconnect connections when streaming starts! + virtual WTErr SetCurrentBufferSize (int newSize);///<Change Current Buffer Size : This is a requset, might not be successful at run time! + +private: + + static void* __SilenceThread(void *This); + void _SilenceThread(); +#if defined(_WINDOWS) + void _usleep(uint64_t usec); +#else + inline void _usleep(uint64_t usec) { ::usleep(usec); } +#endif + static const size_t __m_NumInputChannels = 32; + static const size_t __m_NumOutputChannels = 32; + pthread_t m_SilenceThread; + float *_m_inputBuffer;
+ float *_m_outputBuffer;
+ static uint64_t __get_time_nanos (); +#if defined (_WINDOWS) + HANDLE _waitableTimerForUsleep; +#endif +}; + + +#endif //#ifndef __WCMRNativeAudio_h_ diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/MinMaxUtilities.h b/libs/backends/wavesaudio/wavesapi/miscutils/MinMaxUtilities.h new file mode 100644 index 0000000000..cd48169bf2 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/miscutils/MinMaxUtilities.h @@ -0,0 +1,122 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __MinMaxUtilities_h__ +#define __MinMaxUtilities_h__ + +/* copy to include +#include "MiscUtils/MinMaxUtilities.h" +*/ + +#include "BasicTypes/WUDefines.h" +#include "BasicTypes/WUMathConsts.h" +#include "WavesPublicAPI/wstdint.h" + +// New accelerated templates +#if defined ( __cplusplus ) && !defined (__WUMinMax) +#define __WUMinMax // Also defined in Nativepr.h + + +template<class T> inline T WUMin(const T &a, const T &b) {return (a < b) ? a : b;} // requires only < to be defined for T +template<class T> inline T WUMax(const T &a,const T &b) {return (a < b) ? b : a;} // requires only < to be defined for T +template<class T> inline T WUMinMax(const T &Smallest, const T &Biggest, const T &Val) // requires only < to be defined for T +{ + return ((Val < Smallest) ? Smallest : ((Biggest < Val) ? Biggest : Val)); +} +/* +// Min and Max + template<class T> inline T WUMin(T a,T b) {return (a < b) ? a : b;} // requires only < to be defined for T + template<class T> inline T WUMax(T a,T b) {return (a < b) ? b : a;} // requires only < to be defined for T + template<class T> inline T WUMinMax(T SMALLEST, T BIGGEST, T X) // requires only < to be defined for T + { + return ((X < SMALLEST) ? SMALLEST : ((BIGGEST < X) ? BIGGEST : X)); + } + */ +// Absolute value +#ifdef _WINDOWS + #include <math.h> +#define __abs(x) abs(x) +#define __labs(x) labs(x) +#define __fabs(x) fabs(x) +#endif +#ifdef __GNUC__ + #include <iostream> // why don't know makes it work need to check + #include <cstdlib> + #include <cmath> + +#define __abs(x) std::abs(x) +#define __labs(x) std::labs(x) +#define __fabs(x) std::fabs(x) +#endif + #ifdef __MACOS__ + #ifdef __GNUC__ + #include <iostream> // why don't know makes it work need to check + #include <cmath> +#define __abs(x) std::abs(x) +#define __labs(x) std::labs(x) +#define __fabs(x) std::fabs(x) + #endif + #endif + +// log2: on Windows there's no proper definition for log2, whereas on other platform there is. + #ifndef WUlog2 + #if defined(_WINDOWS) + #define WUlog2(x) (kdOneOverLog2 * log10((x))) + #else + #define WUlog2(x) log2(x) + #endif + #endif + +template <class T> inline T WUAbs(const T &xA) +{ + return (xA > T(0))? xA: -xA; +} + +template <> inline int WUAbs(const int &xA) +{ + return __abs(xA); +} + +//template <> inline int32_t WUAbs(const int32_t &xA)// 64BitConversion +//{ +// return __labs(xA); +//} + +template <> inline float WUAbs(const float &xA) +{ + return (float) __fabs(xA); +} + +template <> inline double WUAbs(const double &xA) +{ + return __fabs(xA); +} + +#endif + +int32_t DllExport WURand(intptr_t in_Seed); +int32_t DllExport WURand(); +int32_t DllExport rand_gen_formula(int32_t rndSeed); + +template <class T> inline bool WUIsEqualWithTolerance(const T &xA, const T &xB, const T &xTolerance) +{ + return (WUAbs(xA - xB) < xTolerance) ? true : false; +} + + +#endif diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.cpp b/libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.cpp new file mode 100644 index 0000000000..e3de715508 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.cpp @@ -0,0 +1,87 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifdef _WINDOWS
+ #include "IncludeWindows.h"
+#endif
+#if defined(__linux__) || defined(__MACOS__)
+ #include <sys/time.h>
+#endif
+
+#include "UMicroseconds.h"
+
+namespace wvNS {
+UMicroseconds& UMicroseconds::ReadTime()
+{
+#ifdef _WINDOWS
+ LARGE_INTEGER Frequency, Count ;
+
+ QueryPerformanceFrequency(&Frequency) ;
+ QueryPerformanceCounter(&Count);
+ theTime = uint64_t((Count.QuadPart * 1000000.0 / Frequency.QuadPart));
+#endif
+
+#if defined(__linux__) || defined(__MACOS__)
+// Mac code replaced by posix calls, to reduce Carbon dependency.
+ timeval buf;
+
+ gettimeofday(&buf,NULL);
+
+ // micro sec
+ theTime = uint64_t(buf.tv_sec) * 1000*1000 + buf.tv_usec;
+#endif
+
+ return *this;
+}
+/*
+ Removed in favor of the posix implementation.
+#ifdef __MACOS__
+ uint32_t UMicroseconds::hi() {return reinterpret_cast<UnsignedWide*>(&theTime)->hi;}
+ uint32_t UMicroseconds::lo() {return reinterpret_cast<UnsignedWide*>(&theTime)->lo;}
+#endif
+*/
+void UMicrosecondsAccumulator::Start()
+{
+ m_start_time.ReadTime();
+}
+
+void UMicrosecondsAccumulator::Stop()
+{
+ UMicroseconds stop_time;
+
+ m_accumulator += stop_time.GetNativeTime() - m_start_time.GetNativeTime();
+}
+
+void UMicrosecondsAccumulator::Clear()
+{
+ m_start_time = 0;
+ m_accumulator = 0;
+}
+
+UMicroseconds UMicrosecondsAccumulator::GetAccumulatedTime() const
+{
+ return m_accumulator;
+}
+
+UMicrosecondsAccumulator& UMicrosecondsAccumulator::operator+=(const UMicrosecondsAccumulator& inaccum_to_add)
+{
+ m_accumulator += inaccum_to_add.GetAccumulatedTime();
+ return *this;
+}
+
+} // namespace wvNS {
diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.h b/libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.h new file mode 100644 index 0000000000..64d1f8824d --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/miscutils/UMicroseconds.h @@ -0,0 +1,123 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __UMicroseconds_h__
+ #define __UMicroseconds_h__
+
+/* Copy to include
+#include "UMicroseconds.h"
+*/
+
+
+
+#include "BasicTypes/WUDefines.h"
+#include "BasicTypes/WUTypes.h"
+
+namespace wvNS {
+// a wraper for Microseconds function from Timer.h
+class DllExport UMicroseconds
+{
+public:
+
+#ifdef _WINDOWS
+ typedef int64_t TimeKeeper;
+#endif
+#ifdef __MACOS__
+ typedef uint64_t TimeKeeper;
+#endif
+#ifdef __linux__
+ typedef uint64_t TimeKeeper;
+#endif
+
+private:
+ TimeKeeper theTime;
+
+public:
+
+ UMicroseconds()
+ {
+ ReadTime();
+ }
+
+ UMicroseconds(const TimeKeeper in_initVal) : theTime(in_initVal) {}
+
+ UMicroseconds(const UMicroseconds& inUM) : theTime(inUM.theTime) {}
+ UMicroseconds& operator=(const UMicroseconds& inUM) {theTime = inUM.theTime; return *this;}
+ UMicroseconds& operator+=(const TimeKeeper in_timeToAdd) {theTime += in_timeToAdd; return *this;}
+
+ UMicroseconds& ReadTime();
+
+ TimeKeeper GetNativeTime() const {return theTime;}
+ operator uint64_t () {return static_cast<uint64_t>(theTime);}
+ operator double () const {return static_cast<const double>(theTime);}
+
+ double Seconds() const {return static_cast<double>(theTime) / double(1000000);}
+ double MilliSeconds() const {return static_cast<double>(theTime) / double(1000);}
+ double MicroSeconds() const {return static_cast<double>(theTime);}
+
+#ifdef __MACOS__
+ uint32_t hi();
+ uint32_t lo();
+#endif
+};
+
+inline UMicroseconds operator-(const UMicroseconds& in_one, const UMicroseconds& in_two)
+{
+ UMicroseconds retVal(in_one.GetNativeTime() - in_two.GetNativeTime());
+ return retVal;
+}
+
+class UMicrosecondsAccumulator
+{
+public:
+ UMicrosecondsAccumulator() : m_start_time(0), m_accumulator(0) {}
+
+ void Start();
+ void Stop();
+ void Clear();
+
+ UMicroseconds GetAccumulatedTime() const;
+
+ UMicrosecondsAccumulator& operator+=(const UMicrosecondsAccumulator&);
+
+protected:
+ UMicroseconds m_start_time;
+ UMicroseconds m_accumulator;
+};
+
+inline UMicroseconds operator-(const UMicrosecondsAccumulator& in_one, const UMicrosecondsAccumulator& in_two)
+{
+ UMicroseconds retVal(in_one.GetAccumulatedTime() - in_two.GetAccumulatedTime());
+ return retVal;
+}
+
+//=========================================================================================//
+inline void MicrosecondDelay(double amt)
+//=========================================================================================//
+{
+ UMicroseconds than;
+ UMicroseconds now;
+
+ do
+ {
+ now.ReadTime();
+ } while ((now.MicroSeconds() - than.MicroSeconds()) < amt);
+}
+
+} // namespace wvNS {
+#endif //#ifndef __UMicroseconds_h__
diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/WCFixedString.h b/libs/backends/wavesaudio/wavesapi/miscutils/WCFixedString.h new file mode 100644 index 0000000000..4c7264b1d5 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/miscutils/WCFixedString.h @@ -0,0 +1,903 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WCFixedString_h__ + #define __WCFixedString_h__ + +/* Copy to include. +#include "WCFixedString.h" +*/ +// do not #include anything else here but standard C++ library files, this file should be free from any and all depandencies +// do not put any DEBUG_s or TRACE_s in this file, since it is used in BgkConsole functions + +#include <algorithm> +#include <cctype> +#include <cstring> +#include <cstdio> + +#ifdef __MACOS__ +#include <strings.h> +#endif + +#include "BasicTypes/WUDefines.h" +#include "BasicTypes/WTByteOrder.h" +#include "WavesPublicAPI/wstdint.h" +#include "MiscUtils/MinMaxUtilities.h" + +// use this macro instead of std :: string to mark the that use of std :: string could not be replaced +// by WFixedString. +#define std_string_approved std::string + +#ifdef __POSIX__ +const char* const kStrNewLine = "\n"; +#endif +#ifdef _WINDOWS +const char* const kStrNewLine = "\r\n"; +#endif + +class DllExport WCFixedStringBase +{ +public: + typedef size_t pos_t; + typedef intptr_t spos_t; // signed position, defined to intptr_t because Windows does not have ssize_t + static const pos_t npos = UINTPTR_MAX; // Same as size_max + + WCFixedStringBase(char* const in_begin, const size_t in_MaxFixedStringLength) : + m_begin(in_begin), + m_MaxFixedStringLength(in_MaxFixedStringLength), + m_end(in_begin) + { + *m_end = '\0'; + } + + inline WCFixedStringBase& operator=(const WCFixedStringBase& in_fixedStrToAssign) + { + if (this != &in_fixedStrToAssign) + { + clear(); + operator<<(in_fixedStrToAssign); + } + + return *this; + } + + inline WCFixedStringBase& operator=(const char* in_CStrToAssign) + { + clear(); + operator<<(in_CStrToAssign); + + return *this; + } + + inline WCFixedStringBase& operator=(const char in_charToAssign) + { + clear(); + operator<<(in_charToAssign); + + return *this; + } + + char operator[](const pos_t in_index) const + { + if (in_index < m_MaxFixedStringLength) + return m_begin[in_index]; + else + return m_begin[m_MaxFixedStringLength]; // in_index was too big + } + + char& operator[](const pos_t in_index) + { + if (in_index < m_MaxFixedStringLength) + return m_begin[in_index]; + else + return m_begin[m_MaxFixedStringLength]; // in_index was too big + } + + inline size_t resize(const size_t in_newSize) + { + m_end = m_begin + WUMin<size_t>(in_newSize, m_MaxFixedStringLength); + *m_end = '\0'; + return size(); + } + + size_t max_size() + { + return m_MaxFixedStringLength; + } + + size_t capacity() + { + return m_MaxFixedStringLength; + } + + + inline char * peek() + { + return m_begin; + } + + inline const char * c_str() const + { + *m_end = '\0'; + return m_begin; + } + + inline void clear() + { + m_end = m_begin; + *m_end = '\0'; + } + + inline size_t size() const + { + return m_end - m_begin; + } + + inline char* begin() const + { + return m_begin; + } + + inline char* end() const + { + return m_end; + } + + inline size_t length() const + { + return size(); + } + + inline bool empty() const + { + return m_begin == m_end; + } + + inline void reverse(char* in_left, char* in_right) + { + char* left = in_left; + char* right = in_right; + while (left < right) + { + char temp = *--right; + *right = *left; + *left++ = temp; + } + } + + inline void reverse() + { + reverse(m_begin, m_end); + } + + inline void to_lower() + { + char* pToDo = m_begin; + + while (pToDo < m_end) + { + *pToDo = static_cast<char>(std::tolower(*pToDo)); + ++pToDo; + } + } + + inline void to_upper() + { + char* pToDo = m_begin; + + while (pToDo < m_end) + { + *pToDo = static_cast<char>(std::toupper(*pToDo)); + ++pToDo; + } + } + + // append a single char in_count times + inline void append(const char in_charToAppend, const size_t in_count) + { + size_t counter = 0; + while ((m_end < m_begin+m_MaxFixedStringLength) && counter++ < in_count) + *m_end++ = in_charToAppend; +#if kEnableDebug == 1 + if (counter < in_count) // if there wasn't enough room for some appended chars + { + m_begin[0] = '@'; // mark the string as overflowed + } +#endif + *m_end = '\0'; + } + + inline void append(const char* in_chars) + { + operator<<(in_chars); + } + + // append "iterator style" + inline void append(const char* in_chars_begin, const char* in_chars_end) + { + const char* curr_char = in_chars_begin; + while ((m_end < m_begin+m_MaxFixedStringLength) && curr_char < in_chars_end && *curr_char != '\0') + *m_end++ = *curr_char++; + +#if kEnableDebug == 1 + if (curr_char < in_chars_end) // if there wasn't enough room for some appended chars + { + m_begin[0] = '@'; // mark the string as overflowed + } +#endif + *m_end = '\0'; + } + + // append from a char* in_count chars, (no \0 is required to terminate the input string) + inline void append(const char* in_chars_begin, const size_t in_count) + { + append(in_chars_begin, in_chars_begin + in_count); + } + + // assign from a char* in_count chars, (no \0 is required to terminate the input string) + inline void assign(const char* in_chars_begin, const size_t in_count) + { + clear(); + append(in_chars_begin, in_chars_begin + in_count); + } + + // assign from a char* , (a \0 is required to terminate the input string) + inline void assign(const char* in_chars_ptr) + { + clear(); + operator<<(in_chars_ptr); + } + + // assign from a char* to a char* + inline void assign(const char* in_begin, const char* in_end) + { + assign(in_begin, size_t(in_end - in_begin)); + } + + inline void append_double_with_precision(const double in_double, const int in_precision) + { + const unsigned int tempBufSize = 32; + char buf[tempBufSize]; + + #ifdef _WINDOWS + _snprintf_s(buf, tempBufSize, tempBufSize - 1, "%.*f", in_precision, in_double); + #endif + #ifdef __MACOS__ + std::snprintf(buf, tempBufSize, "%.*f", in_precision, in_double); + #endif + #ifdef __linux__ + snprintf(buf, tempBufSize, "%.*f", in_precision, in_double); + #endif + + operator<<(buf); + } + + inline void append_uint(const uint64_t in_uint, const int_fast16_t in_base = 10) + { + uint_fast64_t num = in_uint; + + char* lasr_char_before = m_end; + + do { + char remainder(static_cast<char>(num % in_base)); + + if ( remainder < 10 ) + operator<<(char(remainder + '0')); + else + operator<<(char(remainder - 10 + 'A')); + + num /= in_base; + } while (num != 0); + + reverse(lasr_char_before, m_end); + } + + inline void append_hex_binary(const uint8_t* in_binary, const size_t in_size) + { + static const char hexdigits[] = "0123456789ABCDEF"; + +#if _BYTEORDER_BIG_ENDIAN==1 + for (size_t ibyte = 0; ibyte < in_size; ++ibyte) +#elif _BYTEORDER_BIG_ENDIAN==0 + for (size_t ibyte = in_size; ibyte > 0; --ibyte) +#endif + { + operator<<(hexdigits[in_binary[ibyte - 1] >> 4]); + operator<<(hexdigits[in_binary[ibyte - 1] & 0x0F]); + } + } + + inline WCFixedStringBase& operator<<(const char in_charToAppend) + { + if (m_end < m_begin+m_MaxFixedStringLength) + *m_end++ = in_charToAppend; +#if kEnableDebug == 1 + else // if there wasn't enough room for the appended char + { + m_begin[0] = '@'; // mark the string as overflowed + } +#endif + + *m_end = '\0'; + + return *this; + } + + inline WCFixedStringBase& operator<<(const char* const in_strToAppend) + { + if (0 != in_strToAppend) + { + const char* pSource = in_strToAppend; + + while (*pSource != '\0' && m_end < m_begin+m_MaxFixedStringLength) + *m_end++ = *pSource++; + +#if kEnableDebug == 1 + if (*pSource != '\0') // if there wasn't enough room for some appended chars + { + m_begin[0] = '@'; // mark the string as overflowed + } +#endif + *m_end = '\0'; + } + + return *this; + } + + WCFixedStringBase& operator<<(const uint64_t in_uint) + { + append_uint(in_uint, 10); + + return *this; + } + + + // Warning prevention: the operator<< function overload for unsigneds used to create lots + // of warnings once size_t usage was becoming widespread. So for each OS we define only + // those overloads that are actually needed. On Windows 32 bit we still get + // 'warning C4267: 'argument' : conversion from 'size_t' to 'const unsigned int', possible loss of data' + // warning which we do not know how to solve yet. The function DummyFunctionsForWarningTest + // in file WCFixedStringStream.cpp calls all combinations of operator<<(unsigned something) + // And should produce no warnings - (except the C4267 on windows). +#if defined(__MACOS__) // both 32 & 64 bit + WCFixedStringBase& operator<<(const size_t in_uint) { + return operator<<(static_cast<unsigned long long>(in_uint)); + } +#endif +// WCFixedStringBase& operator<<(const unsigned char in_uint) { +// return operator<<(static_cast<const unsigned long long>(in_uint)); +// } +// +// WCFixedStringBase& operator<<(const size_t in_uint) { +// return operator<<(static_cast<const uint64_t>(in_uint)); +// } +// +#if defined(__MACOS__) || defined(_WINDOWS) || defined(__linux__) // both 32 & 64 bit + WCFixedStringBase& operator<<(const unsigned int in_uint) { + return operator<<(static_cast<uint64_t>(in_uint)); + } +#endif +// +#if defined(_WINDOWS) || defined(__linux__) // both 32 & 64 bit + WCFixedStringBase& operator<<(const unsigned long in_uint) { + return operator<<(static_cast<uint64_t>(in_uint)); + } +#endif + + WCFixedStringBase& operator<<(const long long in_int) + { + if (in_int < 0) + operator<<('-'); +#ifdef _WINDOWS +// uintmax_t unsigned_in_num = _abs64(in_int); + uintmax_t unsigned_in_num = in_int < 0 ? static_cast<uintmax_t>(-in_int) : static_cast<uintmax_t>(in_int); +#else + uintmax_t unsigned_in_num = std::abs(in_int); +#endif + append_uint(unsigned_in_num, 10); + + return *this; + } + + WCFixedStringBase& operator<<(const short in_int) { + return operator<<(static_cast<int64_t>(in_int)); + } + + WCFixedStringBase& operator<<(const int in_int) { + return operator<<(static_cast<int64_t>(in_int)); + } + + WCFixedStringBase& operator<<(const long in_int) { + return operator<<(static_cast<int64_t>(in_int)); + } + + WCFixedStringBase& operator<<(const double in_doubleToWrite) + { + append_double_with_precision(in_doubleToWrite, 10); + + return *this; + } + + WCFixedStringBase& operator<<(const float in_floatToWrite) + { + append_double_with_precision(double(in_floatToWrite), 5); + + return *this; + } + + inline WCFixedStringBase& operator<<(const WCFixedStringBase& in_fixedStrToAppend) + { + operator<<(in_fixedStrToAppend.c_str()); + + return *this; + } + + WCFixedStringBase& operator<< (bool abool) + { + return abool ? operator<<("true") : operator<<("false"); + } + + template<typename T> WCFixedStringBase& operator+=(T in_type) + { + return operator<<(in_type); + } + + ptrdiff_t compare(const char* in_to_compare) const + { + ptrdiff_t retVal = 1; + + if (0 != in_to_compare) + { + retVal = strcmp(c_str(), in_to_compare); + } + + return retVal; + } + + + ptrdiff_t compare(const WCFixedStringBase& in_to_compare) const + { + ptrdiff_t retVal = compare(in_to_compare.c_str()); + return retVal; + } + + ptrdiff_t case_insensitive_compare(const char* in_to_compare) const + { + ptrdiff_t retVal = 1; + + if (0 != in_to_compare) + { +#ifdef _WINDOWS + retVal = _stricmp(c_str(), in_to_compare); +#endif +#if defined(__linux__) || defined(__MACOS__) + retVal = strcasecmp(c_str(), in_to_compare); +#endif + } + + return retVal; + } + + ptrdiff_t case_insensitive_compare(const WCFixedStringBase& in_to_compare) const + { + ptrdiff_t retVal = case_insensitive_compare(in_to_compare.c_str()); + return retVal; + } + + pos_t find(const char in_char_to_find) const + { + const char* pCurrChar = m_begin; + while (pCurrChar < m_end && *pCurrChar != in_char_to_find) + ++pCurrChar; + + return (pCurrChar < m_end) ? (pCurrChar - m_begin) : npos; + } + + pos_t rfind(const char in_char_to_find) const + { + pos_t retVal = npos; + const char* pCurrChar = m_end; + + while (pCurrChar != m_begin) + { + --pCurrChar; + if (*pCurrChar == in_char_to_find) + { + retVal = pCurrChar - m_begin; + break; + } + } + + return retVal; + } + + pos_t find(const char* in_chars_to_find, const pos_t in_start_from = 0) const + { + pos_t retVal = npos; + size_t to_find_size = ::strlen(in_chars_to_find); + + if (to_find_size > 0 && to_find_size <= size() && in_start_from < size()) + { + const char* pCurrChar = m_begin + in_start_from; + while ((m_end - pCurrChar) >= (ptrdiff_t)to_find_size) + { + int found = ::memcmp(pCurrChar, in_chars_to_find, to_find_size); + if (0 == found) + { + retVal = (pCurrChar - m_begin); + break; + } + + ++pCurrChar; + } + } + + return retVal; + } + + pos_t rfind(const char* in_chars_to_find) const + { + pos_t retVal = npos; + size_t to_find_size = ::strlen(in_chars_to_find); + + if (to_find_size > 0 && to_find_size <= size()) + { + const char* pCurrChar = m_end - to_find_size; + while (m_begin <= pCurrChar) + { + int found = ::memcmp(pCurrChar, in_chars_to_find, to_find_size); + if (0 == found) + { + retVal = (pCurrChar - m_begin); + break; + } + + --pCurrChar; + } + } + + return retVal; + } + + pos_t find_case_insensitive(const char* in_chars_to_find, const pos_t in_start_from = 0) const + { + pos_t retVal = npos; + size_t to_find_size = ::strlen(in_chars_to_find); + + if (to_find_size > 0 && to_find_size <= size() && in_start_from < size()) + { + const char* pCurrChar = m_begin + in_start_from; + while ((m_end - pCurrChar) >= (ptrdiff_t)to_find_size) + { + size_t i; + for (i = 0; i < to_find_size; ++i) + { + if (tolower(*(pCurrChar+i)) != tolower(in_chars_to_find[i])) + break; + } + + if (i == to_find_size) + { + retVal = (pCurrChar - m_begin); + break; + } + + ++pCurrChar; + } + } + + return retVal; + } + + pos_t find_first_of(const char* in_possibe_chars_to_find, const pos_t in_start_from = 0) const + { + pos_t retVal = npos; + + if (in_start_from < size()) + { + const char* pFoundChar = strpbrk(m_begin + in_start_from, in_possibe_chars_to_find); + if (0 != pFoundChar) + { + retVal = (pFoundChar - m_begin); + } + } + + return retVal; + } + + pos_t find_last_of(const char* in_possibe_chars_to_find, const pos_t in_start_from = 0) const + { + pos_t retVal = npos; + + pos_t curr_location = in_start_from; + + while (size() > curr_location) + { + pos_t found = find_first_of(in_possibe_chars_to_find, curr_location); + if (npos != found) + { + retVal = found; + curr_location = found + 1; + } + else + break; + } + + return retVal; + } + + pos_t find_first_not_of(const char* in_acceptable_chars, const pos_t in_start_from = 0) const + { + pos_t retVal = npos; + + if (in_start_from < size()) + { + retVal = (strspn(m_begin + in_start_from, in_acceptable_chars)); + if (size() <= retVal + in_start_from) + { + retVal = npos; + } + else + { + retVal += in_start_from; + } + } + + return retVal; + } + + pos_t find_last_not_of(const char* in_acceptable_chars, const pos_t in_start_from = 0) const + { + pos_t retVal = npos; + + pos_t curr_location = in_start_from; + + while (size() > curr_location) + { + pos_t found = find_first_not_of(in_acceptable_chars, curr_location); + if (npos != found) + { + retVal = found; + curr_location = found + 1; + } + else + break; + } + + return retVal; + } + + // return true if in_begin_text is found at position 0 OR if in_begin_text is empty + bool begins_with(const char* in_begin_text) const + { + pos_t where = find(in_begin_text, 0); + bool retVal = (0 == where) || (0 == ::strlen(in_begin_text)); + return retVal; + } + + // return true if in_end_text is found at th end OR if in_end_text is empty + bool ends_with(const char* in_end_text) const + { + pos_t where = rfind(in_end_text); + bool retVal = ((size() - strlen(in_end_text)) == where) || (0 == ::strlen(in_end_text)); + return retVal; + } + + size_t replace(const char in_look_for, const char in_replace_with) + { + size_t retVal = 0; + + char* pCurrChar = m_begin; + while (pCurrChar < m_end) + { + if (*pCurrChar == in_look_for) + { + *pCurrChar = in_replace_with; + ++retVal; + } + ++pCurrChar; + } + + return retVal; + } + + // erase in_size chars starting from in_location + void erase(const pos_t in_location, const size_t in_num_chars = 1) + { + if (size() > in_location && in_num_chars > 0) + { + size_t actual_num_chars = WUMin(in_num_chars, size_t(size() - in_location)); + char* pTo = m_begin + in_location; + char* pFrom = pTo + actual_num_chars; + + while (pFrom < m_end) + *pTo++ = *pFrom++; + + resize(size() - actual_num_chars); + } + } + + // erase any char that appear in in_forbidden_chars + void erase_all_of(const char* in_forbidden_chars) + { + pos_t curr_location = 0; + + while (npos != curr_location) + { + curr_location = find_first_of(in_forbidden_chars, curr_location); + if (npos != curr_location) + erase(curr_location); + } + } + + // erase any char that do not appear in in_allowed_chars + void erase_all_not_of(const char* in_allowed_chars) + { + pos_t curr_location = 0; + + while (npos != curr_location) + { + curr_location = find_first_not_of(in_allowed_chars, curr_location); + if (npos != curr_location) + erase(curr_location); + } + } + + //! Copy the content of fixed string to a buffer appending a '\0' at the end. + //! If in_buffer_size is more than the allocated buffer size memory over write will happen! + void copy_to_buffer(const size_t in_buffer_size, char* out_buffer) + { + if (in_buffer_size > 0 && 0 != out_buffer) + { + char* cur_buffer = out_buffer; + const char* cur_fixed = m_begin; + const char* end_buffer = out_buffer + (WUMin<size_t>(in_buffer_size - 1, m_end - m_begin)); + while (cur_buffer < end_buffer) + *cur_buffer++ = *cur_fixed++; + + *cur_buffer = '\0'; + } + } + +protected: + ~WCFixedStringBase() {} + + char* const m_begin; + const size_t m_MaxFixedStringLength; + char* m_end; + +private: + WCFixedStringBase(); + WCFixedStringBase(const WCFixedStringBase& in_fixedStrToCopy); +#if 0 + : + m_begin(in_fixedStrToCopy.m_begin), + m_MaxFixedStringLength(in_fixedStrToCopy.m_MaxFixedStringLength), + m_end(in_fixedStrToCopy.m_end) + { + } +#endif +}; + +template<size_t kMaxFixedStringLength> class DllExport WCFixedString : public WCFixedStringBase +{ +public: + + inline WCFixedString() : + WCFixedStringBase(m_fixedString, kMaxFixedStringLength) + { + } + + inline WCFixedString(const char* const in_strToAssign) : + WCFixedStringBase(m_fixedString, kMaxFixedStringLength) + { + operator<<(in_strToAssign); + } + + inline WCFixedString(const WCFixedStringBase& in_fixedStrToAssign) : + WCFixedStringBase(m_fixedString, kMaxFixedStringLength) + { + operator<<(in_fixedStrToAssign); + } + + inline WCFixedString(const WCFixedString& in_fixedStrToAssign) : + WCFixedStringBase(m_fixedString, kMaxFixedStringLength) + { + operator<<(in_fixedStrToAssign); + } + + inline WCFixedString(const char in_char, const size_t in_count = 1) : + WCFixedStringBase(m_fixedString, kMaxFixedStringLength) + { + append(in_char, in_count); + } + + inline WCFixedString(const char* in_chars, const size_t in_count) : + WCFixedStringBase(m_fixedString, kMaxFixedStringLength) + { + append(in_chars, in_count); + } + + // substr now supports negative in_length, which means "from the end" so + // "abcdefg".substr(1, -1) == "bcdef" + inline const WCFixedString substr(const pos_t in_pos = 0, const spos_t in_length = kMaxFixedStringLength) const + { + pos_t adjusted_pos = WUMin<size_t>(in_pos, size()); + size_t adjusted_length = 0; + if (in_length < 0) + { + adjusted_length = size_t(WUMax<spos_t>(0, spos_t(size() - adjusted_pos) + in_length)); + } + else + adjusted_length = WUMin<size_t>(in_length, size() - adjusted_pos); + + WCFixedString retVal; + retVal.append(m_begin + adjusted_pos, adjusted_length); + + return retVal; + } + +protected: + + char m_fixedString[kMaxFixedStringLength + 1]; // the "+ 1" is so that *m_end is always valid, and we can put the '\0' there}; +}; + +inline bool operator==(const WCFixedStringBase& in_left, const WCFixedStringBase& in_right) +{ + return 0 == in_left.compare(in_right.c_str()); +} + +inline bool operator==(const WCFixedStringBase& in_left, const char* const in_right) +{ + return 0 == in_left.compare(in_right); +} + +inline bool operator!=(const WCFixedStringBase& in_left, const WCFixedStringBase& in_right) +{ + return 0 != in_left.compare(in_right.c_str()); +} + +inline bool operator!=(const WCFixedStringBase& in_left, const char* const in_right) +{ + return 0 != in_left.compare(in_right); +} + +// class WCFixedStringBase +typedef WCFixedString<4> WCFixedString4; +typedef WCFixedString<15> WCFixedString15; +typedef WCFixedString<31> WCFixedString31; +typedef WCFixedString<63> WCFixedString63; +typedef WCFixedString<127> WCFixedString127; +typedef WCFixedString<255> WCFixedString255; +typedef WCFixedString<511> WCFixedString511; +typedef WCFixedString<1023> WCFixedString1023; +typedef WCFixedString<2047> WCFixedString2047; + +template<size_t kSizeOfFirst, size_t kSizeOfSecond> + class WCFixedStringPair : public std::pair< WCFixedString<kSizeOfFirst>, WCFixedString<kSizeOfSecond> > +{ +public: + WCFixedStringPair(const char* const in_firstStr = 0, const char* const in_secondStr = 0) : + std::pair< WCFixedString<kSizeOfFirst>, WCFixedString<kSizeOfSecond> >(in_firstStr, in_secondStr) {} + WCFixedStringPair(const WCFixedStringBase& in_firstStr, const char* const in_secondStr = 0) : + std::pair< WCFixedString<kSizeOfFirst>, WCFixedString<kSizeOfSecond> >(in_firstStr, in_secondStr) {} + WCFixedStringPair(const WCFixedStringBase& in_firstStr, const WCFixedStringBase& in_secondStr) : + std::pair< WCFixedString<kSizeOfFirst>, WCFixedString<kSizeOfSecond> >(in_firstStr, in_secondStr) {} +}; + +#endif // #ifndef __WCFixedString_h__ diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/WUErrors.h b/libs/backends/wavesaudio/wavesapi/miscutils/WUErrors.h new file mode 100644 index 0000000000..11bca17087 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/miscutils/WUErrors.h @@ -0,0 +1,334 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WUErrors_h__ + #define __WUErrors_h__ + +/* Copy to include: +#include "WUErrors.h" +*/ + +#include "BasicTypes/WUTypes.h" + +// General errors +//const WTErr eNoErr = 0; // moved to #include "WavesPublicAPI/WTErr.h" +const WTErr eGenericErr = -1; +const WTErr eUserCanceled = -2; +const WTErr eUnknownErr = -3; +const WTErr eExceptionErr = -4; +const WTErr eEndianError = -5; +const WTErr eThreadSafeError = -6; +const WTErr eSomeThingNotInitailzed = -7; +const WTErr eWrongObjectState = -8; //!< object was not in an acceptable state +const WTErr eUninitalized = -9; +const WTErr eDeprecated = -10; +const WTErr eCommandLineParameter = -11; +const WTErr eNotANumber = -12; //!< expected a number but none was found +const WTErr eNotJustANumber = -13; //!< expected a number and found one but also other stuff (e.g. "123XYZ") +const WTErr eNegativeNumber = -14; //!< expected a positive number and found a negative +const WTErr eTimeOut = -15; //!< something timed out +const WTErr eCoreAudioFailed = -16; //!< Error in a core audio call +const WTErr eSomeThingInitailzedTwice = -17; +const WTErr eGenerateHelpInfo = -18; +const WTErr eOutOfRangeNumber = -19; +const WTErr eMacOnlyCode = -20; +const WTErr eWinOnlyCode = -21; +const WTErr eAppLaunchFailed = -22; //!< failed to launch an application +const WTErr eAppTerminateFailed = -23; //!< failed to terminate an application +const WTErr eAppReturnedError = -24; //!< Non zero exit code from application +const WTErr eNotImplemented = -25; //!< Function is not implmemented +const WTErr eNotEmpty = -26; //!< Something was expected to be empty but is not + +// File Manager errors +const WTErr eFMNoSuchVolume = -1001; +const WTErr eFMFileNotFound = -1002; +const WTErr eFMFileAllreadyExists = -1003; +const WTErr eFMAllreadyOpenWithWritePerm = -1004; +const WTErr eFMEndOfFile = -1005; +const WTErr eFMPermissionErr = -1006; +const WTErr eFMBusyErr = -1007; +const WTErr eFMOpenFailed = -1008; +const WTErr eFMTranslateFileNameFailed = -1009; +const WTErr eFMWTPathRefCreationFailed = -1010; +const WTErr eFMReadFailed = -1011; +const WTErr eFMIllegalPathRef = -1012; +const WTErr eFMFileNotOpened = -1013; +const WTErr eFMFileSizeTooBig = -1014; +const WTErr eFMNoSuchDomain = -1015; +const WTErr eFMNoSuchSystemFolder = -1016; +const WTErr eFMWrongParameters = -1017; +const WTErr eFMIsNotAFolder = -1018; +const WTErr eFMIsAFolder = -1019; +const WTErr eFMIsNotAFile = -1020; +const WTErr eFMIsAFile = -1021; +const WTErr eFMDeleteFailed = -1022; +const WTErr eFMCreateFailed = -1023; +const WTErr eFMPathTooLong = -1024; +const WTErr eFMIOError = -1025; +const WTErr eFMIllegalOpenFileRef = -1026; +const WTErr eFMDiskFull = -1027; +const WTErr eFMFileNotEmpty = -1028; +const WTErr eFMEndOfFolder = -1029; +const WTErr eFMSamePath = -1030; +const WTErr eFMPathTooShort = -1031; +const WTErr eFMIncompletePath = -1032; +const WTErr eFMIsNoAFileSystemLink = -1033; +const WTErr eFMSymlinkBroken = -1034; +const WTErr eFMMoveFailed = -1035; +const WTErr eFMWriteFailed = -1036; +const WTErr eFMTooManyOpenFiles = -1037; +const WTErr eFMTooManySymlinks = -1038; + +// System errors +const WTErr eGenericSystemError = -2000; +const WTErr eSysNoEnvironmentVariable = -2001; +const WTErr eDLLLoadingFailed = -2002; +const WTErr eFuncPoinerNotFound = -2003; +const WTErr eDLLNotFound = -2004; +const WTErr eBundleNotLoaded = -2005; +const WTErr eBundleCreateFailed = -2006; +const WTErr eBundleExecutableNotFound = -2007; +const WTErr eNotABundle = -2008; +const WTErr eInvalideDate = -2009; +const WTErr eNoNetDevice = -2010; +const WTErr eCacheCreatedFromResource = -2011; +const WTErr eNotAValidApplication = -2012; + +// Resource Manager errors +const WTErr eRMResNotFound = -3000; +const WTErr eRMResExists = -3001; //!< a resource exist even though it's not expected to +const WTErr eRMContainerNotFound = -3002; //!< The container was not found in the list of containers +const WTErr eRMResRefNotFound = -3003; //!< The resRef was not found in container's resource list +const WTErr eRMInvalidResRef = -3004; +const WTErr eRMInvalidResContainer = -3005; +const WTErr eRMInvalidNativeResContainer = -3006; +const WTErr eRMAttachResContainerFailed = -3007; +const WTErr eRMInvalidResID = -3008; +const WTErr eRMResUpdateFailed = -3009; + +// Graphic Manager & GUI errors +const WTErr eGMIsNotInitailzed = -3500; +const WTErr eGMInvalidImage = -3501; +const WTErr eGMGenericErr = -3502; +const WTErr eGMNoCurrentContext = -3503; +const WTErr eGUISkinNotFound = -3504; +const WTErr eGMNoVertices = -3505; +const WTErr eGMNoColors = -3506; +const WTErr eGMNoTexture = -3507; +const WTErr eGMIncompatibleOGLVersion = -3508; +const WTErr eGMNoDeviceContext = -3509; +const WTErr eGMNoPixelFormat = -3510; +const WTErr eGMNoOGLContext = -3511; +const WTErr eGMNoOGLContextSharing = -3512; +const WTErr eGMUnsupportedImageFormat = -3513; +const WTErr eGMUninitializedContext = -3514; +const WTErr eControlOutOfRange = -3515; +const WTErr eGMUninitializedFont = -3516; +const WTErr eGMInvalidFontDrawMethod = -3517; +const WTErr eGMUnreleasedTextures = -3518; +const WTErr eGMWrongThread = -3519; +const WTErr eGMDontCommitDraw = -3520; +// Errors in the -5000 -> -5999 are defined in Waves-incs.h + +// Memory errors +const WTErr eMemNewFailed = -4001; //!< Something = new CSomething, returned null +const WTErr eMemNewTPtrFailed = -4002; //!< NewTPtr or NewTPtrClear failed +const WTErr eMemNullPointer = -4003; //!< a null pointer was encountered where it should not +const WTErr eMemObjNotInitialized = -4004; +const WTErr eMemBuffTooShort = -4005; //!< the buffer in question did not have enough space for the operation +const WTErr eInstanciationFailed = -4006; +const WTErr eMemAddressSpaceError = -4007; //!< memory falls outside the legal address space +const WTErr eMemBadPointer = -4008; +const WTErr eMemOutOfMemory = -4009; + +// XML Errors +const WTErr eXMLParserFailed = -6001; +const WTErr eXMLTreeNotValid = -6002; +const WTErr eXMLTreeEmpty = -6003; +const WTErr eXMLElementMissing = -6004; +const WTErr eXMLElementUninitalized = -6005; //!< element was default constructed it has not element name, etc.. +const WTErr eXMLElementIncomplete = -6006; //!< XML parser did not complete building the element +const WTErr eXMLAttribMissing = -6007; + +// Preset errors +const WTErr ePresetFileProblem = -7860; +const WTErr eInvalidFileFormatProblem = -7861; +const WTErr ePresetLockedProblem = -7862; +const WTErr ePresetInfoNotFound = -7863; +const WTErr eDuplicatePluginSpecificTag = -7959; +const WTErr ePluginSpecifcNotExisting = -7960; +const WTErr eBuffSizeToSmall = -7961; +const WTErr eCreatingPopupWhereAnItemExists = -7962; +const WTErr eDeletePluginSpecifcFailed = -7963; +const WTErr eFactoryPresetNumOutOfRange = -7964; +const WTErr eNoFactoryPresets = -7965; +const WTErr eLoadPresetToPlugin_vec_empty = -7966; +const WTErr eFactoryPresetNotFound = -7967; +const WTErr eCantCreateUserPrefFile = -7968; +const WTErr eDataFormatNotSupported = -7969; +const WTErr eCantLoadProcessFunction = -7970; +const WTErr eIllegalChunkIndex = -7971; +const WTErr eIllegalChunkID = -7972; +const WTErr eIllegalChunkVersion = -7973; + + +// Shell errors +const WTErr eNotAPluginFile = -8001; +const WTErr eFaildToLoadPluginDLL = -8002; +const WTErr eNoPluginManager = -8003; +const WTErr eGetAvailablePluginsFailed = -8004; +const WTErr eNoPluginsAvailable = -8005; +const WTErr ePluginSubComponentNotFound = -8006; +const WTErr ePluginOpenFailed = -8007; +const WTErr eSubComponentRejected = -8009; //!< user did not want this sub-component - probably through preferences +const WTErr eIncompatibleNumOfIOs = -8010; //!< e.g. surround sub-component in stereo only shell +const WTErr eStemProblem = -8011; //!< Some problem with stems +const WTErr eComponentTypeNotSupported = -8012; +const WTErr ePluginNotLoaded = -8013; +const WTErr ePluginInstanceNotCreate = -8014; +const WTErr ePluginAlgNotCreate = -8015; +const WTErr ePluginGUINotCreate = -8016; +const WTErr eMissmatchChannelCount = -8017; +const WTErr eIncompatibleVersion = -8018; +const WTErr eIncompatibleAffiliation = -8019; +const WTErr eNoSubComponentsFound = -8020; + +// Net-shell errors +const WTErr eNetShellInitFailed = -9001; + +// Protection errors +const WTErr eWLSLicenseFileNotFound = -10001; +const WTErr eWLSPluginNotAuthorized = -10002; +const WTErr eWLSNoLicenseForPlugin = -10003; +const WTErr eWLSInvalidLicenseFileName = -10004; +const WTErr eWLSInvalidLicenseFileContents = -10005; +const WTErr eWLSInvalidDeviceID = -10006; +const WTErr eWLSInvalidClientID = -10007; +const WTErr eWLSLicenseFileDownloadFailed = -10008; +const WTErr eWLSNoLicensesForClientOrDevice = -10009; +const WTErr eWLSNoLicensesForSomePlugins = -10010; + +// Communication errors +const WTErr eCommEndOfRecievedMessage = -11001; +const WTErr eCommSocketDisconnected = -11002; + +// Window Manager Errors +const WTErr eWMEventNotHandled = -12001; +const WTErr eWMDisposeViewFailed = -12002; + +// Plugin View Manager Errors +const WTErr ePVMPlatformNotSupported = -13001; +const WTErr ePVMAlreadyInitialized = -13002; +const WTErr ePVMIllegalParent = -13003; +const WTErr ePVMCannotCreateView = -13004; +const WTErr ePVMNothingSelected = -13005; +const WTErr ePVMDisabledItemChosen = -13006; +const WTErr ePVMMenuItemNotFound = -13007; +const WTErr ePVMMenuItemNotASubMenu = -13008; +const WTErr ePVMUnknownMenu = -13009; +const WTErr ePVMEmptyNativeViewRef = -13010; +const WTErr ePVMGenericError = -13011; +const WTErr ePVMFunctionNotImplemented = -13012; + +// Plugin View Manager - Menu Errors +const WTErr ePVMCannotCreateMenu = -13501; +const WTErr ePVMCannotSetMenuFont = -13502; +const WTErr ePVMCannotSetMenu = -13503; +const WTErr ePVMItemParentNotExists = -13504; + +// Plugin View Manager - TextField Errors +const WTErr ePVMCannotCreateTextField = -13553; +const WTErr ePVMCannotEmbedTextField = -13554; +const WTErr ePVMNoTextToValidate = -13555; +const WTErr ePVMTextTooLong = -13556; +const WTErr ePVMIllegalCharacter = -13557; + + +// Meter Manager Errors +const WTErr eMM_MeterGetMeterValueForParameterNotConnected = -14000 ; + + +//Surface Driver Manager Errors +const WTErr eSDM_SurfaceDriverAPIFailed = -14101; + +// IPC Errors +const WTErr eIPC_CreateNamedPipeFailed = -14200; +const WTErr eIPC_OpenPipeTimeout = -14201; +const WTErr eIPC_DeleteNamedPipeFailed = -14202; +const WTErr eIPC_SelectOnNamedPipeFailed = -14203; +const WTErr eIPC_ReadFromNamedPipeFailed = -14204; +const WTErr eIPC_ReadEndOfFileFromNamedPipe = -14205; +const WTErr eIPC_CloseNamedPipeFailed = -14206; +const WTErr eIPC_ParseArgsFailed = -14207; +const WTErr eIPC_OpenPipeFailed = -14208; +const WTErr eIPC_SendMsgFailed = -14209; +const WTErr eIPC_SendCommandInvalid = -14210; +const WTErr eIPC_QtTestMode = -14211; +const WTErr eIPC_ChangePermissionOnPipe = -14212; +const WTErr eIPC_ConnectionLost = -14213; + +const WTErr eIPC_InvalidRole = -14213; +const WTErr eIPC_CreateNamedPipeM2SFailed = -14214; +const WTErr eIPC_CreateNamedPipeS2MFailed = -14215; +const WTErr eIPC_ChangePermissionOnPipeM2S = -14216; +const WTErr eIPC_ChangePermissionOnPipeS2M = -14217; +const WTErr eIPC_OpenReadPipeFailed = -14218; +const WTErr eIPC_OpenReadPipeDIsableSigPipe = -14219; +const WTErr eIPC_OpenWritePipeFailed = -14220; +const WTErr eIPC_WritePipeFailed = -14221; +const WTErr eIPC_WritePipeNotOpen = -14222; +const WTErr eIPC_WriteBufferResizeFailed = -14223; +const WTErr eIPC_NotConnectedSendMsgFailed = -14224; +const WTErr eIPC_OpenWritePipeWorkerStoping = -14225; +const WTErr eIPC_SoketSendFailed = -14226; +const WTErr eIPC_PtonFailed = -14227; +const WTErr eIPC_SocketFailed = -14228; +const WTErr eIPC_BindFailed = -14229; +const WTErr eIPC_ListenFailed = -14230; +const WTErr eIPC_ConnectFailed = -14231; +const WTErr eIPC_WsaStartupFailed = -14232; +const WTErr eIPC_UdpSocketCreateFailed = -14233; +const WTErr eIPC_UdpSocketConnectFailed = -14234; +const WTErr eIPC_UdpSocketBinFailed = -14235; +const WTErr eIPC_SetBufferPreambleFailed = -14226; + +// Database errors +const WTErr eDB_BatchRollback = -15501; + +// inventory related errors +const WTErr eUnknown_Device = -16001; +const WTErr eInvNoDevice = -16002; + +// SG protocol service errors +const WTErr eSGProtocolService_Not_Running = -17001; +const WTErr eSGProtocolService_Version_MisMatch = -17002; + +// Error code related to Param +const WTErr eInvalidParam = -18001; + +#define WUIsError(theErrorCode) (eNoErr != (theErrorCode)) +#define WUNoError(theErrorCode) (eNoErr == (theErrorCode)) +#define WUThrowError(theErrorCode) {if(WUIsError(theErrorCode))throw (theErrorCode);} +#define WUThrowErrorIfNil(thePtr , theErrorCode) {if (0 == thePtr )throw (theErrorCode);} +#define WUThrowErrorIfFalse(theBool , theErrorCode) {if (!(theBool))throw (theErrorCode);} +#define WUThrowErrorCodeIfError(err,theErrorCode) {if(WUIsError(err))throw (theErrorCode);} + +// Get the error string that match the error code. +DllExport const char* WTErrName(WTErr wtErr); + +#endif //__WUErrors_h__: diff --git a/libs/backends/wavesaudio/wavesapi/miscutils/safe_delete.h b/libs/backends/wavesaudio/wavesapi/miscutils/safe_delete.h new file mode 100644 index 0000000000..72de3388bf --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/miscutils/safe_delete.h @@ -0,0 +1,36 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __safe_delete_h__ + #define __safe_delete_h__ + + +/* Copy to include: +#include "safe_delete.h" +*/ + +#define safe_delete(__pObject__) {if((__pObject__) != 0) {delete (__pObject__); (__pObject__) = 0;}} + +#define safe_delete_array(__pArray__) {if((__pArray__) != 0) {delete [] (__pArray__); (__pArray__) = 0;}} + +template <class T> void safe_delete_from_iterator(T* pToDelete) +{ + safe_delete(pToDelete); +} + +#endif // __safe_delete_h__ diff --git a/libs/backends/wavesaudio/wavesapi/refmanager/WCRefManager.cpp b/libs/backends/wavesaudio/wavesapi/refmanager/WCRefManager.cpp new file mode 100644 index 0000000000..34c4a41e20 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/refmanager/WCRefManager.cpp @@ -0,0 +1,44 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include "WCRefManager.h" + +/// Construcotr. +WCRefManager::WCRefManager() +{ + m_RefCount = 1; +} + +/// Destructor. +WCRefManager::~WCRefManager() +{ +} + +/// Adds a reference to class. +void WCRefManager::AddRef() +{ + m_RefCount++; +} + +/// Decrements reference count and deletes the object if reference count becomes zero. +void WCRefManager::Release() +{ + m_RefCount--; + if( m_RefCount <= 0 ) + delete this; +}
\ No newline at end of file diff --git a/libs/backends/wavesaudio/wavesapi/refmanager/WCRefManager.h b/libs/backends/wavesaudio/wavesapi/refmanager/WCRefManager.h new file mode 100644 index 0000000000..791978958c --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/refmanager/WCRefManager.h @@ -0,0 +1,80 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef WCREFMANAGER_H +#define WCREFMANAGER_H + + +#define SAFE_RELEASE(p) if (p) {p->Release(); p = NULL;} + + +//In order to use this interface, derive the Interface class +//from WCRefManager_Interface and derive the implementation class +//from WCRefManager_Impl. Further, in the implementation class +//declaration, place the macro WCREFMANAGER_IMPL. +class WCRefManager_Interface +{ +public: + /// Constructor. + WCRefManager_Interface() {}; + /// Destructor. + virtual ~WCRefManager_Interface() {}; + /// Adds a reference to class. + virtual void AddRef() = 0; + /// Decrements reference count and deletes the object if reference count becomes zero. + virtual void Release() = 0; +}; + +///! See details at WCRefManager_Interface for how to use this. +class WCRefManager_Impl +{ +public: + WCRefManager_Impl () : m_RefCount(1) {} + virtual ~WCRefManager_Impl() {} +protected: + /// Variable to store reference count. + unsigned int m_RefCount; + +/// Helper to put implementation in an interface derived class, don't forget to +/// derive the impl from WCRefManager_Impl +#define WCREFMAN_IMPL \ + public: \ + virtual void AddRef() {m_RefCount++;} \ + virtual void Release() {m_RefCount--; if (m_RefCount<=0) delete this;} + +}; + + +class WCRefManager +{ +public: + /// Construcotr. + WCRefManager(); + /// Destructor. + virtual ~WCRefManager(); + /// Adds a reference to class. + void AddRef(); + /// Decrements reference count and deletes the object if reference count becomes zero. + void Release(); + +private: + /// Variable to store reference count. + unsigned int m_RefCount; +}; + +#endif // WCREFMANAGER_H diff --git a/libs/backends/wavesaudio/wavesapi/threads/WCThreadSafe.cpp b/libs/backends/wavesaudio/wavesapi/threads/WCThreadSafe.cpp new file mode 100644 index 0000000000..62fc04007b --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/threads/WCThreadSafe.cpp @@ -0,0 +1,844 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#include "Threads/WCThreadSafe.h" + +#if XPLATFORMTHREADS_WINDOWS + #define _WIN32_WINNT 0x0500 // need at least Windows2000 (for TryEnterCriticalSection() and SignalObjectAndWait() + #include "IncludeWindows.h" + #include <process.h> +#endif // XPLATFORMTHREADS_WINDOWS + + +#if defined(__MACOS__) + #include <CoreServices/CoreServices.h> + #include <stdio.h> +#endif // __MACOS__ + +#if XPLATFORMTHREADS_POSIX + #include </usr/include/unistd.h> // avoid the framework version and use the /usr/include version + #include <pthread.h> + #include <sched.h> + #include <sys/time.h> + #include <errno.h> + #include <signal.h> +// We do this externs because <stdio.h> comes from MSL +extern "C" FILE *popen(const char *command, const char *type); +extern "C" int pclose(FILE *stream); +static int (*BSDfread)( void *, size_t, size_t, FILE * ) = 0; + +#include <string.h> + +#endif //XPLATFORMTHREADS_POSIX + +#include "Akupara/threading/atomic_ops.hpp" +namespace wvNS { +static const unsigned int knMicrosecondsPerSecond = 1000*1000; +static const unsigned int knNanosecondsPerMicrosecond = 1000; +static const unsigned int knNanosecondsPerSecond = knMicrosecondsPerSecond*knNanosecondsPerMicrosecond; + +namespace wvThread +{ + + //-------------------------------------------------------------------------------- + static inline bool EnsureThreadingInitialized() + { + bool bRetval = true; + + return bRetval; + } + //-------------------------------------------------------------------------------- + + + + + //-------------------------------------------------------------------------------- + static uint32_t CalculateTicksPerMicrosecond(); + static uint32_t CalculateTicksPerMicrosecond() + { + uint32_t nTicksPerMicrosecond=0; +#if defined(_WIN32) + LARGE_INTEGER TSC; + ::QueryPerformanceFrequency(&TSC); + nTicksPerMicrosecond = uint32_t (TSC.QuadPart / knMicrosecondsPerSecond); +#elif defined(__linux__) && defined(__i386__) + static const timediff sktd_TSC_MeasurementPeriod = 40*1000; // delay for CalculateTicksPerMicrosecond() to measure the TSC frequency + uint64_t Tstart, Tend; + timeval tvtmp, tvstart, tvend; + + //--------------------- begin measurement code + // poll to align to a tick of gettimeofday + ::gettimeofday(&tvtmp,0); + do { + ::gettimeofday(&tvstart,0); + __asm__ __volatile__ (".byte 0x0f, 0x31" : "=A" (Tstart)); // RDTSC + } while (tvtmp.tv_usec!=tvstart.tv_usec); + // delay some + ::usleep(sktd_TSC_MeasurementPeriod); + // + ::gettimeofday(&tvtmp,0); + do { + ::gettimeofday(&tvend,0); + __asm__ __volatile__ (".byte 0x0f, 0x31" : "=A" (Tend)); // RDTSC + } while (tvtmp.tv_usec!=tvend.tv_usec); + //--------------------- end measurement code + + suseconds_t elapsed_usec = (tvend.tv_sec-tvstart.tv_sec)*knMicrosecondsPerSecond + (tvend.tv_usec-tvstart.tv_usec); + uint64_t elapsed_ticks = Tend-Tstart; + nTicksPerMicrosecond = uint32_t (elapsed_ticks/elapsed_usec); +#endif + return nTicksPerMicrosecond; + } + +#if defined(__MACOS__) //&& !defined(__MACH__) + + + bool FindNetInterfaceByIPAddress(const char *sIP, char *sInterface) // sIP and sInterface are both char[16] + { + FILE *fProcess , *pSubcall; + char sLine[256]="", *pToken, sCommand[150]; + bool res = false; + int iret; + + fProcess = popen("ifconfig -l inet", "r"); + if (fProcess) + { + memset(sInterface, '\0', 16); + iret = BSDfread(sLine, sizeof(char), sizeof(sLine), fProcess); + pToken = strtok(sLine, " "); + while (pToken) + { + sprintf(sCommand, "ifconfig %s | grep \"inet %s \"", pToken, sIP); + + pSubcall = popen(sCommand, "r"); + if (pSubcall) + { + char sSubline[100]=""; + if (BSDfread(sSubline, sizeof(char), sizeof(sSubline), pSubcall)) + { + // found + strcpy(sInterface, pToken); + res = true; + pclose(pSubcall); + break; + } + } + pclose(pSubcall); + pToken = strtok(NULL, " "); + } + + } + pclose(fProcess); + + return res; + } +#endif // MACOS + + timestamp now(void) + { + EnsureThreadingInitialized(); + static const uint32_t nTicksPerMicrosecond = CalculateTicksPerMicrosecond(); +#if defined(_WIN32) + if (nTicksPerMicrosecond) + { + LARGE_INTEGER TSC; + ::QueryPerformanceCounter(&TSC); + return timestamp(uint32_t(TSC.QuadPart/nTicksPerMicrosecond)); + } + else return timestamp(0); +#elif defined(__MACOS__) + if (nTicksPerMicrosecond) {} // prevent 'unused' warnings + UnsignedWide usecs; + ::Microseconds(&usecs); + return timestamp(usecs.lo); +#elif defined(__linux__) && defined(__i386__) && defined(__gnu_linux__) + uint64_t TSC; + __asm__ __volatile__ (".byte 0x0f, 0x31" : "=A" (TSC)); // RDTSC + return timestamp(TSC/nTicksPerMicrosecond); +#elif defined(__linux__) && defined(__PPC__) && defined(__gnu_linux__) + #warning need to implement maybe +#else + #error Dont know how to get microseconds timer ! +#endif // defined(_WIN32) + } + + + void sleep_milliseconds(unsigned int nMillisecs) + { + EnsureThreadingInitialized(); +#if XPLATFORMTHREADS_WINDOWS + ::Sleep(nMillisecs); +#elif XPLATFORMTHREADS_POSIX + ::usleep(nMillisecs*1000); +#else + #error Not implemented for your OS +#endif + } + + +#if XPLATFORMTHREADS_WINDOWS + inline DWORD win32_milliseconds(timediff td) { return (td+499)/1000; } +#endif + + void sleep(timediff _td) + { + if (_td>0) + { + EnsureThreadingInitialized(); +#if XPLATFORMTHREADS_WINDOWS + ::Sleep(win32_milliseconds(_td)); // This is the best we can do in windows +#elif XPLATFORMTHREADS_POSIX + ::usleep(_td); +#else + #error Not implemented for your OS +#endif + } + } + + +#if XPLATFORMTHREADS_WINDOWS + void yield() { ::Sleep(0); } +#elif XPLATFORMTHREADS_POSIX + void yield() { ::sched_yield(); } +#endif + + + + + class ThreadMutexInited::OSDependentMutex : public noncopyableobject + { +#if defined (XPLATFORMTHREADS_WINDOWS) + protected: + CRITICAL_SECTION m_critsec; + public: + + inline OSDependentMutex() { EnsureThreadingInitialized(); ::InitializeCriticalSection(&m_critsec); } + inline ~OSDependentMutex() { EnsureThreadingInitialized(); ::DeleteCriticalSection (&m_critsec); } + inline void obtain() { EnsureThreadingInitialized(); ::EnterCriticalSection (&m_critsec); } + inline void release() { EnsureThreadingInitialized(); ::LeaveCriticalSection (&m_critsec); } + inline bool tryobtain() { EnsureThreadingInitialized(); return TryEnterCriticalSection(&m_critsec)!=FALSE; } + +#elif defined (XPLATFORMTHREADS_POSIX) + protected: + pthread_mutex_t m_ptmutex; + public: + inline OSDependentMutex() + { + EnsureThreadingInitialized(); + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + ::pthread_mutex_init (&m_ptmutex, &attr); + } + inline ~OSDependentMutex() { EnsureThreadingInitialized(); ::pthread_mutex_destroy(&m_ptmutex); } + inline void obtain() { EnsureThreadingInitialized(); ::pthread_mutex_lock (&m_ptmutex); } + inline void release() { EnsureThreadingInitialized(); ::pthread_mutex_unlock (&m_ptmutex); } + inline bool tryobtain() { EnsureThreadingInitialized(); return ::pthread_mutex_trylock(&m_ptmutex)!=EBUSY; } + +#endif + }; + + ThreadMutexInited::ThreadMutexInited() : + m_osdmutex(0) {} + + void ThreadMutexInited::init() + { + if (! is_init()) + { + m_osdmutex = new OSDependentMutex; + } + } + + void ThreadMutexInited::uninit() + { + if (is_init()) + { + delete m_osdmutex; + m_osdmutex = 0; + } + } + + ThreadMutexInited::~ThreadMutexInited() + { + uninit(); + } + + void ThreadMutexInited::obtain() + { + if (is_init()) + { + m_osdmutex->obtain(); + } + } + + void ThreadMutexInited::release() + { + if (is_init()) + { + m_osdmutex->release(); + } + } + + bool ThreadMutexInited::tryobtain() + { + bool retVal = true; + if (is_init()) + { + retVal = m_osdmutex->tryobtain(); + } + return retVal; + } + + class ThreadConditionSignal::OSDependentObject : public noncopyableobject + { +#if defined (XPLATFORMTHREADS_POSIX) + + protected: + pthread_cond_t m_ptcond; + pthread_mutex_t m_ptmutex; + public: + inline OSDependentObject() + { + EnsureThreadingInitialized(); + ::pthread_mutex_init(&m_ptmutex,0); + ::pthread_cond_init(&m_ptcond, 0); + } + inline ~OSDependentObject() { ::pthread_cond_destroy(&m_ptcond), ::pthread_mutex_destroy(&m_ptmutex); } + inline void signal_unicast() { ::pthread_cond_signal(&m_ptcond); } + inline void signal_broadcast() { ::pthread_cond_broadcast(&m_ptcond); } + inline void await_signal() { ::pthread_cond_wait(&m_ptcond, &m_ptmutex); } + inline bool await_signal(timediff td) + { + timespec tspecDeadline; + timeval tvNow; + ::gettimeofday(&tvNow,0); + tspecDeadline.tv_nsec = (tvNow.tv_usec + td%knMicrosecondsPerSecond)*knNanosecondsPerMicrosecond; + tspecDeadline.tv_sec = tvNow.tv_sec + td/knMicrosecondsPerSecond; + if (!(tspecDeadline.tv_nsec < suseconds_t(knNanosecondsPerSecond))) + ++tspecDeadline.tv_sec, tspecDeadline.tv_nsec-=knNanosecondsPerSecond; + return ::pthread_cond_timedwait(&m_ptcond, &m_ptmutex, &tspecDeadline) != ETIMEDOUT; + } + + void obtain_mutex() { ::pthread_mutex_lock(&m_ptmutex); } + bool tryobtain_mutex() { return ::pthread_mutex_trylock(&m_ptmutex)!=EBUSY; } + void release_mutex() { ::pthread_mutex_unlock(&m_ptmutex); } + + +#elif XPLATFORMTHREADS_WINDOWS + protected: + unsigned int m_nWaiterCount; + CRITICAL_SECTION m_csectWaiterCount; + + HANDLE m_hndSemaphoreSignaller; // We keep this semaphore always at 0 count (non-signalled). We use it to release a controlled number of threads. + HANDLE m_hndEventAllWaitersReleased; // auto-reset + HANDLE m_hndMutex; // the mutex associated with the condition + bool m_bBroadcastSignalled; // means that the last waiter must signal m_hndEventAllWaitersReleased when done waiting + + protected: + // - - - - - - - - - - - - - - - - - - - - - - - - + bool await_signal_win32(DWORD dwTimeout) + { + ::EnterCriticalSection(&m_csectWaiterCount); + ++m_nWaiterCount; + ::LeaveCriticalSection(&m_csectWaiterCount); + // This is the actual wait for the signal + bool bWaitSucceeded = ::SignalObjectAndWait(m_hndMutex, m_hndSemaphoreSignaller, dwTimeout, FALSE) == WAIT_OBJECT_0; + // + ::EnterCriticalSection(&m_csectWaiterCount); + bool bLastWaiter = --m_nWaiterCount==0 && m_bBroadcastSignalled; + ::LeaveCriticalSection(&m_csectWaiterCount); + + // re-acquire the mutex + if (bLastWaiter) + ::SignalObjectAndWait(m_hndEventAllWaitersReleased, m_hndMutex, INFINITE, FALSE); + else + ::WaitForSingleObject(m_hndMutex, INFINITE); + return bWaitSucceeded; + } + + + public: + + inline bool await_signal(timediff td) { return await_signal_win32((win32_milliseconds(td))); } + inline void await_signal() { await_signal_win32(INFINITE); } + + OSDependentObject() : m_nWaiterCount(0), m_bBroadcastSignalled(false) + { + EnsureThreadingInitialized(); + ::InitializeCriticalSection(&m_csectWaiterCount); + m_hndEventAllWaitersReleased = ::CreateEvent( + 0, // security + FALSE, // auto-reset + FALSE, // initial state non-sognalled + 0); // name + m_hndSemaphoreSignaller = ::CreateSemaphore( + 0, // security + 0, // initial count (and will stay this way) + 0x100000, // maximum count (should be as large as the maximum number of waiting threads) + 0); // name + m_hndMutex = ::CreateMutex( + 0, // security + FALSE, // not owned initially + 0); // name + //if (m_hndEventAllWaitersReleased==INVALID_HANDLE_VALUE || m_hndSemaphoreSignaller==INVALID_HANDLE_VALUE) + // throw something(); + } + + ~OSDependentObject() + { + ::CloseHandle(m_hndMutex); + ::CloseHandle(m_hndSemaphoreSignaller); + ::CloseHandle(m_hndEventAllWaitersReleased); + ::DeleteCriticalSection(&m_csectWaiterCount); + } + + inline void signal_unicast() + { + ::EnterCriticalSection(&m_csectWaiterCount); + unsigned int nWaiters = m_nWaiterCount; + ::LeaveCriticalSection(&m_csectWaiterCount); + if (nWaiters) + ::ReleaseSemaphore(m_hndSemaphoreSignaller, 1, 0); // release 1 semaphore credit to release one waiting thread + } + + void signal_broadcast() + { + ::EnterCriticalSection(&m_csectWaiterCount); + unsigned int nWaiters = m_nWaiterCount; + if (nWaiters) + { + m_bBroadcastSignalled = true; + ::ReleaseSemaphore(m_hndSemaphoreSignaller, nWaiters, 0); // release as many credits as there are waiting threads + ::LeaveCriticalSection(&m_csectWaiterCount); + ::WaitForSingleObject(m_hndEventAllWaitersReleased, INFINITE); + // at this point all threads are waiting on m_hndMutex, which would be released outside this function call + m_bBroadcastSignalled = false; + } + else + // no one is waiting + ::LeaveCriticalSection(&m_csectWaiterCount); + } + //------------------------------------------------ + inline void obtain_mutex() { ::WaitForSingleObject(m_hndMutex, INFINITE); } + inline bool tryobtain_mutex() { return ::WaitForSingleObject(m_hndMutex,0) == WAIT_OBJECT_0; } + inline void release_mutex() { ::ReleaseMutex(m_hndMutex); } + //------------------------------------------------ +#endif // OS switch + }; + + void ThreadConditionSignal::obtain_mutex() + { + m_osdepobj.obtain_mutex(); + } + bool ThreadConditionSignal::tryobtain_mutex() { return m_osdepobj.tryobtain_mutex(); } + void ThreadConditionSignal::release_mutex() + { + m_osdepobj.release_mutex(); + } + + void ThreadConditionSignal::await_condition() { m_osdepobj.await_signal(); } + bool ThreadConditionSignal::await_condition(timediff tdTimeout) { return m_osdepobj.await_signal(tdTimeout); } + void ThreadConditionSignal::signal_condition_single() { m_osdepobj.signal_unicast(); } + void ThreadConditionSignal::signal_condition_broadcast() { m_osdepobj.signal_broadcast(); } + + ThreadConditionSignal::ThreadConditionSignal() : m_osdepobj(*new OSDependentObject) {} + ThreadConditionSignal::~ThreadConditionSignal() { delete &m_osdepobj; } + + + + + + + + +#if XPLATFORMTHREADS_POSIX + namespace // anon + { + inline int max_FIFO_schedparam() + { + static const int max_priority = ::sched_get_priority_max(SCHED_FIFO); + return max_priority; + } + inline int schedparam_by_percentage(unsigned short percentage) + { + return (max_FIFO_schedparam()*10*percentage+500)/1000; + } + class POSIXThreadPriority + { + public: + int m_SchedPolicy; + int m_SchedPriority; + POSIXThreadPriority(ThreadPriority pri) + { + switch (pri) + { + case ThreadPriority::TimeCritical: m_SchedPolicy=SCHED_FIFO, m_SchedPriority=schedparam_by_percentage(80); break; + case ThreadPriority::AboveNormal: m_SchedPolicy=SCHED_FIFO, m_SchedPriority=schedparam_by_percentage(20); break; + case ThreadPriority::BelowNormal: // fall through to normal; nothing is below normal in POSIX + case ThreadPriority::Normal: // fall through to default + default: m_SchedPolicy=SCHED_OTHER, m_SchedPriority=0; break; + } + } + }; + + } // namespace anonymous +#endif // XPLATFORMTHREADS_POSIX + +#if XPLATFORMTHREADS_WINDOWS + namespace // anon + { + inline int WinThreadPriority(ThreadPriority pri) + { + switch (pri) + { + case ThreadPriority::BelowNormal: return THREAD_PRIORITY_BELOW_NORMAL; + case ThreadPriority::AboveNormal: return THREAD_PRIORITY_ABOVE_NORMAL; + case ThreadPriority::TimeCritical: return THREAD_PRIORITY_TIME_CRITICAL; + case ThreadPriority::Normal: // fall through to default + default: return THREAD_PRIORITY_NORMAL; + } + } + } // namespace anon +#endif // XPLATFORMTHREADS_WINDOWS + + + + void SetMyThreadPriority(ThreadPriority pri) + { +#if XPLATFORMTHREADS_WINDOWS + ::SetThreadPriority(::GetCurrentThread(), WinThreadPriority(pri)); +#endif // XPLATFORMTHREADS_WINDOWS +#if XPLATFORMTHREADS_POSIX + const POSIXThreadPriority posixpri(pri); + sched_param sparam; + ::memset(&sparam, 0, sizeof(sparam)); + sparam.sched_priority = posixpri.m_SchedPriority; +#if defined(__linux__) + ::sched_setscheduler(0, posixpri.m_SchedPolicy, &sparam); // linux uses this function instead of pthread_ +#else + pthread_setschedparam(pthread_self(), posixpri.m_SchedPolicy, &sparam); +#endif +#endif // XPLATFORMTHREADS_POSIX + } + + + struct ThreadWrapperData + { + ThreadFunction *func; + ThreadFunctionArgument arg; + }; + +#if XPLATFORMTHREADS_WINDOWS + static unsigned int __stdcall ThreadWrapper(void * arg) + { + register ThreadWrapperData *twd = reinterpret_cast<ThreadWrapperData*>(arg); + ThreadFunction *func=twd->func; + ThreadFunctionArgument farg=twd->arg; + delete twd; + return DWORD(func(farg)); + } +#elif XPLATFORMTHREADS_POSIX + static void * ThreadWrapper(void *arg) + { + register ThreadWrapperData *twd = reinterpret_cast<ThreadWrapperData*>(arg); + ThreadFunction *func=twd->func; + ThreadFunctionArgument farg=twd->arg; + delete twd; + return reinterpret_cast<void*>(func(farg)); + } + typedef void*(ThreadWrapperFunction)(void*); + + static ThreadWrapperFunction *ThunkedThreadWrapper = ThreadWrapper; + +#endif // OS switch + + + + + + class ThreadHandle::OSDependent + { + public: + static void StartThread(ThreadWrapperData *, ThreadHandle &, ThreadPriority); + static bool KillThread(ThreadHandle); + static bool JoinThread(ThreadHandle, ThreadFunctionReturnType*); + static void Close(ThreadHandle); +#if XPLATFORMTHREADS_WINDOWS + static inline uintptr_t from_oshandle(HANDLE h) { return reinterpret_cast<uintptr_t>(h); } + static inline HANDLE to_oshandle(uintptr_t h) { return reinterpret_cast<HANDLE>(h); } +#elif XPLATFORMTHREADS_POSIX + static inline uintptr_t from_oshandle(pthread_t pt) { return uintptr_t(pt); } + static inline pthread_t to_oshandle(uintptr_t h) { return pthread_t(h); } +#endif // OS switch + }; + +#if XPLATFORMTHREADS_WINDOWS + const ThreadHandle ThreadHandle::Invalid(OSDependent::from_oshandle(INVALID_HANDLE_VALUE)); +#elif XPLATFORMTHREADS_POSIX + const ThreadHandle ThreadHandle::Invalid(OSDependent::from_oshandle(0)); +#endif // OS switch + + inline void ThreadHandle::OSDependent::StartThread(ThreadWrapperData *ptwdata, ThreadHandle &th, ThreadPriority pri) + { +#if XPLATFORMTHREADS_WINDOWS + uintptr_t h = ::_beginthreadex( + 0, // no security attributes, not inheritable + 0, // default stack size + ThreadWrapper, // function to call + (void*)(ptwdata), // argument for function + 0, // creation flags + 0 // where to store thread ID + ); + + if (h) + { + th.m_oshandle = h; + if (pri!=ThreadPriority::Normal) + ::SetThreadPriority(to_oshandle(h), WinThreadPriority(pri)); + } + else + th=Invalid; +#elif XPLATFORMTHREADS_POSIX + pthread_attr_t my_thread_attr, *pmy_thread_attr = 0; + sched_param my_schedparam; + + if (pri!=ThreadPriority::Normal) + { + pmy_thread_attr = &my_thread_attr; + + const POSIXThreadPriority posixpriority(pri); + int result; + result = pthread_attr_init (pmy_thread_attr); + result = pthread_attr_setschedpolicy(pmy_thread_attr, posixpriority.m_SchedPolicy); + + memset(&my_schedparam, 0, sizeof(my_schedparam)); + my_schedparam.sched_priority = posixpriority.m_SchedPriority; + result = pthread_attr_setschedparam(pmy_thread_attr, &my_schedparam); + } + + pthread_t pt; + int anyerr = pthread_create( + &pt, // variable for thread handle + pmy_thread_attr, // default attributes + ThunkedThreadWrapper, + ptwdata + ); + + if (anyerr) + th=Invalid; + else + th.m_oshandle = OSDependent::from_oshandle(pt); +#endif + } + + inline bool ThreadHandle::OSDependent::KillThread(ThreadHandle h) + { +#if XPLATFORMTHREADS_WINDOWS + return ::TerminateThread(to_oshandle(h.m_oshandle), (DWORD)-1) != 0; +#elif XPLATFORMTHREADS_POSIX + return pthread_cancel(to_oshandle(h.m_oshandle)) == 0; +#endif + } + + bool ThreadHandle::OSDependent::JoinThread(ThreadHandle h, ThreadFunctionReturnType *pretval) + { +#if XPLATFORMTHREADS_WINDOWS + const bool kbReturnedOk = (WAIT_OBJECT_0 == ::WaitForSingleObject(OSDependent::to_oshandle(h.m_oshandle), INFINITE)); + if (kbReturnedOk && pretval) + { + DWORD dwExitCode; + ::GetExitCodeThread(to_oshandle(h.m_oshandle), &dwExitCode); + *pretval = (ThreadFunctionReturnType)(dwExitCode); + } + return kbReturnedOk; +#endif +#if XPLATFORMTHREADS_POSIX + ThreadFunctionReturnType ptrExitCode = 0; + int join_return_code = pthread_join(to_oshandle(h.m_oshandle), (void**)ptrExitCode); + const bool kbReturnedOk = (0 == join_return_code); + if (0 != pretval) + { + *pretval = ptrExitCode; + } + return kbReturnedOk; +#endif + } + +#if XPLATFORMTHREADS_WINDOWS + inline void ThreadHandle::OSDependent::Close(ThreadHandle h) + { + ::CloseHandle(OSDependent::to_oshandle(h.m_oshandle)); + } +#endif // XPLATFORMTHREADS_WINDOWS +#if XPLATFORMTHREADS_POSIX + inline void ThreadHandle::OSDependent::Close(ThreadHandle) {} +#endif // XPLATFORMTHREADS_POSIX + + //********************************************************************************************** + + class WCThreadRef::OSDependent + { + public: + static void GetCurrentThreadRef(WCThreadRef& tid); +#if XPLATFORMTHREADS_WINDOWS + static inline uintptr_t from_os(DWORD thread_id) { return (uintptr_t)(thread_id); } + static inline DWORD to_os(uintptr_t thread_id) { return (DWORD)(thread_id); } +#elif XPLATFORMTHREADS_POSIX + static inline uintptr_t from_os(pthread_t thread_id) { return (uintptr_t)(thread_id); } + static inline pthread_t to_os(uintptr_t thread_id) { return pthread_t(thread_id); } +#endif // OS switch + }; + + //********************************************************************************************** + inline void WCThreadRef::OSDependent::GetCurrentThreadRef(WCThreadRef& tid) + { +#if XPLATFORMTHREADS_WINDOWS + DWORD thread_id = ::GetCurrentThreadId(); + tid.m_osThreadRef = OSDependent::from_os(thread_id); + +#elif XPLATFORMTHREADS_POSIX + pthread_t thread_id = ::pthread_self(); + tid.m_osThreadRef = OSDependent::from_os(thread_id); + +#endif // OS switch + } + + //********************************************************************************************** + + ThreadHandle StartThread(ThreadFunction func, ThreadFunctionArgument arg, ThreadPriority thpri) + { + EnsureThreadingInitialized(); + ThreadWrapperData *ptwdata = new ThreadWrapperData; + ptwdata->func = func; + ptwdata->arg = arg; + ThreadHandle thToReturn; + ThreadHandle::OSDependent::StartThread(ptwdata, thToReturn, thpri); + return thToReturn; + } + + bool KillThread(ThreadHandle h) + { + EnsureThreadingInitialized(); + return ThreadHandle::OSDependent::KillThread(h); + } + + bool JoinThread(ThreadHandle h, ThreadFunctionReturnType *pretval) + { + EnsureThreadingInitialized(); + return ThreadHandle::OSDependent::JoinThread(h, pretval); + } + + void Close(ThreadHandle h) + { + EnsureThreadingInitialized(); + return ThreadHandle::OSDependent::Close(h); + } + + //******************************************************************************************* + + WCThreadRef GetCurrentThreadRef() + { + EnsureThreadingInitialized(); // Is it necessary? + WCThreadRef tRefToReturn; + WCThreadRef::OSDependent::GetCurrentThreadRef(tRefToReturn); + return tRefToReturn; + } + + //******************************************************************************************* + + bool IsThreadExists(const WCThreadRef& threadRef) + { +#if XPLATFORMTHREADS_WINDOWS + DWORD dwThreadId = WCThreadRef::OSDependent::to_os((uintptr_t)threadRef); + HANDLE handle = ::OpenThread(SYNCHRONIZE, // dwDesiredAccess - use of the thread handle in any of the wait functions + FALSE, // bInheritHandle - processes do not inherit this handle + dwThreadId); + + // Now we have the handle, check if the associated thread exists: + DWORD retVal = WaitForSingleObject(handle, 0); + if (retVal == WAIT_FAILED) + return false; // the thread does not exists + else + return true; // the thread exists + +#elif XPLATFORMTHREADS_POSIX + pthread_t pthreadRef = WCThreadRef::OSDependent::to_os((uintptr_t)threadRef); + int retVal = pthread_kill(pthreadRef, 0); // send a signal to the thread, but do nothing + if (retVal == ESRCH) + return false; // the thread does not exists + else + return true; // the thread exists + +#endif // OS switch + } + + //******************************************************************************************* + + bool operator==(const WCThreadRef& first, const WCThreadRef& second) + { + return (first.m_osThreadRef == second.m_osThreadRef); + } + + bool operator!=(const WCThreadRef& first, const WCThreadRef& second) + { + return (first.m_osThreadRef != second.m_osThreadRef); + } + + bool operator<(const WCThreadRef& first, const WCThreadRef& second) + { + return (first.m_osThreadRef < second.m_osThreadRef); + } + + bool operator>(const WCThreadRef& first, const WCThreadRef& second) + { + return (first.m_osThreadRef > second.m_osThreadRef); + } + + bool WCAtomicLock::obtain(const uint32_t in_num_trys) + { + bool retVal = false; + + uint32_t timeOut = in_num_trys; + while (true) + { + retVal = Akupara::threading::atomic::compare_and_store<int32_t>(&m_the_lock, int32_t(0), int32_t(1)); + if (retVal) + { + break; + } + else + { + if (--timeOut == 0) + { + break; + } + sleep_milliseconds(1000); + } + } + + return retVal; + } + + void WCAtomicLock::release() + { + m_the_lock = 0; + } + +} // namespace wvThread +} // namespace wvNS { + diff --git a/libs/backends/wavesaudio/wavesapi/threads/WCThreadSafe.h b/libs/backends/wavesaudio/wavesapi/threads/WCThreadSafe.h new file mode 100644 index 0000000000..c97808b059 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/threads/WCThreadSafe.h @@ -0,0 +1,410 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WCThreadSafe_h_ + #define __WCThreadSafe_h_ + +/* Copy to include +#include "Threads/WCThreadSafe.h" +*/ + +// +// * WCThreadSafe.h (used to be called XPlatformOSServices.hpp) +// * +// * Consistent C++ interfaces to common Operating System services. +// * +// * +// * +// * +// * Created 2004-December-13 by Udi Barzilai as XPlatformOSServices.hpp +// * Moved to WCThreadSafe.h by Shai 26/10/2005 +// * 26/10/2005: ThreadMutex now inhetites from ThreadMutexInited +// * namespace changed to wvThread + +#include "WavesPublicAPI/wstdint.h" +#include <string> + +#include "BasicTypes/WUDefines.h" + +#if defined(__linux__) || defined(__MACOS__) + #define XPLATFORMOSSERVICES_UNIX 1 +#endif + +#if defined(_WIN32) + #define XPLATFORMOSSERVICES_WIN32 1 +#endif + +#if XPLATFORMOSSERVICES_WIN32 + #define XPLATFORMTHREADS_WINDOWS 1 +#elif XPLATFORMOSSERVICES_UNIX + #define XPLATFORMTHREADS_POSIX 1 +#endif +namespace wvNS { +typedef uint32_t WTThreadSafetyType; +const WTThreadSafetyType kNoThreadSafetyNeeded = 0; +const WTThreadSafetyType kpthreadsmutexThreadSafety = 1; + + +namespace wvThread +{ + //#include "BasicTypes/WavesAPISetAligment.h" + //Packing affects the layout of classes, and commonly, if packing changes across header files, there can be problems. +#ifdef _WINDOWS +#pragma pack(push) +#pragma pack(8) +#endif + +#ifdef __MACOS__ +#ifdef __GNUC__ +#pragma pack(push, 8) +#endif +#endif + + //-------------------------------------------------------- + typedef int32_t timediff; // in microseconds + static const timediff ktdOneSecond = 1000*1000; + //-------------------------------------------------------- + class timestamp + { + protected: + typedef uint32_t tickcount; + tickcount m_nMicroseconds; // may wrap around + static const tickcount ms_knWraparoundThreshold = ~tickcount(0) ^ (~tickcount(0)>>1); // half the range + + public: + timestamp() : m_nMicroseconds(0) { /* uninitialized */ } + timestamp(const timestamp &_ts) : m_nMicroseconds(_ts.m_nMicroseconds) {} + timestamp &operator=(const timestamp &_rhs) { m_nMicroseconds = _rhs.m_nMicroseconds; return *this; } + explicit timestamp(tickcount _i) : m_nMicroseconds(_i) {} + uint32_t ticks() const { return m_nMicroseconds; } + timediff operator-(timestamp _rhs) const { return timediff(m_nMicroseconds-_rhs.m_nMicroseconds); } + timestamp & operator+=(timediff _t) { m_nMicroseconds+=_t; return *this; } + timestamp & operator-=(timediff _t) { m_nMicroseconds-=_t; return *this; } + timestamp operator+(timediff _t) const { return timestamp(m_nMicroseconds+_t); } + timestamp operator-(timediff _t) const { return timestamp(m_nMicroseconds-_t); } + bool operator==(timestamp _rhs) const { return m_nMicroseconds==_rhs.m_nMicroseconds; } + bool operator!=(timestamp _rhs) const { return m_nMicroseconds!=_rhs.m_nMicroseconds; } + bool operator< (timestamp _rhs) const { return m_nMicroseconds-_rhs.m_nMicroseconds >= ms_knWraparoundThreshold; } + static timestamp null() { return timestamp(0); } + bool is_null() const { return m_nMicroseconds==0; } + }; + //-------------------------------------------------------- +#ifdef __MACOS__ + bool FindNetInterfaceByIPAddress(const char *sIP, char *sInterface); +#endif // MACOS + //-------------------------------------------------------- + timestamp now(); + //-------------------------------------------------------- + DllExport void sleep(timediff); + DllExport void sleep_milliseconds(unsigned int nMillisecs); + //-------------------------------------------------------- + void yield(); + //-------------------------------------------------------- + + + + typedef uintptr_t os_dependent_handle_type; + + //-------------------------------------------------------- + typedef int ThreadFunctionReturnType; + typedef void * ThreadFunctionArgument; + //-------------------------------------------------------- + typedef ThreadFunctionReturnType (ThreadFunction)(ThreadFunctionArgument); + //-------------------------------------------------------- + class ThreadHandle + { + public: + class OSDependent; + protected: + uintptr_t m_oshandle; // hopefully this is good enough for all systems + public: + static const ThreadHandle Invalid; + protected: + ThreadHandle(uintptr_t n) : m_oshandle(n) {} + public: + ThreadHandle() : m_oshandle(Invalid.m_oshandle) {} + bool is_invalid() const { return !m_oshandle || m_oshandle==Invalid.m_oshandle; } + }; + //-------------------------------------------------------- + class ThreadPriority + { + public: enum value { BelowNormal=1, Normal=2, AboveNormal=3, TimeCritical=4 }; + protected: value m_value; + public: ThreadPriority(value v) : m_value(v) {} + public: operator value() const { return m_value; } + }; + //-------------------------------------------------------- + void SetMyThreadPriority(ThreadPriority); + //-------------------------------------------------------- + ThreadHandle StartThread(ThreadFunction, ThreadFunctionArgument, ThreadPriority=ThreadPriority::Normal); + bool JoinThread(ThreadHandle, ThreadFunctionReturnType * = 0); + bool KillThread(ThreadHandle); // use only for abnormal termination + void Close(ThreadHandle); // should be called once for every handle obtained from StartThread. + //-------------------------------------------------------- + + + + + //-------------------------------------------------------- + class DllExport noncopyableobject + { + protected: + noncopyableobject() {} + private: + noncopyableobject(const noncopyableobject &); + noncopyableobject & operator=(const noncopyableobject &); + }; + //-------------------------------------------------------- + + + //-------------------------------------------------------- + // Thread Mutex class that needs to be explicitly initialized + class DllExport ThreadMutexInited : public noncopyableobject + { + protected: + class OSDependentMutex; + OSDependentMutex* m_osdmutex; + + public: + ThreadMutexInited(); + ~ThreadMutexInited(); + + void init(); + void uninit(); + inline bool is_init() { return 0 != m_osdmutex; } + void obtain(); + bool tryobtain(); + void release(); + + private: + ThreadMutexInited(const ThreadMutexInited&); // cannot be copied + ThreadMutexInited& operator=(const ThreadMutexInited&); // cannot be copied + + public: + class lock : public noncopyableobject + { + protected: + ThreadMutexInited &m_mutex; + public: + inline lock(ThreadMutexInited &mtx) : m_mutex(mtx) { m_mutex.obtain(); } + inline ~lock() { m_mutex.release(); } + }; + class trylock : public noncopyableobject + { + protected: + ThreadMutexInited &m_mutex; + bool m_bObtained; + public: + inline trylock(ThreadMutexInited &mtx) : m_mutex(mtx), m_bObtained(false) { m_bObtained = m_mutex.tryobtain(); } + inline ~trylock() { if (m_bObtained) m_mutex.release(); } + inline operator bool() const { return m_bObtained; } + }; + }; + //-------------------------------------------------------- + + // Thread Mutex class that is automatically initialized + class ThreadMutex : public ThreadMutexInited + { + public: + ThreadMutex() {init();} + }; + + //-------------------------------------------------------- + class DllExport ThreadConditionSignal : public noncopyableobject + { + protected: + class OSDependentObject; + OSDependentObject &m_osdepobj; + + protected: + void obtain_mutex(); + bool tryobtain_mutex(); + void release_mutex(); + + public: + class lock : public noncopyableobject + { + protected: + ThreadConditionSignal &m_tcs; + public: + lock(ThreadConditionSignal &tcs) : m_tcs(tcs) { m_tcs.obtain_mutex(); } + ~lock() { m_tcs.release_mutex(); } + }; + class trylock : public noncopyableobject + { + protected: + ThreadConditionSignal &m_tcs; + bool m_bObtained; + public: + trylock(ThreadConditionSignal &tcs) : m_tcs(tcs), m_bObtained(false) { m_bObtained = m_tcs.tryobtain_mutex(); } + ~trylock() { if (m_bObtained) m_tcs.release_mutex(); } + operator bool() const { return m_bObtained; } + }; + + public: + ThreadConditionSignal(); + ~ThreadConditionSignal(); + + // IMPORTANT: All of the functions below MUST be called ONLY while holding a lock for this object !!! + void await_condition(); + bool await_condition(timediff tdTimeout); + void signal_condition_single(); + void signal_condition_broadcast(); + }; + //-------------------------------------------------------- + + + + + + //-------------------------------------------------------- + // A doorbell is a simple communication mechanism that allows + // one thread two wake another when there is some work to be done. + // The signal is 'clear on read'. This class is not intended for + // multi-way communication (i.e. more than two threads). +//#define XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR (!XPLATFORMTHREADS_WINDOWS && !XPLATFORMOSSERVICES_MACOS) +#ifdef XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR +#undef XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR +#endif +#define XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR 1 +#if XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR + class doorbell_type + { + protected: + ThreadConditionSignal m_signal; + bool m_rang; + protected: + template<bool wait_forever> bool wait_for_ring_internal(timediff timeout) + {// mutex + ThreadConditionSignal::lock guard(m_signal); + if (!m_rang) + { + if (wait_forever) + { + m_signal.await_condition(); + } + else + { + m_signal.await_condition(timeout); + } + } + const bool rang = m_rang; + m_rang = false; + return rang; + }// mutex + + public: + doorbell_type() : m_rang(false) {} + inline ~doorbell_type() {} + inline void ring() + {// mutex + ThreadConditionSignal::lock guard(m_signal); + m_rang = true; + m_signal.signal_condition_single(); + }// mutex + inline bool wait_for_ring() { return wait_for_ring_internal<true>(0); } + inline bool wait_for_ring(timediff timeout) { return wait_for_ring_internal<false>(timeout); } + }; +#else + class doorbell_type : public noncopyableobject + { + protected: + os_dependent_handle_type m_os_dependent_handle; + protected: + template<bool wait_forever> bool wait_for_ring_internal(timediff); + public: + doorbell_type(); + ~doorbell_type(); + void ring(); + bool wait_for_ring(); + bool wait_for_ring(timediff timeout); + }; +#endif // XPLATFORMTHREADS_DOORBELL_INLINE_USING_COND_VAR + //-------------------------------------------------------- + + //--------------------------------------------------------------- + class DllExport WCThreadRef // Class which holds the threadRef, DWORD in Windows and pthread_t in POSIX (Mac, Unix) + { + public: + class OSDependent; // the class which contains the OS Dependent implementation + + WCThreadRef() : m_osThreadRef(0) {} + bool is_invalid() const { return m_osThreadRef == 0;} + + operator uintptr_t() const {return m_osThreadRef;} + + protected: + uintptr_t m_osThreadRef; + WCThreadRef(uintptr_t n) : m_osThreadRef(n) {} + + friend DllExport bool operator==(const WCThreadRef& first, const WCThreadRef& second); + friend DllExport bool operator!=(const WCThreadRef& first, const WCThreadRef& second); + friend DllExport bool operator<(const WCThreadRef& first, const WCThreadRef& second); + friend DllExport bool operator>(const WCThreadRef& first, const WCThreadRef& second); + }; + + DllExport WCThreadRef GetCurrentThreadRef(); // getting the current thread reference - cross-platform implemented + bool IsThreadExists(const WCThreadRef& threadRef); // correct to the very snapshot of time of execution + + //--------------------------------------------------------------- + + class DllExport WCAtomicLock + { + public: + WCAtomicLock() : m_the_lock(0) {} + bool obtain(const uint32_t in_num_trys = 1); + void release(); + private: + int32_t m_the_lock; + }; + + //#include "BasicTypes/WavesAPIResetAligment.h" +#ifdef _WINDOWS +#pragma pack(pop) +#endif + +#ifdef __MACOS__ +#ifdef __GNUC__ +#pragma pack(pop) +#endif +#endif + +class WCStThreadMutexLocker +{ +public: + WCStThreadMutexLocker(wvNS::wvThread::ThreadMutexInited& in_mutex) : + m_mutex(in_mutex) + { + m_mutex.obtain(); + } + + ~WCStThreadMutexLocker() + { + m_mutex.release(); + } +protected: + wvNS::wvThread::ThreadMutexInited& m_mutex; + WCStThreadMutexLocker(const WCStThreadMutexLocker&); + WCStThreadMutexLocker& operator=(const WCStThreadMutexLocker&); +}; + +} // namespace wvThread + + +} //namespace wvNS { +#endif // #ifndef __WCThreadSafe_h_ diff --git a/libs/backends/wavesaudio/wavesapi/wavespublicapi/1.0/WavesPublicAPI_Defines.h b/libs/backends/wavesaudio/wavesapi/wavespublicapi/1.0/WavesPublicAPI_Defines.h new file mode 100644 index 0000000000..470ce77c62 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/wavespublicapi/1.0/WavesPublicAPI_Defines.h @@ -0,0 +1,59 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __WavesPublicAPI_Defines_h__ + #define __WavesPublicAPI_Defines_h__ + +/*Copy to include +#include "WavesPublicAPI_Defines.h" +*/ + +#ifdef __MACOS__ + + #ifdef __GNUC__ + #define WPAPI_DllExport __attribute__ ((visibility("default"))) + #define __WPAPI_CDECL + #define __WPAPI_STDCALL + + #else + + #define WPAPI_DllExport __declspec(export) + #define __WPAPI_CDECL + #define __WPAPI_STDCALL + + #endif + +#endif + + +#ifdef _WINDOWS + #define WPAPI_DllExport __declspec(dllexport) + #define __WPAPI_CDECL __cdecl + #define __WPAPI_STDCALL __stdcall +#endif + +#ifdef __linux__ + + #define WPAPI_DllExport __attribute__ ((visibility("default"))) + + #define __WPAPI_CDECL + #define __WPAPI_STDCALL + +#endif + +#endif //__WavesPublicAPI_Defines_h__ diff --git a/libs/backends/wavesaudio/wavesapi/wavespublicapi/WTErr.h b/libs/backends/wavesaudio/wavesapi/wavespublicapi/WTErr.h new file mode 100644 index 0000000000..6c6a0b9dec --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/wavespublicapi/WTErr.h @@ -0,0 +1,45 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +/////////////////////////////////////////////////////////////////////////////////////////////////////// +// \file WTErr.h, defines basic error type and "No Error" code +// All users may use their own error codes with this type, as long as eNoErr remains defined here +/////////////////////////////////////////////////////////////////////////////////////////////////////// +#ifndef __WTErr_h__ +#define __WTErr_h__ + +/* Copy to include: +#include "WavesPublicAPI/WTErr.h" +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "WavesPublicAPI/wstdint.h" + +typedef int32_t WTErr; // Waves Type Error +const WTErr eNoErr = 0; + + +#ifdef __cplusplus +} //extern "C" { +#endif + +#endif // __WTErr_h__ + diff --git a/libs/backends/wavesaudio/wavesapi/wavespublicapi/wstdint.h b/libs/backends/wavesaudio/wavesapi/wavespublicapi/wstdint.h new file mode 100644 index 0000000000..a933696638 --- /dev/null +++ b/libs/backends/wavesaudio/wavesapi/wavespublicapi/wstdint.h @@ -0,0 +1,358 @@ +/* + Copyright (C) 2013 Waves Audio Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ +#ifndef __stdint_h__ +#define __stdint_h__ + +/* Copy to include +#include "wstdint.h" +*/ + + +#ifdef __MACOS__ + #include <stddef.h> + #include </usr/include/stdint.h> // Mac has this file in /usr/includez +#endif +#ifdef __linux__ + #if ! defined(__STDC_LIMIT_MACROS) + #define __STDC_LIMIT_MACROS + #endif + + #include <stddef.h> + #include </usr/include/stdint.h> +#endif + +#if (defined (_WINDOWS) || defined(WIN32) || defined(WIN64)) +#if (_MSC_VER > 1600) || defined(__MINGW64__) + // Taken from MSDN official page: + // In Visual Studio 2010 _MSC_VER is defined as 1600, In Visual Studio 2012 _MSC_VER is defined as 1700. + #include <stdint.h> +#else +#ifndef _STDINT_H + #define _STDINT_H // this will prevent Altura's CGBase.h from defining int32_t +#endif +/* + * ISO C 99 <stdint.h> for platforms that lack it. + * <http://www.opengroup.org/onlinepubs/007904975/basedefs/stdint.h.html> + */ + +/* Get wchar_t, WCHAR_MIN, WCHAR_MAX. */ +#include <stddef.h> +/* Get CHAR_BIT, LONG_MIN, LONG_MAX, ULONG_MAX. */ +#include <limits.h> + +/* Get those types that are already defined in other system include files. */ +#if defined(__FreeBSD__) +# include <sys/inttypes.h> +#endif + +#if defined(__sun) && HAVE_SYS_INTTYPES_H +# include <sys/inttypes.h> + /* Solaris 7 <sys/inttypes.h> has the types except the *_fast*_t types, and + the macros except for *_FAST*_*, INTPTR_MIN, PTRDIFF_MIN, PTRDIFF_MAX. + But note that <sys/int_types.h> contains only the type definitions! */ +# define HAVE_SYSTEM_INTTYPES +#endif +#if (defined(__hpux) || defined(_AIX)) && HAVE_INTTYPES_H +# include <inttypes.h> + /* HP-UX 10 <inttypes.h> has nearly everything, except UINT_LEAST8_MAX, + UINT_FAST8_MAX, PTRDIFF_MIN, PTRDIFF_MAX. */ + /* AIX 4 <inttypes.h> has nearly everything, except INTPTR_MIN, INTPTR_MAX, + UINTPTR_MAX, PTRDIFF_MIN, PTRDIFF_MAX. */ +# define HAVE_SYSTEM_INTTYPES +#endif +#if !(defined(UNIX_CYGWIN32) && defined(__BIT_TYPES_DEFINED__)) +# define NEED_SIGNED_INT_TYPES +#endif + +#if !defined(HAVE_SYSTEM_INTTYPES) + +/* 7.18.1.1. Exact-width integer types */ +#if !defined(__FreeBSD__) + +#if defined(_MSC_VER) +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +#else // _MSC_VER + +#ifdef NEED_SIGNED_INT_TYPES +typedef signed char int8_t; +#endif +typedef unsigned char uint8_t; + +#ifdef NEED_SIGNED_INT_TYPES +typedef short int16_t; +#endif +typedef unsigned short uint16_t; + +#ifdef NEED_SIGNED_INT_TYPES +typedef int int32_t; +#endif +typedef unsigned int uint32_t; + +#if 0 +#ifdef NEED_SIGNED_INT_TYPES +typedef long int64_t; +#endif +typedef unsigned long uint64_t; +#elif 0 +#ifdef NEED_SIGNED_INT_TYPES +typedef long long int64_t; +#endif +typedef unsigned long long uint64_t; +#endif + +#endif // _MSC_VER + +#endif /* !FreeBSD */ + +/* 7.18.1.2. Minimum-width integer types */ + +typedef int8_t int_least8_t; +typedef uint8_t uint_least8_t; +typedef int16_t int_least16_t; +typedef uint16_t uint_least16_t; +#if !defined(kAlturaAlreadyDefinesInt32) +typedef int32_t int_least32_t; +#endif +typedef uint32_t uint_least32_t; +typedef int64_t int_least64_t; +typedef uint64_t uint_least64_t; + + +/* 7.18.1.3. Fastest minimum-width integer types */ + +typedef int32_t int_fast8_t; +typedef uint32_t uint_fast8_t; +typedef int32_t int_fast16_t; +typedef uint32_t uint_fast16_t; +typedef int32_t int_fast32_t; +typedef uint32_t uint_fast32_t; +typedef int64_t int_fast64_t; +typedef uint64_t uint_fast64_t; + + +/* 7.18.1.4. Integer types capable of holding object pointers */ + +#if !defined(__FreeBSD__) + +/* On some platforms (like IRIX6 MIPS with -n32) sizeof(void*) < sizeof(long), + but this doesn't matter here. */ +#if !defined(_INTPTR_T_DEFINED) +typedef long intptr_t; +#define _INTPTR_T_DEFINED +#endif +#if !defined(_UINTPTR_T_DEFINED) +typedef unsigned long uintptr_t; +#define _UINTPTR_T_DEFINED +#endif + +#endif /* !FreeBSD */ + +/* 7.18.1.5. Greatest-width integer types */ + + +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; +#if 0 || 0 +typedef int32_t intmax_t; +typedef uint32_t uintmax_t; +#endif + +/* 7.18.2. Limits of specified-width integer types */ + +//#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) + +/* 7.18.2.1. Limits of exact-width integer types */ + +#define INT8_MIN -128 +#define INT8_MAX 127 +#define UINT8_MAX 255U +#define INT16_MIN -32768 +#define INT16_MAX 32767 +#define UINT16_MAX 65535U +#define INT32_MIN (~INT32_MAX) +#define INT32_MAX 2147483647 +#define UINT32_MAX 4294967295U +#if 0 +#define INT64_MIN (~INT64_MIN) +#define INT64_MAX 9223372036854775807L +#define UINT64_MAX 18446744073709551615UL +#elif 0 +#define INT64_MIN (~INT64_MIN) +#define INT64_MAX 9223372036854775807LL +#define UINT64_MAX 18446744073709551615ULL +#endif + +/* 7.18.2.2. Limits of minimum-width integer types */ + +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#if 0 || 0 +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST64_MAX UINT64_MAX +#endif + +/* 7.18.2.3. Limits of fastest minimum-width integer types */ + +#define INT_FAST8_MIN INT32_MIN +#define INT_FAST8_MAX INT32_MAX +#define UINT_FAST8_MAX UINT32_MAX +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST16_MAX INT32_MAX +#define UINT_FAST16_MAX UINT32_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define UINT_FAST32_MAX UINT32_MAX +#if 0 || 0 +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST64_MAX UINT64_MAX +#endif + +/* 7.18.2.4. Limits of integer types capable of holding object pointers */ + +#define INTPTR_MIN LONG_MIN +#define INTPTR_MAX LONG_MAX +#define UINTPTR_MAX ULONG_MAX + +/* 7.18.2.5. Limits of greatest-width integer types */ + +#if 0 || 0 +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX +#else +#define INTMAX_MIN INT32_MIN +#define INTMAX_MAX INT32_MAX +#define UINTMAX_MAX UINT32_MAX +#endif + +/* 7.18.3. Limits of other integer types */ + +#define PTRDIFF_MIN (~(ptrdiff_t)0 << (sizeof(ptrdiff_t)*CHAR_BIT-1)) +#define PTRDIFF_MAX (~PTRDIFF_MIN) + +/* This may be wrong... */ +#define SIG_ATOMIC_MIN 0 +#define SIG_ATOMIC_MAX 127 + +//#define SIZE_MAX (~(size_t)0) + +/* wchar_t limits already defined in <stddef.h>. */ +/* wint_t limits already defined in <wchar.h>. */ + +//#endif + +/* 7.18.4. Macros for integer constants */ + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) + +/* 7.18.4.1. Macros for minimum-width integer constants */ + +#ifdef INT8_C +#undef INT8_C +#endif +#define INT8_C(x) x + +#ifdef UINT8_C +#undef UINT8_C +#endif +#define UINT8_C(x) x##U + +#ifdef INT16_C +#undef INT16_C +#endif +#define INT16_C(x) x + +#ifdef UINT16_C +#undef UINT16_C +#endif +#define UINT16_C(x) x##U + +#ifdef INT32_C +#undef INT32_C +#endif +#define INT32_C(x) x + +#ifdef UINT32_C +#undef UINT32_C +#endif +#define UINT32_C(x) x##U + +// INT64_C and UINT64_C definitions +#ifdef INT64_C +#undef INT64_C +#endif +#ifdef UINT64_C +#undef UINT64_C +#endif +#if 0 +#define INT64_C(x) x##L +#define UINT64_C(x) x##UL +#elif 0 +#define INT64_C(x) x##LL +#define UINT64_C(x) x##ULL +#endif // #if 0 + +/* 7.18.4.2. Macros for greatest-width integer constants */ + +// INTMAX_C and UINTMAX_C definitions +#ifdef INTMAX_C +#undef INTMAX_C +#endif +#ifdef UINTMAX_C +#undef UINTMAX_C +#endif + +#if 0 +#define INTMAX_C(x) x##L +#define UINTMAX_C(x) x##UL +#elif 0 +#define INTMAX_C(x) x##LL +#define UINTMAX_C(x) x##ULL +#else +#define INTMAX_C(x) x +#define UINTMAX_C(x) x##U +#endif + +#endif + +#endif /* !HAVE_SYSTEM_INTTYPES */ + +#endif /* (_MSC_VER < 1400) */ + +#endif /* #ifdef _WINDOWS */ + +#endif /* __stdint_h__ */ diff --git a/libs/backends/wavesaudio/wscript b/libs/backends/wavesaudio/wscript new file mode 100644 index 0000000000..2d4ee0a2e7 --- /dev/null +++ b/libs/backends/wavesaudio/wscript @@ -0,0 +1,73 @@ +#!/usr/bin/env python +from waflib.extras import autowaf as autowaf +import os +import sys +import re + +# Library version (UNIX style major, minor, micro) +# major increment <=> incompatible changes +# minor increment <=> compatible changes (additions) +# micro increment <=> no interface changes +WAVESAUDIOBACKEND_VERSION = '0.0.1' +I18N_PACKAGE = 'wavesaudio-backend' + +# Mandatory variables +top = '.' +out = 'build' + +def options(opt): + autowaf.set_options(opt) + +def configure(conf): + autowaf.configure(conf) + +def build(bld): + obj = bld(features = 'cxx cxxshlib') + if bld.env['build_target'] == 'mountain_lion': + obj.framework = 'CoreMidi' + else: + obj.framework = 'CoreMIDI' + obj.source = [ + 'waves_audiobackend.cc', + 'waves_audiobackend.latency.cc', + 'waves_audiobackend.midi.cc', + 'waves_audiobackend.port_engine.cc', + 'waves_dataport.cc', + 'waves_audioport.cc', + 'waves_midiport.cc', + 'waves_midi_device_manager.cc', + 'waves_midi_device.cc', + 'waves_midi_event.cc', + 'waves_midi_buffer.cc', + 'wavesapi/refmanager/WCRefManager.cpp', + 'wavesapi/devicemanager/WCMRAudioDeviceManager.cpp', + 'wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp', + 'wavesapi/devicemanager/WCMRNativeAudio.cpp', + 'wavesapi/threads/WCThreadSafe.cpp', + 'portmidi/src/pm_common/pmutil.c', + 'portmidi/src/pm_common/portmidi.c', + 'portmidi/src/pm_mac/pmmac.c', + 'portmidi/src/pm_mac/pmmacosxcm.c', + 'portmidi/src/pm_mac/finddefault.c', + 'portmidi/src/pm_mac/readbinaryplist.c', + 'portmidi/src/porttime/ptmacosx_mach.c' + ] + obj.includes = ['.', + 'wavesapi', + 'wavesapi/refmanager', + 'wavesapi/wavespublicapi', + 'wavesapi/devicemanager', + 'wavesapi/miscutils', + 'portmidi', + 'portmidi/src/pm_common' + ] + obj.cxxflags = [ '-fPIC' ] + obj.name = 'waves_audiobackend' + obj.target = 'waves_audiobackend' + obj.use = [ 'libardour', 'libpbd' ] + obj.vnum = WAVESAUDIOBACKEND_VERSION + obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardour3', 'backends') + obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"', + '__MACOS__', + 'ARDOURBACKEND_DLL_EXPORTS' + ] |