@@ -1,25 +0,0 @@ | |||
{ | |||
"id": "juce_audio_basics", | |||
"name": "JUCE audio and midi data classes", | |||
"version": "4.1.0", | |||
"description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_core", "version": "matching" }], | |||
"include": "juce_audio_basics.h", | |||
"compile": [ { "file": "juce_audio_basics.cpp", "target": "! xcode" }, | |||
{ "file": "juce_audio_basics.mm", "target": "xcode" } ], | |||
"browse": [ "buffers/*", | |||
"effects/*", | |||
"midi/*", | |||
"mpe/*", | |||
"sources/*", | |||
"synthesisers/*" ], | |||
"OSXFrameworks": "Accelerate", | |||
"iOSFrameworks": "Accelerate" | |||
} |
@@ -1,169 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_AUDIOCDBURNER_H_INCLUDED | |||
#define JUCE_AUDIOCDBURNER_H_INCLUDED | |||
#if JUCE_USE_CDBURNER || DOXYGEN | |||
//============================================================================== | |||
/** | |||
*/ | |||
class AudioCDBurner : public ChangeBroadcaster | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Returns a list of available optical drives. | |||
Use openDevice() to open one of the items from this list. | |||
*/ | |||
static StringArray findAvailableDevices(); | |||
/** Tries to open one of the optical drives. | |||
The deviceIndex is an index into the array returned by findAvailableDevices(). | |||
*/ | |||
static AudioCDBurner* openDevice (const int deviceIndex); | |||
/** Destructor. */ | |||
~AudioCDBurner(); | |||
//============================================================================== | |||
enum DiskState | |||
{ | |||
unknown, /**< An error condition, if the device isn't responding. */ | |||
trayOpen, /**< The drive is currently open. Note that a slot-loading drive | |||
may seem to be permanently open. */ | |||
noDisc, /**< The drive has no disk in it. */ | |||
writableDiskPresent, /**< The drive contains a writeable disk. */ | |||
readOnlyDiskPresent /**< The drive contains a read-only disk. */ | |||
}; | |||
/** Returns the current status of the device. | |||
To get informed when the drive's status changes, attach a ChangeListener to | |||
the AudioCDBurner. | |||
*/ | |||
DiskState getDiskState() const; | |||
/** Returns true if there's a writable disk in the drive. */ | |||
bool isDiskPresent() const; | |||
/** Sends an eject signal to the drive. | |||
The eject will happen asynchronously, so you can use getDiskState() and | |||
waitUntilStateChange() to monitor its progress. | |||
*/ | |||
bool openTray(); | |||
/** Blocks the current thread until the drive's state changes, or until the timeout expires. | |||
@returns the device's new state | |||
*/ | |||
DiskState waitUntilStateChange (int timeOutMilliseconds); | |||
//============================================================================== | |||
/** Returns the set of possible write speeds that the device can handle. | |||
These are as a multiple of 'normal' speed, so e.g. '24x' returns 24, etc. | |||
Note that if there's no media present in the drive, this value may be unavailable! | |||
@see setWriteSpeed, getWriteSpeed | |||
*/ | |||
Array<int> getAvailableWriteSpeeds() const; | |||
//============================================================================== | |||
/** Tries to enable or disable buffer underrun safety on devices that support it. | |||
@returns true if it's now enabled. If the device doesn't support it, this | |||
will always return false. | |||
*/ | |||
bool setBufferUnderrunProtection (bool shouldBeEnabled); | |||
//============================================================================== | |||
/** Returns the number of free blocks on the disk. | |||
There are 75 blocks per second, at 44100Hz. | |||
*/ | |||
int getNumAvailableAudioBlocks() const; | |||
/** Adds a track to be written. | |||
The source passed-in here will be kept by this object, and it will | |||
be used and deleted at some point in the future, either during the | |||
burn() method or when this AudioCDBurner object is deleted. Your caller | |||
method shouldn't keep a reference to it or use it again after passing | |||
it in here. | |||
*/ | |||
bool addAudioTrack (AudioSource* source, int numSamples); | |||
//============================================================================== | |||
/** Receives progress callbacks during a cd-burn operation. | |||
@see AudioCDBurner::burn() | |||
*/ | |||
class BurnProgressListener | |||
{ | |||
public: | |||
BurnProgressListener() noexcept {} | |||
virtual ~BurnProgressListener() {} | |||
/** Called at intervals to report on the progress of the AudioCDBurner. | |||
To cancel the burn, return true from this method. | |||
*/ | |||
virtual bool audioCDBurnProgress (float proportionComplete) = 0; | |||
}; | |||
/** Runs the burn process. | |||
This method will block until the operation is complete. | |||
@param listener the object to receive callbacks about progress | |||
@param ejectDiscAfterwards whether to eject the disk after the burn completes | |||
@param performFakeBurnForTesting if true, no data will actually be written to the disk | |||
@param writeSpeed one of the write speeds from getAvailableWriteSpeeds(), or | |||
0 or less to mean the fastest speed. | |||
*/ | |||
String burn (BurnProgressListener* listener, | |||
bool ejectDiscAfterwards, | |||
bool performFakeBurnForTesting, | |||
int writeSpeed); | |||
/** If a burn operation is currently in progress, this tells it to stop | |||
as soon as possible. | |||
It's also possible to stop the burn process by returning true from | |||
BurnProgressListener::audioCDBurnProgress() | |||
*/ | |||
void abortBurn(); | |||
private: | |||
//============================================================================== | |||
AudioCDBurner (const int deviceIndex); | |||
class Pimpl; | |||
friend struct ContainerDeletePolicy<Pimpl>; | |||
ScopedPointer<Pimpl> pimpl; | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDBurner) | |||
}; | |||
#endif | |||
#endif // JUCE_AUDIOCDBURNER_H_INCLUDED |
@@ -1,57 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#if JUCE_USE_CDREADER | |||
int AudioCDReader::getNumTracks() const | |||
{ | |||
return trackStartSamples.size() - 1; | |||
} | |||
int AudioCDReader::getPositionOfTrackStart (int trackNum) const | |||
{ | |||
return trackStartSamples [trackNum]; | |||
} | |||
const Array<int>& AudioCDReader::getTrackOffsets() const | |||
{ | |||
return trackStartSamples; | |||
} | |||
int AudioCDReader::getCDDBId() | |||
{ | |||
int checksum = 0; | |||
const int numTracks = getNumTracks(); | |||
for (int i = 0; i < numTracks; ++i) | |||
for (int offset = (trackStartSamples.getUnchecked(i) + 88200) / 44100; offset > 0; offset /= 10) | |||
checksum += offset % 10; | |||
const int length = (trackStartSamples.getLast() - trackStartSamples.getFirst()) / 44100; | |||
// CCLLLLTT: checksum, length, tracks | |||
return ((checksum & 0xff) << 24) | (length << 8) | numTracks; | |||
} | |||
#endif |
@@ -1,174 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_AUDIOCDREADER_H_INCLUDED | |||
#define JUCE_AUDIOCDREADER_H_INCLUDED | |||
#if JUCE_USE_CDREADER || DOXYGEN | |||
//============================================================================== | |||
/** | |||
A type of AudioFormatReader that reads from an audio CD. | |||
One of these can be used to read a CD as if it's one big audio stream. Use the | |||
getPositionOfTrackStart() method to find where the individual tracks are | |||
within the stream. | |||
@see AudioFormatReader | |||
*/ | |||
class JUCE_API AudioCDReader : public AudioFormatReader | |||
{ | |||
public: | |||
//============================================================================== | |||
/** Returns a list of names of Audio CDs currently available for reading. | |||
If there's a CD drive but no CD in it, this might return an empty list, or | |||
possibly a device that can be opened but which has no tracks, depending | |||
on the platform. | |||
@see createReaderForCD | |||
*/ | |||
static StringArray getAvailableCDNames(); | |||
/** Tries to create an AudioFormatReader that can read from an Audio CD. | |||
@param index the index of one of the available CDs - use getAvailableCDNames() | |||
to find out how many there are. | |||
@returns a new AudioCDReader object, or nullptr if it couldn't be created. The | |||
caller will be responsible for deleting the object returned. | |||
*/ | |||
static AudioCDReader* createReaderForCD (const int index); | |||
//============================================================================== | |||
/** Destructor. */ | |||
~AudioCDReader(); | |||
/** Implementation of the AudioFormatReader method. */ | |||
bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
int64 startSampleInFile, int numSamples) override; | |||
/** Checks whether the CD has been removed from the drive. */ | |||
bool isCDStillPresent() const; | |||
/** Returns the total number of tracks (audio + data). */ | |||
int getNumTracks() const; | |||
/** Finds the sample offset of the start of a track. | |||
@param trackNum the track number, where trackNum = 0 is the first track | |||
and trackNum = getNumTracks() means the end of the CD. | |||
*/ | |||
int getPositionOfTrackStart (int trackNum) const; | |||
/** Returns true if a given track is an audio track. | |||
@param trackNum the track number, where 0 is the first track. | |||
*/ | |||
bool isTrackAudio (int trackNum) const; | |||
/** Returns an array of sample offsets for the start of each track, followed by | |||
the sample position of the end of the CD. | |||
*/ | |||
const Array<int>& getTrackOffsets() const; | |||
/** Refreshes the object's table of contents. | |||
If the disc has been ejected and a different one put in since this | |||
object was created, this will cause it to update its idea of how many tracks | |||
there are, etc. | |||
*/ | |||
void refreshTrackLengths(); | |||
/** Enables scanning for indexes within tracks. | |||
@see getLastIndex | |||
*/ | |||
void enableIndexScanning (bool enabled); | |||
/** Returns the index number found during the last read() call. | |||
Index scanning is turned off by default - turn it on with enableIndexScanning(). | |||
Then when the read() method is called, if it comes across an index within that | |||
block, the index number is stored and returned by this method. | |||
Some devices might not support indexes, of course. | |||
(If you don't know what CD indexes are, it's unlikely you'll ever need them). | |||
@see enableIndexScanning | |||
*/ | |||
int getLastIndex() const; | |||
/** Scans a track to find the position of any indexes within it. | |||
@param trackNumber the track to look in, where 0 is the first track on the disc | |||
@returns an array of sample positions of any index points found (not including | |||
the index that marks the start of the track) | |||
*/ | |||
Array<int> findIndexesInTrack (const int trackNumber); | |||
/** Returns the CDDB id number for the CD. | |||
It's not a great way of identifying a disc, but it's traditional. | |||
*/ | |||
int getCDDBId(); | |||
/** Tries to eject the disk. | |||
Ejecting the disk might not actually be possible, e.g. if some other process is using it. | |||
*/ | |||
void ejectDisk(); | |||
//============================================================================== | |||
enum | |||
{ | |||
framesPerSecond = 75, | |||
samplesPerFrame = 44100 / framesPerSecond | |||
}; | |||
private: | |||
//============================================================================== | |||
Array<int> trackStartSamples; | |||
#if JUCE_MAC | |||
File volumeDir; | |||
Array<File> tracks; | |||
int currentReaderTrack; | |||
ScopedPointer<AudioFormatReader> reader; | |||
AudioCDReader (const File& volume); | |||
#elif JUCE_WINDOWS | |||
bool audioTracks [100]; | |||
void* handle; | |||
MemoryBlock buffer; | |||
bool indexingEnabled; | |||
int lastIndex, firstFrameInBuffer, samplesInBuffer; | |||
AudioCDReader (void* handle); | |||
int getIndexAt (int samplePos); | |||
#elif JUCE_LINUX | |||
AudioCDReader(); | |||
#endif | |||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDReader) | |||
}; | |||
#endif | |||
#endif // JUCE_AUDIOCDREADER_H_INCLUDED |
@@ -1,28 +0,0 @@ | |||
{ | |||
"id": "juce_audio_devices", | |||
"name": "JUCE audio and midi I/O device classes", | |||
"version": "4.1.0", | |||
"description": "Classes to play and record from audio and midi i/o devices.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_audio_basics", "version": "matching" }, | |||
{ "id": "juce_audio_formats", "version": "matching" }, | |||
{ "id": "juce_events", "version": "matching" } ], | |||
"include": "juce_audio_devices.h", | |||
"compile": [ { "file": "juce_audio_devices.cpp", "target": "! xcode" }, | |||
{ "file": "juce_audio_devices.mm", "target": "xcode" } ], | |||
"browse": [ "audio_io/*", | |||
"midi_io/*", | |||
"sources/*", | |||
"audio_cd/*", | |||
"native/*" ], | |||
"OSXFrameworks": "CoreAudio CoreMIDI DiscRecording", | |||
"iOSFrameworks": "AudioToolbox CoreMIDI CoreAudio AVFoundation", | |||
"LinuxLibs": "asound", | |||
"mingwLibs": "winmm" | |||
} |
@@ -1,77 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
AudioCDReader::AudioCDReader() | |||
: AudioFormatReader (0, "CD Audio") | |||
{ | |||
} | |||
StringArray AudioCDReader::getAvailableCDNames() | |||
{ | |||
StringArray names; | |||
return names; | |||
} | |||
AudioCDReader* AudioCDReader::createReaderForCD (const int index) | |||
{ | |||
return nullptr; | |||
} | |||
AudioCDReader::~AudioCDReader() | |||
{ | |||
} | |||
void AudioCDReader::refreshTrackLengths() | |||
{ | |||
} | |||
bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
int64 startSampleInFile, int numSamples) | |||
{ | |||
return false; | |||
} | |||
bool AudioCDReader::isCDStillPresent() const | |||
{ | |||
return false; | |||
} | |||
bool AudioCDReader::isTrackAudio (int trackNum) const | |||
{ | |||
return false; | |||
} | |||
void AudioCDReader::enableIndexScanning (bool b) | |||
{ | |||
} | |||
int AudioCDReader::getLastIndex() const | |||
{ | |||
return 0; | |||
} | |||
Array<int> AudioCDReader::findIndexesInTrack (const int trackNumber) | |||
{ | |||
return Array<int>(); | |||
} |
@@ -1,455 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
const int kilobytesPerSecond1x = 176; | |||
struct AudioTrackProducerClass : public ObjCClass <NSObject> | |||
{ | |||
AudioTrackProducerClass() : ObjCClass <NSObject> ("JUCEAudioTrackProducer_") | |||
{ | |||
addIvar<AudioSourceHolder*> ("source"); | |||
addMethod (@selector (initWithAudioSourceHolder:), initWithAudioSourceHolder, "@@:^v"); | |||
addMethod (@selector (cleanupTrackAfterBurn:), cleanupTrackAfterBurn, "v@:@"); | |||
addMethod (@selector (cleanupTrackAfterVerification:), cleanupTrackAfterVerification, "c@:@"); | |||
addMethod (@selector (estimateLengthOfTrack:), estimateLengthOfTrack, "Q@:@"); | |||
addMethod (@selector (prepareTrack:forBurn:toMedia:), prepareTrack, "c@:@@@"); | |||
addMethod (@selector (prepareTrackForVerification:), prepareTrackForVerification, "c@:@"); | |||
addMethod (@selector (produceDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), | |||
produceDataForTrack, "I@:@^cIQI^I"); | |||
addMethod (@selector (producePreGapForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), | |||
produceDataForTrack, "I@:@^cIQI^I"); | |||
addMethod (@selector (verifyDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), | |||
produceDataForTrack, "I@:@^cIQI^I"); | |||
registerClass(); | |||
} | |||
struct AudioSourceHolder | |||
{ | |||
AudioSourceHolder (AudioSource* s, int numFrames) | |||
: source (s), readPosition (0), lengthInFrames (numFrames) | |||
{ | |||
} | |||
~AudioSourceHolder() | |||
{ | |||
if (source != nullptr) | |||
source->releaseResources(); | |||
} | |||
ScopedPointer<AudioSource> source; | |||
int readPosition, lengthInFrames; | |||
}; | |||
private: | |||
static id initWithAudioSourceHolder (id self, SEL, AudioSourceHolder* source) | |||
{ | |||
self = sendSuperclassMessage (self, @selector (init)); | |||
object_setInstanceVariable (self, "source", source); | |||
return self; | |||
} | |||
static AudioSourceHolder* getSource (id self) | |||
{ | |||
return getIvar<AudioSourceHolder*> (self, "source"); | |||
} | |||
static void dealloc (id self, SEL) | |||
{ | |||
delete getSource (self); | |||
sendSuperclassMessage (self, @selector (dealloc)); | |||
} | |||
static void cleanupTrackAfterBurn (id self, SEL, DRTrack*) {} | |||
static BOOL cleanupTrackAfterVerification (id self, SEL, DRTrack*) { return true; } | |||
static uint64_t estimateLengthOfTrack (id self, SEL, DRTrack*) | |||
{ | |||
return getSource (self)->lengthInFrames; | |||
} | |||
static BOOL prepareTrack (id self, SEL, DRTrack*, DRBurn*, NSDictionary*) | |||
{ | |||
if (AudioSourceHolder* const source = getSource (self)) | |||
{ | |||
source->source->prepareToPlay (44100 / 75, 44100); | |||
source->readPosition = 0; | |||
} | |||
return true; | |||
} | |||
static BOOL prepareTrackForVerification (id self, SEL, DRTrack*) | |||
{ | |||
if (AudioSourceHolder* const source = getSource (self)) | |||
source->source->prepareToPlay (44100 / 75, 44100); | |||
return true; | |||
} | |||
static uint32_t produceDataForTrack (id self, SEL, DRTrack*, char* buffer, | |||
uint32_t bufferLength, uint64_t /*address*/, | |||
uint32_t /*blockSize*/, uint32_t* /*flags*/) | |||
{ | |||
if (AudioSourceHolder* const source = getSource (self)) | |||
{ | |||
const int numSamples = jmin ((int) bufferLength / 4, | |||
(source->lengthInFrames * (44100 / 75)) - source->readPosition); | |||
if (numSamples > 0) | |||
{ | |||
AudioSampleBuffer tempBuffer (2, numSamples); | |||
AudioSourceChannelInfo info (tempBuffer); | |||
source->source->getNextAudioBlock (info); | |||
typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> CDSampleFormat; | |||
typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; | |||
CDSampleFormat left (buffer, 2); | |||
left.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (0)), numSamples); | |||
CDSampleFormat right (buffer + 2, 2); | |||
right.convertSamples (SourceSampleFormat (tempBuffer.getReadPointer (1)), numSamples); | |||
source->readPosition += numSamples; | |||
} | |||
return numSamples * 4; | |||
} | |||
return 0; | |||
} | |||
static uint32_t producePreGapForTrack (id self, SEL, DRTrack*, char* buffer, | |||
uint32_t bufferLength, uint64_t /*address*/, | |||
uint32_t /*blockSize*/, uint32_t* /*flags*/) | |||
{ | |||
zeromem (buffer, bufferLength); | |||
return bufferLength; | |||
} | |||
static BOOL verifyDataForTrack (id self, SEL, DRTrack*, const char*, | |||
uint32_t /*bufferLength*/, uint64_t /*address*/, | |||
uint32_t /*blockSize*/, uint32_t* /*flags*/) | |||
{ | |||
return true; | |||
} | |||
}; | |||
struct OpenDiskDevice | |||
{ | |||
OpenDiskDevice (DRDevice* d) | |||
: device (d), | |||
tracks ([[NSMutableArray alloc] init]), | |||
underrunProtection (true) | |||
{ | |||
} | |||
~OpenDiskDevice() | |||
{ | |||
[tracks release]; | |||
} | |||
void addSourceTrack (AudioSource* source, int numSamples) | |||
{ | |||
if (source != nullptr) | |||
{ | |||
const int numFrames = (numSamples + 587) / 588; | |||
static AudioTrackProducerClass cls; | |||
NSObject* producer = [cls.createInstance() performSelector: @selector (initWithAudioSourceHolder:) | |||
withObject: (id) new AudioTrackProducerClass::AudioSourceHolder (source, numFrames)]; | |||
DRTrack* track = [[DRTrack alloc] initWithProducer: producer]; | |||
{ | |||
NSMutableDictionary* p = [[track properties] mutableCopy]; | |||
[p setObject: [DRMSF msfWithFrames: numFrames] forKey: DRTrackLengthKey]; | |||
[p setObject: [NSNumber numberWithUnsignedShort: 2352] forKey: DRBlockSizeKey]; | |||
[p setObject: [NSNumber numberWithInt: 0] forKey: DRDataFormKey]; | |||
[p setObject: [NSNumber numberWithInt: 0] forKey: DRBlockTypeKey]; | |||
[p setObject: [NSNumber numberWithInt: 0] forKey: DRTrackModeKey]; | |||
[p setObject: [NSNumber numberWithInt: 0] forKey: DRSessionFormatKey]; | |||
[track setProperties: p]; | |||
[p release]; | |||
} | |||
[tracks addObject: track]; | |||
[track release]; | |||
[producer release]; | |||
} | |||
} | |||
String burn (AudioCDBurner::BurnProgressListener* listener, | |||
bool shouldEject, bool peformFakeBurnForTesting, int burnSpeed) | |||
{ | |||
DRBurn* burn = [DRBurn burnForDevice: device]; | |||
if (! [device acquireExclusiveAccess]) | |||
return "Couldn't open or write to the CD device"; | |||
[device acquireMediaReservation]; | |||
NSMutableDictionary* d = [[burn properties] mutableCopy]; | |||
[d autorelease]; | |||
[d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; | |||
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; | |||
[d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey]; | |||
if (burnSpeed > 0) | |||
[d setObject: [NSNumber numberWithFloat: burnSpeed * kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey]; | |||
if (! underrunProtection) | |||
[d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey]; | |||
[burn setProperties: d]; | |||
[burn writeLayout: tracks]; | |||
for (;;) | |||
{ | |||
Thread::sleep (300); | |||
float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; | |||
if (listener != nullptr && listener->audioCDBurnProgress (progress)) | |||
{ | |||
[burn abort]; | |||
return "User cancelled the write operation"; | |||
} | |||
if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) | |||
return "Write operation failed"; | |||
if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) | |||
break; | |||
NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] | |||
objectForKey: DRErrorStatusErrorStringKey]; | |||
if ([err length] > 0) | |||
return nsStringToJuce (err); | |||
} | |||
[device releaseMediaReservation]; | |||
[device releaseExclusiveAccess]; | |||
return String::empty; | |||
} | |||
DRDevice* device; | |||
NSMutableArray* tracks; | |||
bool underrunProtection; | |||
}; | |||
//============================================================================== | |||
class AudioCDBurner::Pimpl : public Timer | |||
{ | |||
public: | |||
Pimpl (AudioCDBurner& b, int deviceIndex) : owner (b) | |||
{ | |||
if (DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]) | |||
{ | |||
device = new OpenDiskDevice (dev); | |||
lastState = getDiskState(); | |||
startTimer (1000); | |||
} | |||
} | |||
~Pimpl() | |||
{ | |||
stopTimer(); | |||
} | |||
void timerCallback() override | |||
{ | |||
const DiskState state = getDiskState(); | |||
if (state != lastState) | |||
{ | |||
lastState = state; | |||
owner.sendChangeMessage(); | |||
} | |||
} | |||
DiskState getDiskState() const | |||
{ | |||
if ([device->device isValid]) | |||
{ | |||
NSDictionary* status = [device->device status]; | |||
NSString* state = [status objectForKey: DRDeviceMediaStateKey]; | |||
if ([state isEqualTo: DRDeviceMediaStateNone]) | |||
{ | |||
if ([[status objectForKey: DRDeviceIsTrayOpenKey] boolValue]) | |||
return trayOpen; | |||
return noDisc; | |||
} | |||
if ([state isEqualTo: DRDeviceMediaStateMediaPresent]) | |||
{ | |||
if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0) | |||
return writableDiskPresent; | |||
return readOnlyDiskPresent; | |||
} | |||
} | |||
return unknown; | |||
} | |||
bool openTray() { return [device->device isValid] && [device->device ejectMedia]; } | |||
Array<int> getAvailableWriteSpeeds() const | |||
{ | |||
Array<int> results; | |||
if ([device->device isValid]) | |||
for (id kbPerSec in [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]) | |||
results.add ([kbPerSec intValue] / kilobytesPerSecond1x); | |||
return results; | |||
} | |||
bool setBufferUnderrunProtection (const bool shouldBeEnabled) | |||
{ | |||
if ([device->device isValid]) | |||
{ | |||
device->underrunProtection = shouldBeEnabled; | |||
return shouldBeEnabled && [[[device->device status] objectForKey: DRDeviceCanUnderrunProtectCDKey] boolValue]; | |||
} | |||
return false; | |||
} | |||
int getNumAvailableAudioBlocks() const | |||
{ | |||
return [[[[device->device status] objectForKey: DRDeviceMediaInfoKey] | |||
objectForKey: DRDeviceMediaBlocksFreeKey] intValue]; | |||
} | |||
ScopedPointer<OpenDiskDevice> device; | |||
private: | |||
DiskState lastState; | |||
AudioCDBurner& owner; | |||
}; | |||
//============================================================================== | |||
AudioCDBurner::AudioCDBurner (const int deviceIndex) | |||
{ | |||
pimpl = new Pimpl (*this, deviceIndex); | |||
} | |||
AudioCDBurner::~AudioCDBurner() | |||
{ | |||
} | |||
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) | |||
{ | |||
ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex)); | |||
if (b->pimpl->device == nil) | |||
b = nullptr; | |||
return b.release(); | |||
} | |||
StringArray AudioCDBurner::findAvailableDevices() | |||
{ | |||
StringArray s; | |||
for (NSDictionary* dic in [DRDevice devices]) | |||
if (NSString* name = [dic valueForKey: DRDeviceProductNameKey]) | |||
s.add (nsStringToJuce (name)); | |||
return s; | |||
} | |||
AudioCDBurner::DiskState AudioCDBurner::getDiskState() const | |||
{ | |||
return pimpl->getDiskState(); | |||
} | |||
bool AudioCDBurner::isDiskPresent() const | |||
{ | |||
return getDiskState() == writableDiskPresent; | |||
} | |||
bool AudioCDBurner::openTray() | |||
{ | |||
return pimpl->openTray(); | |||
} | |||
AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds) | |||
{ | |||
const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds; | |||
DiskState oldState = getDiskState(); | |||
DiskState newState = oldState; | |||
while (newState == oldState && Time::currentTimeMillis() < timeout) | |||
{ | |||
newState = getDiskState(); | |||
Thread::sleep (100); | |||
} | |||
return newState; | |||
} | |||
Array<int> AudioCDBurner::getAvailableWriteSpeeds() const | |||
{ | |||
return pimpl->getAvailableWriteSpeeds(); | |||
} | |||
bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled) | |||
{ | |||
return pimpl->setBufferUnderrunProtection (shouldBeEnabled); | |||
} | |||
int AudioCDBurner::getNumAvailableAudioBlocks() const | |||
{ | |||
return pimpl->getNumAvailableAudioBlocks(); | |||
} | |||
bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps) | |||
{ | |||
if ([pimpl->device->device isValid]) | |||
{ | |||
pimpl->device->addSourceTrack (source, numSamps); | |||
return true; | |||
} | |||
return false; | |||
} | |||
String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, | |||
bool ejectDiscAfterwards, | |||
bool performFakeBurnForTesting, | |||
int writeSpeed) | |||
{ | |||
if ([pimpl->device->device isValid]) | |||
return pimpl->device->burn (listener, ejectDiscAfterwards, performFakeBurnForTesting, writeSpeed); | |||
return "Couldn't open or write to the CD device"; | |||
} |
@@ -1,261 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
namespace CDReaderHelpers | |||
{ | |||
inline const XmlElement* getElementForKey (const XmlElement& xml, const String& key) | |||
{ | |||
forEachXmlChildElementWithTagName (xml, child, "key") | |||
if (child->getAllSubText().trim() == key) | |||
return child->getNextElement(); | |||
return nullptr; | |||
} | |||
static int getIntValueForKey (const XmlElement& xml, const String& key, int defaultValue = -1) | |||
{ | |||
const XmlElement* const block = getElementForKey (xml, key); | |||
return block != nullptr ? block->getAllSubText().trim().getIntValue() : defaultValue; | |||
} | |||
// Get the track offsets for a CD given an XmlElement representing its TOC.Plist. | |||
// Returns NULL on success, otherwise a const char* representing an error. | |||
static const char* getTrackOffsets (XmlDocument& xmlDocument, Array<int>& offsets) | |||
{ | |||
const ScopedPointer<XmlElement> xml (xmlDocument.getDocumentElement()); | |||
if (xml == nullptr) | |||
return "Couldn't parse XML in file"; | |||
const XmlElement* const dict = xml->getChildByName ("dict"); | |||
if (dict == nullptr) | |||
return "Couldn't get top level dictionary"; | |||
const XmlElement* const sessions = getElementForKey (*dict, "Sessions"); | |||
if (sessions == nullptr) | |||
return "Couldn't find sessions key"; | |||
const XmlElement* const session = sessions->getFirstChildElement(); | |||
if (session == nullptr) | |||
return "Couldn't find first session"; | |||
const int leadOut = getIntValueForKey (*session, "Leadout Block"); | |||
if (leadOut < 0) | |||
return "Couldn't find Leadout Block"; | |||
const XmlElement* const trackArray = getElementForKey (*session, "Track Array"); | |||
if (trackArray == nullptr) | |||
return "Couldn't find Track Array"; | |||
forEachXmlChildElement (*trackArray, track) | |||
{ | |||
const int trackValue = getIntValueForKey (*track, "Start Block"); | |||
if (trackValue < 0) | |||
return "Couldn't find Start Block in the track"; | |||
offsets.add (trackValue * AudioCDReader::samplesPerFrame - 88200); | |||
} | |||
offsets.add (leadOut * AudioCDReader::samplesPerFrame - 88200); | |||
return nullptr; | |||
} | |||
static void findDevices (Array<File>& cds) | |||
{ | |||
File volumes ("/Volumes"); | |||
volumes.findChildFiles (cds, File::findDirectories, false); | |||
for (int i = cds.size(); --i >= 0;) | |||
if (! cds.getReference(i).getChildFile (".TOC.plist").exists()) | |||
cds.remove (i); | |||
} | |||
struct TrackSorter | |||
{ | |||
static int getCDTrackNumber (const File& file) | |||
{ | |||
return file.getFileName().initialSectionContainingOnly ("0123456789").getIntValue(); | |||
} | |||
static int compareElements (const File& first, const File& second) | |||
{ | |||
const int firstTrack = getCDTrackNumber (first); | |||
const int secondTrack = getCDTrackNumber (second); | |||
jassert (firstTrack > 0 && secondTrack > 0); | |||
return firstTrack - secondTrack; | |||
} | |||
}; | |||
} | |||
//============================================================================== | |||
StringArray AudioCDReader::getAvailableCDNames() | |||
{ | |||
Array<File> cds; | |||
CDReaderHelpers::findDevices (cds); | |||
StringArray names; | |||
for (int i = 0; i < cds.size(); ++i) | |||
names.add (cds.getReference(i).getFileName()); | |||
return names; | |||
} | |||
AudioCDReader* AudioCDReader::createReaderForCD (const int index) | |||
{ | |||
Array<File> cds; | |||
CDReaderHelpers::findDevices (cds); | |||
if (cds[index].exists()) | |||
return new AudioCDReader (cds[index]); | |||
return nullptr; | |||
} | |||
AudioCDReader::AudioCDReader (const File& volume) | |||
: AudioFormatReader (0, "CD Audio"), | |||
volumeDir (volume), | |||
currentReaderTrack (-1), | |||
reader (0) | |||
{ | |||
sampleRate = 44100.0; | |||
bitsPerSample = 16; | |||
numChannels = 2; | |||
usesFloatingPointData = false; | |||
refreshTrackLengths(); | |||
} | |||
AudioCDReader::~AudioCDReader() | |||
{ | |||
} | |||
void AudioCDReader::refreshTrackLengths() | |||
{ | |||
tracks.clear(); | |||
trackStartSamples.clear(); | |||
lengthInSamples = 0; | |||
volumeDir.findChildFiles (tracks, File::findFiles | File::ignoreHiddenFiles, false, "*.aiff"); | |||
CDReaderHelpers::TrackSorter sorter; | |||
tracks.sort (sorter); | |||
const File toc (volumeDir.getChildFile (".TOC.plist")); | |||
if (toc.exists()) | |||
{ | |||
XmlDocument doc (toc); | |||
const char* error = CDReaderHelpers::getTrackOffsets (doc, trackStartSamples); | |||
ignoreUnused (error); // could be logged.. | |||
lengthInSamples = trackStartSamples.getLast() - trackStartSamples.getFirst(); | |||
} | |||
} | |||
bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
int64 startSampleInFile, int numSamples) | |||
{ | |||
while (numSamples > 0) | |||
{ | |||
int track = -1; | |||
for (int i = 0; i < trackStartSamples.size() - 1; ++i) | |||
{ | |||
if (startSampleInFile < trackStartSamples.getUnchecked (i + 1)) | |||
{ | |||
track = i; | |||
break; | |||
} | |||
} | |||
if (track < 0) | |||
return false; | |||
if (track != currentReaderTrack) | |||
{ | |||
reader = nullptr; | |||
if (FileInputStream* const in = tracks [track].createInputStream()) | |||
{ | |||
BufferedInputStream* const bin = new BufferedInputStream (in, 65536, true); | |||
AiffAudioFormat format; | |||
reader = format.createReaderFor (bin, true); | |||
if (reader == nullptr) | |||
currentReaderTrack = -1; | |||
else | |||
currentReaderTrack = track; | |||
} | |||
} | |||
if (reader == nullptr) | |||
return false; | |||
const int startPos = (int) (startSampleInFile - trackStartSamples.getUnchecked (track)); | |||
const int numAvailable = (int) jmin ((int64) numSamples, reader->lengthInSamples - startPos); | |||
reader->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startPos, numAvailable); | |||
numSamples -= numAvailable; | |||
startSampleInFile += numAvailable; | |||
} | |||
return true; | |||
} | |||
bool AudioCDReader::isCDStillPresent() const | |||
{ | |||
return volumeDir.exists(); | |||
} | |||
void AudioCDReader::ejectDisk() | |||
{ | |||
JUCE_AUTORELEASEPOOL | |||
{ | |||
[[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; | |||
} | |||
} | |||
bool AudioCDReader::isTrackAudio (int trackNum) const | |||
{ | |||
return tracks [trackNum].hasFileExtension (".aiff"); | |||
} | |||
void AudioCDReader::enableIndexScanning (bool) | |||
{ | |||
// any way to do this on a Mac?? | |||
} | |||
int AudioCDReader::getLastIndex() const | |||
{ | |||
return 0; | |||
} | |||
Array<int> AudioCDReader::findIndexesInTrack (const int /*trackNumber*/) | |||
{ | |||
return Array<int>(); | |||
} |
@@ -1,411 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
namespace CDBurnerHelpers | |||
{ | |||
IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master) | |||
{ | |||
CoInitialize (0); | |||
IDiscMaster* dm; | |||
IDiscRecorder* result = nullptr; | |||
if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0, | |||
CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, | |||
IID_IDiscMaster, | |||
(void**) &dm))) | |||
{ | |||
if (SUCCEEDED (dm->Open())) | |||
{ | |||
IEnumDiscRecorders* drEnum = nullptr; | |||
if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum))) | |||
{ | |||
IDiscRecorder* dr = nullptr; | |||
DWORD dummy; | |||
int index = 0; | |||
while (drEnum->Next (1, &dr, &dummy) == S_OK) | |||
{ | |||
if (indexToOpen == index) | |||
{ | |||
result = dr; | |||
break; | |||
} | |||
else if (list != nullptr) | |||
{ | |||
BSTR path; | |||
if (SUCCEEDED (dr->GetPath (&path))) | |||
list->add ((const WCHAR*) path); | |||
} | |||
++index; | |||
dr->Release(); | |||
} | |||
drEnum->Release(); | |||
} | |||
if (master == 0) | |||
dm->Close(); | |||
} | |||
if (master != nullptr) | |||
*master = dm; | |||
else | |||
dm->Release(); | |||
} | |||
return result; | |||
} | |||
} | |||
//============================================================================== | |||
class AudioCDBurner::Pimpl : public ComBaseClassHelper <IDiscMasterProgressEvents>, | |||
public Timer | |||
{ | |||
public: | |||
Pimpl (AudioCDBurner& owner_, IDiscMaster* discMaster_, IDiscRecorder* discRecorder_) | |||
: owner (owner_), discMaster (discMaster_), discRecorder (discRecorder_), redbook (0), | |||
listener (0), progress (0), shouldCancel (false) | |||
{ | |||
HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); | |||
jassert (SUCCEEDED (hr)); | |||
hr = discMaster->SetActiveDiscRecorder (discRecorder); | |||
//jassert (SUCCEEDED (hr)); | |||
lastState = getDiskState(); | |||
startTimer (2000); | |||
} | |||
~Pimpl() {} | |||
void releaseObjects() | |||
{ | |||
discRecorder->Close(); | |||
if (redbook != nullptr) | |||
redbook->Release(); | |||
discRecorder->Release(); | |||
discMaster->Release(); | |||
Release(); | |||
} | |||
JUCE_COMRESULT QueryCancel (boolean* pbCancel) | |||
{ | |||
if (listener != nullptr && ! shouldCancel) | |||
shouldCancel = listener->audioCDBurnProgress (progress); | |||
*pbCancel = shouldCancel; | |||
return S_OK; | |||
} | |||
JUCE_COMRESULT NotifyBlockProgress (long nCompleted, long nTotal) | |||
{ | |||
progress = nCompleted / (float) nTotal; | |||
shouldCancel = listener != nullptr && listener->audioCDBurnProgress (progress); | |||
return E_NOTIMPL; | |||
} | |||
JUCE_COMRESULT NotifyPnPActivity (void) { return E_NOTIMPL; } | |||
JUCE_COMRESULT NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; } | |||
JUCE_COMRESULT NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; } | |||
JUCE_COMRESULT NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } | |||
JUCE_COMRESULT NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } | |||
JUCE_COMRESULT NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; } | |||
JUCE_COMRESULT NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; } | |||
class ScopedDiscOpener | |||
{ | |||
public: | |||
ScopedDiscOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); } | |||
~ScopedDiscOpener() { pimpl.discRecorder->Close(); } | |||
private: | |||
Pimpl& pimpl; | |||
JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener) | |||
}; | |||
DiskState getDiskState() | |||
{ | |||
const ScopedDiscOpener opener (*this); | |||
long type, flags; | |||
HRESULT hr = discRecorder->QueryMediaType (&type, &flags); | |||
if (FAILED (hr)) | |||
return unknown; | |||
if (type != 0 && (flags & MEDIA_WRITABLE) != 0) | |||
return writableDiskPresent; | |||
if (type == 0) | |||
return noDisc; | |||
return readOnlyDiskPresent; | |||
} | |||
int getIntProperty (const LPOLESTR name, const int defaultReturn) const | |||
{ | |||
ComSmartPtr<IPropertyStorage> prop; | |||
if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress()))) | |||
return defaultReturn; | |||
PROPSPEC iPropSpec; | |||
iPropSpec.ulKind = PRSPEC_LPWSTR; | |||
iPropSpec.lpwstr = name; | |||
PROPVARIANT iPropVariant; | |||
return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)) | |||
? defaultReturn : (int) iPropVariant.lVal; | |||
} | |||
bool setIntProperty (const LPOLESTR name, const int value) const | |||
{ | |||
ComSmartPtr<IPropertyStorage> prop; | |||
if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress()))) | |||
return false; | |||
PROPSPEC iPropSpec; | |||
iPropSpec.ulKind = PRSPEC_LPWSTR; | |||
iPropSpec.lpwstr = name; | |||
PROPVARIANT iPropVariant; | |||
if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))) | |||
return false; | |||
iPropVariant.lVal = (long) value; | |||
return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt)) | |||
&& SUCCEEDED (discRecorder->SetRecorderProperties (prop)); | |||
} | |||
void timerCallback() override | |||
{ | |||
const DiskState state = getDiskState(); | |||
if (state != lastState) | |||
{ | |||
lastState = state; | |||
owner.sendChangeMessage(); | |||
} | |||
} | |||
AudioCDBurner& owner; | |||
DiskState lastState; | |||
IDiscMaster* discMaster; | |||
IDiscRecorder* discRecorder; | |||
IRedbookDiscMaster* redbook; | |||
AudioCDBurner::BurnProgressListener* listener; | |||
float progress; | |||
bool shouldCancel; | |||
}; | |||
//============================================================================== | |||
AudioCDBurner::AudioCDBurner (const int deviceIndex) | |||
{ | |||
IDiscMaster* discMaster = nullptr; | |||
IDiscRecorder* discRecorder = CDBurnerHelpers::enumCDBurners (0, deviceIndex, &discMaster); | |||
if (discRecorder != nullptr) | |||
pimpl = new Pimpl (*this, discMaster, discRecorder); | |||
} | |||
AudioCDBurner::~AudioCDBurner() | |||
{ | |||
if (pimpl != nullptr) | |||
pimpl.release()->releaseObjects(); | |||
} | |||
StringArray AudioCDBurner::findAvailableDevices() | |||
{ | |||
StringArray devs; | |||
CDBurnerHelpers::enumCDBurners (&devs, -1, 0); | |||
return devs; | |||
} | |||
AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) | |||
{ | |||
ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex)); | |||
if (b->pimpl == 0) | |||
b = nullptr; | |||
return b.release(); | |||
} | |||
AudioCDBurner::DiskState AudioCDBurner::getDiskState() const | |||
{ | |||
return pimpl->getDiskState(); | |||
} | |||
bool AudioCDBurner::isDiskPresent() const | |||
{ | |||
return getDiskState() == writableDiskPresent; | |||
} | |||
bool AudioCDBurner::openTray() | |||
{ | |||
const Pimpl::ScopedDiscOpener opener (*pimpl); | |||
return SUCCEEDED (pimpl->discRecorder->Eject()); | |||
} | |||
AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds) | |||
{ | |||
const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds; | |||
DiskState oldState = getDiskState(); | |||
DiskState newState = oldState; | |||
while (newState == oldState && Time::currentTimeMillis() < timeout) | |||
{ | |||
newState = getDiskState(); | |||
Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis()))); | |||
} | |||
return newState; | |||
} | |||
Array<int> AudioCDBurner::getAvailableWriteSpeeds() const | |||
{ | |||
Array<int> results; | |||
const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1); | |||
const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 }; | |||
for (int i = 0; i < numElementsInArray (speeds); ++i) | |||
if (speeds[i] <= maxSpeed) | |||
results.add (speeds[i]); | |||
results.addIfNotAlreadyThere (maxSpeed); | |||
return results; | |||
} | |||
bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled) | |||
{ | |||
if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0) | |||
return false; | |||
pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0); | |||
return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0; | |||
} | |||
int AudioCDBurner::getNumAvailableAudioBlocks() const | |||
{ | |||
long blocksFree = 0; | |||
pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree); | |||
return blocksFree; | |||
} | |||
String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards, | |||
bool performFakeBurnForTesting, int writeSpeed) | |||
{ | |||
pimpl->setIntProperty (L"WriteSpeed", writeSpeed > 0 ? writeSpeed : -1); | |||
pimpl->listener = listener; | |||
pimpl->progress = 0; | |||
pimpl->shouldCancel = false; | |||
UINT_PTR cookie; | |||
HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie); | |||
hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting, | |||
ejectDiscAfterwards); | |||
String error; | |||
if (hr != S_OK) | |||
{ | |||
const char* e = "Couldn't open or write to the CD device"; | |||
if (hr == IMAPI_E_USERABORT) | |||
e = "User cancelled the write operation"; | |||
else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN) | |||
e = "No Disk present"; | |||
error = e; | |||
} | |||
pimpl->discMaster->ProgressUnadvise (cookie); | |||
pimpl->listener = 0; | |||
return error; | |||
} | |||
bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) | |||
{ | |||
if (audioSource == 0) | |||
return false; | |||
ScopedPointer<AudioSource> source (audioSource); | |||
long bytesPerBlock; | |||
HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock); | |||
const int samplesPerBlock = bytesPerBlock / 4; | |||
bool ok = true; | |||
hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); | |||
HeapBlock<byte> buffer (bytesPerBlock); | |||
AudioSampleBuffer sourceBuffer (2, samplesPerBlock); | |||
int samplesDone = 0; | |||
source->prepareToPlay (samplesPerBlock, 44100.0); | |||
while (ok) | |||
{ | |||
{ | |||
AudioSourceChannelInfo info (&sourceBuffer, 0, samplesPerBlock); | |||
sourceBuffer.clear(); | |||
source->getNextAudioBlock (info); | |||
} | |||
buffer.clear (bytesPerBlock); | |||
typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, | |||
AudioData::Interleaved, AudioData::NonConst> CDSampleFormat; | |||
typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, | |||
AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; | |||
CDSampleFormat left (buffer, 2); | |||
left.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (0)), samplesPerBlock); | |||
CDSampleFormat right (buffer + 2, 2); | |||
right.convertSamples (SourceSampleFormat (sourceBuffer.getReadPointer (1)), samplesPerBlock); | |||
hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); | |||
if (FAILED (hr)) | |||
ok = false; | |||
samplesDone += samplesPerBlock; | |||
if (samplesDone >= numSamples) | |||
break; | |||
} | |||
hr = pimpl->redbook->CloseAudioTrack(); | |||
return ok && hr == S_OK; | |||
} |
@@ -1,22 +0,0 @@ | |||
{ | |||
"id": "juce_audio_formats", | |||
"name": "JUCE audio file format codecs", | |||
"version": "4.1.0", | |||
"description": "Classes for reading and writing various audio file formats.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_audio_basics", "version": "matching" } ], | |||
"include": "juce_audio_formats.h", | |||
"compile": [ { "file": "juce_audio_formats.cpp", "target": "! xcode" }, | |||
{ "file": "juce_audio_formats.mm", "target": "xcode" } ], | |||
"browse": [ "format/*", | |||
"codecs/*", | |||
"sampler/*" ], | |||
"OSXFrameworks": "CoreAudio CoreMIDI QuartzCore AudioToolbox", | |||
"iOSFrameworks": "AudioToolbox QuartzCore" | |||
} |
@@ -1,26 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#define JUCE_INCLUDED_AAX_IN_MM 1 | |||
#include "juce_AAX_Wrapper.cpp" |
@@ -1,69 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#define UseExtendedThingResource 1 | |||
#include <AudioUnit/AudioUnit.r> | |||
//============================================================================== | |||
/* The AppConfig.h file should be a file in your project, containing info to describe the | |||
plugin's name, type, etc. The introjucer will generate this file automatically for you. | |||
You may need to adjust the include path of your project to make sure it can be | |||
found by this include statement. (Don't hack this file to change the include path) | |||
*/ | |||
#include "AppConfig.h" | |||
//============================================================================== | |||
// component resources for Audio Unit | |||
#define RES_ID 1000 | |||
#define COMP_TYPE JucePlugin_AUMainType | |||
#define COMP_SUBTYPE JucePlugin_AUSubType | |||
#define COMP_MANUF JucePlugin_AUManufacturerCode | |||
#define VERSION JucePlugin_VersionCode | |||
#define NAME JucePlugin_Manufacturer ": " JucePlugin_Name | |||
#define DESCRIPTION JucePlugin_Desc | |||
#define ENTRY_POINT JucePlugin_AUExportPrefixQuoted "Entry" | |||
#include "AUResources.r" | |||
//============================================================================== | |||
// component resources for Audio Unit Carbon View | |||
#ifndef BUILD_AU_CARBON_UI | |||
#define BUILD_AU_CARBON_UI 1 | |||
#endif | |||
#if BUILD_AU_CARBON_UI | |||
#define RES_ID 2000 | |||
#define COMP_TYPE kAudioUnitCarbonViewComponentType | |||
#define COMP_SUBTYPE JucePlugin_AUSubType | |||
#define COMP_MANUF JucePlugin_AUManufacturerCode | |||
#define VERSION JucePlugin_VersionCode | |||
#define NAME JucePlugin_Manufacturer ": " JucePlugin_Name " View" | |||
#define DESCRIPTION NAME | |||
#define ENTRY_POINT JucePlugin_AUExportPrefixQuoted "ViewEntry" | |||
#include "AUResources.r" | |||
#endif |
@@ -1,6 +0,0 @@ | |||
/* | |||
This dummy file is added to the resources section of the project to | |||
force XCode to create some resources for the dpm. If there aren't any | |||
resources, PT will refuse to load the plugin.. | |||
*/ |
@@ -1,63 +0,0 @@ | |||
{ | |||
"id": "juce_audio_plugin_client", | |||
"name": "JUCE audio plugin wrapper classes", | |||
"version": "4.1.0", | |||
"description": "Classes for building VST, VST3, RTAS, AAX and AU plugins.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_gui_basics", "version": "matching" }, | |||
{ "id": "juce_audio_basics", "version": "matching" }, | |||
{ "id": "juce_audio_processors", "version": "matching" } ], | |||
"include": "juce_audio_plugin_client.h", | |||
"compile": [ { "file": "VST/juce_VST_Wrapper.cpp" }, | |||
{ "file": "VST/juce_VST_Wrapper.mm", "target": "xcode" }, | |||
{ "file": "VST3/juce_VST3_Wrapper.cpp" }, | |||
{ "file": "RTAS/juce_RTAS_DigiCode1.cpp", "warnings": "disabled", "stdcall": "1", "target": "xcode, msvc" }, | |||
{ "file": "RTAS/juce_RTAS_DigiCode2.cpp", "warnings": "disabled", "stdcall": "1", "target": "xcode, msvc" }, | |||
{ "file": "RTAS/juce_RTAS_DigiCode3.cpp", "warnings": "disabled", "stdcall": "1", "target": "xcode, msvc" }, | |||
{ "file": "RTAS/juce_RTAS_MacResources.r", "target": "xcode", "RTASOnly": "1" }, | |||
{ "file": "RTAS/juce_RTAS_MacUtilities.mm", "target": "xcode" }, | |||
{ "file": "RTAS/juce_RTAS_WinResources.rsr", "target": "msvc" }, | |||
{ "file": "RTAS/juce_RTAS_WinUtilities.cpp", "target": "msvc", "warnings": "disabled", "stdcall": "1" }, | |||
{ "file": "RTAS/juce_RTAS_Wrapper.cpp", "warnings": "disabled", "stdcall": "1", "target": "xcode, msvc" }, | |||
{ "file": "AU/juce_AU_Resources.r", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/juce_AU_Wrapper.mm", "target": "xcode" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUBuffer.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUCarbonViewBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUCarbonViewControl.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUCarbonViewDispatch.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUDispatch.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUInputElement.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUMIDIBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUOutputBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUOutputElement.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/AUScopeElement.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/CAAUParameter.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/CAAudioChannelLayout.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/CAMutex.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/CAStreamBasicDescription.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/CAVectorUnit.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/CarbonEventHandler.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/ComponentBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AU/CoreAudioUtilityClasses/MusicDeviceBase.cpp", "warnings": "disabled", "target": "xcode", "AudioUnitOnly": "1" }, | |||
{ "file": "AAX/juce_AAX_Wrapper.cpp", "target": "xcode, msvc" }, | |||
{ "file": "AAX/juce_AAX_Wrapper.mm", "target": "xcode" }, | |||
{ "file": "utility/juce_PluginUtilities.cpp" } | |||
], | |||
"browse": [ "AU/*.cpp", | |||
"AU/*.mm", | |||
"AU/*.h", | |||
"RTAS/*.cpp", | |||
"RTAS/*.mm", | |||
"RTAS/*.h", | |||
"VST/*", | |||
"VST3/*", | |||
"AAX/*", | |||
"utility/*" | |||
] | |||
} |
@@ -1,386 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
struct PluginBusUtilities | |||
{ | |||
//============================================================================== | |||
typedef Array<AudioProcessor::AudioProcessorBus> AudioBusArray; | |||
//============================================================================== | |||
PluginBusUtilities (AudioProcessor& plugin, bool markDiscreteLayoutsAsSupported) | |||
: processor (plugin), | |||
dynamicInBuses (false), | |||
dynamicOutBuses (false), | |||
addDiscreteLayouts (markDiscreteLayoutsAsSupported) | |||
{ | |||
} | |||
//============================================================================== | |||
// the first layout is the default layout | |||
struct SupportedBusLayouts | |||
{ | |||
enum | |||
{ | |||
pseudoChannelBitNum = 90 // use this bit index to check if plug-in really doesn't care about layouts | |||
}; | |||
//============================================================================== | |||
SupportedBusLayouts() : defaultLayoutIndex (0), busIgnoresLayout (true), canBeDisabled (false) {} | |||
AudioChannelSet& getDefault() noexcept { return supportedLayouts.getReference (defaultLayoutIndex); } | |||
const AudioChannelSet& getDefault() const noexcept { return supportedLayouts.getReference (defaultLayoutIndex); } | |||
void updateDefaultLayout (const AudioChannelSet& defaultLayout) noexcept { defaultLayoutIndex = jmax (supportedLayouts.indexOf (defaultLayout), 0); } | |||
bool busSupportsNumChannels (int numChannels) const noexcept { return getDefaultLayoutForChannelNum (numChannels) != nullptr; } | |||
//============================================================================== | |||
const AudioChannelSet* getDefaultLayoutForChannelNum (int channelNum) const noexcept | |||
{ | |||
const AudioChannelSet& dflt = getDefault(); | |||
if (dflt.size() == channelNum) | |||
return &dflt; | |||
for (int i = 0; i < supportedLayouts.size(); ++i) | |||
{ | |||
const AudioChannelSet& layout = supportedLayouts.getReference (i); | |||
if (layout.size() == channelNum) | |||
return &layout; | |||
} | |||
return nullptr; | |||
} | |||
int maxNumberOfChannels() const noexcept | |||
{ | |||
int maxChannels = 0; | |||
for (int i = 0; i < supportedLayouts.size(); ++i) | |||
maxChannels = jmax (maxChannels, supportedLayouts.getReference (i).size()); | |||
return maxChannels; | |||
} | |||
int defaultLayoutIndex; | |||
bool busIgnoresLayout, canBeDisabled, isEnabledByDefault; | |||
SortedSet<AudioChannelSet> supportedLayouts; | |||
}; | |||
//============================================================================== | |||
AudioBusArray& getFilterBus (bool inputBus) noexcept { return inputBus ? processor.busArrangement.inputBuses : processor.busArrangement.outputBuses; } | |||
const AudioBusArray& getFilterBus (bool inputBus) const noexcept { return inputBus ? processor.busArrangement.inputBuses : processor.busArrangement.outputBuses; } | |||
int getBusCount (bool inputBus) const noexcept { return getFilterBus (inputBus).size(); } | |||
AudioChannelSet getChannelSet (bool inputBus, int bus) noexcept { return getFilterBus (inputBus).getReference (bus).channels; } | |||
int getNumChannels (bool inp, int bus) const noexcept { return isPositiveAndBelow (bus, getBusCount (inp)) ? getFilterBus (inp).getReference (bus).channels.size() : 0; } | |||
bool isBusEnabled (bool inputBus, int bus) const noexcept { return (getNumChannels (inputBus, bus) > 0); } | |||
bool hasInputs (int bus) const noexcept { return isBusEnabled (true, bus); } | |||
bool hasOutputs (int bus) const noexcept { return isBusEnabled (false, bus); } | |||
int getNumEnabledBuses (bool inputBus) const noexcept { int i; for (i = 0; i < getBusCount (inputBus); ++i) if (! isBusEnabled (inputBus, i)) break; return i; } | |||
int findTotalNumChannels (bool isInput, int busOffset = 0) const noexcept | |||
{ | |||
int total = 0; | |||
const AudioBusArray& ioBuses = getFilterBus (isInput); | |||
for (int i = busOffset; i < ioBuses.size(); ++i) | |||
total += ioBuses.getReference (i).channels.size(); | |||
return total; | |||
} | |||
//============================================================================== | |||
void restoreBusArrangement (const AudioProcessor::AudioBusArrangement& original) const | |||
{ | |||
const int numInputBuses = getBusCount (true); | |||
const int numOutputBuses = getBusCount (false); | |||
jassert (original.inputBuses. size() == numInputBuses); | |||
jassert (original.outputBuses.size() == numOutputBuses); | |||
for (int busNr = 0; busNr < numInputBuses; ++busNr) | |||
processor.setPreferredBusArrangement (true, busNr, original.inputBuses.getReference (busNr).channels); | |||
for (int busNr = 0; busNr < numOutputBuses; ++busNr) | |||
processor.setPreferredBusArrangement (false, busNr, original.outputBuses.getReference (busNr).channels); | |||
} | |||
//============================================================================== | |||
Array<SupportedBusLayouts>& getSupportedLayouts (bool isInput) noexcept { return isInput ? inputLayouts : outputLayouts; } | |||
const Array<SupportedBusLayouts>& getSupportedLayouts (bool isInput) const noexcept { return isInput ? inputLayouts : outputLayouts; } | |||
SupportedBusLayouts& getSupportedBusLayouts (bool isInput, int busNr) noexcept { return getSupportedLayouts (isInput).getReference (busNr); } | |||
const SupportedBusLayouts& getSupportedBusLayouts (bool isInput, int busNr) const noexcept { return getSupportedLayouts (isInput).getReference (busNr); } | |||
bool busIgnoresLayout (bool inp, int bus) const noexcept | |||
{ | |||
return isPositiveAndBelow (bus, getSupportedLayouts (inp).size()) ? getSupportedBusLayouts (inp, bus).busIgnoresLayout : true; | |||
} | |||
const AudioChannelSet& getDefaultLayoutForBus (bool isInput, int busIdx) const noexcept { return getSupportedBusLayouts (isInput, busIdx).getDefault(); } | |||
bool hasDynamicInBuses() const noexcept { return dynamicInBuses; } | |||
bool hasDynamicOutBuses() const noexcept { return dynamicOutBuses; } | |||
void clear (int inputCount, int outputCount) | |||
{ | |||
inputLayouts.clear(); | |||
inputLayouts.resize (inputCount); | |||
outputLayouts.clear(); | |||
outputLayouts.resize (outputCount); | |||
} | |||
//============================================================================== | |||
AudioChannelSet getDefaultLayoutForChannelNumAndBus (bool isInput, int busIdx, int channelNum) const noexcept | |||
{ | |||
if (const AudioChannelSet* set = getSupportedBusLayouts (isInput, busIdx).getDefaultLayoutForChannelNum (channelNum)) | |||
return *set; | |||
return AudioChannelSet::canonicalChannelSet (channelNum); | |||
} | |||
void findAllCompatibleLayouts() | |||
{ | |||
{ | |||
ScopedBusRestorer restorer (*this); | |||
clear (getBusCount (true), getBusCount (false)); | |||
for (int i = 0; i < getBusCount (true); ++i) findAllCompatibleLayoutsForBus (true, i); | |||
for (int i = 0; i < getBusCount (false); ++i) findAllCompatibleLayoutsForBus (false, i); | |||
} | |||
// find the defaults | |||
for (int i = 0; i < getBusCount (true); ++i) | |||
updateDefaultLayout (true, i); | |||
for (int i = 0; i < getBusCount (false); ++i) | |||
updateDefaultLayout (false, i); | |||
// can any of the buses be disabled/enabled | |||
dynamicInBuses = doesPlugInHaveDynamicBuses (true); | |||
dynamicOutBuses = doesPlugInHaveDynamicBuses (false); | |||
} | |||
//============================================================================== | |||
void enableAllBuses() | |||
{ | |||
for (int busIdx = 1; busIdx < getBusCount (true); ++busIdx) | |||
if (getChannelSet (true, busIdx) == AudioChannelSet::disabled()) | |||
processor.setPreferredBusArrangement (true, busIdx, getDefaultLayoutForBus (true, busIdx)); | |||
for (int busIdx = 1; busIdx < getBusCount (false); ++busIdx) | |||
if (getChannelSet (false, busIdx) == AudioChannelSet::disabled()) | |||
processor.setPreferredBusArrangement (false, busIdx, getDefaultLayoutForBus (false, busIdx)); | |||
} | |||
//============================================================================== | |||
// Helper class which restores the original arrangement when it leaves scope | |||
class ScopedBusRestorer | |||
{ | |||
public: | |||
ScopedBusRestorer (PluginBusUtilities& bUtils) | |||
: busUtils (bUtils), | |||
originalArr (bUtils.processor.busArrangement), | |||
shouldRestore (true) | |||
{} | |||
~ScopedBusRestorer() | |||
{ | |||
if (shouldRestore) | |||
busUtils.restoreBusArrangement (originalArr); | |||
} | |||
void release() noexcept { shouldRestore = false; } | |||
private: | |||
PluginBusUtilities& busUtils; | |||
const AudioProcessor::AudioBusArrangement originalArr; | |||
bool shouldRestore; | |||
JUCE_DECLARE_NON_COPYABLE (ScopedBusRestorer) | |||
}; | |||
//============================================================================== | |||
AudioProcessor& processor; | |||
private: | |||
friend class ScopedBusRestorer; | |||
//============================================================================== | |||
Array<SupportedBusLayouts> inputLayouts, outputLayouts; | |||
bool dynamicInBuses, dynamicOutBuses, addDiscreteLayouts; | |||
//============================================================================== | |||
bool busIgnoresLayoutForChannelNum (bool isInput, int busNr, int channelNum) | |||
{ | |||
AudioChannelSet set; | |||
// If the plug-in does not complain about setting it's layout to an undefined layout | |||
// then we assume that the plug-in ignores the layout alltogether | |||
for (int i = 0; i < channelNum; ++i) | |||
set.addChannel (static_cast<AudioChannelSet::ChannelType> (SupportedBusLayouts::pseudoChannelBitNum + i)); | |||
return processor.setPreferredBusArrangement (isInput, busNr, set); | |||
} | |||
void findAllCompatibleLayoutsForBus (bool isInput, int busNr) | |||
{ | |||
const int maxNumChannels = 9; | |||
SupportedBusLayouts& layouts = getSupportedBusLayouts (isInput, busNr); | |||
layouts.supportedLayouts.clear(); | |||
// check if the plug-in bus can be disabled | |||
layouts.canBeDisabled = processor.setPreferredBusArrangement (isInput, busNr, AudioChannelSet()); | |||
layouts.busIgnoresLayout = true; | |||
for (int i = 1; i <= maxNumChannels; ++i) | |||
{ | |||
const bool ignoresLayoutForChannel = busIgnoresLayoutForChannelNum (isInput, busNr, i); | |||
Array<AudioChannelSet> sets = layoutListCompatibleWithChannelCount (addDiscreteLayouts, i); | |||
for (int j = 0; j < sets.size(); ++j) | |||
{ | |||
const AudioChannelSet& layout = sets.getReference (j); | |||
if (processor.setPreferredBusArrangement (isInput, busNr, layout)) | |||
{ | |||
if (! ignoresLayoutForChannel) | |||
layouts.busIgnoresLayout = false; | |||
layouts.supportedLayouts.add (layout); | |||
} | |||
} | |||
} | |||
// You cannot add a bus in your processor wich does not support any layouts! It must at least support one. | |||
jassert (layouts.supportedLayouts.size() > 0); | |||
} | |||
bool doesPlugInHaveDynamicBuses (bool isInput) const | |||
{ | |||
for (int i = 0; i < getBusCount (isInput); ++i) | |||
if (getSupportedBusLayouts (isInput, i).canBeDisabled) | |||
return true; | |||
return false; | |||
} | |||
void updateDefaultLayout (bool isInput, int busIdx) | |||
{ | |||
SupportedBusLayouts& layouts = getSupportedBusLayouts (isInput, busIdx); | |||
AudioChannelSet set = getChannelSet (isInput, busIdx); | |||
layouts.isEnabledByDefault = (set.size() > 0); | |||
if (layouts.isEnabledByDefault) | |||
layouts.supportedLayouts.add (set); | |||
// If you hit this assertion then you are disabling the main bus by default | |||
// which is unsupported | |||
jassert (layouts.isEnabledByDefault || busIdx >= 0); | |||
if (set == AudioChannelSet()) | |||
{ | |||
const bool mainBusHasInputs = hasInputs (0); | |||
const bool mainBusHasOutputs = hasOutputs (0); | |||
if (busIdx != 0 && (mainBusHasInputs || mainBusHasOutputs)) | |||
{ | |||
// the AudioProcessor does not give us any default layout | |||
// for an aux bus. Use the same number of channels as the | |||
// default layout on the main bus as a sensible default for | |||
// the aux bus | |||
const bool useInput = mainBusHasInputs && mainBusHasOutputs ? isInput : mainBusHasInputs; | |||
const AudioChannelSet& dfltLayout = getSupportedBusLayouts (useInput, 0).getDefault(); | |||
if ((layouts.defaultLayoutIndex = layouts.supportedLayouts.indexOf (dfltLayout)) >= 0) | |||
return; | |||
// no exact match: try at least to match the number of channels | |||
for (int i = 0; i < layouts.supportedLayouts.size(); ++i) | |||
{ | |||
if (layouts.supportedLayouts.getReference (i).size() == dfltLayout.size()) | |||
{ | |||
layouts.defaultLayoutIndex = i; | |||
return; | |||
} | |||
} | |||
} | |||
if (layouts.busIgnoresLayout) | |||
set = AudioChannelSet::discreteChannels (set.size()); | |||
} | |||
layouts.updateDefaultLayout (set); | |||
} | |||
static Array<AudioChannelSet> layoutListCompatibleWithChannelCount (bool addDiscrete, const int channelCount) noexcept | |||
{ | |||
jassert (channelCount > 0); | |||
Array<AudioChannelSet> sets; | |||
if (addDiscrete) | |||
sets.add (AudioChannelSet::discreteChannels (channelCount)); | |||
switch (channelCount) | |||
{ | |||
case 1: | |||
sets.add (AudioChannelSet::mono()); | |||
break; | |||
case 2: | |||
sets.add (AudioChannelSet::stereo()); | |||
break; | |||
case 3: | |||
sets.add (AudioChannelSet::createLCR()); | |||
break; | |||
case 4: | |||
sets.add (AudioChannelSet::createLCRS()); | |||
sets.add (AudioChannelSet::quadraphonic()); | |||
sets.add (AudioChannelSet::ambisonic()); | |||
break; | |||
case 5: | |||
sets.add (AudioChannelSet::pentagonal()); | |||
sets.add (AudioChannelSet::create5point0()); | |||
break; | |||
case 6: | |||
sets.add (AudioChannelSet::hexagonal()); | |||
sets.add (AudioChannelSet::create5point1()); | |||
sets.add (AudioChannelSet::create6point0()); | |||
break; | |||
case 7: | |||
sets.add (AudioChannelSet::create6point1()); | |||
sets.add (AudioChannelSet::create7point0()); | |||
sets.add (AudioChannelSet::createFront7point0()); | |||
break; | |||
case 8: | |||
sets.add (AudioChannelSet::octagonal()); | |||
sets.add (AudioChannelSet::create7point1()); | |||
sets.add (AudioChannelSet::createFront7point1()); | |||
break; | |||
} | |||
return sets; | |||
} | |||
JUCE_DECLARE_NON_COPYABLE (PluginBusUtilities) | |||
}; |
@@ -1,26 +0,0 @@ | |||
{ | |||
"id": "juce_audio_processors", | |||
"name": "JUCE audio plugin hosting classes", | |||
"version": "4.1.0", | |||
"description": "Classes for loading and playing VST, AU, or internally-generated audio processors.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_gui_extra", "version": "matching" }, | |||
{ "id": "juce_audio_basics", "version": "matching" } ], | |||
"include": "juce_audio_processors.h", | |||
"compile": [ { "file": "juce_audio_processors.cpp", "target": "! xcode" }, | |||
{ "file": "juce_audio_processors.mm", "target": "xcode" } ], | |||
"browse": [ "processors/*", | |||
"format/*", | |||
"format_types/*", | |||
"scanning/*", | |||
"utilities/*" | |||
], | |||
"OSXFrameworks": "CoreAudio CoreMIDI AudioToolbox", | |||
"iOSFrameworks": "AudioToolbox" | |||
} |
@@ -1,203 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
AudioChannelSet::AudioChannelSet (uint32 c) : channels (c) {} | |||
bool AudioChannelSet::operator== (const AudioChannelSet& other) const noexcept { return channels == other.channels; } | |||
bool AudioChannelSet::operator!= (const AudioChannelSet& other) const noexcept { return channels != other.channels; } | |||
bool AudioChannelSet::operator< (const AudioChannelSet& other) const noexcept { return channels < other.channels; } | |||
String AudioChannelSet::getChannelTypeName (AudioChannelSet::ChannelType type) | |||
{ | |||
if (type >= discreteChannel0) | |||
return String ("Discrete ") + String (type - discreteChannel0 + 1); | |||
switch (type) | |||
{ | |||
case left: return NEEDS_TRANS("Left"); | |||
case right: return NEEDS_TRANS("Right"); | |||
case centre: return NEEDS_TRANS("Centre"); | |||
case subbass: return NEEDS_TRANS("Subbass"); | |||
case surroundLeft: return NEEDS_TRANS("Left Surround"); | |||
case surroundRight: return NEEDS_TRANS("Right Surround"); | |||
case centreLeft: return NEEDS_TRANS("Centre Left"); | |||
case centreRight: return NEEDS_TRANS("Centre Right"); | |||
case surround: return NEEDS_TRANS("Surround"); | |||
case sideLeft: return NEEDS_TRANS("Side Left"); | |||
case sideRight: return NEEDS_TRANS("Side Right"); | |||
case topMiddle: return NEEDS_TRANS("Top Middle"); | |||
case topFrontLeft: return NEEDS_TRANS("Top Front Left"); | |||
case topFrontCentre: return NEEDS_TRANS("Top Front Centre"); | |||
case topFrontRight: return NEEDS_TRANS("Top Front Right"); | |||
case topRearLeft: return NEEDS_TRANS("Top Rear Left"); | |||
case topRearCentre: return NEEDS_TRANS("Top Rear Centre"); | |||
case topRearRight: return NEEDS_TRANS("Top Rear Right"); | |||
case wideLeft: return NEEDS_TRANS("Wide Left"); | |||
case wideRight: return NEEDS_TRANS("Wide Right"); | |||
case subbass2: return NEEDS_TRANS("Subbass 2"); | |||
case ambisonicW: return NEEDS_TRANS("Ambisonic W"); | |||
case ambisonicX: return NEEDS_TRANS("Ambisonic X"); | |||
case ambisonicY: return NEEDS_TRANS("Ambisonic Y"); | |||
case ambisonicZ: return NEEDS_TRANS("Ambisonic Z"); | |||
default: break; | |||
} | |||
return "Unknown"; | |||
} | |||
String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelType type) | |||
{ | |||
if (type >= discreteChannel0) | |||
return String (type - discreteChannel0 + 1); | |||
switch (type) | |||
{ | |||
case left: return "L"; | |||
case right: return "R"; | |||
case centre: return "C"; | |||
case subbass: return "Lfe"; | |||
case surroundLeft: return "Ls"; | |||
case surroundRight: return "Rs"; | |||
case centreLeft: return "Lc"; | |||
case centreRight: return "Rc"; | |||
case surround: return "S"; | |||
case sideLeft: return "Sl"; | |||
case sideRight: return "Sr"; | |||
case topMiddle: return "Tm"; | |||
case topFrontLeft: return "Tfl"; | |||
case topFrontCentre: return "Tfc"; | |||
case topFrontRight: return "Tfr"; | |||
case topRearLeft: return "Trl"; | |||
case topRearCentre: return "Trc"; | |||
case topRearRight: return "Trr"; | |||
case wideLeft: return "Wl"; | |||
case wideRight: return "Wr"; | |||
case subbass2: return "Lfe2"; | |||
case ambisonicW: return "W"; | |||
case ambisonicX: return "X"; | |||
case ambisonicY: return "Y"; | |||
case ambisonicZ: return "Z"; | |||
default: break; | |||
} | |||
return ""; | |||
} | |||
String AudioChannelSet::getSpeakerArrangementAsString() const | |||
{ | |||
StringArray speakerTypes; | |||
Array<AudioChannelSet::ChannelType> speakers = getChannelTypes(); | |||
for (int i = 0; i < speakers.size(); ++i) | |||
{ | |||
String name = getAbbreviatedChannelTypeName (speakers.getReference (i)); | |||
if (name.isNotEmpty()) | |||
speakerTypes.add (name); | |||
} | |||
return speakerTypes.joinIntoString (" "); | |||
} | |||
int AudioChannelSet::size() const noexcept | |||
{ | |||
return channels.countNumberOfSetBits(); | |||
} | |||
AudioChannelSet::ChannelType AudioChannelSet::getTypeOfChannel (int index) const noexcept | |||
{ | |||
int bit = channels.findNextSetBit(0); | |||
for (int i = 0; i < index && bit >= 0; ++i) | |||
bit = channels.findNextSetBit (bit + 1); | |||
return static_cast<ChannelType> (bit); | |||
} | |||
int AudioChannelSet::getChannelIndexForType (AudioChannelSet::ChannelType type) const noexcept | |||
{ | |||
int idx = 0; | |||
for (int bit = channels.findNextSetBit (0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) | |||
{ | |||
if (static_cast<ChannelType> (bit) == type) | |||
return idx; | |||
idx++; | |||
} | |||
return -1; | |||
} | |||
Array<AudioChannelSet::ChannelType> AudioChannelSet::getChannelTypes() const | |||
{ | |||
Array<ChannelType> result; | |||
for (int bit = channels.findNextSetBit(0); bit >= 0; bit = channels.findNextSetBit (bit + 1)) | |||
result.add (static_cast<ChannelType> (bit)); | |||
return result; | |||
} | |||
void AudioChannelSet::addChannel (ChannelType newChannel) | |||
{ | |||
const int bit = static_cast<int> (newChannel); | |||
jassert (bit >= 0 && bit < 1024); | |||
channels.setBit (bit); | |||
} | |||
AudioChannelSet AudioChannelSet::disabled() { return AudioChannelSet(); } | |||
AudioChannelSet AudioChannelSet::mono() { return AudioChannelSet (1u << centre); } | |||
AudioChannelSet AudioChannelSet::stereo() { return AudioChannelSet ((1u << left) | (1u << right)); } | |||
AudioChannelSet AudioChannelSet::createLCR() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre)); } | |||
AudioChannelSet AudioChannelSet::createLCRS() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::quadraphonic() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::pentagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre)); } | |||
AudioChannelSet AudioChannelSet::hexagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::octagonal() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centre) | (1u << surround) | (1u << wideLeft) | (1u << wideRight)); } | |||
AudioChannelSet AudioChannelSet::ambisonic() { return AudioChannelSet ((1u << ambisonicW) | (1u << ambisonicX) | (1u << ambisonicY) | (1u << ambisonicZ)); } | |||
AudioChannelSet AudioChannelSet::create5point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight)); } | |||
AudioChannelSet AudioChannelSet::create5point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight)); } | |||
AudioChannelSet AudioChannelSet::create6point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create6point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight) | (1u << surround)); } | |||
AudioChannelSet AudioChannelSet::create7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << sideLeft) | (1u << sideRight) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::create7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << sideLeft) | (1u << sideRight) | (1u << surroundLeft) | (1u << surroundRight)); } | |||
AudioChannelSet AudioChannelSet::createFront7point0() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); } | |||
AudioChannelSet AudioChannelSet::createFront7point1() { return AudioChannelSet ((1u << left) | (1u << right) | (1u << centre) | (1u << subbass) | (1u << surroundLeft) | (1u << surroundRight) | (1u << centreLeft) | (1u << centreRight)); } | |||
AudioChannelSet AudioChannelSet::discreteChannels (int numChannels) | |||
{ | |||
AudioChannelSet s; | |||
s.channels.setRange (discreteChannel0, numChannels, true); | |||
return s; | |||
} | |||
AudioChannelSet AudioChannelSet::canonicalChannelSet (int numChannels) | |||
{ | |||
if (numChannels == 1) return AudioChannelSet::mono(); | |||
if (numChannels == 2) return AudioChannelSet::stereo(); | |||
if (numChannels == 4) return AudioChannelSet::quadraphonic(); | |||
return discreteChannels (numChannels); | |||
} |
@@ -1,191 +0,0 @@ | |||
/* | |||
============================================================================== | |||
This file is part of the JUCE library. | |||
Copyright (c) 2015 - ROLI Ltd. | |||
Permission is granted to use this software under the terms of either: | |||
a) the GPL v2 (or any later version) | |||
b) the Affero GPL v3 | |||
Details of these licenses can be found at: www.gnu.org/licenses | |||
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. | |||
------------------------------------------------------------------------------ | |||
To release a closed-source product which uses JUCE, commercial licenses are | |||
available: visit www.juce.com for more information. | |||
============================================================================== | |||
*/ | |||
#ifndef JUCE_AUDIOCHANNELSET_H_INCLUDED | |||
#define JUCE_AUDIOCHANNELSET_H_INCLUDED | |||
//============================================================================== | |||
/** | |||
Represents a set of audio channel types. | |||
For example, you might have a set of left + right channels, which is a stereo | |||
channel set. It is a collection of values from the AudioChannelSet::ChannelType | |||
enum, where each type may only occur once within the set. | |||
@see AudioProcessorBus | |||
*/ | |||
class JUCE_API AudioChannelSet | |||
{ | |||
public: | |||
/** Creates an empty channel set. | |||
You can call addChannel to add channels to the set. | |||
*/ | |||
AudioChannelSet() noexcept {} | |||
/** Creates a zero-channel set which can be used to indicate that a | |||
bus is disabled. */ | |||
static AudioChannelSet disabled(); | |||
/** Creates a one-channel mono set. */ | |||
static AudioChannelSet mono(); | |||
/** Creates a set containing a left and right channel. */ | |||
static AudioChannelSet stereo(); | |||
/** Creates a set containing a left, right and centre channels. */ | |||
static AudioChannelSet createLCR(); | |||
/** Creates a set containing a left, right, centre and surround channels. */ | |||
static AudioChannelSet createLCRS(); | |||
/** Creates a set for quadraphonic surround setup. */ | |||
static AudioChannelSet quadraphonic(); | |||
/** Creates a set for pentagonal surround setup. */ | |||
static AudioChannelSet pentagonal(); | |||
/** Creates a set for hexagonal surround setup. */ | |||
static AudioChannelSet hexagonal(); | |||
/** Creates a set for octagonal surround setup. */ | |||
static AudioChannelSet octagonal(); | |||
/** Creates a set for ambisonic surround setups. */ | |||
static AudioChannelSet ambisonic(); | |||
/** Creates a set for a 5.0 surround setup. */ | |||
static AudioChannelSet create5point0(); | |||
/** Creates a set for a 5.1 surround setup. */ | |||
static AudioChannelSet create5point1(); | |||
/** Creates a set for a 6.0 surround setup. */ | |||
static AudioChannelSet create6point0(); | |||
/** Creates a set for a 6.1 surround setup. */ | |||
static AudioChannelSet create6point1(); | |||
/** Creates a set for a 7.0 surround setup. */ | |||
static AudioChannelSet create7point0(); | |||
/** Creates a set for a 7.1 surround setup. */ | |||
static AudioChannelSet create7point1(); | |||
/** Creates a set for a 7.0 surround setup (with side instead of rear speakers). */ | |||
static AudioChannelSet createFront7point0(); | |||
/** Creates a set for a 7.1 surround setup (with side instead of rear speakers). */ | |||
static AudioChannelSet createFront7point1(); | |||
/** Creates a set of untyped discrete channels. */ | |||
static AudioChannelSet discreteChannels (int numChannels); | |||
/** Create a canonical channel set for a given number of channels. | |||
For example, numChannels = 1 will return mono, numChannels = 2 will return stereo, etc. */ | |||
static AudioChannelSet canonicalChannelSet (int numChannels); | |||
//============================================================================== | |||
/** Represents different audio channel types. */ | |||
enum ChannelType | |||
{ | |||
unknown = 0, | |||
left = 1, | |||
right = 2, | |||
centre = 3, | |||
subbass = 4, | |||
surroundLeft = 5, | |||
surroundRight = 6, | |||
centreLeft = 7, | |||
centreRight = 8, | |||
surround = 9, | |||
sideLeft = 10, | |||
sideRight = 11, | |||
topMiddle = 12, | |||
topFrontLeft = 13, | |||
topFrontCentre = 14, | |||
topFrontRight = 15, | |||
topRearLeft = 16, | |||
topRearCentre = 17, | |||
topRearRight = 18, | |||
wideLeft = 19, | |||
wideRight = 20, | |||
subbass2 = 21, | |||
ambisonicW = 22, | |||
ambisonicX = 23, | |||
ambisonicY = 24, | |||
ambisonicZ = 25, | |||
discreteChannel0 = 64 /**< Non-typed individual channels are indexed upwards from this value. */ | |||
}; | |||
/** Returns the name of a given channel type. For example, this method may return "Surround Left". */ | |||
static String getChannelTypeName (ChannelType); | |||
/** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */ | |||
static String getAbbreviatedChannelTypeName (ChannelType); | |||
//============================================================================== | |||
/** Adds a channel to the set. */ | |||
void addChannel (ChannelType newChannelType); | |||
/** Returns the number of channels in the set. */ | |||
int size() const noexcept; | |||
/** Returns the number of channels in the set. */ | |||
bool isDisabled() const noexcept { return size() == 0; } | |||
/** Returns an array of all the types in this channel set. */ | |||
Array<ChannelType> getChannelTypes() const; | |||
/** Returns the type of one of the channels in the set, by index. */ | |||
ChannelType getTypeOfChannel (int channelIndex) const noexcept; | |||
/** Returns the index for a particular channel-type. | |||
Will return -1 if the this set does not contain a channel of this type. */ | |||
int getChannelIndexForType (ChannelType type) const noexcept; | |||
/** Returns a string containing a whitespace-separated list of speaker types | |||
corresponding to each channel. For example in a 5.1 arrangement, | |||
the string may be "L R C Lfe Ls Rs". If the speaker arrangement is unknown, | |||
the returned string will be empty.*/ | |||
String getSpeakerArrangementAsString() const; | |||
//============================================================================== | |||
bool operator== (const AudioChannelSet&) const noexcept; | |||
bool operator!= (const AudioChannelSet&) const noexcept; | |||
bool operator< (const AudioChannelSet&) const noexcept; | |||
private: | |||
BigInteger channels; | |||
explicit AudioChannelSet (uint32); | |||
}; | |||
#endif // JUCE_AUDIOCHANNELSET_H_INCLUDED |
@@ -1,126 +0,0 @@ | |||
static ThreadLocalValue<AudioProcessor::WrapperType> wrapperTypeBeingCreated; | |||
void JUCE_CALLTYPE AudioProcessor::setTypeOfNextNewPlugin (AudioProcessor::WrapperType type) | |||
{ | |||
wrapperTypeBeingCreated = type; | |||
} | |||
AudioProcessor::AudioProcessor() | |||
: wrapperType (wrapperTypeBeingCreated.get()), | |||
playHead (nullptr), | |||
currentSampleRate (0), | |||
blockSize (0), | |||
latencySamples (0), | |||
#if JUCE_DEBUG | |||
textRecursionCheck (false), | |||
#endif | |||
suspended (false), | |||
nonRealtime (false), | |||
processingPrecision (singlePrecision) | |||
{ | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; | |||
#else | |||
const short channelConfigs[][2] = { {2, 2} }; | |||
#endif | |||
#if ! JucePlugin_IsMidiEffect | |||
#if ! JucePlugin_IsSynth | |||
busArrangement.inputBuses.add (AudioProcessorBus ("Input", AudioChannelSet::canonicalChannelSet (channelConfigs[0][0]))); | |||
#endif | |||
busArrangement.outputBuses.add (AudioProcessorBus ("Output", AudioChannelSet::canonicalChannelSet (channelConfigs[0][1]))); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
#if ! JucePlugin_IsSynth | |||
AudioProcessor::setPreferredBusArrangement (true, 0, AudioChannelSet::stereo()); | |||
#endif | |||
AudioProcessor::setPreferredBusArrangement (false, 0, AudioChannelSet::stereo()); | |||
#endif | |||
#endif | |||
updateSpeakerFormatStrings(); | |||
} | |||
bool AudioProcessor::setPreferredBusArrangement (bool isInput, int busIndex, const AudioChannelSet& preferredSet) | |||
{ | |||
const int oldNumInputs = getTotalNumInputChannels(); | |||
const int oldNumOutputs = getTotalNumOutputChannels(); | |||
Array<AudioProcessorBus>& buses = isInput ? busArrangement.inputBuses : busArrangement.outputBuses; | |||
const int numBuses = buses.size(); | |||
if (! isPositiveAndBelow (busIndex, numBuses)) | |||
return false; | |||
AudioProcessorBus& bus = buses.getReference (busIndex); | |||
#ifdef JucePlugin_PreferredChannelConfigurations | |||
// the user is using the deprecated way to specify channel configurations | |||
if (numBuses > 0 && busIndex == 0) | |||
{ | |||
const short channelConfigs[][2] = { JucePlugin_PreferredChannelConfigurations }; | |||
const int numChannelConfigs = sizeof (channelConfigs) / sizeof (*channelConfigs); | |||
// we need the main bus in the opposite direction | |||
Array<AudioProcessorBus>& oppositeBuses = isInput ? busArrangement.outputBuses : busArrangement.inputBuses; | |||
AudioProcessorBus* oppositeBus = (busIndex < oppositeBuses.size()) ? &oppositeBuses.getReference (0) : nullptr; | |||
// get the target number of channels | |||
const int mainBusNumChannels = preferredSet.size(); | |||
const int mainBusOppositeChannels = (oppositeBus != nullptr) ? oppositeBus->channels.size() : 0; | |||
const int dir = isInput ? 0 : 1; | |||
// find a compatible channel configuration on the opposite bus which is the closest match | |||
// to the current number of channels on that bus | |||
int distance = std::numeric_limits<int>::max(); | |||
int bestConfiguration = -1; | |||
for (int i = 0; i < numChannelConfigs; ++i) | |||
{ | |||
// is the configuration compatible with the preferred set | |||
if (channelConfigs[i][dir] == mainBusNumChannels) | |||
{ | |||
const int configChannels = channelConfigs[i][dir^1]; | |||
const int channelDifference = std::abs (configChannels - mainBusOppositeChannels); | |||
if (channelDifference < distance) | |||
{ | |||
distance = channelDifference; | |||
bestConfiguration = configChannels; | |||
// we can exit if we found a perfect match | |||
if (distance == 0) | |||
break; | |||
} | |||
} | |||
} | |||
// unable to find a good configuration | |||
if (bestConfiguration == -1) | |||
return false; | |||
// did the number of channels change on the opposite bus? | |||
if (mainBusOppositeChannels != bestConfiguration && oppositeBus != nullptr) | |||
{ | |||
// if the channels on the opposite bus are the same as the preferred set | |||
// then also copy over the layout information. If not, then assume | |||
// a cononical channel layout | |||
if (bestConfiguration == mainBusNumChannels) | |||
oppositeBus->channels = preferredSet; | |||
else | |||
oppositeBus->channels = AudioChannelSet::canonicalChannelSet (bestConfiguration); | |||
} | |||
} | |||
#endif | |||
bus.channels = preferredSet; | |||
if (oldNumInputs != getTotalNumInputChannels() || oldNumOutputs != getTotalNumOutputChannels()) | |||
{ | |||
updateSpeakerFormatStrings(); | |||
numChannelsChanged(); | |||
} | |||
return true; | |||
} |
@@ -1,24 +0,0 @@ | |||
{ | |||
"id": "juce_audio_utils", | |||
"name": "JUCE extra audio utility classes", | |||
"version": "4.1.0", | |||
"description": "Classes for audio-related GUI and miscellaneous tasks.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_gui_basics", "version": "matching" }, | |||
{ "id": "juce_audio_devices", "version": "matching" }, | |||
{ "id": "juce_audio_processors", "version": "matching" }, | |||
{ "id": "juce_audio_formats", "version": "matching" } ], | |||
"include": "juce_audio_utils.h", | |||
"compile": [ { "file": "juce_audio_utils.cpp", "target": "! xcode" }, | |||
{ "file": "juce_audio_utils.mm", "target": "xcode" } ], | |||
"browse": [ "gui/*", | |||
"players/*", | |||
"native/*" ], | |||
"iOSFrameworks": "CoreAudioKit" | |||
} |
@@ -1,38 +0,0 @@ | |||
{ | |||
"id": "juce_core", | |||
"name": "JUCE core classes", | |||
"version": "4.1.0", | |||
"description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "ISC Permissive", | |||
"dependencies": [], | |||
"include": "juce_core.h", | |||
"compile": [ { "file": "juce_core.cpp", "target": "! xcode" }, | |||
{ "file": "juce_core.mm", "target": "xcode" } ], | |||
"browse": [ "text/*", | |||
"maths/*", | |||
"memory/*", | |||
"containers/*", | |||
"threads/*", | |||
"time/*", | |||
"files/*", | |||
"network/*", | |||
"streams/*", | |||
"logging/*", | |||
"system/*", | |||
"xml/*", | |||
"javascript/*", | |||
"zip/*", | |||
"unit_tests/*", | |||
"misc/*", | |||
"native/*" ], | |||
"OSXFrameworks": "Cocoa IOKit", | |||
"iOSFrameworks": "Foundation", | |||
"LinuxLibs": "rt dl pthread", | |||
"mingwLibs": "uuid wsock32 wininet version ole32 ws2_32 oleaut32 imm32 comdlg32 shlwapi rpcrt4 winmm" | |||
} |
@@ -1,18 +0,0 @@ | |||
{ | |||
"id": "juce_cryptography", | |||
"name": "JUCE cryptography classes", | |||
"version": "4.1.0", | |||
"description": "Classes for various basic cryptography functions, including RSA, Blowfish, MD5, SHA, etc.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_core", "version": "matching" } ], | |||
"include": "juce_cryptography.h", | |||
"compile": [ { "file": "juce_cryptography.cpp", "target": "! xcode" }, | |||
{ "file": "juce_cryptography.mm", "target": "xcode" } ], | |||
"browse": [ "encryption/*", | |||
"hashing/*" ] | |||
} |
@@ -1,20 +0,0 @@ | |||
{ | |||
"id": "juce_data_structures", | |||
"name": "JUCE data model helper classes", | |||
"version": "4.1.0", | |||
"description": "Classes for undo/redo management, and smart data structures.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_core", "version": "matching" }, | |||
{ "id": "juce_events", "version": "matching" } ], | |||
"include": "juce_data_structures.h", | |||
"compile": [ { "file": "juce_data_structures.cpp", "target": "! xcode" }, | |||
{ "file": "juce_data_structures.mm", "target": "xcode" } ], | |||
"browse": [ "values/*", | |||
"undomanager/*", | |||
"app_properties/*" ] | |||
} |
@@ -1,23 +0,0 @@ | |||
{ | |||
"id": "juce_events", | |||
"name": "JUCE message and event handling classes", | |||
"version": "4.1.0", | |||
"description": "Classes for running an application's main event loop and sending/receiving messages, timers, etc.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_core", "version": "matching" } ], | |||
"include": "juce_events.h", | |||
"compile": [ { "file": "juce_events.cpp", "target": "! xcode" }, | |||
{ "file": "juce_events.mm", "target": "xcode" } ], | |||
"browse": [ "messages/*", | |||
"timers/*", | |||
"broadcasters/*", | |||
"interprocess/*", | |||
"native/*" ], | |||
"LinuxLibs": "X11" | |||
} |
@@ -1,30 +0,0 @@ | |||
{ | |||
"id": "juce_graphics", | |||
"name": "JUCE graphics classes", | |||
"version": "4.1.0", | |||
"description": "Classes for 2D vector graphics, image loading/saving, font handling, etc.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_core", "version": "matching" }, | |||
{ "id": "juce_events", "version": "matching" } ], | |||
"include": "juce_graphics.h", | |||
"compile": [ { "file": "juce_graphics.cpp", "target": "! xcode" }, | |||
{ "file": "juce_graphics.mm", "target": "xcode" } ], | |||
"browse": [ "colour/*", | |||
"contexts/*", | |||
"images/*", | |||
"image_formats/*", | |||
"geometry/*", | |||
"placement/*", | |||
"fonts/*", | |||
"effects/*", | |||
"native/*" ], | |||
"OSXFrameworks": "Cocoa QuartzCore", | |||
"iOSFrameworks": "CoreGraphics CoreText QuartzCore", | |||
"LinuxLibs": "X11 Xinerama Xext freetype" | |||
} |
@@ -1,40 +0,0 @@ | |||
{ | |||
"id": "juce_gui_basics", | |||
"name": "JUCE GUI core classes", | |||
"version": "4.1.0", | |||
"description": "Basic user-interface components and related classes.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_core", "version": "matching" }, | |||
{ "id": "juce_events", "version": "matching" }, | |||
{ "id": "juce_graphics", "version": "matching" }, | |||
{ "id": "juce_data_structures", "version": "matching" } ], | |||
"include": "juce_gui_basics.h", | |||
"compile": [ { "file": "juce_gui_basics.cpp", "target": "! xcode" }, | |||
{ "file": "juce_gui_basics.mm", "target": "xcode" } ], | |||
"browse": [ "components/*", | |||
"mouse/*", | |||
"keyboard/*", | |||
"widgets/*", | |||
"windows/*", | |||
"menus/*", | |||
"layout/*", | |||
"buttons/*", | |||
"positioning/*", | |||
"drawables/*", | |||
"properties/*", | |||
"lookandfeel/*", | |||
"filebrowser/*", | |||
"commands/*", | |||
"misc/*", | |||
"application/*", | |||
"native/*" ], | |||
"OSXFrameworks": "Cocoa Carbon QuartzCore", | |||
"iOSFrameworks": "UIKit", | |||
"LinuxLibs": "X11 Xinerama Xext" | |||
} |
@@ -1,24 +0,0 @@ | |||
{ | |||
"id": "juce_gui_extra", | |||
"name": "JUCE extended GUI classes", | |||
"version": "4.1.0", | |||
"description": "Miscellaneous GUI classes for specialised tasks.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_gui_basics", "version": "matching" } ], | |||
"include": "juce_gui_extra.h", | |||
"compile": [ { "file": "juce_gui_extra.cpp", "target": "! xcode" }, | |||
{ "file": "juce_gui_extra.mm", "target": "xcode" } ], | |||
"browse": [ "code_editor/*", | |||
"documents/*", | |||
"embedding/*", | |||
"lookandfeel/*", | |||
"misc/*", | |||
"native/*" ], | |||
"OSXFrameworks": "WebKit" | |||
} |
@@ -1,18 +0,0 @@ | |||
{ | |||
"id": "juce_tracktion_marketplace", | |||
"name": "JUCE Tracktion marketplace support", | |||
"version": "4.1.0", | |||
"description": "JUCE classes for online product authentication via the Tracktion marketplace.", | |||
"website": "http://www.juce.com/juce", | |||
"license": "GPL/Commercial", | |||
"dependencies": [ { "id": "juce_data_structures", "version": "matching" }, | |||
{ "id": "juce_cryptography", "version": "matching" } ], | |||
"include": "juce_tracktion_marketplace.h", | |||
"compile": [ { "file": "juce_tracktion_marketplace.cpp" } ], | |||
"browse": [ "marketplace/*" ] | |||
} |