1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

New methods in AudioDeviceManager to easily play sounds from files or audio buffers.

This commit is contained in:
jules 2015-11-02 15:12:03 +00:00
parent b1a8470514
commit e440a83ad7
2 changed files with 337 additions and 38 deletions

View file

@ -93,7 +93,6 @@ AudioDeviceManager::AudioDeviceManager()
numOutputChansNeeded (2),
listNeedsScanning (true),
inputLevel (0),
testSoundPosition (0),
cpuUsageMs (0),
timeToCpuScale (0)
{
@ -589,8 +588,6 @@ void AudioDeviceManager::stopDevice()
{
if (currentAudioDevice != nullptr)
currentAudioDevice->stop();
testSound = nullptr;
}
void AudioDeviceManager::closeAudioDevice()
@ -762,20 +759,6 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat
for (int i = 0; i < numOutputChannels; ++i)
zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples);
}
if (testSound != nullptr)
{
const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition);
const float* const src = testSound->getReadPointer (0, testSoundPosition);
for (int i = 0; i < numOutputChannels; ++i)
for (int j = 0; j < numSamps; ++j)
outputChannelData [i][j] += src[j];
testSoundPosition += numSamps;
if (testSoundPosition >= testSound->getNumSamples())
testSound = nullptr;
}
}
void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device)
@ -944,42 +927,315 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
}
//==============================================================================
void AudioDeviceManager::playTestSound()
// This is an AudioTransportSource which will own it's assigned source
class AudioSourceOwningTransportSource : public AudioTransportSource
{
{ // cunningly nested to swap, unlock and delete in that order.
ScopedPointer<AudioSampleBuffer> oldSound;
public:
AudioSourceOwningTransportSource() {}
~AudioSourceOwningTransportSource() { setSource (nullptr); }
void setSource (PositionableAudioSource* newSource)
{
if (src != newSource)
{
const ScopedLock sl (audioCallbackLock);
oldSound = testSound;
ScopedPointer<PositionableAudioSource> oldSourceDeleter (src);
src = newSource;
// tell the base class about the new source before deleting the old one
AudioTransportSource::setSource (newSource);
}
}
testSoundPosition = 0;
private:
ScopedPointer<PositionableAudioSource> src;
if (currentAudioDevice != nullptr)
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource)
};
//==============================================================================
// An Audio player which will remove itself from the AudioDeviceManager's
// callback list once it finishes playing its source
class AutoRemovingSourcePlayer : public AudioSourcePlayer,
private ChangeListener
{
public:
struct DeleteOnMessageThread : public CallbackMessage
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
DeleteOnMessageThread (AutoRemovingSourcePlayer* p) : parent (p) {}
void messageCallback() override { delete parent; }
const double frequency = 440.0;
const float amplitude = 0.5f;
AutoRemovingSourcePlayer* parent;
};
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
//==============================================================================
AutoRemovingSourcePlayer (AudioDeviceManager& deviceManager, bool ownSource)
: manager (deviceManager),
deleteWhenDone (ownSource),
hasAddedCallback (false),
recursiveEntry (false)
{
}
AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength);
void changeListenerCallback (ChangeBroadcaster* source) override
{
if (AudioTransportSource* currentTransport
= dynamic_cast<AudioTransportSource*> (getCurrentSource()))
{
ignoreUnused (source);
jassert (source == currentTransport);
for (int i = 0; i < soundLength; ++i)
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
if (! currentTransport->isPlaying())
{
// this will call audioDeviceStopped!
manager.removeAudioCallback (this);
}
else if (! hasAddedCallback)
{
hasAddedCallback = true;
manager.addAudioCallback (this);
}
}
}
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
void audioDeviceStopped() override
{
if (! recursiveEntry)
{
ScopedValueSetter<bool> s (recursiveEntry, true, false);
const ScopedLock sl (audioCallbackLock);
testSound = newSound;
manager.removeAudioCallback (this);
AudioSourcePlayer::audioDeviceStopped();
if (MessageManager* mm = MessageManager::getInstanceWithoutCreating())
{
if (mm->isThisTheMessageThread())
delete this;
else
(new DeleteOnMessageThread (this))->post();
}
}
}
void setSource (AudioTransportSource* newSource)
{
AudioSource* oldSource = getCurrentSource();
if (AudioTransportSource* oldTransport = dynamic_cast<AudioTransportSource*> (oldSource))
oldTransport->removeChangeListener (this);
if (newSource != nullptr)
newSource->addChangeListener (this);
AudioSourcePlayer::setSource (newSource);
if (deleteWhenDone)
delete oldSource;
}
private:
// only allow myself to be deleted when my audio callback has been removed
~AutoRemovingSourcePlayer()
{
setSource (nullptr);
}
AudioDeviceManager& manager;
bool deleteWhenDone, hasAddedCallback, recursiveEntry;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer)
};
//==============================================================================
// An AudioSource which simply outputs a buffer
class AudioSampleBufferSource : public PositionableAudioSource
{
public:
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool shouldLoop, bool ownBuffer)
: position (0),
buffer (audioBuffer),
looping (shouldLoop),
deleteWhenDone (ownBuffer)
{}
~AudioSampleBufferSource()
{
if (deleteWhenDone)
delete buffer;
}
//==============================================================================
void setNextReadPosition (int64 newPosition) override
{
jassert (newPosition >= 0);
if (looping)
newPosition = newPosition % static_cast<int64> (buffer->getNumSamples());
position = jmin (buffer->getNumSamples(), static_cast<int> (newPosition));
}
int64 getNextReadPosition() const override
{
return static_cast<int64> (position);
}
int64 getTotalLength() const override
{
return static_cast<int64> (buffer->getNumSamples());
}
bool isLooping() const override
{
return looping;
}
void setLooping (bool shouldLoop) override
{
looping = shouldLoop;
}
//==============================================================================
void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override
{
ignoreUnused (samplesPerBlockExpected, sampleRate);
}
void releaseResources() override
{}
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
int max = jmin (buffer->getNumSamples() - position, bufferToFill.numSamples);
jassert (max >= 0);
{
int ch;
int maxInChannels = buffer->getNumChannels();
int maxOutChannels = jmin (bufferToFill.buffer->getNumChannels(),
jmax (maxInChannels, 2));
for (ch = 0; ch < maxOutChannels; ch++)
{
int inChannel = ch % maxInChannels;
if (max > 0)
bufferToFill.buffer->copyFrom (ch, bufferToFill.startSample, *buffer, inChannel, position, max);
}
for (; ch < bufferToFill.buffer->getNumChannels(); ++ch)
bufferToFill.buffer->clear (ch, bufferToFill.startSample, bufferToFill.numSamples);
}
position += max;
if (looping)
position = position % buffer->getNumSamples();
}
private:
//==============================================================================
int position;
AudioSampleBuffer* buffer;
bool looping, deleteWhenDone;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource)
};
void AudioDeviceManager::playSound (const File& file)
{
if (file.existsAsFile())
{
AudioFormatManager formatManager;
formatManager.registerBasicFormats();
playSound (formatManager.createReaderFor (file), true);
}
}
void AudioDeviceManager::playSound (const char* resourceData, int resourceSize)
{
if (resourceData != nullptr && resourceSize > 0)
{
AudioFormatManager formatManager;
formatManager.registerBasicFormats();
MemoryInputStream* mem = new MemoryInputStream (resourceData,
static_cast<size_t> (resourceSize),
false);
playSound (formatManager.createReaderFor (mem), true);
}
}
void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished)
{
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
}
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished)
{
if (audioSource != nullptr && currentAudioDevice != nullptr)
{
if (AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource))
{
AutoRemovingSourcePlayer* player = new AutoRemovingSourcePlayer (*this, deleteWhenFinished);
player->setSource (transport);
}
else
{
AudioTransportSource* transportSource;
if (deleteWhenFinished)
{
AudioSourceOwningTransportSource* owningTransportSource = new AudioSourceOwningTransportSource();
owningTransportSource->setSource (audioSource);
transportSource = owningTransportSource;
}
else
{
transportSource = new AudioTransportSource;
transportSource->setSource (audioSource);
}
// recursively call myself
playSound (transportSource, true);
transportSource->start();
}
}
else
{
if (deleteWhenFinished)
delete audioSource;
}
}
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished)
{
playSound (new AudioSampleBufferSource (buffer, false, deleteWhenFinished), true);
}
void AudioDeviceManager::playTestSound()
{
const double sampleRate = currentAudioDevice->getCurrentSampleRate();
const int soundLength = (int) sampleRate;
const double frequency = 440.0;
const float amplitude = 0.5f;
const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency);
AudioSampleBuffer* newSound = new AudioSampleBuffer (1, soundLength);
for (int i = 0; i < soundLength; ++i)
newSound->setSample (0, i, amplitude * (float) std::sin (i * phasePerSample));
newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f);
newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f);
playSound (newSound, true);
}
//==============================================================================
void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement)
{
if (enableMeasurement)

View file

@ -404,6 +404,51 @@ public:
*/
void playTestSound();
/** Plays a sound from a file. */
void playSound (const File& file);
/** Convenient method to play sound from a JUCE resource. */
void playSound (const void* resourceData, size_t resourceSize);
/** Plays the sound from an audio format reader.
If deleteWhenFinished is true then the format reader will be
automatically deleted once the sound has finished playing.
*/
void playSound (AudioFormatReader* buffer, bool deleteWhenFinished = false);
/** Plays the sound from a positionable audio source.
This will output the sound coming from a positionable audio source.
This gives you slightly more control over the sound playback compared
to the other playSound methods. For example, if you would like to
stop the sound prematurely you can call this method with a
TransportAudioSource and then call audioSource->stop. Note that,
you must call audioSource->start to start the playback, if your
audioSource is a TransportAudioSource.
The audio device manager will not hold any references to this audio
source once the audio source has stopped playing for any reason,
for example when the sound has finished playing or when you have
called audioSource->stop. Therefore, calling audioSource->start() on
a finished audioSource will not restart the sound again. If this is
desired simply call playSound with the same audioSource again.
@param deleteWhenFinished If this is true then the audio source will
be deleted once the device manager has
finished playing.
*/
void playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished = false);
/** Plays the sound from an audio sample buffer.
This will output the sound contained in an audio sample buffer. If
deleteWhenFinished is true then the audio sample buffer will be
automatically deleted once the sound has finished playing.
*/
void playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished = false);
//==============================================================================
/** Turns on level-measuring.
When enabled, the device manager will measure the peak input level
@ -452,8 +497,6 @@ private:
mutable bool listNeedsScanning;
Atomic<int> inputLevelMeasurementEnabledCount;
double inputLevel;
ScopedPointer<AudioSampleBuffer> testSound;
int testSoundPosition;
AudioSampleBuffer tempBuffer;
struct MidiCallbackInfo