@@ -1,486 +1,487 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
Copyright 2004-7 by Raw Material Software ltd. | |||
------------------------------------------------------------------------------ | |||
JUCE can be redistributed and/or modified 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. | |||
JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the | |||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
Boston, MA 02111-1307 USA | |||
------------------------------------------------------------------------------ | |||
If you'd like to release a closed-source product which uses JUCE, commercial | |||
licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
more information. | |||
============================================================================== | |||
*/ | |||
#include "../../../juce_Config.h" | |||
#if JUCE_ALSA | |||
#include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
#include <alsa/asoundlib.h> | |||
BEGIN_JUCE_NAMESPACE | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" | |||
#include "../../../src/juce_core/threads/juce_Thread.h" | |||
#include "../../../src/juce_core/basics/juce_Time.h" | |||
//============================================================================== | |||
static snd_seq_t* iterateDevices (const bool forInput, | |||
StringArray& deviceNamesFound, | |||
const int deviceIndexToOpen) | |||
{ | |||
snd_seq_t* returnedHandle = 0; | |||
snd_seq_t* seqHandle; | |||
if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
: SND_SEQ_OPEN_OUTPUT, 0) == 0) | |||
{ | |||
snd_seq_system_info_t* systemInfo; | |||
snd_seq_client_info_t* clientInfo; | |||
if (snd_seq_system_info_malloc (&systemInfo) == 0) | |||
{ | |||
if (snd_seq_system_info (seqHandle, systemInfo) == 0 | |||
&& snd_seq_client_info_malloc (&clientInfo) == 0) | |||
{ | |||
int numClients = snd_seq_system_info_get_cur_clients (systemInfo); | |||
while (--numClients >= 0 && returnedHandle == 0) | |||
{ | |||
if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) | |||
{ | |||
snd_seq_port_info_t* portInfo; | |||
if (snd_seq_port_info_malloc (&portInfo) == 0) | |||
{ | |||
int numPorts = snd_seq_client_info_get_num_ports (clientInfo); | |||
const int client = snd_seq_client_info_get_client (clientInfo); | |||
snd_seq_port_info_set_client (portInfo, client); | |||
snd_seq_port_info_set_port (portInfo, -1); | |||
while (--numPorts >= 0) | |||
{ | |||
if (snd_seq_query_next_port (seqHandle, portInfo) == 0 | |||
&& (snd_seq_port_info_get_capability (portInfo) | |||
& (forInput ? SND_SEQ_PORT_CAP_READ | |||
: SND_SEQ_PORT_CAP_WRITE)) != 0) | |||
{ | |||
deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); | |||
if (deviceNamesFound.size() == deviceIndexToOpen + 1) | |||
{ | |||
const int sourcePort = snd_seq_port_info_get_port (portInfo); | |||
const int sourceClient = snd_seq_client_info_get_client (clientInfo); | |||
if (sourcePort != -1) | |||
{ | |||
snd_seq_set_client_name (seqHandle, | |||
forInput ? "Juce Midi Input" | |||
: "Juce Midi Output"); | |||
const int portId | |||
= snd_seq_create_simple_port (seqHandle, | |||
forInput ? "Juce Midi In Port" | |||
: "Juce Midi Out Port", | |||
forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) | |||
: (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), | |||
SND_SEQ_PORT_TYPE_MIDI_GENERIC); | |||
snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort); | |||
returnedHandle = seqHandle; | |||
} | |||
} | |||
} | |||
} | |||
snd_seq_port_info_free (portInfo); | |||
} | |||
} | |||
} | |||
snd_seq_client_info_free (clientInfo); | |||
} | |||
snd_seq_system_info_free (systemInfo); | |||
} | |||
if (returnedHandle == 0) | |||
snd_seq_close (seqHandle); | |||
} | |||
deviceNamesFound.appendNumbersToDuplicates (true, true); | |||
return returnedHandle; | |||
} | |||
static snd_seq_t* createDevice (const bool forInput, | |||
const String& deviceNameToOpen) | |||
{ | |||
snd_seq_t* seqHandle = 0; | |||
if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
: SND_SEQ_OPEN_OUTPUT, 0) == 0) | |||
{ | |||
snd_seq_set_client_name (seqHandle, | |||
(const char*) (forInput ? (deviceNameToOpen + T(" Input")) | |||
: (deviceNameToOpen + T(" Output")))); | |||
const int portId | |||
= snd_seq_create_simple_port (seqHandle, | |||
forInput ? "in" | |||
: "out", | |||
forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) | |||
: (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), | |||
forInput ? SND_SEQ_PORT_TYPE_APPLICATION | |||
: SND_SEQ_PORT_TYPE_MIDI_GENERIC); | |||
if (portId < 0) | |||
{ | |||
snd_seq_close (seqHandle); | |||
seqHandle = 0; | |||
} | |||
} | |||
return seqHandle; | |||
} | |||
//============================================================================== | |||
class MidiOutputDevice | |||
{ | |||
public: | |||
MidiOutputDevice (MidiOutput* const midiOutput_, | |||
snd_seq_t* const seqHandle_) | |||
: | |||
midiOutput (midiOutput_), | |||
seqHandle (seqHandle_), | |||
maxEventSize (16 * 1024) | |||
{ | |||
jassert (seqHandle != 0 && midiOutput != 0); | |||
snd_midi_event_new (maxEventSize, &midiParser); | |||
} | |||
~MidiOutputDevice() | |||
{ | |||
snd_midi_event_free (midiParser); | |||
snd_seq_close (seqHandle); | |||
} | |||
void sendMessageNow (const MidiMessage& message) | |||
{ | |||
if (message.getRawDataSize() > maxEventSize) | |||
{ | |||
maxEventSize = message.getRawDataSize(); | |||
snd_midi_event_free (midiParser); | |||
snd_midi_event_new (maxEventSize, &midiParser); | |||
} | |||
snd_seq_event_t event; | |||
snd_seq_ev_clear (&event); | |||
snd_midi_event_encode (midiParser, | |||
message.getRawData(), | |||
message.getRawDataSize(), | |||
&event); | |||
snd_midi_event_reset_encode (midiParser); | |||
snd_seq_ev_set_source (&event, 0); | |||
snd_seq_ev_set_subs (&event); | |||
snd_seq_ev_set_direct (&event); | |||
snd_seq_event_output_direct (seqHandle, &event); | |||
} | |||
juce_UseDebuggingNewOperator | |||
private: | |||
MidiOutput* const midiOutput; | |||
snd_seq_t* const seqHandle; | |||
snd_midi_event_t* midiParser; | |||
int maxEventSize; | |||
}; | |||
const StringArray MidiOutput::getDevices() | |||
{ | |||
StringArray devices; | |||
iterateDevices (false, devices, -1); | |||
return devices; | |||
} | |||
int MidiOutput::getDefaultDeviceIndex() | |||
{ | |||
return 0; | |||
} | |||
MidiOutput* MidiOutput::openDevice (int deviceIndex) | |||
{ | |||
MidiOutput* newDevice = 0; | |||
StringArray devices; | |||
snd_seq_t* const handle = iterateDevices (false, devices, deviceIndex); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiOutput(); | |||
newDevice->internal = new MidiOutputDevice (newDevice, handle); | |||
} | |||
return newDevice; | |||
} | |||
MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | |||
{ | |||
MidiOutput* newDevice = 0; | |||
snd_seq_t* const handle = createDevice (false, deviceName); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiOutput(); | |||
newDevice->internal = new MidiOutputDevice (newDevice, handle); | |||
} | |||
return newDevice; | |||
} | |||
MidiOutput::~MidiOutput() | |||
{ | |||
MidiOutputDevice* const device = (MidiOutputDevice*) internal; | |||
delete device; | |||
} | |||
void MidiOutput::reset() | |||
{ | |||
} | |||
bool MidiOutput::getVolume (float& leftVol, float& rightVol) | |||
{ | |||
return false; | |||
} | |||
void MidiOutput::setVolume (float leftVol, float rightVol) | |||
{ | |||
} | |||
void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
{ | |||
((MidiOutputDevice*) internal)->sendMessageNow (message); | |||
} | |||
//============================================================================== | |||
class MidiInputThread : public Thread | |||
{ | |||
public: | |||
MidiInputThread (MidiInput* const midiInput_, | |||
snd_seq_t* const seqHandle_, | |||
MidiInputCallback* const callback_) | |||
: Thread (T("Juce MIDI Input")), | |||
midiInput (midiInput_), | |||
seqHandle (seqHandle_), | |||
callback (callback_) | |||
{ | |||
jassert (seqHandle != 0 && callback != 0 && midiInput != 0); | |||
} | |||
~MidiInputThread() | |||
{ | |||
snd_seq_close (seqHandle); | |||
} | |||
void run() | |||
{ | |||
const int maxEventSize = 16 * 1024; | |||
snd_midi_event_t* midiParser; | |||
if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) | |||
{ | |||
uint8* const buffer = (uint8*) juce_malloc (maxEventSize); | |||
const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); | |||
struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd)); | |||
snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); | |||
while (! threadShouldExit()) | |||
{ | |||
if (poll (pfd, numPfds, 500) > 0) | |||
{ | |||
snd_seq_event_t* inputEvent = 0; | |||
snd_seq_nonblock (seqHandle, 1); | |||
do | |||
{ | |||
if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) | |||
{ | |||
// xxx what about SYSEXes that are too big for the buffer? | |||
const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); | |||
snd_midi_event_reset_decode (midiParser); | |||
if (numBytes > 0) | |||
{ | |||
const MidiMessage message ((const uint8*) buffer, | |||
numBytes, | |||
Time::getMillisecondCounter() * 0.001); | |||
callback->handleIncomingMidiMessage (midiInput, message); | |||
} | |||
snd_seq_free_event (inputEvent); | |||
} | |||
} | |||
while (snd_seq_event_input_pending (seqHandle, 0) > 0); | |||
snd_seq_free_event (inputEvent); | |||
} | |||
} | |||
snd_midi_event_free (midiParser); | |||
juce_free (buffer); | |||
} | |||
}; | |||
juce_UseDebuggingNewOperator | |||
private: | |||
MidiInput* const midiInput; | |||
snd_seq_t* const seqHandle; | |||
MidiInputCallback* const callback; | |||
}; | |||
//============================================================================== | |||
MidiInput::MidiInput (const String& name_) | |||
: name (name_), | |||
internal (0) | |||
{ | |||
} | |||
MidiInput::~MidiInput() | |||
{ | |||
stop(); | |||
MidiInputThread* const thread = (MidiInputThread*) internal; | |||
delete thread; | |||
} | |||
void MidiInput::start() | |||
{ | |||
((MidiInputThread*) internal)->startThread(); | |||
} | |||
void MidiInput::stop() | |||
{ | |||
((MidiInputThread*) internal)->stopThread (3000); | |||
} | |||
int MidiInput::getDefaultDeviceIndex() | |||
{ | |||
return 0; | |||
} | |||
const StringArray MidiInput::getDevices() | |||
{ | |||
StringArray devices; | |||
iterateDevices (true, devices, -1); | |||
return devices; | |||
} | |||
MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) | |||
{ | |||
MidiInput* newDevice = 0; | |||
StringArray devices; | |||
snd_seq_t* const handle = iterateDevices (true, devices, deviceIndex); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiInput (devices [deviceIndex]); | |||
newDevice->internal = new MidiInputThread (newDevice, handle, callback); | |||
} | |||
return newDevice; | |||
} | |||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) | |||
{ | |||
MidiInput* newDevice = 0; | |||
snd_seq_t* const handle = createDevice (true, deviceName); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiInput (deviceName); | |||
newDevice->internal = new MidiInputThread (newDevice, handle, callback); | |||
} | |||
return newDevice; | |||
} | |||
END_JUCE_NAMESPACE | |||
//============================================================================== | |||
#else | |||
//============================================================================== | |||
// (These are just stub functions if ALSA is unavailable...) | |||
#include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
BEGIN_JUCE_NAMESPACE | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" | |||
//============================================================================== | |||
const StringArray MidiOutput::getDevices() { return StringArray(); } | |||
int MidiOutput::getDefaultDeviceIndex() { return 0; } | |||
MidiOutput* MidiOutput::openDevice (int) { return 0; } | |||
MidiOutput* MidiOutput::createNewDevice (const String&) { return 0; } | |||
MidiOutput::~MidiOutput() {} | |||
void MidiOutput::reset() {} | |||
bool MidiOutput::getVolume (float&, float&) { return false; } | |||
void MidiOutput::setVolume (float, float) {} | |||
void MidiOutput::sendMessageNow (const MidiMessage&) {} | |||
MidiInput::MidiInput (const String& name_) | |||
: name (name_), | |||
internal (0) | |||
{} | |||
MidiInput::~MidiInput() {} | |||
void MidiInput::start() {} | |||
void MidiInput::stop() {} | |||
int MidiInput::getDefaultDeviceIndex() { return 0; } | |||
const StringArray MidiInput::getDevices() { return StringArray(); } | |||
MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return 0; } | |||
MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return 0; } | |||
END_JUCE_NAMESPACE | |||
#endif | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
Copyright 2004-7 by Raw Material Software ltd. | |||
------------------------------------------------------------------------------ | |||
JUCE can be redistributed and/or modified 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. | |||
JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the | |||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
Boston, MA 02111-1307 USA | |||
------------------------------------------------------------------------------ | |||
If you'd like to release a closed-source product which uses JUCE, commercial | |||
licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
more information. | |||
============================================================================== | |||
*/ | |||
#include "../../../juce_Config.h" | |||
#if JUCE_ALSA | |||
#include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
#include <alsa/asoundlib.h> | |||
BEGIN_JUCE_NAMESPACE | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" | |||
#include "../../../src/juce_core/threads/juce_Thread.h" | |||
#include "../../../src/juce_core/basics/juce_Time.h" | |||
//============================================================================== | |||
static snd_seq_t* iterateDevices (const bool forInput, | |||
StringArray& deviceNamesFound, | |||
const int deviceIndexToOpen) | |||
{ | |||
snd_seq_t* returnedHandle = 0; | |||
snd_seq_t* seqHandle; | |||
if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
: SND_SEQ_OPEN_OUTPUT, 0) == 0) | |||
{ | |||
snd_seq_system_info_t* systemInfo; | |||
snd_seq_client_info_t* clientInfo; | |||
if (snd_seq_system_info_malloc (&systemInfo) == 0) | |||
{ | |||
if (snd_seq_system_info (seqHandle, systemInfo) == 0 | |||
&& snd_seq_client_info_malloc (&clientInfo) == 0) | |||
{ | |||
int numClients = snd_seq_system_info_get_cur_clients (systemInfo); | |||
while (--numClients >= 0 && returnedHandle == 0) | |||
{ | |||
if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) | |||
{ | |||
snd_seq_port_info_t* portInfo; | |||
if (snd_seq_port_info_malloc (&portInfo) == 0) | |||
{ | |||
int numPorts = snd_seq_client_info_get_num_ports (clientInfo); | |||
const int client = snd_seq_client_info_get_client (clientInfo); | |||
snd_seq_port_info_set_client (portInfo, client); | |||
snd_seq_port_info_set_port (portInfo, -1); | |||
while (--numPorts >= 0) | |||
{ | |||
if (snd_seq_query_next_port (seqHandle, portInfo) == 0 | |||
&& (snd_seq_port_info_get_capability (portInfo) | |||
& (forInput ? SND_SEQ_PORT_CAP_READ | |||
: SND_SEQ_PORT_CAP_WRITE)) != 0) | |||
{ | |||
deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); | |||
if (deviceNamesFound.size() == deviceIndexToOpen + 1) | |||
{ | |||
const int sourcePort = snd_seq_port_info_get_port (portInfo); | |||
const int sourceClient = snd_seq_client_info_get_client (clientInfo); | |||
if (sourcePort != -1) | |||
{ | |||
snd_seq_set_client_name (seqHandle, | |||
forInput ? "Juce Midi Input" | |||
: "Juce Midi Output"); | |||
const int portId | |||
= snd_seq_create_simple_port (seqHandle, | |||
forInput ? "Juce Midi In Port" | |||
: "Juce Midi Out Port", | |||
forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) | |||
: (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), | |||
SND_SEQ_PORT_TYPE_MIDI_GENERIC); | |||
snd_seq_connect_from (seqHandle, portId, sourceClient, sourcePort); | |||
returnedHandle = seqHandle; | |||
} | |||
} | |||
} | |||
} | |||
snd_seq_port_info_free (portInfo); | |||
} | |||
} | |||
} | |||
snd_seq_client_info_free (clientInfo); | |||
} | |||
snd_seq_system_info_free (systemInfo); | |||
} | |||
if (returnedHandle == 0) | |||
snd_seq_close (seqHandle); | |||
} | |||
deviceNamesFound.appendNumbersToDuplicates (true, true); | |||
return returnedHandle; | |||
} | |||
static snd_seq_t* createDevice (const bool forInput, | |||
const String& deviceNameToOpen) | |||
{ | |||
snd_seq_t* seqHandle = 0; | |||
if (snd_seq_open (&seqHandle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
: SND_SEQ_OPEN_OUTPUT, 0) == 0) | |||
{ | |||
snd_seq_set_client_name (seqHandle, | |||
(const char*) (forInput ? (deviceNameToOpen + T(" Input")) | |||
: (deviceNameToOpen + T(" Output")))); | |||
const int portId | |||
= snd_seq_create_simple_port (seqHandle, | |||
forInput ? "in" | |||
: "out", | |||
forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) | |||
: (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), | |||
forInput ? SND_SEQ_PORT_TYPE_APPLICATION | |||
: SND_SEQ_PORT_TYPE_MIDI_GENERIC); | |||
if (portId < 0) | |||
{ | |||
snd_seq_close (seqHandle); | |||
seqHandle = 0; | |||
} | |||
} | |||
return seqHandle; | |||
} | |||
//============================================================================== | |||
class MidiOutputDevice | |||
{ | |||
public: | |||
MidiOutputDevice (MidiOutput* const midiOutput_, | |||
snd_seq_t* const seqHandle_) | |||
: | |||
midiOutput (midiOutput_), | |||
seqHandle (seqHandle_), | |||
maxEventSize (16 * 1024) | |||
{ | |||
jassert (seqHandle != 0 && midiOutput != 0); | |||
snd_midi_event_new (maxEventSize, &midiParser); | |||
} | |||
~MidiOutputDevice() | |||
{ | |||
snd_midi_event_free (midiParser); | |||
snd_seq_close (seqHandle); | |||
} | |||
void sendMessageNow (const MidiMessage& message) | |||
{ | |||
if (message.getRawDataSize() > maxEventSize) | |||
{ | |||
maxEventSize = message.getRawDataSize(); | |||
snd_midi_event_free (midiParser); | |||
snd_midi_event_new (maxEventSize, &midiParser); | |||
} | |||
snd_seq_event_t event; | |||
snd_seq_ev_clear (&event); | |||
snd_midi_event_encode (midiParser, | |||
message.getRawData(), | |||
message.getRawDataSize(), | |||
&event); | |||
snd_midi_event_reset_encode (midiParser); | |||
snd_seq_ev_set_source (&event, 0); | |||
snd_seq_ev_set_subs (&event); | |||
snd_seq_ev_set_direct (&event); | |||
snd_seq_event_output (seqHandle, &event); | |||
snd_seq_drain_output (seqHandle); | |||
} | |||
juce_UseDebuggingNewOperator | |||
private: | |||
MidiOutput* const midiOutput; | |||
snd_seq_t* const seqHandle; | |||
snd_midi_event_t* midiParser; | |||
int maxEventSize; | |||
}; | |||
const StringArray MidiOutput::getDevices() | |||
{ | |||
StringArray devices; | |||
iterateDevices (false, devices, -1); | |||
return devices; | |||
} | |||
int MidiOutput::getDefaultDeviceIndex() | |||
{ | |||
return 0; | |||
} | |||
MidiOutput* MidiOutput::openDevice (int deviceIndex) | |||
{ | |||
MidiOutput* newDevice = 0; | |||
StringArray devices; | |||
snd_seq_t* const handle = iterateDevices (false, devices, deviceIndex); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiOutput(); | |||
newDevice->internal = new MidiOutputDevice (newDevice, handle); | |||
} | |||
return newDevice; | |||
} | |||
MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | |||
{ | |||
MidiOutput* newDevice = 0; | |||
snd_seq_t* const handle = createDevice (false, deviceName); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiOutput(); | |||
newDevice->internal = new MidiOutputDevice (newDevice, handle); | |||
} | |||
return newDevice; | |||
} | |||
MidiOutput::~MidiOutput() | |||
{ | |||
MidiOutputDevice* const device = (MidiOutputDevice*) internal; | |||
delete device; | |||
} | |||
void MidiOutput::reset() | |||
{ | |||
} | |||
bool MidiOutput::getVolume (float& leftVol, float& rightVol) | |||
{ | |||
return false; | |||
} | |||
void MidiOutput::setVolume (float leftVol, float rightVol) | |||
{ | |||
} | |||
void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
{ | |||
((MidiOutputDevice*) internal)->sendMessageNow (message); | |||
} | |||
//============================================================================== | |||
class MidiInputThread : public Thread | |||
{ | |||
public: | |||
MidiInputThread (MidiInput* const midiInput_, | |||
snd_seq_t* const seqHandle_, | |||
MidiInputCallback* const callback_) | |||
: Thread (T("Juce MIDI Input")), | |||
midiInput (midiInput_), | |||
seqHandle (seqHandle_), | |||
callback (callback_) | |||
{ | |||
jassert (seqHandle != 0 && callback != 0 && midiInput != 0); | |||
} | |||
~MidiInputThread() | |||
{ | |||
snd_seq_close (seqHandle); | |||
} | |||
void run() | |||
{ | |||
const int maxEventSize = 16 * 1024; | |||
snd_midi_event_t* midiParser; | |||
if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) | |||
{ | |||
uint8* const buffer = (uint8*) juce_malloc (maxEventSize); | |||
const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); | |||
struct pollfd* const pfd = (struct pollfd*) alloca (numPfds * sizeof (struct pollfd)); | |||
snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); | |||
while (! threadShouldExit()) | |||
{ | |||
if (poll (pfd, numPfds, 500) > 0) | |||
{ | |||
snd_seq_event_t* inputEvent = 0; | |||
snd_seq_nonblock (seqHandle, 1); | |||
do | |||
{ | |||
if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) | |||
{ | |||
// xxx what about SYSEXes that are too big for the buffer? | |||
const int numBytes = snd_midi_event_decode (midiParser, buffer, maxEventSize, inputEvent); | |||
snd_midi_event_reset_decode (midiParser); | |||
if (numBytes > 0) | |||
{ | |||
const MidiMessage message ((const uint8*) buffer, | |||
numBytes, | |||
Time::getMillisecondCounter() * 0.001); | |||
callback->handleIncomingMidiMessage (midiInput, message); | |||
} | |||
snd_seq_free_event (inputEvent); | |||
} | |||
} | |||
while (snd_seq_event_input_pending (seqHandle, 0) > 0); | |||
snd_seq_free_event (inputEvent); | |||
} | |||
} | |||
snd_midi_event_free (midiParser); | |||
juce_free (buffer); | |||
} | |||
}; | |||
juce_UseDebuggingNewOperator | |||
private: | |||
MidiInput* const midiInput; | |||
snd_seq_t* const seqHandle; | |||
MidiInputCallback* const callback; | |||
}; | |||
//============================================================================== | |||
MidiInput::MidiInput (const String& name_) | |||
: name (name_), | |||
internal (0) | |||
{ | |||
} | |||
MidiInput::~MidiInput() | |||
{ | |||
stop(); | |||
MidiInputThread* const thread = (MidiInputThread*) internal; | |||
delete thread; | |||
} | |||
void MidiInput::start() | |||
{ | |||
((MidiInputThread*) internal)->startThread(); | |||
} | |||
void MidiInput::stop() | |||
{ | |||
((MidiInputThread*) internal)->stopThread (3000); | |||
} | |||
int MidiInput::getDefaultDeviceIndex() | |||
{ | |||
return 0; | |||
} | |||
const StringArray MidiInput::getDevices() | |||
{ | |||
StringArray devices; | |||
iterateDevices (true, devices, -1); | |||
return devices; | |||
} | |||
MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) | |||
{ | |||
MidiInput* newDevice = 0; | |||
StringArray devices; | |||
snd_seq_t* const handle = iterateDevices (true, devices, deviceIndex); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiInput (devices [deviceIndex]); | |||
newDevice->internal = new MidiInputThread (newDevice, handle, callback); | |||
} | |||
return newDevice; | |||
} | |||
MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) | |||
{ | |||
MidiInput* newDevice = 0; | |||
snd_seq_t* const handle = createDevice (true, deviceName); | |||
if (handle != 0) | |||
{ | |||
newDevice = new MidiInput (deviceName); | |||
newDevice->internal = new MidiInputThread (newDevice, handle, callback); | |||
} | |||
return newDevice; | |||
} | |||
END_JUCE_NAMESPACE | |||
//============================================================================== | |||
#else | |||
//============================================================================== | |||
// (These are just stub functions if ALSA is unavailable...) | |||
#include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
BEGIN_JUCE_NAMESPACE | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiOutput.h" | |||
#include "../../../src/juce_appframework/audio/devices/juce_MidiInput.h" | |||
//============================================================================== | |||
const StringArray MidiOutput::getDevices() { return StringArray(); } | |||
int MidiOutput::getDefaultDeviceIndex() { return 0; } | |||
MidiOutput* MidiOutput::openDevice (int) { return 0; } | |||
MidiOutput* MidiOutput::createNewDevice (const String&) { return 0; } | |||
MidiOutput::~MidiOutput() {} | |||
void MidiOutput::reset() {} | |||
bool MidiOutput::getVolume (float&, float&) { return false; } | |||
void MidiOutput::setVolume (float, float) {} | |||
void MidiOutput::sendMessageNow (const MidiMessage&) {} | |||
MidiInput::MidiInput (const String& name_) | |||
: name (name_), | |||
internal (0) | |||
{} | |||
MidiInput::~MidiInput() {} | |||
void MidiInput::start() {} | |||
void MidiInput::stop() {} | |||
int MidiInput::getDefaultDeviceIndex() { return 0; } | |||
const StringArray MidiInput::getDevices() { return StringArray(); } | |||
MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return 0; } | |||
MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return 0; } | |||
END_JUCE_NAMESPACE | |||
#endif |
@@ -1,149 +1,161 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
Copyright 2004-7 by Raw Material Software ltd. | |||
------------------------------------------------------------------------------ | |||
JUCE can be redistributed and/or modified 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. | |||
JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the | |||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
Boston, MA 02111-1307 USA | |||
------------------------------------------------------------------------------ | |||
If you'd like to release a closed-source product which uses JUCE, commercial | |||
licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
more information. | |||
============================================================================== | |||
*/ | |||
// (This file gets included by juce_mac_NativeCode.mm, rather than being | |||
// compiled on its own). | |||
#ifdef JUCE_INCLUDED_FILE | |||
//============================================================================== | |||
class NSViewComponentInternal : public ComponentMovementWatcher | |||
{ | |||
Component* const owner; | |||
NSViewComponentPeer* currentPeer; | |||
bool wasShowing; | |||
public: | |||
NSView* view; | |||
//============================================================================== | |||
NSViewComponentInternal (NSView* view_, Component* const owner_) | |||
: ComponentMovementWatcher (owner_), | |||
owner (owner_), | |||
currentPeer (0), | |||
wasShowing (false), | |||
view (view_) | |||
{ | |||
[view retain]; | |||
if (owner_->isShowing()) | |||
componentPeerChanged(); | |||
} | |||
~NSViewComponentInternal() | |||
{ | |||
[view removeFromSuperview]; | |||
[view release]; | |||
} | |||
//============================================================================== | |||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) | |||
{ | |||
Component* const topComp = owner->getTopLevelComponent(); | |||
if (topComp->getPeer() != 0) | |||
{ | |||
int x = 0, y = 0; | |||
owner->relativePositionToOtherComponent (topComp, x, y); | |||
NSRect r; | |||
r.origin.x = (float) x; | |||
r.origin.y = (float) y; | |||
r.size.width = (float) owner->getWidth(); | |||
r.size.height = (float) owner->getHeight(); | |||
r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height); | |||
[view setFrame: r]; | |||
} | |||
} | |||
void componentPeerChanged() | |||
{ | |||
NSViewComponentPeer* const peer = dynamic_cast <NSViewComponentPeer*> (owner->getPeer()); | |||
if (currentPeer != peer) | |||
{ | |||
[view removeFromSuperview]; | |||
currentPeer = peer; | |||
if (peer != 0) | |||
{ | |||
[peer->view addSubview: view]; | |||
componentMovedOrResized (false, false); | |||
} | |||
} | |||
[view setHidden: ! owner->isShowing()]; | |||
} | |||
void componentVisibilityChanged (Component&) | |||
{ | |||
componentPeerChanged(); | |||
} | |||
juce_UseDebuggingNewOperator | |||
private: | |||
NSViewComponentInternal (const NSViewComponentInternal&); | |||
const NSViewComponentInternal& operator= (const NSViewComponentInternal&); | |||
}; | |||
//============================================================================== | |||
NSViewComponent::NSViewComponent() | |||
: info (0) | |||
{ | |||
} | |||
NSViewComponent::~NSViewComponent() | |||
{ | |||
delete info; | |||
} | |||
void NSViewComponent::setView (void* view) | |||
{ | |||
if (view != getView()) | |||
{ | |||
deleteAndZero (info); | |||
if (view != 0) | |||
info = new NSViewComponentInternal ((NSView*) view, this); | |||
} | |||
} | |||
void* NSViewComponent::getView() const | |||
{ | |||
return info == 0 ? 0 : info->view; | |||
} | |||
void NSViewComponent::paint (Graphics& g) | |||
{ | |||
} | |||
#endif | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
Copyright 2004-7 by Raw Material Software ltd. | |||
------------------------------------------------------------------------------ | |||
JUCE can be redistributed and/or modified 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. | |||
JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the | |||
Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
Boston, MA 02111-1307 USA | |||
------------------------------------------------------------------------------ | |||
If you'd like to release a closed-source product which uses JUCE, commercial | |||
licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
more information. | |||
============================================================================== | |||
*/ | |||
// (This file gets included by juce_mac_NativeCode.mm, rather than being | |||
// compiled on its own). | |||
#ifdef JUCE_INCLUDED_FILE | |||
//============================================================================== | |||
class NSViewComponentInternal : public ComponentMovementWatcher | |||
{ | |||
Component* const owner; | |||
NSViewComponentPeer* currentPeer; | |||
bool wasShowing; | |||
public: | |||
NSView* const view; | |||
//============================================================================== | |||
NSViewComponentInternal (NSView* const view_, Component* const owner_) | |||
: ComponentMovementWatcher (owner_), | |||
owner (owner_), | |||
currentPeer (0), | |||
wasShowing (false), | |||
view (view_) | |||
{ | |||
[view_ retain]; | |||
if (owner_->isShowing()) | |||
componentPeerChanged(); | |||
} | |||
~NSViewComponentInternal() | |||
{ | |||
[view removeFromSuperview]; | |||
[view release]; | |||
} | |||
//============================================================================== | |||
void componentMovedOrResized (Component& comp, bool wasMoved, bool wasResized) | |||
{ | |||
ComponentMovementWatcher::componentMovedOrResized (comp, wasMoved, wasResized); | |||
// The ComponentMovementWatcher version of this method avoids calling | |||
// us when the top-level comp is resized, but for an NSView we need to know this | |||
// because with inverted co-ords, we need to update the position even if the | |||
// top-left pos hasn't changed | |||
if (comp.isOnDesktop() && wasResized) | |||
componentMovedOrResized (wasMoved, wasResized); | |||
} | |||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) | |||
{ | |||
Component* const topComp = owner->getTopLevelComponent(); | |||
if (topComp->getPeer() != 0) | |||
{ | |||
int x = 0, y = 0; | |||
owner->relativePositionToOtherComponent (topComp, x, y); | |||
NSRect r; | |||
r.origin.x = (float) x; | |||
r.origin.y = (float) y; | |||
r.size.width = (float) owner->getWidth(); | |||
r.size.height = (float) owner->getHeight(); | |||
r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height); | |||
[view setFrame: r]; | |||
} | |||
} | |||
void componentPeerChanged() | |||
{ | |||
NSViewComponentPeer* const peer = dynamic_cast <NSViewComponentPeer*> (owner->getPeer()); | |||
if (currentPeer != peer) | |||
{ | |||
[view removeFromSuperview]; | |||
currentPeer = peer; | |||
if (peer != 0) | |||
{ | |||
[peer->view addSubview: view]; | |||
componentMovedOrResized (false, false); | |||
} | |||
} | |||
[view setHidden: ! owner->isShowing()]; | |||
} | |||
void componentVisibilityChanged (Component&) | |||
{ | |||
componentPeerChanged(); | |||
} | |||
juce_UseDebuggingNewOperator | |||
private: | |||
NSViewComponentInternal (const NSViewComponentInternal&); | |||
const NSViewComponentInternal& operator= (const NSViewComponentInternal&); | |||
}; | |||
//============================================================================== | |||
NSViewComponent::NSViewComponent() | |||
: info (0) | |||
{ | |||
} | |||
NSViewComponent::~NSViewComponent() | |||
{ | |||
delete info; | |||
} | |||
void NSViewComponent::setView (void* view) | |||
{ | |||
if (view != getView()) | |||
{ | |||
deleteAndZero (info); | |||
if (view != 0) | |||
info = new NSViewComponentInternal ((NSView*) view, this); | |||
} | |||
} | |||
void* NSViewComponent::getView() const | |||
{ | |||
return info == 0 ? 0 : info->view; | |||
} | |||
void NSViewComponent::paint (Graphics& g) | |||
{ | |||
} | |||
#endif |
@@ -80,9 +80,23 @@ BEGIN_JUCE_NAMESPACE | |||
#undef Point | |||
//============================================================================== | |||
/** This suffix is used for naming all Obj-C classes that are used inside juce. | |||
Because of the flat naming structure used by Obj-C, you can get horrible situations where | |||
two DLLs are loaded into a host, each of which uses classes with the same names, and these get | |||
cross-linked so that when you make a call to a class that you thought was private, it ends up | |||
actually calling into a similarly named class in the other module's address space. | |||
By changing this macro to a unique value, you ensure that all the obj-C classes in your app | |||
have unique names, and should avoid this problem. | |||
If you're using the amalgamated version, you can just set this macro to something unique before | |||
you include juce_amalgamated.cpp. | |||
*/ | |||
#ifndef JUCE_ObjCExtraSuffix | |||
#define JUCE_ObjCExtraSuffix 2 | |||
#define JUCE_ObjCExtraSuffix 3 | |||
#endif | |||
#define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d | |||
#define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d) | |||
#define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_ObjCExtraSuffix) | |||
@@ -1,300 +1,312 @@ | |||
The Juce Polymorphic Plugin Project! | |||
==================================== | |||
(c) 2008 by Raw Material Software, visit www.rawmaterialsoftware.com for more info. | |||
----------------------------------------------------------------------------------------------------- | |||
The purpose of this framework is to make is simple to write an audio plugin in a generic | |||
way, which can then be compiled as a VST, AudioUnit, RTAS, or any combination of these. | |||
It's "polymorphic" because the output is a single binary module that acts as all of the | |||
different plugin formats at the same time. This means that you only need to maintain one | |||
project, and only need to perform one build to create a multi-format plugin. | |||
Also included are some helper classes that make it easy to create a stand-alone app to | |||
run your plugin without a host. This might be useful in its own right, but can also be very | |||
handy when developing your plugin, because you can build, test and debug it without needing | |||
to keep restarting a 3rd-party host. | |||
How does it work? | |||
================= | |||
To create your plugin, you just create a subclass of the AudioPluginInstance class to | |||
perform the processing. And your plugin UI is written like any normal Juce UI component. | |||
All the platform-specific code is hidden away in wrapper classes that you just add to | |||
your project - you should (hopefully) never need to even see the inner workings of these. | |||
Licensing issues | |||
================ | |||
Juce is released under the GPL (Gnu Public License) - this means that you're free to use | |||
and redistribute it as long as your products are also released under the GPL. Basically | |||
this means that if you use it, you also have to give away your source code. | |||
If you want to release a closed-source application, you can buy a commercial license | |||
that lets you avoid this restriction - see http://www.rawmaterialsoftware.com/juce for more info, | |||
or see the comments at the top of all the Juce source files. | |||
If you're building the VST projects or releasing a VST, you'll need have a look at Steinberg's | |||
developer site to see what licensing rules apply these days. Their website's at | |||
http://www.steinberg.net | |||
If you're building an RTAS then you'll need to sign Digidesign's developer license to get | |||
their SDK. Visit http://www.digidesign.com for more info. | |||
Getting Started | |||
=============== | |||
There's also a 'demo' folder - this contains an example plugin which can be built in all | |||
the different formats. | |||
Have a look at the demo classes to see how it works, and then to create a real plugin, | |||
you'll need to replace the demo files with your own code. | |||
I've tried to add helpful comments where you might run across common compile errors, to | |||
help describe what you might be doing wrong, as getting a build set-up for some of these | |||
formats can be a bit of a pain. Please let me know if you find there's anything missing | |||
from these instructions or anything I could change to help smooth the build process along | |||
a bit. | |||
I'd recommend NOT putting your own plugin code inside the demo plugin directory - it's | |||
much neater to keep it somewhere separate and to alter the projects to point to your | |||
files instead of the demo ones. That way when new versions of this library come out, it'll | |||
make it easier to update to the latest code. | |||
----------------------------------------------------------------------------------------------------- | |||
Prerequisites for building a VST | |||
================================ | |||
- Visit http://www.steinberg.net and jump through whatever hoops are necessary to download | |||
and install the VST SDK. | |||
- Make sure your include path contains an entry for the "vstsdk2.4" folder containing the SDK. | |||
----------------------------------------------------------------------------------------------------- | |||
Prerequisites for building an RTAS | |||
================================== | |||
- Contact Digidesign, ask to become a Digidesign Development Partner, sign the relevent | |||
agreements and NDAs. | |||
- From the Digidesign website, download their latest Plug-In SDK | |||
- Install the SDK and build some of the demo plugins to make sure it all works. | |||
- In Visual Studio: Add all of these to your include path: | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common\Platform | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public | |||
C:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugIns\DSPManager\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\SADriver\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\DigiPublic\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\Fic\Interfaces\DAEClient | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\Cmn | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\DOA | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\PPC_H | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\AppSupport | |||
c:\yourdirectory\PT_80_SDK\AvidCode\AVX2sdk\AVX\avx2\avx2sdk\inc | |||
C:\yourdirectory\PT_80_SDK\xplat\AVX\avx2\avx2sdk\inc | |||
- In Visual Studio: Using the Digidesign demo projects in the SDK, make sure you've compiled | |||
debug and release versions of the following static libraries: | |||
DAE.lib, DigiExt.lib, DSI.lib, PlugInLib.lib. | |||
- In XCode: After installing the Digidesign SDK, make sure you've run the config_SDK_for_Mac | |||
command in the SDK's root directory. This sets up some of the tools that it needs. | |||
- In XCode: If you're using the Digi files CommonDebugSettings.xcconfig and CommonReleaseSettings.xcconfig, | |||
then you'll probably have to remove the "-x c++" option from their OTHER_CFLAGS setting, because | |||
that prevents it compiling obj-C. Also, you might need to comment-out the GCC_PREFIX_HEADER setting, | |||
unless you can persuade precompiled headers to work (I've never managed to get them working myself..) | |||
You'll also probably want to add a "MacBag" setting to these files, rather than putting it into | |||
your project - e.g. "MacBag = /Users/jules/SDKs/PT_80_SDK/MacBag" | |||
----------------------------------------------------------------------------------------------------- | |||
Choosing the formats to build | |||
============================= | |||
Each plugin project needs to contain a JucePluginCharacteristics.h file, which holds all the | |||
plugin-specific build details. In here, there are three macros that you can set to enable each | |||
of the available formats: | |||
#define JucePlugin_Build_VST 1 | |||
#define JucePlugin_Build_RTAS 1 | |||
#define JucePlugin_Build_AU 1 | |||
You can set these to 0 to disable the formats that you don't want to build, and this will avoid | |||
any compilation problems if, for example, you don't have the appropriate SDK for a particular format. | |||
----------------------------------------------------------------------------------------------------- | |||
Creating a PC VST/RTAS plugin in Visual Studio | |||
============================================== | |||
- First try loading the VST demo project in JuceAudioPlugin/demo/build. Hopefully this | |||
should build correctly. | |||
- Create a new, empty, win32 project using Visual Studio. Choose "DLL" as the type of | |||
product to build | |||
- If building an RTAS, add to your project all the juce_RTAS_*.cpp files from the wrapper/RTAS folder. | |||
- If building a VST, add to your project all the juce_VST_*.cpp files from the wrapper/VST folder. | |||
- Create yourself a JucePluginCharacteristics.h file, starting with a copy of the one in the | |||
demo project. Go through each item inside it carefully, and set them to the appropriate value | |||
for your plugin. | |||
- Under "Additional Include Directories", add the folder in which you're going to put | |||
your JucePluginCharacteristics.h file. | |||
- If you're doing an RTAS, change these project settings (these can all be ignored if you're only doing a VST): | |||
- Set "C++/Code Generation/Runtime Library" to be "Multi-threaded DLL" or "Multi-threaded Debug DLL" | |||
- Set the "Linker/Input/Module Definition file" to point to "wrapper/RTAS/juce_RTAS_WinExports.def" | |||
- Under "Linker/Input/Delay loaded DLLs", add the following: | |||
"DAE.dll; DigiExt.dll; DSI.dll; PluginLib.dll; DSPManager.dll" | |||
- You may (or may not) need to add "libcmtd.lib; libcmt.lib" to the "Linker/Input/Ignore Specific Library" setting. | |||
- For ONLY the following files: | |||
juce_RTAS_Wrapper.cpp, juce_RTAS_DigiCode1.cpp, juce_RTAS_DigiCode2.cpp, juce_RTAS_DigiCode3.cpp, | |||
change their "C++/Advanced/Calling Convention" property to "__stdcall". All other files should | |||
be left with the default calling convention of "__cdecl" | |||
- Set the "Linker/General/Output File" property to "$(OutDir)\$(ProjectName).dpm" (If you're building | |||
a polymorphic VST/RTAS, then you can simply copy or rename the finished .dpm file to a .dll, and | |||
it'll function as a VST) | |||
- Under "Custom build step", add the following command: | |||
copy /Y "\yourdirectory\juce\extras\audio plugins\wrapper\RTAS\juce_RTAS_WinResources.rsr" "$(TargetPath)".rsr | |||
The corresponding "Outputs" setting for this must be set to "$(TargetPath)".rsr | |||
(This will copy and rename the juce_RTAS_WinResources.rsr file to sit next to the finished .dpm file. It's | |||
a dummy resource file, but PT will refuse to load the plugin unless it has a corresponding .rsr file) | |||
- Because the RTAS code duplicates some win32 constants, you might need to force it to link correctly | |||
by adding "/FORCE:multiple" to the linker's additional command line options. | |||
- You might want to change the output directory to "\Program Files\Common Files\Digidesign\DAE\Plug-Ins\" | |||
if you want the built plugin to go directly into the PT plugins folder | |||
- When setting properties, remember to change them for both your debug and release builds! | |||
- Create your actual plugin classes and add them to the project. Obviously this is the hard bit! | |||
- Add the amalgamated juce source file to the project - have a look at the demo app for neat ways of doing this. | |||
- NOTE: on Windows, because RTAS uses the altura mac-style code, there are annoying clashes caused if | |||
you also include the Apple QuickTime headers, so you might need to turn off quicktime by setting the | |||
juce config macro: #define JUCE_QUICKTIME 0 | |||
- NOTE: If you're using MSVC2005 to build your plugin, the users will need to | |||
have the Microsoft VC8 Runtime installed on their machines, otherwise the DLL will | |||
silently fail to load. You should probably add the runtime to your plugin's installer, | |||
and you can get a copy of it here: | |||
http://www.microsoft.com/downloads/details.aspx?FamilyID=32bc1bee-a3f9-4c13-9c99-220b62a191ee&DisplayLang=en | |||
----------------------------------------------------------------------------------------------------- | |||
Creating a Mac AU/VST/RTAS plugin in XCode | |||
========================================== | |||
- For an AU, make sure that the JucePlugin_Build_AU is enabled in your JucePluginCharacteristics.h | |||
- In XCode, create a new project based on the "Audio Unit Effect" template | |||
- XCode will create a bunch of template source files for you - you can remove all of these from the project | |||
and delete them | |||
- In the target settings, clear the "Exported Symbols File" setting. The exports are specified by directives | |||
within the wrapper code, so don't need to be listed explicitly. | |||
- All all the Apple frameworks that Juce normally requires to the "External Frameworks" list | |||
- Add all the juce_AU_* files from the /wrapper/AU directory to your project | |||
- The template project creates an AUPublic group that contains lots of AudioUnit source files. But | |||
it leaves out files that it thinks you might not need, e.g. if you chose an "Audio Unit Effect" project, | |||
then it won't add the classes for handling MIDI. So you'll probably need to go into this folder | |||
and check that it contains AUMIDIBase.cpp, AUMidiEffectBase.cpp, MusicDeviceBase.cpp, etc | |||
- As for the PC, you'll need to make sure your project contains a correctly set-up JucePluginCharacteristics.h | |||
file - start with a copy of the one in the demo plugin project, and go through it making sure that | |||
all the values make sense for your plugin. | |||
- Create your actual plugin classes and add them to the project. Obviously this is the hard bit! | |||
You should now be able to build a functional AU! If you want VST support as well, then read on... | |||
- Make sure that the JucePlugin_Build_VST is enabled in your JucePluginCharacteristics.h | |||
- For VST support, add all the juce_VST_* files from /wrapper/VST | |||
- In your target info settings, add the vstsdk2_4 folder to your "Header Search Paths" list | |||
- Make sure that in your Info.plist, the "Bundle Name" value is correctly set to the name of your plugin. | |||
Now, if you compile, the resulting bundle should work as both a VST and AU - you can simply copy or rename it, | |||
changing the suffix to ".vst", and put it in your VST folder. | |||
If you also want to build an RTAS, then carry on reading... | |||
- Make sure that the JucePlugin_Build_RTAS is enabled in your JucePluginCharacteristics.h | |||
- After installing the Digidesign SDK, make sure you've run the config_SDK_for_Mac command in | |||
its root directory. This sets up some of the tools that it needs. | |||
- Add the files from /wrapper/RTAS to your project. Obviously a couple of these are for Windows, so | |||
you shouldn't add those | |||
- In the Digi SDK, in /AlturaPorts/TDMPlugins/common/mac, there are two config files: | |||
CommonDebugSettings.xconfig and CommonReleaseSettings.xconfig | |||
These contain lots of Digi hackery to get their stuff to compile, so you should add them to your project | |||
and change your project's settings to use these files as their base config. Even so, it's all a bit of a mess, | |||
and you may need to tweak them a bit to get it to work on your system. | |||
- In your target settings, add a custom build setting called "MacBag", and set this to the path where the | |||
"MacBag" folder of the Digi SDK lives. | |||
- Add the following to your "Header Search Paths" setting (it's easiest to copy-and-paste this setting from | |||
the demo project): | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/PlugInLibrary/**" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/DSPManager/**" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/common" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/common/PI_LibInterface" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/PACEProtection/**" | |||
"$(MacBag)/../AlturaPorts/OMS/Headers" | |||
"$(MacBag)/../AlturaPorts/Fic/Interfaces/**" | |||
"$(MacBag)/../AlturaPorts/Fic/Source/SignalNets" | |||
"$(MacBag)/../AlturaPorts/DSIPublicInterface/PublicHeaders" | |||
"$(MacBag)/../DAEWin/Include" | |||
"$(MacBag)/../AlturaPorts/DigiPublic/Interfaces" | |||
"$(MacBag)/../AlturaPorts/DigiPublic" | |||
"$(MacBag)/../AlturaPorts/NewFileLibs/DOA" | |||
"$(MacBag)/../AlturaPorts/NewFileLibs/Cmn" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/SignalProcessing/**" | |||
"$(MacBag)/../AvidCode/AVX2sdk/AVX/avx2/avx2sdk/inc" | |||
"$(MacBag)/../AvidCode/AVX2sdk/AVX/avx2/avx2sdk/utils" | |||
- If you get include errors compiling some of the DigiDesign code, you may need to | |||
add "/Developer/Headers/FlatCarbon" to your header search path. | |||
- In the SDK, find the PluginLibrary.xcodeproj file, and add this to your "External frameworks and Libraries". | |||
If you've already compiled this library, you can open its item in your XCode project treeview, to find | |||
the "libPluginLibrary.a" item inside it. Drag this subitem down to your Target/"Link Binary With Libraries" | |||
build stage and drop it there to add it to the link process. | |||
- In your Info.plist, change the "Bundle OS Type Code" to "TDMw", and the "Bundle Creator OS Type Code" to | |||
"PTul". | |||
- You may need to remove the "OTHER_CFLAGS = -x c++" from the RTAS settings file to stop it complaining about | |||
obj-C code | |||
You should now be able to build an RTAS! Again, just renaming the finished bundle to ".dpm" and | |||
putting it in your RTAS folder should be do the trick. | |||
If you get any weird build problems, a good tip is to try comparing the demo plugin's build settings with your | |||
own - this should usually show up what's missing. | |||
Note about exported symbols: | |||
When XCode builds the plugin, I've had unpredictable results when trying to stop it from exporting | |||
all of the internal functions as public symbols. There are some flags that are supposed to turn this | |||
off, but sometimes they don't seem to have any effect, and using an explicit exports file also | |||
seems a bit hit-and-miss. (If anyone knows better and can get this working, please let me know!) | |||
Anyway, as well as being wasteful and showing everyone what's inside your plugin, leaving all | |||
the symbols in there will cause fatal crashes when used with Tracktion, or alongside any other | |||
Juce-based plugins. A way of making sure your plugin is stripped is to use the command | |||
"strip -x -S YourPlugin.vst/Contents/MacOS/YourPlugin" after bulding it, which removes the | |||
unnecessary symbols (although in my experience this also doesn't seem to work all the time, | |||
so it's a good idea to check it using the unix "nm" command). | |||
The Juce Polymorphic Plugin Project! | |||
==================================== | |||
(c) 2008 by Raw Material Software, visit www.rawmaterialsoftware.com for more info. | |||
----------------------------------------------------------------------------------------------------- | |||
The purpose of this framework is to make is simple to write an audio plugin in a generic | |||
way, which can then be compiled as a VST, AudioUnit, RTAS, or any combination of these. | |||
It's "polymorphic" because the output is a single binary module that acts as all of the | |||
different plugin formats at the same time. This means that you only need to maintain one | |||
project, and only need to perform one build to create a multi-format plugin. | |||
Also included are some helper classes that make it easy to create a stand-alone app to | |||
run your plugin without a host. This might be useful in its own right, but can also be very | |||
handy when developing your plugin, because you can build, test and debug it without needing | |||
to keep restarting a 3rd-party host. | |||
How does it work? | |||
================= | |||
To create your plugin, you just create a subclass of the AudioPluginInstance class to | |||
perform the processing. And your plugin UI is written like any normal Juce UI component. | |||
All the platform-specific code is hidden away in wrapper classes that you just add to | |||
your project - you should (hopefully) never need to even see the inner workings of these. | |||
Licensing issues | |||
================ | |||
Juce is released under the GPL (Gnu Public License) - this means that you're free to use | |||
and redistribute it as long as your products are also released under the GPL. Basically | |||
this means that if you use it, you also have to give away your source code. | |||
If you want to release a closed-source application, you can buy a commercial license | |||
that lets you avoid this restriction - see http://www.rawmaterialsoftware.com/juce for more info, | |||
or see the comments at the top of all the Juce source files. | |||
If you're building the VST projects or releasing a VST, you'll need have a look at Steinberg's | |||
developer site to see what licensing rules apply these days. Their website's at | |||
http://www.steinberg.net | |||
If you're building an RTAS then you'll need to sign Digidesign's developer license to get | |||
their SDK. Visit http://www.digidesign.com for more info. | |||
Getting Started | |||
=============== | |||
There's also a 'demo' folder - this contains an example plugin which can be built in all | |||
the different formats. | |||
Have a look at the demo classes to see how it works, and then to create a real plugin, | |||
you'll need to replace the demo files with your own code. | |||
I've tried to add helpful comments where you might run across common compile errors, to | |||
help describe what you might be doing wrong, as getting a build set-up for some of these | |||
formats can be a bit of a pain. Please let me know if you find there's anything missing | |||
from these instructions or anything I could change to help smooth the build process along | |||
a bit. | |||
I'd recommend NOT putting your own plugin code inside the demo plugin directory - it's | |||
much neater to keep it somewhere separate and to alter the projects to point to your | |||
files instead of the demo ones. That way when new versions of this library come out, it'll | |||
make it easier to update to the latest code. | |||
----------------------------------------------------------------------------------------------------- | |||
Prerequisites for building a VST | |||
================================ | |||
- Visit http://www.steinberg.net and jump through whatever hoops are necessary to download | |||
and install the VST SDK. | |||
- Make sure your include path contains an entry for the "vstsdk2.4" folder containing the SDK. | |||
----------------------------------------------------------------------------------------------------- | |||
Prerequisites for building an RTAS | |||
================================== | |||
- Contact Digidesign, ask to become a Digidesign Development Partner, sign the relevent | |||
agreements and NDAs. | |||
- From the Digidesign website, download their latest Plug-In SDK | |||
- Install the SDK and build some of the demo plugins to make sure it all works. | |||
- In Visual Studio: Add all of these to your include path: | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\EffectClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ProcessClasses\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Utilities | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\RTASP_Adapt | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\CoreClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Controls | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Meters | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\ViewClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\DSPClasses | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\PluginLibrary\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\common\Platform | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugins\SignalProcessing\Public | |||
C:\yourdirectory\PT_80_SDK\AlturaPorts\TDMPlugIns\DSPManager\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\SADriver\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\DigiPublic\Interfaces | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\Fic\Interfaces\DAEClient | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\Cmn | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\NewFileLibs\DOA | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\PPC_H | |||
c:\yourdirectory\PT_80_SDK\AlturaPorts\AlturaSource\AppSupport | |||
c:\yourdirectory\PT_80_SDK\AvidCode\AVX2sdk\AVX\avx2\avx2sdk\inc | |||
C:\yourdirectory\PT_80_SDK\xplat\AVX\avx2\avx2sdk\inc | |||
- In Visual Studio: Using the Digidesign demo projects in the SDK, make sure you've compiled | |||
debug and release versions of the following static libraries: | |||
DAE.lib, DigiExt.lib, DSI.lib, PlugInLib.lib. | |||
- In XCode: After installing the Digidesign SDK, make sure you've run the config_SDK_for_Mac | |||
command in the SDK's root directory. This sets up some of the tools that it needs. | |||
- In XCode: If you're using the Digi files CommonDebugSettings.xcconfig and CommonReleaseSettings.xcconfig, | |||
then you'll probably have to remove the "-x c++" option from their OTHER_CFLAGS setting, because | |||
that prevents it compiling obj-C. Also, you might need to comment-out the GCC_PREFIX_HEADER setting, | |||
unless you can persuade precompiled headers to work (I've never managed to get them working myself..) | |||
You'll also probably want to add a "MacBag" setting to these files, rather than putting it into | |||
your project - e.g. "MacBag = /Users/jules/SDKs/PT_80_SDK/MacBag" | |||
----------------------------------------------------------------------------------------------------- | |||
Choosing the formats to build | |||
============================= | |||
Each plugin project needs to contain a JucePluginCharacteristics.h file, which holds all the | |||
plugin-specific build details. In here, there are three macros that you can set to enable each | |||
of the available formats: | |||
#define JucePlugin_Build_VST 1 | |||
#define JucePlugin_Build_RTAS 1 | |||
#define JucePlugin_Build_AU 1 | |||
You can set these to 0 to disable the formats that you don't want to build, and this will avoid | |||
any compilation problems if, for example, you don't have the appropriate SDK for a particular format. | |||
----------------------------------------------------------------------------------------------------- | |||
Creating a PC VST/RTAS plugin in Visual Studio | |||
============================================== | |||
- First try loading the VST demo project in JuceAudioPlugin/demo/build. Hopefully this | |||
should build correctly. | |||
- Create a new, empty, win32 project using Visual Studio. Choose "DLL" as the type of | |||
product to build | |||
- If building an RTAS, add to your project all the juce_RTAS_*.cpp files from the wrapper/RTAS folder. | |||
- If building a VST, add to your project all the juce_VST_*.cpp files from the wrapper/VST folder. | |||
- Create yourself a JucePluginCharacteristics.h file, starting with a copy of the one in the | |||
demo project. Go through each item inside it carefully, and set them to the appropriate value | |||
for your plugin. | |||
- Under "Additional Include Directories", add the folder in which you're going to put | |||
your JucePluginCharacteristics.h file. | |||
- If you're doing an RTAS, change these project settings (these can all be ignored if you're only doing a VST): | |||
- Set "C++/Code Generation/Runtime Library" to be "Multi-threaded DLL" or "Multi-threaded Debug DLL" | |||
- Set the "Linker/Input/Module Definition file" to point to "wrapper/RTAS/juce_RTAS_WinExports.def" | |||
- Under "Linker/Input/Delay loaded DLLs", add the following: | |||
"DAE.dll; DigiExt.dll; DSI.dll; PluginLib.dll; DSPManager.dll" | |||
- You may (or may not) need to add "libcmtd.lib; libcmt.lib" to the "Linker/Input/Ignore Specific Library" setting. | |||
- For ONLY the following files: | |||
juce_RTAS_Wrapper.cpp, juce_RTAS_DigiCode1.cpp, juce_RTAS_DigiCode2.cpp, juce_RTAS_DigiCode3.cpp, | |||
change their "C++/Advanced/Calling Convention" property to "__stdcall". All other files should | |||
be left with the default calling convention of "__cdecl" | |||
- Set the "Linker/General/Output File" property to "$(OutDir)\$(ProjectName).dpm" (If you're building | |||
a polymorphic VST/RTAS, then you can simply copy or rename the finished .dpm file to a .dll, and | |||
it'll function as a VST) | |||
- Under "Custom build step", add the following command: | |||
copy /Y "\yourdirectory\juce\extras\audio plugins\wrapper\RTAS\juce_RTAS_WinResources.rsr" "$(TargetPath)".rsr | |||
The corresponding "Outputs" setting for this must be set to "$(TargetPath)".rsr | |||
(This will copy and rename the juce_RTAS_WinResources.rsr file to sit next to the finished .dpm file. It's | |||
a dummy resource file, but PT will refuse to load the plugin unless it has a corresponding .rsr file) | |||
- Because the RTAS code duplicates some win32 constants, you might need to force it to link correctly | |||
by adding "/FORCE:multiple" to the linker's additional command line options. | |||
- You might want to change the output directory to "\Program Files\Common Files\Digidesign\DAE\Plug-Ins\" | |||
if you want the built plugin to go directly into the PT plugins folder | |||
- When setting properties, remember to change them for both your debug and release builds! | |||
- Create your actual plugin classes and add them to the project. Obviously this is the hard bit! | |||
- Add the amalgamated juce source file to the project - have a look at the demo app for neat ways of doing this. | |||
- NOTE: on Windows, because RTAS uses the altura mac-style code, there are annoying clashes caused if | |||
you also include the Apple QuickTime headers, so you might need to turn off quicktime by setting the | |||
juce config macro: #define JUCE_QUICKTIME 0 | |||
- NOTE: If you're using MSVC2005 to build your plugin, the users will need to | |||
have the Microsoft VC8 Runtime installed on their machines, otherwise the DLL will | |||
silently fail to load. You should probably add the runtime to your plugin's installer, | |||
and you can get a copy of it here: | |||
http://www.microsoft.com/downloads/details.aspx?FamilyID=32bc1bee-a3f9-4c13-9c99-220b62a191ee&DisplayLang=en | |||
----------------------------------------------------------------------------------------------------- | |||
Creating a Mac AU/VST/RTAS plugin in XCode | |||
========================================== | |||
- For an AU, make sure that the JucePlugin_Build_AU is enabled in your JucePluginCharacteristics.h | |||
- In XCode, create a new project based on the "Audio Unit Effect" template | |||
- XCode will create a bunch of template source files for you - you can remove all of these from the project | |||
and delete them | |||
- In the target settings, clear the "Exported Symbols File" setting. The exports are specified by directives | |||
within the wrapper code, so don't need to be listed explicitly. | |||
- All all the Apple frameworks that Juce normally requires to the "External Frameworks" list | |||
- Add all the juce_AU_* files from the /wrapper/AU directory to your project | |||
- The template project creates an AUPublic group that contains lots of AudioUnit source files. But | |||
it leaves out files that it thinks you might not need, e.g. if you chose an "Audio Unit Effect" project, | |||
then it won't add the classes for handling MIDI. So you'll probably need to go into this folder | |||
and check that it contains AUMIDIBase.cpp, AUMidiEffectBase.cpp, MusicDeviceBase.cpp, etc | |||
- As for the PC, you'll need to make sure your project contains a correctly set-up JucePluginCharacteristics.h | |||
file - start with a copy of the one in the demo plugin project, and go through it making sure that | |||
all the values make sense for your plugin. | |||
- Because of the flat naming structure used by Objective-C, if a host loads several different plugins which | |||
all contain slightly different versions of the juce library, you can get nasty situations where all their obj-C | |||
classes are cross-linked to the similarly-named class in other modules, and everything turns into a big mess... | |||
To avoid this, you're advised to set a unique JUCE_ObjCExtraSuffix value (you'll find this in juce_mac_NativeCode.mm, | |||
or if you're using the amalgamated version, you can just set it before including juce_amalgamated.cpp). Choose a | |||
suffix that's unique to both the name and version of your plugin. | |||
- Create your actual plugin classes and add them to the project. Obviously this is the hard bit! | |||
You should now be able to build a functional AU! If you want VST support as well, then read on... | |||
- Make sure that the JucePlugin_Build_VST is enabled in your JucePluginCharacteristics.h | |||
- For VST support, add all the juce_VST_* files from /wrapper/VST | |||
- In your target info settings, add the vstsdk2_4 folder to your "Header Search Paths" list | |||
- Make sure that in your Info.plist, the "Bundle Name" value is correctly set to the name of your plugin. | |||
Now, if you compile, the resulting bundle should work as both a VST and AU - you can simply copy or rename it, | |||
changing the suffix to ".vst", and put it in your VST folder. | |||
NOTE! If you use the Finder to rename your xyz.component file to xyz.vst, it might look like it's done | |||
exactly this... but in fact, the Finder may have secretly renamed it as "xyz.vst.component", even though | |||
it shows "xyz.vst" as the name on the screen. I have wasted quite a lot of time angrily wondering why my VSTs | |||
don't seem to work, before realising that this is what has happened. You should use the command-line to rename | |||
it correctly. | |||
If you also want to build an RTAS, then carry on reading... | |||
- Make sure that the JucePlugin_Build_RTAS is enabled in your JucePluginCharacteristics.h | |||
- After installing the Digidesign SDK, make sure you've run the config_SDK_for_Mac command in | |||
its root directory. This sets up some of the tools that it needs. | |||
- Add the files from /wrapper/RTAS to your project. Obviously a couple of these are for Windows, so | |||
you shouldn't add those | |||
- In the Digi SDK, in /AlturaPorts/TDMPlugins/common/mac, there are two config files: | |||
CommonDebugSettings.xconfig and CommonReleaseSettings.xconfig | |||
These contain lots of Digi hackery to get their stuff to compile, so you should add them to your project | |||
and change your project's settings to use these files as their base config. Even so, it's all a bit of a mess, | |||
and you may need to tweak them a bit to get it to work on your system. | |||
- In your target settings, add a custom build setting called "MacBag", and set this to the path where the | |||
"MacBag" folder of the Digi SDK lives. | |||
- Add the following to your "Header Search Paths" setting (it's easiest to copy-and-paste this setting from | |||
the demo project): | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/PlugInLibrary/**" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/DSPManager/**" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/SupplementalPlugInLib/Encryption" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/SupplementalPlugInLib/GraphicsExtensions" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/common" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/common/PI_LibInterface" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/PACEProtection/**" | |||
"$(MacBag)/../AlturaPorts/OMS/Headers" | |||
"$(MacBag)/../AlturaPorts/Fic/Interfaces/**" | |||
"$(MacBag)/../AlturaPorts/Fic/Source/SignalNets" | |||
"$(MacBag)/../AlturaPorts/DSIPublicInterface/PublicHeaders" | |||
"$(MacBag)/../DAEWin/Include" | |||
"$(MacBag)/../AlturaPorts/DigiPublic/Interfaces" | |||
"$(MacBag)/../AlturaPorts/DigiPublic" | |||
"$(MacBag)/../AlturaPorts/NewFileLibs/DOA" | |||
"$(MacBag)/../AlturaPorts/NewFileLibs/Cmn" | |||
"$(MacBag)/../AlturaPorts/TDMPlugIns/SignalProcessing/**" | |||
"$(MacBag)/../AvidCode/AVX2sdk/AVX/avx2/avx2sdk/inc" | |||
"$(MacBag)/../AvidCode/AVX2sdk/AVX/avx2/avx2sdk/utils" | |||
- If you get include errors compiling some of the DigiDesign code, you may need to | |||
add "/Developer/Headers/FlatCarbon" to your header search path. | |||
- In the SDK, find the PluginLibrary.xcodeproj file, and add this to your "External frameworks and Libraries". | |||
If you've already compiled this library, you can open its item in your XCode project treeview, to find | |||
the "libPluginLibrary.a" item inside it. Drag this subitem down to your Target/"Link Binary With Libraries" | |||
build stage and drop it there to add it to the link process. | |||
- In your Info.plist, change the "Bundle OS Type Code" to "TDMw", and the "Bundle Creator OS Type Code" to | |||
"PTul". | |||
- You may need to remove the "OTHER_CFLAGS = -x c++" from the RTAS settings file to stop it complaining about | |||
obj-C code | |||
You should now be able to build an RTAS! Again, just renaming the finished bundle to ".dpm" and | |||
putting it in your RTAS folder should be do the trick. | |||
If you get any weird build problems, a good tip is to try comparing the demo plugin's build settings with your | |||
own - this should usually show up what's missing. | |||
Note about exported symbols: | |||
When XCode builds the plugin, I've had unpredictable results when trying to stop it from exporting | |||
all of the internal functions as public symbols. There are some flags that are supposed to turn this | |||
off, but sometimes they don't seem to have any effect, and using an explicit exports file also | |||
seems a bit hit-and-miss. (If anyone knows better and can get this working, please let me know!) | |||
Anyway, as well as being wasteful and showing everyone what's inside your plugin, leaving all | |||
the symbols in there will cause fatal crashes when used with Tracktion, or alongside any other | |||
Juce-based plugins. A way of making sure your plugin is stripped is to use the command | |||
"strip -x -S YourPlugin.vst/Contents/MacOS/YourPlugin" after bulding it, which removes the | |||
unnecessary symbols (although in my experience this also doesn't seem to work all the time, | |||
so it's a good idea to check it using the unix "nm" command). |
@@ -5,23 +5,25 @@ | |||
<key>CFBundleDevelopmentRegion</key> | |||
<string>English</string> | |||
<key>CFBundleExecutable</key> | |||
<string>${EXECUTABLE_NAME}</string> | |||
<string>EXECUTABLE_NAME</string> | |||
<key>CFBundleGetInfoString</key> | |||
<string>VERSION_STR</string> | |||
<key>CFBundleIconFile</key> | |||
<string></string> | |||
<key>CFBundleIdentifier</key> | |||
<string>com.rawmaterialsoftware.${PRODUCT_NAME:identifier}</string> | |||
<string>com.rawmaterialsoftware.PRODUCT_NAME</string> | |||
<key>CFBundleName</key> | |||
<string>${PRODUCT_NAME}</string> | |||
<string>PRODUCT_NAME</string> | |||
<key>CFBundleInfoDictionaryVersion</key> | |||
<string>6.0</string> | |||
<key>CFBundlePackageType</key> | |||
<string>BNDL</string> | |||
<string>TDMw</string> | |||
<key>CFBundleShortVersionString</key> | |||
<string>1.1</string> | |||
<key>CFBundleSignature</key> | |||
<string>????</string> | |||
<string>PTul</string> | |||
<key>CFBundleVersion</key> | |||
<string>1.1</string> | |||
<string>VERSION_NUM</string> | |||
<key>CSResourcesFileMapped</key> | |||
<true/> | |||
</dict> | |||
@@ -49,9 +49,8 @@ | |||
843796E00EFBFD16002A2725 /* juce_RTAS_MacResources.r in Rez */ = {isa = PBXBuildFile; fileRef = 843796D90EFBFD16002A2725 /* juce_RTAS_MacResources.r */; }; | |||
843796E10EFBFD16002A2725 /* juce_RTAS_MacUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 843796DA0EFBFD16002A2725 /* juce_RTAS_MacUtilities.mm */; }; | |||
843796E20EFBFD16002A2725 /* juce_RTAS_Wrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 843796DB0EFBFD16002A2725 /* juce_RTAS_Wrapper.cpp */; }; | |||
843796F60EFC0102002A2725 /* CommonDebugSettings.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 843796F40EFC0102002A2725 /* CommonDebugSettings.xcconfig */; }; | |||
843796F70EFC0102002A2725 /* CommonReleaseSettings.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 843796F50EFC0102002A2725 /* CommonReleaseSettings.xcconfig */; }; | |||
843797050EFC0269002A2725 /* libPluginLibrary.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 843797030EFC022E002A2725 /* libPluginLibrary.a */; }; | |||
849817021010B3C500297ECA /* JuceDemoPlugin.component in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8D01CCD20486CAD60068D4B7 /* JuceDemoPlugin.component */; }; | |||
84D3AB5F0FCC744600EA8080 /* AUCarbonViewBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84D3AB5E0FCC744600EA8080 /* AUCarbonViewBase.cpp */; }; | |||
84D3AB630FCC749100EA8080 /* AUCarbonViewBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 84D3AB620FCC749100EA8080 /* AUCarbonViewBase.h */; }; | |||
84D3AB670FCC74B300EA8080 /* CarbonEventHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84D3AB650FCC74B300EA8080 /* CarbonEventHandler.cpp */; }; | |||
@@ -102,6 +101,19 @@ | |||
}; | |||
/* End PBXContainerItemProxy section */ | |||
/* Begin PBXCopyFilesBuildPhase section */ | |||
849817161010B3D100297ECA /* CopyFiles */ = { | |||
isa = PBXCopyFilesBuildPhase; | |||
buildActionMask = 2147483647; | |||
dstPath = "~/Library/Audio/Plug-Ins/Components"; | |||
dstSubfolderSpec = 0; | |||
files = ( | |||
849817021010B3C500297ECA /* JuceDemoPlugin.component in CopyFiles */, | |||
); | |||
runOnlyForDeploymentPostprocessing = 0; | |||
}; | |||
/* End PBXCopyFilesBuildPhase section */ | |||
/* Begin PBXFileReference section */ | |||
089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; }; | |||
3EEA126B089847F5002C6BFC /* CAVectorUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = CAVectorUnit.cpp; sourceTree = "<group>"; }; | |||
@@ -500,6 +512,7 @@ | |||
8D01CCCB0486CAD60068D4B7 /* Sources */, | |||
8D01CCCD0486CAD60068D4B7 /* Frameworks */, | |||
8D01CCCF0486CAD60068D4B7 /* Rez */, | |||
849817161010B3D100297ECA /* CopyFiles */, | |||
); | |||
buildRules = ( | |||
); | |||
@@ -550,8 +563,6 @@ | |||
buildActionMask = 2147483647; | |||
files = ( | |||
8D01CCCA0486CAD60068D4B7 /* InfoPlist.strings in Resources */, | |||
843796F60EFC0102002A2725 /* CommonDebugSettings.xcconfig in Resources */, | |||
843796F70EFC0102002A2725 /* CommonReleaseSettings.xcconfig in Resources */, | |||
); | |||
runOnlyForDeploymentPostprocessing = 0; | |||
}; | |||
@@ -638,6 +649,7 @@ | |||
INFOPLIST_FILE = Info.plist; | |||
INSTALL_PATH = "$(HOME)/Library/Audio/Plug-Ins/Components/"; | |||
LIBRARY_STYLE = Bundle; | |||
MACOSX_DEPLOYMENT_TARGET = 10.4; | |||
OTHER_LDFLAGS = "-bundle"; | |||
OTHER_REZFLAGS = "-d ppc_$ppc -d i386_$i386 -d ppc64_$ppc64 -d x86_64_$x86_64 -I /System/Library/Frameworks/CoreServices.framework/Frameworks/CarbonCore.framework/Versions/A/Headers -I \"$(DEVELOPER_DIR)/Examples/CoreAudio/AudioUnits/AUPublic/AUBase\""; | |||
PRODUCT_NAME = JuceDemoPlugin; | |||
@@ -39,20 +39,22 @@ ifeq ($(CONFIG),Release) | |||
endif | |||
OBJECTS := \ | |||
$(OBJDIR)/MainDemoWindow.o \ | |||
$(OBJDIR)/BinaryData.o \ | |||
$(OBJDIR)/ApplicationStartup.o \ | |||
$(OBJDIR)/BinaryData.o \ | |||
$(OBJDIR)/juce_LibrarySource.o \ | |||
$(OBJDIR)/TreeViewDemo.o \ | |||
$(OBJDIR)/MainDemoWindow.o \ | |||
$(OBJDIR)/WebBrowserDemo.o \ | |||
$(OBJDIR)/WidgetsDemo.o \ | |||
$(OBJDIR)/DragAndDropDemo.o \ | |||
$(OBJDIR)/FontsAndTextDemo.o \ | |||
$(OBJDIR)/InterprocessCommsDemo.o \ | |||
$(OBJDIR)/OpenGLDemo.o \ | |||
$(OBJDIR)/PathsAndTransformsDemo.o \ | |||
$(OBJDIR)/ThreadingDemo.o \ | |||
$(OBJDIR)/TreeViewDemo.o \ | |||
$(OBJDIR)/QuickTimeDemo.o \ | |||
$(OBJDIR)/TableDemo.o \ | |||
$(OBJDIR)/ThreadingDemo.o \ | |||
$(OBJDIR)/OpenGLDemo.o \ | |||
$(OBJDIR)/PathsAndTransformsDemo.o \ | |||
$(OBJDIR)/FontsAndTextDemo.o \ | |||
$(OBJDIR)/InterprocessCommsDemo.o \ | |||
$(OBJDIR)/DragAndDropDemo.o \ | |||
$(OBJDIR)/CameraDemo.o \ | |||
$(OBJDIR)/AudioDemo.o \ | |||
MKDIR_TYPE := msdos | |||
@@ -95,7 +97,7 @@ else | |||
-@if exist $(subst /,\,$(OBJDIR)) rmdir /s /q $(subst /,\,$(OBJDIR)) | |||
endif | |||
$(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp | |||
$(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
@@ -105,17 +107,17 @@ $(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp | |||
$(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp | |||
$(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/TreeViewDemo.o: ../../src/demos/TreeViewDemo.cpp | |||
$(OBJDIR)/WebBrowserDemo.o: ../../src/demos/WebBrowserDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
@@ -125,17 +127,22 @@ $(OBJDIR)/WidgetsDemo.o: ../../src/demos/WidgetsDemo.cpp | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/DragAndDropDemo.o: ../../src/demos/DragAndDropDemo.cpp | |||
$(OBJDIR)/ThreadingDemo.o: ../../src/demos/ThreadingDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/FontsAndTextDemo.o: ../../src/demos/FontsAndTextDemo.cpp | |||
$(OBJDIR)/TreeViewDemo.o: ../../src/demos/TreeViewDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/InterprocessCommsDemo.o: ../../src/demos/InterprocessCommsDemo.cpp | |||
$(OBJDIR)/QuickTimeDemo.o: ../../src/demos/QuickTimeDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/TableDemo.o: ../../src/demos/TableDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
@@ -150,17 +157,22 @@ $(OBJDIR)/PathsAndTransformsDemo.o: ../../src/demos/PathsAndTransformsDemo.cpp | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/QuickTimeDemo.o: ../../src/demos/QuickTimeDemo.cpp | |||
$(OBJDIR)/FontsAndTextDemo.o: ../../src/demos/FontsAndTextDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/TableDemo.o: ../../src/demos/TableDemo.cpp | |||
$(OBJDIR)/InterprocessCommsDemo.o: ../../src/demos/InterprocessCommsDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/ThreadingDemo.o: ../../src/demos/ThreadingDemo.cpp | |||
$(OBJDIR)/DragAndDropDemo.o: ../../src/demos/DragAndDropDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
$(OBJDIR)/CameraDemo.o: ../../src/demos/CameraDemo.cpp | |||
-@$(CMD_MKOBJDIR) | |||
@echo $(notdir $<) | |||
@$(CXX) $(CXXFLAGS) -o "$@" -c "$<" | |||
@@ -189,7 +189,8 @@ private: | |||
void setFile (const File& newFile) | |||
{ | |||
setJucerComponentFile (document, component, | |||
newFile.getRelativePathFrom (document.getFile().getParentDirectory())); | |||
newFile.getRelativePathFrom (document.getFile().getParentDirectory()) | |||
.replaceCharacter (T('\\'), T('/'))); | |||
} | |||
const File getFile() const | |||
@@ -999,7 +999,8 @@ private: | |||
void setFile (const File& newFile) | |||
{ | |||
document.perform (new JucerCompFileChangeAction (component, *document.getComponentLayout(), tabIndex, | |||
newFile.getRelativePathFrom (document.getFile().getParentDirectory())), | |||
newFile.getRelativePathFrom (document.getFile().getParentDirectory()) | |||
.replaceCharacter (T('\\'), T('/'))), | |||
T("Change tab component file")); | |||
} | |||
@@ -302,7 +302,11 @@ void BinaryResources::fillInGeneratedCode (GeneratedCode& code) const | |||
const MemoryBlock& mb = resources[i]->data; | |||
defs << "// JUCER_RESOURCE: " << name << ", " << mb.getSize() | |||
<< ", \"" << File (resources[i]->originalFilename).getRelativePathFrom (code.document->getFile()) << "\"\n"; | |||
<< ", \"" | |||
<< File (resources[i]->originalFilename) | |||
.getRelativePathFrom (code.document->getFile()) | |||
.replaceCharacter (T('\\'), T('/')) | |||
<< "\"\n"; | |||
String line1; | |||
line1 << "static const unsigned char resource_" | |||
@@ -15826,7 +15826,8 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job, | |||
} | |||
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, | |||
const int timeOutMs) | |||
const int timeOutMs, | |||
const bool deleteInactiveJobs) | |||
{ | |||
lock.enter(); | |||
@@ -15842,6 +15843,9 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, | |||
else | |||
{ | |||
jobs.remove (i); | |||
if (deleteInactiveJobs) | |||
delete job; | |||
} | |||
} | |||
@@ -30344,7 +30348,7 @@ bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) | |||
const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() | |||
{ | |||
return FileSearchPath ("(Default AudioUnit locations)"); | |||
return FileSearchPath ("/(Default AudioUnit locations)"); | |||
} | |||
#endif | |||
@@ -96044,13 +96048,9 @@ local block_state deflate_rle(s, flush) | |||
#endif | |||
/********* End of inlined file: deflate.c *********/ | |||
/********* Start of inlined file: infback.c *********/ | |||
/* | |||
This code is largely copied from inflate.c. Normally either infback.o or | |||
inflate.o would be linked into an application--not both. The interface | |||
with inffast.c is retained so that optimized assembler-coded versions of | |||
inflate_fast() can be used with either inflate.c or infback.c. | |||
*/ | |||
//#include "zlib/infback.c" | |||
/********* Start of inlined file: inffast.c *********/ | |||
/********* Start of inlined file: inftrees.h *********/ | |||
/* WARNING: this file should *not* be used by applications. It is | |||
@@ -96237,710 +96237,6 @@ struct inflate_state { | |||
void inflate_fast OF((z_streamp strm, unsigned start)); | |||
/********* End of inlined file: inffast.h *********/ | |||
/* function prototypes */ | |||
local void fixedtables1 OF((struct inflate_state FAR *state)); | |||
/* | |||
strm provides memory allocation functions in zalloc and zfree, or | |||
Z_NULL to use the library memory allocation functions. | |||
windowBits is in the range 8..15, and window is a user-supplied | |||
window and output buffer that is 2**windowBits bytes. | |||
*/ | |||
int ZEXPORT inflateBackInit_(z_streamp strm, int windowBits, unsigned char FAR *window, const char *version, int stream_size) | |||
{ | |||
struct inflate_state FAR *state; | |||
if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || | |||
stream_size != (int)(sizeof(z_stream))) | |||
return Z_VERSION_ERROR; | |||
if (strm == Z_NULL || window == Z_NULL || | |||
windowBits < 8 || windowBits > 15) | |||
return Z_STREAM_ERROR; | |||
strm->msg = Z_NULL; /* in case we return an error */ | |||
if (strm->zalloc == (alloc_func)0) { | |||
strm->zalloc = zcalloc; | |||
strm->opaque = (voidpf)0; | |||
} | |||
if (strm->zfree == (free_func)0) strm->zfree = zcfree; | |||
state = (struct inflate_state FAR *)ZALLOC(strm, 1, | |||
sizeof(struct inflate_state)); | |||
if (state == Z_NULL) return Z_MEM_ERROR; | |||
Tracev((stderr, "inflate: allocated\n")); | |||
strm->state = (struct internal_state FAR *)state; | |||
state->dmax = 32768U; | |||
state->wbits = windowBits; | |||
state->wsize = 1U << windowBits; | |||
state->window = window; | |||
state->write = 0; | |||
state->whave = 0; | |||
return Z_OK; | |||
} | |||
/* | |||
Return state with length and distance decoding tables and index sizes set to | |||
fixed code decoding. Normally this returns fixed tables from inffixed.h. | |||
If BUILDFIXED is defined, then instead this routine builds the tables the | |||
first time it's called, and returns those tables the first time and | |||
thereafter. This reduces the size of the code by about 2K bytes, in | |||
exchange for a little execution time. However, BUILDFIXED should not be | |||
used for threaded applications, since the rewriting of the tables and virgin | |||
may not be thread-safe. | |||
*/ | |||
local void fixedtables1 (struct inflate_state FAR *state) | |||
{ | |||
#ifdef BUILDFIXED | |||
static int virgin = 1; | |||
static code *lenfix, *distfix; | |||
static code fixed[544]; | |||
/* build fixed huffman tables if first call (may not be thread safe) */ | |||
if (virgin) { | |||
unsigned sym, bits; | |||
static code *next; | |||
/* literal/length table */ | |||
sym = 0; | |||
while (sym < 144) state->lens[sym++] = 8; | |||
while (sym < 256) state->lens[sym++] = 9; | |||
while (sym < 280) state->lens[sym++] = 7; | |||
while (sym < 288) state->lens[sym++] = 8; | |||
next = fixed; | |||
lenfix = next; | |||
bits = 9; | |||
inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); | |||
/* distance table */ | |||
sym = 0; | |||
while (sym < 32) state->lens[sym++] = 5; | |||
distfix = next; | |||
bits = 5; | |||
inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); | |||
/* do this just once */ | |||
virgin = 0; | |||
} | |||
#else /* !BUILDFIXED */ | |||
/********* Start of inlined file: inffixed.h *********/ | |||
/* inffixed.h -- table for decoding fixed codes | |||
* Generated automatically by makefixed(). | |||
*/ | |||
/* WARNING: this file should *not* be used by applications. It | |||
is part of the implementation of the compression library and | |||
is subject to change. Applications should only use zlib.h. | |||
*/ | |||
static const code lenfix[512] = { | |||
{96,7,0},{0,8,80},{0,8,16},{20,8,115},{18,7,31},{0,8,112},{0,8,48}, | |||
{0,9,192},{16,7,10},{0,8,96},{0,8,32},{0,9,160},{0,8,0},{0,8,128}, | |||
{0,8,64},{0,9,224},{16,7,6},{0,8,88},{0,8,24},{0,9,144},{19,7,59}, | |||
{0,8,120},{0,8,56},{0,9,208},{17,7,17},{0,8,104},{0,8,40},{0,9,176}, | |||
{0,8,8},{0,8,136},{0,8,72},{0,9,240},{16,7,4},{0,8,84},{0,8,20}, | |||
{21,8,227},{19,7,43},{0,8,116},{0,8,52},{0,9,200},{17,7,13},{0,8,100}, | |||
{0,8,36},{0,9,168},{0,8,4},{0,8,132},{0,8,68},{0,9,232},{16,7,8}, | |||
{0,8,92},{0,8,28},{0,9,152},{20,7,83},{0,8,124},{0,8,60},{0,9,216}, | |||
{18,7,23},{0,8,108},{0,8,44},{0,9,184},{0,8,12},{0,8,140},{0,8,76}, | |||
{0,9,248},{16,7,3},{0,8,82},{0,8,18},{21,8,163},{19,7,35},{0,8,114}, | |||
{0,8,50},{0,9,196},{17,7,11},{0,8,98},{0,8,34},{0,9,164},{0,8,2}, | |||
{0,8,130},{0,8,66},{0,9,228},{16,7,7},{0,8,90},{0,8,26},{0,9,148}, | |||
{20,7,67},{0,8,122},{0,8,58},{0,9,212},{18,7,19},{0,8,106},{0,8,42}, | |||
{0,9,180},{0,8,10},{0,8,138},{0,8,74},{0,9,244},{16,7,5},{0,8,86}, | |||
{0,8,22},{64,8,0},{19,7,51},{0,8,118},{0,8,54},{0,9,204},{17,7,15}, | |||
{0,8,102},{0,8,38},{0,9,172},{0,8,6},{0,8,134},{0,8,70},{0,9,236}, | |||
{16,7,9},{0,8,94},{0,8,30},{0,9,156},{20,7,99},{0,8,126},{0,8,62}, | |||
{0,9,220},{18,7,27},{0,8,110},{0,8,46},{0,9,188},{0,8,14},{0,8,142}, | |||
{0,8,78},{0,9,252},{96,7,0},{0,8,81},{0,8,17},{21,8,131},{18,7,31}, | |||
{0,8,113},{0,8,49},{0,9,194},{16,7,10},{0,8,97},{0,8,33},{0,9,162}, | |||
{0,8,1},{0,8,129},{0,8,65},{0,9,226},{16,7,6},{0,8,89},{0,8,25}, | |||
{0,9,146},{19,7,59},{0,8,121},{0,8,57},{0,9,210},{17,7,17},{0,8,105}, | |||
{0,8,41},{0,9,178},{0,8,9},{0,8,137},{0,8,73},{0,9,242},{16,7,4}, | |||
{0,8,85},{0,8,21},{16,8,258},{19,7,43},{0,8,117},{0,8,53},{0,9,202}, | |||
{17,7,13},{0,8,101},{0,8,37},{0,9,170},{0,8,5},{0,8,133},{0,8,69}, | |||
{0,9,234},{16,7,8},{0,8,93},{0,8,29},{0,9,154},{20,7,83},{0,8,125}, | |||
{0,8,61},{0,9,218},{18,7,23},{0,8,109},{0,8,45},{0,9,186},{0,8,13}, | |||
{0,8,141},{0,8,77},{0,9,250},{16,7,3},{0,8,83},{0,8,19},{21,8,195}, | |||
{19,7,35},{0,8,115},{0,8,51},{0,9,198},{17,7,11},{0,8,99},{0,8,35}, | |||
{0,9,166},{0,8,3},{0,8,131},{0,8,67},{0,9,230},{16,7,7},{0,8,91}, | |||
{0,8,27},{0,9,150},{20,7,67},{0,8,123},{0,8,59},{0,9,214},{18,7,19}, | |||
{0,8,107},{0,8,43},{0,9,182},{0,8,11},{0,8,139},{0,8,75},{0,9,246}, | |||
{16,7,5},{0,8,87},{0,8,23},{64,8,0},{19,7,51},{0,8,119},{0,8,55}, | |||
{0,9,206},{17,7,15},{0,8,103},{0,8,39},{0,9,174},{0,8,7},{0,8,135}, | |||
{0,8,71},{0,9,238},{16,7,9},{0,8,95},{0,8,31},{0,9,158},{20,7,99}, | |||
{0,8,127},{0,8,63},{0,9,222},{18,7,27},{0,8,111},{0,8,47},{0,9,190}, | |||
{0,8,15},{0,8,143},{0,8,79},{0,9,254},{96,7,0},{0,8,80},{0,8,16}, | |||
{20,8,115},{18,7,31},{0,8,112},{0,8,48},{0,9,193},{16,7,10},{0,8,96}, | |||
{0,8,32},{0,9,161},{0,8,0},{0,8,128},{0,8,64},{0,9,225},{16,7,6}, | |||
{0,8,88},{0,8,24},{0,9,145},{19,7,59},{0,8,120},{0,8,56},{0,9,209}, | |||
{17,7,17},{0,8,104},{0,8,40},{0,9,177},{0,8,8},{0,8,136},{0,8,72}, | |||
{0,9,241},{16,7,4},{0,8,84},{0,8,20},{21,8,227},{19,7,43},{0,8,116}, | |||
{0,8,52},{0,9,201},{17,7,13},{0,8,100},{0,8,36},{0,9,169},{0,8,4}, | |||
{0,8,132},{0,8,68},{0,9,233},{16,7,8},{0,8,92},{0,8,28},{0,9,153}, | |||
{20,7,83},{0,8,124},{0,8,60},{0,9,217},{18,7,23},{0,8,108},{0,8,44}, | |||
{0,9,185},{0,8,12},{0,8,140},{0,8,76},{0,9,249},{16,7,3},{0,8,82}, | |||
{0,8,18},{21,8,163},{19,7,35},{0,8,114},{0,8,50},{0,9,197},{17,7,11}, | |||
{0,8,98},{0,8,34},{0,9,165},{0,8,2},{0,8,130},{0,8,66},{0,9,229}, | |||
{16,7,7},{0,8,90},{0,8,26},{0,9,149},{20,7,67},{0,8,122},{0,8,58}, | |||
{0,9,213},{18,7,19},{0,8,106},{0,8,42},{0,9,181},{0,8,10},{0,8,138}, | |||
{0,8,74},{0,9,245},{16,7,5},{0,8,86},{0,8,22},{64,8,0},{19,7,51}, | |||
{0,8,118},{0,8,54},{0,9,205},{17,7,15},{0,8,102},{0,8,38},{0,9,173}, | |||
{0,8,6},{0,8,134},{0,8,70},{0,9,237},{16,7,9},{0,8,94},{0,8,30}, | |||
{0,9,157},{20,7,99},{0,8,126},{0,8,62},{0,9,221},{18,7,27},{0,8,110}, | |||
{0,8,46},{0,9,189},{0,8,14},{0,8,142},{0,8,78},{0,9,253},{96,7,0}, | |||
{0,8,81},{0,8,17},{21,8,131},{18,7,31},{0,8,113},{0,8,49},{0,9,195}, | |||
{16,7,10},{0,8,97},{0,8,33},{0,9,163},{0,8,1},{0,8,129},{0,8,65}, | |||
{0,9,227},{16,7,6},{0,8,89},{0,8,25},{0,9,147},{19,7,59},{0,8,121}, | |||
{0,8,57},{0,9,211},{17,7,17},{0,8,105},{0,8,41},{0,9,179},{0,8,9}, | |||
{0,8,137},{0,8,73},{0,9,243},{16,7,4},{0,8,85},{0,8,21},{16,8,258}, | |||
{19,7,43},{0,8,117},{0,8,53},{0,9,203},{17,7,13},{0,8,101},{0,8,37}, | |||
{0,9,171},{0,8,5},{0,8,133},{0,8,69},{0,9,235},{16,7,8},{0,8,93}, | |||
{0,8,29},{0,9,155},{20,7,83},{0,8,125},{0,8,61},{0,9,219},{18,7,23}, | |||
{0,8,109},{0,8,45},{0,9,187},{0,8,13},{0,8,141},{0,8,77},{0,9,251}, | |||
{16,7,3},{0,8,83},{0,8,19},{21,8,195},{19,7,35},{0,8,115},{0,8,51}, | |||
{0,9,199},{17,7,11},{0,8,99},{0,8,35},{0,9,167},{0,8,3},{0,8,131}, | |||
{0,8,67},{0,9,231},{16,7,7},{0,8,91},{0,8,27},{0,9,151},{20,7,67}, | |||
{0,8,123},{0,8,59},{0,9,215},{18,7,19},{0,8,107},{0,8,43},{0,9,183}, | |||
{0,8,11},{0,8,139},{0,8,75},{0,9,247},{16,7,5},{0,8,87},{0,8,23}, | |||
{64,8,0},{19,7,51},{0,8,119},{0,8,55},{0,9,207},{17,7,15},{0,8,103}, | |||
{0,8,39},{0,9,175},{0,8,7},{0,8,135},{0,8,71},{0,9,239},{16,7,9}, | |||
{0,8,95},{0,8,31},{0,9,159},{20,7,99},{0,8,127},{0,8,63},{0,9,223}, | |||
{18,7,27},{0,8,111},{0,8,47},{0,9,191},{0,8,15},{0,8,143},{0,8,79}, | |||
{0,9,255} | |||
}; | |||
static const code distfix[32] = { | |||
{16,5,1},{23,5,257},{19,5,17},{27,5,4097},{17,5,5},{25,5,1025}, | |||
{21,5,65},{29,5,16385},{16,5,3},{24,5,513},{20,5,33},{28,5,8193}, | |||
{18,5,9},{26,5,2049},{22,5,129},{64,5,0},{16,5,2},{23,5,385}, | |||
{19,5,25},{27,5,6145},{17,5,7},{25,5,1537},{21,5,97},{29,5,24577}, | |||
{16,5,4},{24,5,769},{20,5,49},{28,5,12289},{18,5,13},{26,5,3073}, | |||
{22,5,193},{64,5,0} | |||
}; | |||
/********* End of inlined file: inffixed.h *********/ | |||
#endif /* BUILDFIXED */ | |||
state->lencode = lenfix; | |||
state->lenbits = 9; | |||
state->distcode = distfix; | |||
state->distbits = 5; | |||
} | |||
/* Macros for inflateBack(): */ | |||
/* Load returned state from inflate_fast() */ | |||
#define LOAD() \ | |||
do { \ | |||
put = strm->next_out; \ | |||
left = strm->avail_out; \ | |||
next = strm->next_in; \ | |||
have = strm->avail_in; \ | |||
hold = state->hold; \ | |||
bits = state->bits; \ | |||
} while (0) | |||
/* Set state from registers for inflate_fast() */ | |||
#define RESTORE() \ | |||
do { \ | |||
strm->next_out = put; \ | |||
strm->avail_out = left; \ | |||
strm->next_in = next; \ | |||
strm->avail_in = have; \ | |||
state->hold = hold; \ | |||
state->bits = bits; \ | |||
} while (0) | |||
/* Clear the input bit accumulator */ | |||
#define INITBITS() \ | |||
do { \ | |||
hold = 0; \ | |||
bits = 0; \ | |||
} while (0) | |||
/* Assure that some input is available. If input is requested, but denied, | |||
then return a Z_BUF_ERROR from inflateBack(). */ | |||
#define PULL() \ | |||
do { \ | |||
if (have == 0) { \ | |||
have = in(in_desc, &next); \ | |||
if (have == 0) { \ | |||
next = Z_NULL; \ | |||
ret = Z_BUF_ERROR; \ | |||
goto inf_leave; \ | |||
} \ | |||
} \ | |||
} while (0) | |||
/* Get a byte of input into the bit accumulator, or return from inflateBack() | |||
with an error if there is no input available. */ | |||
#define PULLBYTE() \ | |||
do { \ | |||
PULL(); \ | |||
have--; \ | |||
hold += (unsigned long)(*next++) << bits; \ | |||
bits += 8; \ | |||
} while (0) | |||
/* Assure that there are at least n bits in the bit accumulator. If there is | |||
not enough available input to do that, then return from inflateBack() with | |||
an error. */ | |||
#define NEEDBITS(n) \ | |||
do { \ | |||
while (bits < (unsigned)(n)) \ | |||
PULLBYTE(); \ | |||
} while (0) | |||
/* Return the low n bits of the bit accumulator (n < 16) */ | |||
#define BITS(n) \ | |||
((unsigned)hold & ((1U << (n)) - 1)) | |||
/* Remove n bits from the bit accumulator */ | |||
#define DROPBITS(n) \ | |||
do { \ | |||
hold >>= (n); \ | |||
bits -= (unsigned)(n); \ | |||
} while (0) | |||
/* Remove zero to seven bits as needed to go to a byte boundary */ | |||
#define BYTEBITS() \ | |||
do { \ | |||
hold >>= bits & 7; \ | |||
bits -= bits & 7; \ | |||
} while (0) | |||
/* Assure that some output space is available, by writing out the window | |||
if it's full. If the write fails, return from inflateBack() with a | |||
Z_BUF_ERROR. */ | |||
#define ROOM() \ | |||
do { \ | |||
if (left == 0) { \ | |||
put = state->window; \ | |||
left = state->wsize; \ | |||
state->whave = left; \ | |||
if (out(out_desc, put, left)) { \ | |||
ret = Z_BUF_ERROR; \ | |||
goto inf_leave; \ | |||
} \ | |||
} \ | |||
} while (0) | |||
/* | |||
strm provides the memory allocation functions and window buffer on input, | |||
and provides information on the unused input on return. For Z_DATA_ERROR | |||
returns, strm will also provide an error message. | |||
in() and out() are the call-back input and output functions. When | |||
inflateBack() needs more input, it calls in(). When inflateBack() has | |||
filled the window with output, or when it completes with data in the | |||
window, it calls out() to write out the data. The application must not | |||
change the provided input until in() is called again or inflateBack() | |||
returns. The application must not change the window/output buffer until | |||
inflateBack() returns. | |||
in() and out() are called with a descriptor parameter provided in the | |||
inflateBack() call. This parameter can be a structure that provides the | |||
information required to do the read or write, as well as accumulated | |||
information on the input and output such as totals and check values. | |||
in() should return zero on failure. out() should return non-zero on | |||
failure. If either in() or out() fails, than inflateBack() returns a | |||
Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it | |||
was in() or out() that caused in the error. Otherwise, inflateBack() | |||
returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format | |||
error, or Z_MEM_ERROR if it could not allocate memory for the state. | |||
inflateBack() can also return Z_STREAM_ERROR if the input parameters | |||
are not correct, i.e. strm is Z_NULL or the state was not initialized. | |||
*/ | |||
int ZEXPORT inflateBack(z_streamp strm, in_func in, void FAR *in_desc, out_func out, void FAR *out_desc) | |||
{ | |||
struct inflate_state FAR *state; | |||
unsigned char FAR *next; /* next input */ | |||
unsigned char FAR *put; /* next output */ | |||
unsigned have, left; /* available input and output */ | |||
unsigned long hold; /* bit buffer */ | |||
unsigned bits; /* bits in bit buffer */ | |||
unsigned copy; /* number of stored or match bytes to copy */ | |||
unsigned char FAR *from; /* where to copy match bytes from */ | |||
code thisx; /* current decoding table entry */ | |||
code last; /* parent table entry */ | |||
unsigned len; /* length to copy for repeats, bits to drop */ | |||
int ret; /* return code */ | |||
static const unsigned short order[19] = /* permutation of code lengths */ | |||
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; | |||
/* Check that the strm exists and that the state was initialized */ | |||
if (strm == Z_NULL || strm->state == Z_NULL) | |||
return Z_STREAM_ERROR; | |||
state = (struct inflate_state FAR *)strm->state; | |||
/* Reset the state */ | |||
strm->msg = Z_NULL; | |||
state->mode = TYPE; | |||
state->last = 0; | |||
state->whave = 0; | |||
next = strm->next_in; | |||
have = next != Z_NULL ? strm->avail_in : 0; | |||
hold = 0; | |||
bits = 0; | |||
put = state->window; | |||
left = state->wsize; | |||
/* Inflate until end of block marked as last */ | |||
for (;;) | |||
switch (state->mode) { | |||
case TYPE: | |||
/* determine and dispatch block type */ | |||
if (state->last) { | |||
BYTEBITS(); | |||
state->mode = DONE; | |||
break; | |||
} | |||
NEEDBITS(3); | |||
state->last = BITS(1); | |||
DROPBITS(1); | |||
switch (BITS(2)) { | |||
case 0: /* stored block */ | |||
Tracev((stderr, "inflate: stored block%s\n", | |||
state->last ? " (last)" : "")); | |||
state->mode = STORED; | |||
break; | |||
case 1: /* fixed block */ | |||
fixedtables1(state); | |||
Tracev((stderr, "inflate: fixed codes block%s\n", | |||
state->last ? " (last)" : "")); | |||
state->mode = LEN; /* decode codes */ | |||
break; | |||
case 2: /* dynamic block */ | |||
Tracev((stderr, "inflate: dynamic codes block%s\n", | |||
state->last ? " (last)" : "")); | |||
state->mode = TABLE; | |||
break; | |||
case 3: | |||
strm->msg = (char *)"invalid block type"; | |||
state->mode = BAD; | |||
} | |||
DROPBITS(2); | |||
break; | |||
case STORED: | |||
/* get and verify stored block length */ | |||
BYTEBITS(); /* go to byte boundary */ | |||
NEEDBITS(32); | |||
if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { | |||
strm->msg = (char *)"invalid stored block lengths"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
state->length = (unsigned)hold & 0xffff; | |||
Tracev((stderr, "inflate: stored length %u\n", | |||
state->length)); | |||
INITBITS(); | |||
/* copy stored block from input to output */ | |||
while (state->length != 0) { | |||
copy = state->length; | |||
PULL(); | |||
ROOM(); | |||
if (copy > have) copy = have; | |||
if (copy > left) copy = left; | |||
zmemcpy(put, next, copy); | |||
have -= copy; | |||
next += copy; | |||
left -= copy; | |||
put += copy; | |||
state->length -= copy; | |||
} | |||
Tracev((stderr, "inflate: stored end\n")); | |||
state->mode = TYPE; | |||
break; | |||
case TABLE: | |||
/* get dynamic table entries descriptor */ | |||
NEEDBITS(14); | |||
state->nlen = BITS(5) + 257; | |||
DROPBITS(5); | |||
state->ndist = BITS(5) + 1; | |||
DROPBITS(5); | |||
state->ncode = BITS(4) + 4; | |||
DROPBITS(4); | |||
#ifndef PKZIP_BUG_WORKAROUND | |||
if (state->nlen > 286 || state->ndist > 30) { | |||
strm->msg = (char *)"too many length or distance symbols"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
#endif | |||
Tracev((stderr, "inflate: table sizes ok\n")); | |||
/* get code length code lengths (not a typo) */ | |||
state->have = 0; | |||
while (state->have < state->ncode) { | |||
NEEDBITS(3); | |||
state->lens[order[state->have++]] = (unsigned short)BITS(3); | |||
DROPBITS(3); | |||
} | |||
while (state->have < 19) | |||
state->lens[order[state->have++]] = 0; | |||
state->next = state->codes; | |||
state->lencode = (code const FAR *)(state->next); | |||
state->lenbits = 7; | |||
ret = inflate_table(CODES, state->lens, 19, &(state->next), | |||
&(state->lenbits), state->work); | |||
if (ret) { | |||
strm->msg = (char *)"invalid code lengths set"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
Tracev((stderr, "inflate: code lengths ok\n")); | |||
/* get length and distance code code lengths */ | |||
state->have = 0; | |||
while (state->have < state->nlen + state->ndist) { | |||
for (;;) { | |||
thisx = state->lencode[BITS(state->lenbits)]; | |||
if ((unsigned)(thisx.bits) <= bits) break; | |||
PULLBYTE(); | |||
} | |||
if (thisx.val < 16) { | |||
NEEDBITS(thisx.bits); | |||
DROPBITS(thisx.bits); | |||
state->lens[state->have++] = thisx.val; | |||
} | |||
else { | |||
if (thisx.val == 16) { | |||
NEEDBITS(thisx.bits + 2); | |||
DROPBITS(thisx.bits); | |||
if (state->have == 0) { | |||
strm->msg = (char *)"invalid bit length repeat"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
len = (unsigned)(state->lens[state->have - 1]); | |||
copy = 3 + BITS(2); | |||
DROPBITS(2); | |||
} | |||
else if (thisx.val == 17) { | |||
NEEDBITS(thisx.bits + 3); | |||
DROPBITS(thisx.bits); | |||
len = 0; | |||
copy = 3 + BITS(3); | |||
DROPBITS(3); | |||
} | |||
else { | |||
NEEDBITS(thisx.bits + 7); | |||
DROPBITS(thisx.bits); | |||
len = 0; | |||
copy = 11 + BITS(7); | |||
DROPBITS(7); | |||
} | |||
if (state->have + copy > state->nlen + state->ndist) { | |||
strm->msg = (char *)"invalid bit length repeat"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
while (copy--) | |||
state->lens[state->have++] = (unsigned short)len; | |||
} | |||
} | |||
/* handle error breaks in while */ | |||
if (state->mode == BAD) break; | |||
/* build code tables */ | |||
state->next = state->codes; | |||
state->lencode = (code const FAR *)(state->next); | |||
state->lenbits = 9; | |||
ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), | |||
&(state->lenbits), state->work); | |||
if (ret) { | |||
strm->msg = (char *)"invalid literal/lengths set"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
state->distcode = (code const FAR *)(state->next); | |||
state->distbits = 6; | |||
ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, | |||
&(state->next), &(state->distbits), state->work); | |||
if (ret) { | |||
strm->msg = (char *)"invalid distances set"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
Tracev((stderr, "inflate: codes ok\n")); | |||
state->mode = LEN; | |||
case LEN: | |||
/* use inflate_fast() if we have enough input and output */ | |||
if (have >= 6 && left >= 258) { | |||
RESTORE(); | |||
if (state->whave < state->wsize) | |||
state->whave = state->wsize - left; | |||
inflate_fast(strm, state->wsize); | |||
LOAD(); | |||
break; | |||
} | |||
/* get a literal, length, or end-of-block code */ | |||
for (;;) { | |||
thisx = state->lencode[BITS(state->lenbits)]; | |||
if ((unsigned)(thisx.bits) <= bits) break; | |||
PULLBYTE(); | |||
} | |||
if (thisx.op && (thisx.op & 0xf0) == 0) { | |||
last = thisx; | |||
for (;;) { | |||
thisx = state->lencode[last.val + | |||
(BITS(last.bits + last.op) >> last.bits)]; | |||
if ((unsigned)(last.bits + thisx.bits) <= bits) break; | |||
PULLBYTE(); | |||
} | |||
DROPBITS(last.bits); | |||
} | |||
DROPBITS(thisx.bits); | |||
state->length = (unsigned)thisx.val; | |||
/* process literal */ | |||
if (thisx.op == 0) { | |||
Tracevv((stderr, thisx.val >= 0x20 && thisx.val < 0x7f ? | |||
"inflate: literal '%c'\n" : | |||
"inflate: literal 0x%02x\n", thisx.val)); | |||
ROOM(); | |||
*put++ = (unsigned char)(state->length); | |||
left--; | |||
state->mode = LEN; | |||
break; | |||
} | |||
/* process end of block */ | |||
if (thisx.op & 32) { | |||
Tracevv((stderr, "inflate: end of block\n")); | |||
state->mode = TYPE; | |||
break; | |||
} | |||
/* invalid code */ | |||
if (thisx.op & 64) { | |||
strm->msg = (char *)"invalid literal/length code"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
/* length code -- get extra bits, if any */ | |||
state->extra = (unsigned)(thisx.op) & 15; | |||
if (state->extra != 0) { | |||
NEEDBITS(state->extra); | |||
state->length += BITS(state->extra); | |||
DROPBITS(state->extra); | |||
} | |||
Tracevv((stderr, "inflate: length %u\n", state->length)); | |||
/* get distance code */ | |||
for (;;) { | |||
thisx = state->distcode[BITS(state->distbits)]; | |||
if ((unsigned)(thisx.bits) <= bits) break; | |||
PULLBYTE(); | |||
} | |||
if ((thisx.op & 0xf0) == 0) { | |||
last = thisx; | |||
for (;;) { | |||
thisx = state->distcode[last.val + | |||
(BITS(last.bits + last.op) >> last.bits)]; | |||
if ((unsigned)(last.bits + thisx.bits) <= bits) break; | |||
PULLBYTE(); | |||
} | |||
DROPBITS(last.bits); | |||
} | |||
DROPBITS(thisx.bits); | |||
if (thisx.op & 64) { | |||
strm->msg = (char *)"invalid distance code"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
state->offset = (unsigned)thisx.val; | |||
/* get distance extra bits, if any */ | |||
state->extra = (unsigned)(thisx.op) & 15; | |||
if (state->extra != 0) { | |||
NEEDBITS(state->extra); | |||
state->offset += BITS(state->extra); | |||
DROPBITS(state->extra); | |||
} | |||
if (state->offset > state->wsize - (state->whave < state->wsize ? | |||
left : 0)) { | |||
strm->msg = (char *)"invalid distance too far back"; | |||
state->mode = BAD; | |||
break; | |||
} | |||
Tracevv((stderr, "inflate: distance %u\n", state->offset)); | |||
/* copy match from window to output */ | |||
do { | |||
ROOM(); | |||
copy = state->wsize - state->offset; | |||
if (copy < left) { | |||
from = put + copy; | |||
copy = left - copy; | |||
} | |||
else { | |||
from = put - state->offset; | |||
copy = left; | |||
} | |||
if (copy > state->length) copy = state->length; | |||
state->length -= copy; | |||
left -= copy; | |||
do { | |||
*put++ = *from++; | |||
} while (--copy); | |||
} while (state->length != 0); | |||
break; | |||
case DONE: | |||
/* inflate stream terminated properly -- write leftover output */ | |||
ret = Z_STREAM_END; | |||
if (left < state->wsize) { | |||
if (out(out_desc, state->window, state->wsize - left)) | |||
ret = Z_BUF_ERROR; | |||
} | |||
goto inf_leave; | |||
case BAD: | |||
ret = Z_DATA_ERROR; | |||
goto inf_leave; | |||
default: /* can't happen, but makes compilers happy */ | |||
ret = Z_STREAM_ERROR; | |||
goto inf_leave; | |||
} | |||
/* Return unused input */ | |||
inf_leave: | |||
strm->next_in = next; | |||
strm->avail_in = have; | |||
return ret; | |||
} | |||
int ZEXPORT inflateBackEnd (z_streamp strm) | |||
{ | |||
if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) | |||
return Z_STREAM_ERROR; | |||
ZFREE(strm, strm->state); | |||
strm->state = Z_NULL; | |||
Tracev((stderr, "inflate: end\n")); | |||
return Z_OK; | |||
} | |||
/********* End of inlined file: infback.c *********/ | |||
/********* Start of inlined file: inffast.c *********/ | |||
/********* Start of inlined file: inffast.h *********/ | |||
/* WARNING: this file should *not* be used by applications. It is | |||
part of the implementation of the compression library and is | |||
subject to change. Applications should only use zlib.h. | |||
*/ | |||
void inflate_fast OF((z_streamp strm, unsigned start)); | |||
/********* End of inlined file: inffast.h *********/ | |||
#ifndef ASMINF | |||
/* Allow machine dependent optimization for post-increment or pre-increment. | |||
@@ -100332,62 +99628,7 @@ local void copy_block(deflate_state *s, | |||
} | |||
/********* End of inlined file: trees.c *********/ | |||
/********* Start of inlined file: uncompr.c *********/ | |||
/* @(#) $Id: uncompr.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ | |||
#define ZLIB_INTERNAL | |||
/* =========================================================================== | |||
Decompresses the source buffer into the destination buffer. sourceLen is | |||
the byte length of the source buffer. Upon entry, destLen is the total | |||
size of the destination buffer, which must be large enough to hold the | |||
entire uncompressed data. (The size of the uncompressed data must have | |||
been saved previously by the compressor and transmitted to the decompressor | |||
by some mechanism outside the scope of this compression library.) | |||
Upon exit, destLen is the actual size of the compressed buffer. | |||
This function can be used to decompress a whole file at once if the | |||
input file is mmap'ed. | |||
uncompress returns Z_OK if success, Z_MEM_ERROR if there was not | |||
enough memory, Z_BUF_ERROR if there was not enough room in the output | |||
buffer, or Z_DATA_ERROR if the input data was corrupted. | |||
*/ | |||
int ZEXPORT uncompress (Bytef *dest, | |||
uLongf *destLen, | |||
const Bytef *source, | |||
uLong sourceLen) | |||
{ | |||
z_stream stream; | |||
int err; | |||
stream.next_in = (Bytef*)source; | |||
stream.avail_in = (uInt)sourceLen; | |||
/* Check for source > 64K on 16-bit machine: */ | |||
if ((uLong)stream.avail_in != sourceLen) return Z_BUF_ERROR; | |||
stream.next_out = dest; | |||
stream.avail_out = (uInt)*destLen; | |||
if ((uLong)stream.avail_out != *destLen) return Z_BUF_ERROR; | |||
stream.zalloc = (alloc_func)0; | |||
stream.zfree = (free_func)0; | |||
err = inflateInit(&stream); | |||
if (err != Z_OK) return err; | |||
err = inflate(&stream, Z_FINISH); | |||
if (err != Z_STREAM_END) { | |||
inflateEnd(&stream); | |||
if (err == Z_NEED_DICT || (err == Z_BUF_ERROR && stream.avail_in == 0)) | |||
return Z_DATA_ERROR; | |||
return err; | |||
} | |||
*destLen = stream.total_out; | |||
err = inflateEnd(&stream); | |||
return err; | |||
} | |||
/********* End of inlined file: uncompr.c *********/ | |||
//#include "zlib/uncompr.c" | |||
/********* Start of inlined file: zutil.c *********/ | |||
/* @(#) $Id: zutil.c,v 1.1 2007/06/07 17:54:37 jules_rms Exp $ */ | |||
@@ -262226,7 +261467,8 @@ public: | |||
snd_seq_ev_set_subs (&event); | |||
snd_seq_ev_set_direct (&event); | |||
snd_seq_event_output_direct (seqHandle, &event); | |||
snd_seq_event_output (seqHandle, &event); | |||
snd_seq_drain_output (seqHandle); | |||
} | |||
juce_UseDebuggingNewOperator | |||
@@ -266089,9 +265331,23 @@ BEGIN_JUCE_NAMESPACE | |||
#undef Point | |||
/** This suffix is used for naming all Obj-C classes that are used inside juce. | |||
Because of the flat naming structure used by Obj-C, you can get horrible situations where | |||
two DLLs are loaded into a host, each of which uses classes with the same names, and these get | |||
cross-linked so that when you make a call to a class that you thought was private, it ends up | |||
actually calling into a similarly named class in the other module's address space. | |||
By changing this macro to a unique value, you ensure that all the obj-C classes in your app | |||
have unique names, and should avoid this problem. | |||
If you're using the amalgamated version, you can just set this macro to something unique before | |||
you include juce_amalgamated.cpp. | |||
*/ | |||
#ifndef JUCE_ObjCExtraSuffix | |||
#define JUCE_ObjCExtraSuffix 2 | |||
#define JUCE_ObjCExtraSuffix 3 | |||
#endif | |||
#define appendMacro1(a, b, c, d) a ## _ ## b ## _ ## c ## _ ## d | |||
#define appendMacro2(a, b, c, d) appendMacro1(a, b, c, d) | |||
#define MakeObjCClassName(rootName) appendMacro2 (rootName, JUCE_MAJOR_VERSION, JUCE_MINOR_VERSION, JUCE_ObjCExtraSuffix) | |||
@@ -270072,16 +269328,16 @@ class NSViewComponentInternal : public ComponentMovementWatcher | |||
bool wasShowing; | |||
public: | |||
NSView* view; | |||
NSView* const view; | |||
NSViewComponentInternal (NSView* view_, Component* const owner_) | |||
NSViewComponentInternal (NSView* const view_, Component* const owner_) | |||
: ComponentMovementWatcher (owner_), | |||
owner (owner_), | |||
currentPeer (0), | |||
wasShowing (false), | |||
view (view_) | |||
{ | |||
[view retain]; | |||
[view_ retain]; | |||
if (owner_->isShowing()) | |||
componentPeerChanged(); | |||
@@ -270093,6 +269349,18 @@ public: | |||
[view release]; | |||
} | |||
void componentMovedOrResized (Component& comp, bool wasMoved, bool wasResized) | |||
{ | |||
ComponentMovementWatcher::componentMovedOrResized (comp, wasMoved, wasResized); | |||
// The ComponentMovementWatcher version of this method avoids calling | |||
// us when the top-level comp is resized, but for an NSView we need to know this | |||
// because with inverted co-ords, we need to update the position even if the | |||
// top-left pos hasn't changed | |||
if (comp.isOnDesktop() && wasResized) | |||
componentMovedOrResized (wasMoved, wasResized); | |||
} | |||
void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) | |||
{ | |||
Component* const topComp = owner->getTopLevelComponent(); | |||
@@ -66,7 +66,7 @@ | |||
See also SystemStats::getJUCEVersion() for a string version. | |||
*/ | |||
#define JUCE_MAJOR_VERSION 1 | |||
#define JUCE_MINOR_VERSION 46 | |||
#define JUCE_MINOR_VERSION 50 | |||
/** Current Juce version number. | |||
@@ -10115,6 +10115,26 @@ public: | |||
lock.exit(); | |||
} | |||
/** Inserts or replaces an object in the array, assuming it is sorted. | |||
This is similar to addSorted, but if a matching element already exists, then it will be | |||
replaced by the new one, rather than the new one being added as well. | |||
*/ | |||
template <class ElementComparator> | |||
void addOrReplaceSorted (ElementComparator& comparator, | |||
ObjectClass* newObject) throw() | |||
{ | |||
lock.enter(); | |||
const int index = findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed); | |||
if (index > 0 && comparator.compareElements (newObject, this->elements [index - 1]) == 0) | |||
set (index - 1, newObject); // replace an existing object that matches | |||
else | |||
insert (index, newObject); // no match, so insert the new one | |||
lock.exit(); | |||
} | |||
/** Removes an object from the array. | |||
This will remove the object at a given index and move back all the | |||
@@ -15095,11 +15115,16 @@ public: | |||
methods called to try to interrupt them | |||
@param timeOutMilliseconds the length of time this method should wait for all the jobs to finish | |||
before giving up and returning false | |||
@param deleteInactiveJobs if true, any jobs that aren't currently running will be deleted. If false, | |||
they will simply be removed from the pool. Jobs that are already running when | |||
this method is called can choose whether they should be deleted by | |||
returning jobHasFinishedAndShouldBeDeleted from their runJob() method. | |||
@returns true if all jobs are successfully stopped and removed; false if the timeout period | |||
expires while waiting for them to stop | |||
*/ | |||
bool removeAllJobs (const bool interruptRunningJobs, | |||
const int timeOutMilliseconds); | |||
const int timeOutMilliseconds, | |||
const bool deleteInactiveJobs = false); | |||
/** Returns the number of jobs currently running or queued. | |||
*/ | |||
@@ -1535,7 +1535,7 @@ bool AudioUnitPluginFormat::doesPluginStillExist (const PluginDescription& desc) | |||
const FileSearchPath AudioUnitPluginFormat::getDefaultLocationsToSearch() | |||
{ | |||
return FileSearchPath ("(Default AudioUnit locations)"); | |||
return FileSearchPath ("/(Default AudioUnit locations)"); | |||
} | |||
#endif | |||
@@ -38,7 +38,7 @@ | |||
See also SystemStats::getJUCEVersion() for a string version. | |||
*/ | |||
#define JUCE_MAJOR_VERSION 1 | |||
#define JUCE_MINOR_VERSION 46 | |||
#define JUCE_MINOR_VERSION 50 | |||
/** Current Juce version number. | |||
@@ -442,6 +442,26 @@ public: | |||
lock.exit(); | |||
} | |||
/** Inserts or replaces an object in the array, assuming it is sorted. | |||
This is similar to addSorted, but if a matching element already exists, then it will be | |||
replaced by the new one, rather than the new one being added as well. | |||
*/ | |||
template <class ElementComparator> | |||
void addOrReplaceSorted (ElementComparator& comparator, | |||
ObjectClass* newObject) throw() | |||
{ | |||
lock.enter(); | |||
const int index = findInsertIndexInSortedArray (comparator, this->elements, newObject, 0, numUsed); | |||
if (index > 0 && comparator.compareElements (newObject, this->elements [index - 1]) == 0) | |||
set (index - 1, newObject); // replace an existing object that matches | |||
else | |||
insert (index, newObject); // no match, so insert the new one | |||
lock.exit(); | |||
} | |||
//============================================================================== | |||
/** Removes an object from the array. | |||
@@ -46,14 +46,13 @@ namespace zlibNamespace | |||
#define ZLIB_INTERNAL | |||
#define NO_DUMMY_DECL | |||
#include "zlib/zlib.h" | |||
#include "zlib/adler32.c" | |||
#include "zlib/compress.c" | |||
#undef DO1 | |||
#undef DO8 | |||
#include "zlib/crc32.c" | |||
#include "zlib/deflate.c" | |||
#include "zlib/infback.c" | |||
//#include "zlib/infback.c" | |||
#include "zlib/inffast.c" | |||
#undef PULLBYTE | |||
#undef LOAD | |||
@@ -65,7 +64,7 @@ namespace zlibNamespace | |||
#include "zlib/inflate.c" | |||
#include "zlib/inftrees.c" | |||
#include "zlib/trees.c" | |||
#include "zlib/uncompr.c" | |||
//#include "zlib/uncompr.c" | |||
#include "zlib/zutil.c" | |||
#undef Byte | |||
} | |||
@@ -264,7 +264,8 @@ bool ThreadPool::removeJob (ThreadPoolJob* const job, | |||
} | |||
bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, | |||
const int timeOutMs) | |||
const int timeOutMs, | |||
const bool deleteInactiveJobs) | |||
{ | |||
lock.enter(); | |||
@@ -280,6 +281,9 @@ bool ThreadPool::removeAllJobs (const bool interruptRunningJobs, | |||
else | |||
{ | |||
jobs.remove (i); | |||
if (deleteInactiveJobs) | |||
delete job; | |||
} | |||
} | |||
@@ -218,11 +218,16 @@ public: | |||
methods called to try to interrupt them | |||
@param timeOutMilliseconds the length of time this method should wait for all the jobs to finish | |||
before giving up and returning false | |||
@param deleteInactiveJobs if true, any jobs that aren't currently running will be deleted. If false, | |||
they will simply be removed from the pool. Jobs that are already running when | |||
this method is called can choose whether they should be deleted by | |||
returning jobHasFinishedAndShouldBeDeleted from their runJob() method. | |||
@returns true if all jobs are successfully stopped and removed; false if the timeout period | |||
expires while waiting for them to stop | |||
*/ | |||
bool removeAllJobs (const bool interruptRunningJobs, | |||
const int timeOutMilliseconds); | |||
const int timeOutMilliseconds, | |||
const bool deleteInactiveJobs = false); | |||
/** Returns the number of jobs currently running or queued. | |||
*/ | |||