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 +#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 // 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 +#include +#include +#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 + +#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 + +#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, isysex_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 +#include +#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 = ""; }; + 3DE21390124655760033C839 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = ""; }; + 3DE213BE1246557F0033C839 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = ""; }; + 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 = ""; }; + 89D0F0220F392F20007831A7 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 89D0F0230F392F20007831A7 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 89D0F03E0F39304A007831A7 /* JavaApplicationStub */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = JavaApplicationStub; path = /System/Library/Frameworks/JavaVM.framework/Versions/A/Resources/MacOS/JavaApplicationStub; sourceTree = ""; }; + 89D0F0840F394066007831A7 /* JavaNativeFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaNativeFoundation.framework; path = /System/Library/Frameworks/JavaVM.framework/Versions/A/Frameworks/JavaNativeFoundation.framework; sourceTree = ""; }; + 89D0F1390F3948A9007831A7 /* pmdefaults/make */ = {isa = PBXFileReference; lastKnownFileType = folder; path = pmdefaults/make; sourceTree = ""; }; + 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 = ""; }; + 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 = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 89D0F15D0F3A0FF7007831A7 /* pmdefaults.jar */, + 8D1107320486CEB800E47090 /* PmDefaults.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* pmdefaults */ = { + isa = PBXGroup; + children = ( + 3DE216101246ABE30033C839 /* libpmjni.dylib */, + 89D0F0260F392F48007831A7 /* Source */, + 89D0F0200F392F20007831A7 /* Resources */, + 89D0F1390F3948A9007831A7 /* pmdefaults/make */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = pmdefaults; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 3DE2136A124652E20033C839 /* pm_java */ = { + isa = PBXGroup; + children = ( + 3DE21379124653150033C839 /* pmdefaults */, + 3DE2137A1246531D0033C839 /* jportmidi */, + ); + name = pm_java; + path = ..; + sourceTree = ""; + }; + 3DE21379124653150033C839 /* pmdefaults */ = { + isa = PBXGroup; + children = ( + 3DE2137D124653CB0033C839 /* PmDefaultsFrame.java */, + 3DE2137B1246538B0033C839 /* PmDefaults.java */, + ); + name = pmdefaults; + sourceTree = ""; + }; + 3DE2137A1246531D0033C839 /* jportmidi */ = { + isa = PBXGroup; + children = ( + 3DE21382124654DE0033C839 /* JPortMidiException.java */, + 3DE21381124654CF0033C839 /* JPortMidiApi.java */, + 3DE21380124654BC0033C839 /* JPortMidi.java */, + ); + name = jportmidi; + sourceTree = ""; + }; + 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 = ""; + }; + 89D0F0260F392F48007831A7 /* Source */ = { + isa = PBXGroup; + children = ( + 3DE2136A124652E20033C839 /* pm_java */, + ); + name = Source; + path = pmdefaults/src; + sourceTree = ""; + }; +/* 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 = ""; + }; + 89D0F0210F392F20007831A7 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 89D0F0220F392F20007831A7 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* 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 @@ + + + + + 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 new file mode 100644 index 0000000000..104c0fe910 Binary files /dev/null and b/libs/backends/wavesaudio/portmidi/src/pm_mac/pm_mac.xcodeproj/project.xcworkspace/xcuserdata/VKamyshniy.xcuserdatad/UserInterfaceState.xcuserstate differ 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 @@ + + + + + SchemeUserState + + Assemble Application.xcscheme + + orderHint + 4 + + Compile Java.xcscheme + + orderHint + 3 + + CopyJavaSources.xcscheme + + orderHint + 2 + + JPortMidiHeaders.xcscheme + + orderHint + 0 + + PmDefaults.xcscheme + + orderHint + 1 + + + SuppressBuildableAutocreation + + 3D634CAB1247805C0020F829 + + primary + + + 3DE2142D124662AA0033C839 + + primary + + + 89D0F0480F393A6F007831A7 + + primary + + + 89D0F1C90F3B704E007831A7 + + primary + + + 8D1107260486CEB800E47090 + + primary + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + "Nothing to do for install-headers phase" + + 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 new file mode 100644 index 0000000000..c7e5600f98 Binary files /dev/null and b/libs/backends/wavesaudio/portmidi/src/pm_mac/pmdefaults/resources/English.lproj/InfoPlist.strings differ 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 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + JavaApplicationStub + CFBundleIconFile + pmdefaults.icns + CFBundleIdentifier + + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + PmDefaults + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CFBundleShortVersionString + 1.0 + Java + + ClassPath + $JAVAROOT/pmdefaults.jar + JVMVersion + 1.5+ + MainClass + pmdefaults.PmDefaults + Properties + + apple.laf.useScreenMenuBar + true + + + + 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 + * 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 + +//#define CM_DEBUG 1 + +#include "portmidi.h" +#include "pmutil.h" +#include "pminternal.h" +#include "porttime.h" +#include "pmmac.h" +#include "pmmacosxcm.h" + +#include +#include + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include "readbinaryplist.h" +#include + +#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 CF$UIDvalue, 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 /* 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 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 + +/* 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 +#include "porttime.h" + +/* asserts used to verify portMidi code logic is sound; later may want + something more graceful */ +#include +#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 +#include +#include + +#import +#import +#import +#import +#include + +#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 +WavesAudioBackend::enumerate_drivers () const +{ + // this backend does not suppose driver selection + assert (false); + + return std::vector (); +} + + +int +WavesAudioBackend::set_driver (const std::string& /*drivername*/) +{ + //Waves audio backend does not suppose driver selection + assert (false); + + return -1; +} + + +std::vector +WavesAudioBackend::enumerate_devices () const +{ + /* COMMENTED DBG LOGS */ std::cout << "WavesAudioBackend::enumerate_devices (): " << std::endl; + + std::vector 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 +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 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 (); + } + + sr = device->SamplingRates (); + /* COMMENTED DBG LOGS */ std::cout << "\tFound " << sr.size () << " sample rates for " << device->DeviceName () << ":"; + + std::vector sample_rates (sr.begin (), sr.end ()); + + /* COMMENTED DBG LOGS */ for (std::vector::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 +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 (); + } + + std::vector 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::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 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 ("")) << "]!" << 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 (_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(_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 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 (arg); + boost::function 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::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::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::iterator it = _physical_audio_inputs.begin (); + it != _physical_audio_inputs.end(); + ++it) + { + memcpy((*it)->buffer(), *buffer, copied_bytes); + ++buffer; + } +#else + std::vector::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::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::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 __instance; + + +boost::shared_ptr +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 +#include +#include + +#include +#include + +#include + +#include "ardour/types.h" +#include "ardour/audio_backend.h" + +#include "waves_midi_device_manager.h" + +#ifdef __MACOS__ + +#include + +class ArdourAudioDeviceManager : public WCMRCoreAudioDeviceManager +{ + public: + ArdourAudioDeviceManager (WCMRAudioDeviceManagerClient *client) : WCMRCoreAudioDeviceManager (client, eFullDuplexDevices, true, eCABS_Simple, false) {}; +}; + +#elif defined (_WINDOWS) + +#include + +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 enumerate_drivers () const; + + virtual int set_driver (const std::string& /*drivername*/); + + virtual std::vector enumerate_devices () const; + + virtual std::vector available_sample_rates (const std::string& device) const; + + virtual float default_sample_rate () const; + + virtual std::vector 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 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 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& 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&, 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& port_names); + + virtual void get_physical_inputs (DataType type, std::vector& 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 _backend_threads; + static const size_t __max_raw_midi_buffer_size; + + static const std::vector __available_midi_options; + bool _use_midi; + + struct ThreadData { + WavesAudioBackend* engine; + boost::function f; + size_t stacksize; + + ThreadData (WavesAudioBackend* e, boost::function fp, size_t stacksz) + : engine (e) , f (fp) , stacksize (stacksz) {} + }; + + static boost::shared_ptr __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 _physical_audio_inputs; + std::vector _physical_audio_outputs; + std::vector _physical_midi_inputs; + std::vector _physical_midi_outputs; + std::vector _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 _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 + +#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 WavesAudioBackend::__available_midi_options = boost::assign::list_of ("None") ("CoreMIDI"); + +#elif _WINDOWS + +const std::vector WavesAudioBackend::__available_midi_options = boost::assign::list_of ("None") ("Multimedia Extensions"); + +#endif + + +std::vector +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 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::const_iterator it = physical_midi_ports.begin (); it != physical_midi_ports.end (); ++it) { + std::vector::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 (_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& devices = _midi_device_manager.devices (); + + for (std::vector::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 (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 (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 (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 (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::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::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::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& 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::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::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::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::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::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& 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& connected_ports = ((WavesDataPort*)port_handle)->get_connections (); + + for (std::vector::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& 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::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::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& 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::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::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 !" << 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 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::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 (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 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::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 (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 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::const_iterator it = physical_audio_ports.begin (); it != physical_audio_ports.end (); ++it) { + std::vector::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::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::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::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& 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 _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 () + , _name (name) +{ +} + +WavesMidiBuffer::~WavesMidiBuffer () +{ + clear (); +} + +void WavesMidiBuffer::clear () +{ + for (WavesMidiBufferIterator it = begin (); it != end (); ++it) + delete *it; + + std::vector::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 +{ +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::iterator WavesMidiBufferIterator; +typedef std::vector::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 +#include +#include + +#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 + +#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::const_iterator it = _devices.begin (); it != _devices.end (); ++it) { + (*it)->read_midi (); + } +} + + +void WavesMidiDeviceManager::do_write () +{ + for (std::vector::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& 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 _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 +#include +#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::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 + 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(&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(in_PtrToPack); + // This packing is xPlatform coding for x32 and x64 + // #ifdef for x64 - intptr_t is 64 bit + retVal.LSW = static_cast(t_PtrToPack & intptr_t(0xFFFFFFFF)); + retVal.MSW = (static_cast(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(in_ComPtrToUnpack.MSW); + PtrAt64 <<= 32; + PtrAt64 |= static_cast(in_ComPtrToUnpack.LSW); + retVal = static_cast(PtrAt64); + + + // #ifdef for x32 - intptr_t is 32 bit +// retVal = static_cast(retVal.LSW); + + return retVal; +} + + +////////////////////////////////////////////////////////////////////////// +inline uint32_t ComPtr_to_DSP( const intptr_t PtrToConvert, char* pDataStruct ) +{ + + *(reinterpret_cast(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(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 +#include +/******************************************************************************** + 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 WTPathString; +typedef WCFixedString 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::value + // For templates that "return" a type, use template_name::type + + + // Integer log2 functions + //------------------------------------------------------------------------ + template + 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 + struct compiletime_log2_ceiling { static const unsigned int value=compiletime_bit_count_to_represent::value; }; + + template<> + struct compiletime_log2_ceiling<0> {}; // no value for 0 argument + //------------------------------------------------------------------------ + template + struct compiletime_log2_floor { static const unsigned int value=compiletime_bit_count_to_represent::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 + struct compiletime_assert; + + template<> + struct compiletime_assert { static const bool value=true; }; + + template<> + struct compiletime_assert {}; // no value member for false assertion -> compile time error + //------------------------------------------------------------------------ + + + // Select type - selects one of two types based on a boolean + //------------------------------------------------------------------------ + template + struct compiletime_select_type; + + template + struct compiletime_select_type { typedef _true_type type; }; + + template + struct compiletime_select_type { typedef _false_type type; }; + //------------------------------------------------------------------------ + + + + + + // Integer types by byte count + //------------------------------------------------------------------------ + namespace detail + { + template + 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 + 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::value; // if you get a compilation error here then integer_with_byte_count is not defined correctly + }; + //------------------------------------------------------------------------ + template + struct signed_integer_with_byte_count : public integer_with_byte_count<_size,true> {}; + + template + 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 + struct integral_constant + { + static const _T value = _v; + typedef _T value_type; + typedef integral_constant<_T, _v> type; + }; // struct integral_constant + typedef integral_constant false_type; + typedef integral_constant true_type; + //------------------------------------------------------------------------ + template struct is_same : public false_type {}; + template 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 struct signed_unsigned_pair; +#define AKUPARA_SIGNED_UNSIGNED_INTEGER_PAIR(index, base_type_name) \ + template<> struct signed_unsigned_pair { 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 { 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 struct filtered_type { typedef _T type; }; + template<> struct filtered_type {}; // no type defined + + // search for _T in signed type list + template 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 struct find_in_signed_type_list_from_index<0, _T> { static const unsigned int value = 0; }; + template struct find_in_signed_type_list : public find_in_signed_type_list_from_index {}; + + // search for _T in unsigned type list + template 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 struct find_in_unsigned_type_list_from_index<0, _T> { static const unsigned int value = 0; }; + template struct find_in_unsigned_type_list : public find_in_unsigned_type_list_from_index {}; + + template struct equivalent_signed_type; + template struct equivalent_signed_type { typedef _T type; }; + template struct equivalent_signed_type { typedef typename filtered_type< typename signed_unsigned_pair< find_in_unsigned_type_list<_T>::value >::signed_type >::type type; }; + + template struct equivalent_unsigned_type; + template struct equivalent_unsigned_type { typedef typename filtered_type< typename signed_unsigned_pair< find_in_signed_type_list<_T>::value >::unsigned_type >::type type; }; + template struct equivalent_unsigned_type { typedef _T type; }; + } // namespace detail + //------------------------------------------------------------------------ + template struct is_signed { static const bool value = detail::find_in_signed_type_list <_T>::value != 0; }; + template struct is_unsigned { static const bool value = detail::find_in_unsigned_type_list<_T>::value != 0; }; + //------------------------------------------------------------------------ + template struct equivalent_signed_type : public detail::equivalent_signed_type < is_signed<_T>::value, is_unsigned<_T>::value, _T > {}; + template 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 struct implements_load : public false_type {}; // simple assignment from memory (assumes naturally aligned address) + template struct implements_store : public false_type {}; // simple assignment to memory (assumes naturally aligned address) + template struct implements_CAS : public false_type {}; // compare_and_store() + template struct implements_LL_SC : public false_type {}; // load_linked(), store_conditional() + template struct implements_add : public false_type {}; // add(), subtract() + template struct implements_fetch_and_add : public false_type {}; // fetch_and_add(), fetch_and_subtract() + template 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 bool compare_and_store(volatile _integer_type * operand_address, const _integer_type & expected_value, const _integer_type & value_to_store); + + template _integer_type load_linked(volatile _integer_type * operand_address); + template bool store_conditional(volatile _integer_type * operand_address, const _integer_type & value_to_store); + + template void add(volatile _integer_type * operand_address, const _integer_type & addend); + template void subtract(volatile _integer_type * operand_address, const _integer_type & subtrahend); + + template _integer_type fetch_and_add(volatile _integer_type * operand_address, const _integer_type & addend); + template _integer_type fetch_and_subtract(volatile _integer_type * operand_address, const _integer_type & subtrahend); + + template _integer_type add_and_fetch(volatile _integer_type * operand_address, const _integer_type & addend); + template _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 + 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::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 ::value, + bool _implements_LL_SC = machine::implements_LL_SC::value> + struct implementation_CAS + { + static const bool s_exists = false; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for native CAS support + template + 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 + 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 + 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::value, + bool _1 = machine::implements_add_and_fetch::value, + bool _2 = machine::implements_LL_SC ::value, + bool _3 = machine::implements_CAS ::value> + struct implementation_FAA + { + static const bool s_exists = false; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for native support + template + 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 + 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 + 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 + 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 + 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::value, + bool _1 = machine::implements_fetch_and_add::value, + bool _2 = machine::implements_LL_SC ::value, + bool _3 = machine::implements_CAS ::value> + struct implementation_AAF + { + static const bool s_exists = false; + }; + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // specialization for native support + template + 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 + 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 + 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 + 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 + 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 + inline void add(volatile _integer_type * operand_address, const _integer_type & addend) + { + if (machine::implements_add::value) + machine::add(operand_address, addend); + else if (machine::implements_fetch_and_add::value) + machine::fetch_and_add(operand_address, addend); + else if (machine::implements_add_and_fetch::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 + 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 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(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(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(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(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(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(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 ( ...) +#endif + +#include +#include +#include +#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& 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& WCMRAudioDevice::OutputChannels () +{ + return (m_OutputChannels); +} + + + + +//********************************************************************************************** +// WCMRAudioDevice::SamplingRates +// +//! Retrieves supported sampling rate information. +//! +//! \param none +//! +//! \return A vector with supported sampling rates. +//! +//********************************************************************************************** +const std::vector& 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& 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& 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& 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::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::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 +#include +#include +#include "WCRefManager.h" +#include "BasicTypes/WUTypes.h" +#include "WUErrors.h" + +#define WCUNUSEDPARAM(a) + +//forward decl. +class WCMRAudioConnection; +class WCMRAudioDevice; +class WCMRAudioDeviceManager; + +typedef std::vector WCMRAudioDeviceList; ///< Vector for audio devices +typedef std::vector::iterator WCMRAudioDeviceListIter; ///< Vector iterator for audio devices +typedef std::vector::const_iterator WCMRAudioDeviceListConstIter; ///< Vector iterator for audio devices +typedef std::vector 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);///& InputChannels();///& OutputChannels();///& SamplingRates();///& BufferSizes();///& InputLevels();///& OutputLevels();/// m_InputChannels; ///< List of input channel names. + std::vector m_OutputChannels; ///< List of output channel names. + std::vector m_SamplingRates; ///< List of available sampling rates. + std::vector 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 m_InputLevels; ///< List of input levels. + std::vector 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 +#include "MiscUtils/safe_delete.h" +#include +#include + +// 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 + +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::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::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& 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& 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& 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::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::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(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& 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::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::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::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 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& 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::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::iterator deviceIter; + for (deviceIter = m_Devices.begin(); deviceIter != m_Devices.end(); /*This is purposefully blank*/) + { + WCMRCoreAudioDevice *pDeviceToWorkUpon = dynamic_cast(*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(*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 +#include + +#include + +#include + +//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);///& InputChannels();///& OutputChannels();///& SamplingRates();///& BufferSizes();///& 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 +#endif + +#include "WCMRNativeAudio.h" +#include "MiscUtils/safe_delete.h" +#include +#include + +#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::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);/// inline T WUMin(const T &a, const T &b) {return (a < b) ? a : b;} // requires only < to be defined for T +template inline T WUMax(const T &a,const T &b) {return (a < b) ? b : a;} // requires only < to be defined for T +template 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 inline T WUMin(T a,T b) {return (a < b) ? a : b;} // requires only < to be defined for T + template inline T WUMax(T a,T b) {return (a < b) ? b : a;} // requires only < to be defined for T + template 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 +#define __abs(x) abs(x) +#define __labs(x) labs(x) +#define __fabs(x) fabs(x) +#endif +#ifdef __GNUC__ + #include // why don't know makes it work need to check + #include + #include + +#define __abs(x) std::abs(x) +#define __labs(x) std::labs(x) +#define __fabs(x) std::fabs(x) +#endif + #ifdef __MACOS__ + #ifdef __GNUC__ + #include // why don't know makes it work need to check + #include +#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 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 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 +#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(&theTime)->hi;} + uint32_t UMicroseconds::lo() {return reinterpret_cast(&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(theTime);} + operator double () const {return static_cast(theTime);} + + double Seconds() const {return static_cast(theTime) / double(1000000);} + double MilliSeconds() const {return static_cast(theTime) / double(1000);} + double MicroSeconds() const {return static_cast(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 +#include +#include +#include + +#ifdef __MACOS__ +#include +#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(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(std::tolower(*pToDo)); + ++pToDo; + } + } + + inline void to_upper() + { + char* pToDo = m_begin; + + while (pToDo < m_end) + { + *pToDo = static_cast(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(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(in_uint)); + } +#endif +// WCFixedStringBase& operator<<(const unsigned char in_uint) { +// return operator<<(static_cast(in_uint)); +// } +// +// WCFixedStringBase& operator<<(const size_t in_uint) { +// return operator<<(static_cast(in_uint)); +// } +// +#if defined(__MACOS__) || defined(_WINDOWS) || defined(__linux__) // both 32 & 64 bit + WCFixedStringBase& operator<<(const unsigned int in_uint) { + return operator<<(static_cast(in_uint)); + } +#endif +// +#if defined(_WINDOWS) || defined(__linux__) // both 32 & 64 bit + WCFixedStringBase& operator<<(const unsigned long in_uint) { + return operator<<(static_cast(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(-in_int) : static_cast(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(in_int)); + } + + WCFixedStringBase& operator<<(const int in_int) { + return operator<<(static_cast(in_int)); + } + + WCFixedStringBase& operator<<(const long in_int) { + return operator<<(static_cast(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 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(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 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(in_pos, size()); + size_t adjusted_length = 0; + if (in_length < 0) + { + adjusted_length = size_t(WUMax(0, spos_t(size() - adjusted_pos) + in_length)); + } + else + adjusted_length = WUMin(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 + class WCFixedStringPair : public std::pair< WCFixedString, WCFixedString > +{ +public: + WCFixedStringPair(const char* const in_firstStr = 0, const char* const in_secondStr = 0) : + std::pair< WCFixedString, WCFixedString >(in_firstStr, in_secondStr) {} + WCFixedStringPair(const WCFixedStringBase& in_firstStr, const char* const in_secondStr = 0) : + std::pair< WCFixedString, WCFixedString >(in_firstStr, in_secondStr) {} + WCFixedStringPair(const WCFixedStringBase& in_firstStr, const WCFixedStringBase& in_secondStr) : + std::pair< WCFixedString, WCFixedString >(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 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 +#endif // XPLATFORMTHREADS_WINDOWS + + +#if defined(__MACOS__) + #include + #include +#endif // __MACOS__ + +#if XPLATFORMTHREADS_POSIX + #include // avoid the framework version and use the /usr/include version + #include + #include + #include + #include + #include +// We do this externs because 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 + +#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(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(arg); + ThreadFunction *func=twd->func; + ThreadFunctionArgument farg=twd->arg; + delete twd; + return reinterpret_cast(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(h); } + static inline HANDLE to_oshandle(uintptr_t h) { return reinterpret_cast(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(&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 + +#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_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(0); } + inline bool wait_for_ring(timediff timeout) { return wait_for_ring_internal(timeout); } + }; +#else + class doorbell_type : public noncopyableobject + { + protected: + os_dependent_handle_type m_os_dependent_handle; + protected: + template 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 + #include // Mac has this file in /usr/includez +#endif +#ifdef __linux__ + #if ! defined(__STDC_LIMIT_MACROS) + #define __STDC_LIMIT_MACROS + #endif + + #include + #include +#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 +#else +#ifndef _STDINT_H + #define _STDINT_H // this will prevent Altura's CGBase.h from defining int32_t +#endif +/* + * ISO C 99 for platforms that lack it. + * + */ + +/* Get wchar_t, WCHAR_MIN, WCHAR_MAX. */ +#include +/* Get CHAR_BIT, LONG_MIN, LONG_MAX, ULONG_MAX. */ +#include + +/* Get those types that are already defined in other system include files. */ +#if defined(__FreeBSD__) +# include +#endif + +#if defined(__sun) && HAVE_SYS_INTTYPES_H +# include + /* Solaris 7 has the types except the *_fast*_t types, and + the macros except for *_FAST*_*, INTPTR_MIN, PTRDIFF_MIN, PTRDIFF_MAX. + But note that contains only the type definitions! */ +# define HAVE_SYSTEM_INTTYPES +#endif +#if (defined(__hpux) || defined(_AIX)) && HAVE_INTTYPES_H +# include + /* HP-UX 10 has nearly everything, except UINT_LEAST8_MAX, + UINT_FAST8_MAX, PTRDIFF_MIN, PTRDIFF_MAX. */ + /* AIX 4 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 . */ +/* wint_t limits already defined in . */ + +//#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' + ]