mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Moved simple sound player to audio_utils module
This commit is contained in:
parent
1fcae3675c
commit
a347689d96
7 changed files with 462 additions and 268 deletions
269
modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
Executable file → Normal file
269
modules/juce_audio_devices/audio_io/juce_AudioDeviceManager.cpp
Executable file → Normal file
|
|
@ -86,71 +86,12 @@ private:
|
|||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// This is an AudioTransportSource which will own it's assigned source
|
||||
struct AudioSourceOwningTransportSource : public AudioTransportSource
|
||||
{
|
||||
AudioSourceOwningTransportSource (PositionableAudioSource* s) : source (s)
|
||||
{
|
||||
AudioTransportSource::setSource (s);
|
||||
}
|
||||
|
||||
~AudioSourceOwningTransportSource()
|
||||
{
|
||||
setSource (nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPointer<PositionableAudioSource> source;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// An AudioSourcePlayer which will remove itself from the AudioDeviceManager's
|
||||
// callback list once it finishes playing its source
|
||||
struct AutoRemovingSourcePlayer : public AudioSourcePlayer,
|
||||
private Timer
|
||||
{
|
||||
AutoRemovingSourcePlayer (AudioDeviceManager& dm, AudioTransportSource* ts, bool ownSource)
|
||||
: manager (dm), transportSource (ts, ownSource)
|
||||
{
|
||||
jassert (ts != nullptr);
|
||||
manager.addAudioCallback (this);
|
||||
AudioSourcePlayer::setSource (transportSource);
|
||||
startTimerHz (10);
|
||||
}
|
||||
|
||||
~AutoRemovingSourcePlayer()
|
||||
{
|
||||
setSource (nullptr);
|
||||
manager.removeAudioCallback (this);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (getCurrentSource() == nullptr || ! transportSource->isPlaying())
|
||||
delete this;
|
||||
}
|
||||
|
||||
void audioDeviceStopped() override
|
||||
{
|
||||
AudioSourcePlayer::audioDeviceStopped();
|
||||
setSource (nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
AudioDeviceManager& manager;
|
||||
OptionalScopedPointer<AudioTransportSource> transportSource;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingSourcePlayer)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AudioDeviceManager::AudioDeviceManager()
|
||||
: numInputChansNeeded (0),
|
||||
numOutputChansNeeded (2),
|
||||
listNeedsScanning (true),
|
||||
testSoundPosition (0),
|
||||
cpuUsageMs (0),
|
||||
timeToCpuScale (0)
|
||||
{
|
||||
|
|
@ -161,10 +102,6 @@ AudioDeviceManager::~AudioDeviceManager()
|
|||
{
|
||||
currentAudioDevice = nullptr;
|
||||
defaultMidiOutput = nullptr;
|
||||
|
||||
for (int i = 0; i < callbacks.size(); ++i)
|
||||
if (AutoRemovingSourcePlayer* p = dynamic_cast<AutoRemovingSourcePlayer*> (callbacks.getUnchecked(i)))
|
||||
delete p;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -649,6 +586,8 @@ void AudioDeviceManager::stopDevice()
|
|||
{
|
||||
if (currentAudioDevice != nullptr)
|
||||
currentAudioDevice->stop();
|
||||
|
||||
testSound = nullptr;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::closeAudioDevice()
|
||||
|
|
@ -797,6 +736,20 @@ 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)
|
||||
|
|
@ -964,158 +917,6 @@ void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName)
|
|||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
// An AudioSource which simply outputs a buffer
|
||||
class AudioSampleBufferSource : public PositionableAudioSource
|
||||
{
|
||||
public:
|
||||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels)
|
||||
: buffer (audioBuffer, ownBuffer),
|
||||
position (0), looping (false), playAcrossAllChannels (playOnAllChannels)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
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, double) override {}
|
||||
void releaseResources() override {}
|
||||
|
||||
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
|
||||
{
|
||||
bufferToFill.clearActiveBufferRegion();
|
||||
|
||||
const int bufferSize = buffer->getNumSamples();
|
||||
const int samplesNeeded = bufferToFill.numSamples;
|
||||
const int samplesToCopy = jmin (bufferSize - position, samplesNeeded);
|
||||
|
||||
if (samplesToCopy > 0)
|
||||
{
|
||||
int maxInChannels = buffer->getNumChannels();
|
||||
int maxOutChannels = bufferToFill.buffer->getNumChannels();
|
||||
|
||||
if (! playAcrossAllChannels)
|
||||
maxOutChannels = jmin (maxOutChannels, maxInChannels);
|
||||
|
||||
for (int i = 0; i < maxOutChannels; ++i)
|
||||
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer,
|
||||
i % maxInChannels, position, samplesToCopy);
|
||||
}
|
||||
|
||||
position += samplesNeeded;
|
||||
|
||||
if (looping)
|
||||
position %= bufferSize;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OptionalScopedPointer<AudioSampleBuffer> buffer;
|
||||
int position;
|
||||
bool looping, playAcrossAllChannels;
|
||||
|
||||
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 void* resourceData, size_t resourceSize)
|
||||
{
|
||||
if (resourceData != nullptr && resourceSize > 0)
|
||||
{
|
||||
AudioFormatManager formatManager;
|
||||
formatManager.registerBasicFormats();
|
||||
MemoryInputStream* mem = new MemoryInputStream (resourceData, resourceSize, false);
|
||||
playSound (formatManager.createReaderFor (mem), true);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDeviceManager::playSound (AudioFormatReader* reader, bool deleteWhenFinished)
|
||||
{
|
||||
if (reader != nullptr)
|
||||
playSound (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::playSound (AudioSampleBuffer* buffer, bool deleteWhenFinished, bool playOnAllOutputChannels)
|
||||
{
|
||||
if (buffer != nullptr)
|
||||
playSound (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true);
|
||||
}
|
||||
|
||||
void AudioDeviceManager::playSound (PositionableAudioSource* audioSource, bool deleteWhenFinished)
|
||||
{
|
||||
if (audioSource != nullptr && currentAudioDevice != nullptr)
|
||||
{
|
||||
AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource);
|
||||
|
||||
if (transport == nullptr)
|
||||
{
|
||||
if (deleteWhenFinished)
|
||||
{
|
||||
transport = new AudioSourceOwningTransportSource (audioSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
transport = new AudioTransportSource();
|
||||
transport->setSource (audioSource);
|
||||
deleteWhenFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
transport->start();
|
||||
new AutoRemovingSourcePlayer (*this, transport, deleteWhenFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (deleteWhenFinished)
|
||||
delete audioSource;
|
||||
}
|
||||
}
|
||||
|
||||
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, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
AudioDeviceManager::LevelMeter::LevelMeter() noexcept : level() {}
|
||||
|
||||
|
|
@ -1160,6 +961,42 @@ double AudioDeviceManager::LevelMeter::getCurrentLevel() const noexcept
|
|||
return level;
|
||||
}
|
||||
|
||||
void AudioDeviceManager::playTestSound()
|
||||
{
|
||||
{ // cunningly nested to swap, unlock and delete in that order.
|
||||
ScopedPointer<AudioSampleBuffer> oldSound;
|
||||
|
||||
{
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
oldSound = testSound;
|
||||
}
|
||||
}
|
||||
|
||||
testSoundPosition = 0;
|
||||
|
||||
if (currentAudioDevice != nullptr)
|
||||
{
|
||||
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* const 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);
|
||||
|
||||
const ScopedLock sl (audioCallbackLock);
|
||||
testSound = newSound;
|
||||
}
|
||||
}
|
||||
|
||||
double AudioDeviceManager::getCurrentInputLevel() const noexcept { return inputLevelMeter.getCurrentLevel(); }
|
||||
double AudioDeviceManager::getCurrentOutputLevel() const noexcept { return outputLevelMeter.getCurrentLevel(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -404,57 +404,6 @@ 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 audioSource the audio source to play
|
||||
@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.
|
||||
|
||||
If playOnAllOutputChannels is true, then if there are more output channels
|
||||
than buffer channels, then the ones that are available will be re-used on
|
||||
multiple outputs so that something is sent to all output channels. If it
|
||||
is false, then the buffer will just be played on the first output channels.
|
||||
*/
|
||||
void playSound (AudioSampleBuffer* buffer,
|
||||
bool deleteWhenFinished = false,
|
||||
bool playOnAllOutputChannels = false);
|
||||
|
||||
//==============================================================================
|
||||
/** Turns on level-measuring for input channels.
|
||||
@see getCurrentInputLevel()
|
||||
|
|
@ -519,6 +468,9 @@ private:
|
|||
ScopedPointer<MidiOutput> defaultMidiOutput;
|
||||
CriticalSection audioCallbackLock, midiCallbackLock;
|
||||
|
||||
ScopedPointer<AudioSampleBuffer> testSound;
|
||||
int testSoundPosition;
|
||||
|
||||
double cpuUsageMs, timeToCpuScale;
|
||||
|
||||
struct LevelMeter
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@
|
|||
website: http://www.juce.com/juce
|
||||
license: GPL/Commercial
|
||||
|
||||
dependencies: juce_audio_basics, juce_audio_formats, juce_events
|
||||
dependencies: juce_audio_basics, juce_events
|
||||
OSXFrameworks: CoreAudio CoreMIDI DiscRecording
|
||||
iOSFrameworks: CoreAudio CoreMIDI AudioToolbox AVFoundation
|
||||
linuxPackages: alsa
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ namespace juce
|
|||
#include "gui/juce_AudioVisualiserComponent.cpp"
|
||||
#include "gui/juce_MidiKeyboardComponent.cpp"
|
||||
#include "gui/juce_AudioAppComponent.cpp"
|
||||
#include "players/juce_SoundPlayer.cpp"
|
||||
#include "players/juce_AudioProcessorPlayer.cpp"
|
||||
|
||||
#if JUCE_MAC
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ namespace juce
|
|||
#include "gui/juce_MidiKeyboardComponent.h"
|
||||
#include "gui/juce_AudioAppComponent.h"
|
||||
#include "gui/juce_BluetoothMidiDevicePairingDialogue.h"
|
||||
#include "players/juce_SoundPlayer.h"
|
||||
#include "players/juce_AudioProcessorPlayer.h"
|
||||
|
||||
}
|
||||
|
|
|
|||
273
modules/juce_audio_utils/players/juce_SoundPlayer.cpp
Normal file
273
modules/juce_audio_utils/players/juce_SoundPlayer.cpp
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
//==============================================================================
|
||||
// This is an AudioTransportSource which will own it's assigned source
|
||||
struct AudioSourceOwningTransportSource : public AudioTransportSource
|
||||
{
|
||||
AudioSourceOwningTransportSource (PositionableAudioSource* s) : source (s)
|
||||
{
|
||||
AudioTransportSource::setSource (s);
|
||||
}
|
||||
|
||||
~AudioSourceOwningTransportSource()
|
||||
{
|
||||
setSource (nullptr);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedPointer<PositionableAudioSource> source;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourceOwningTransportSource)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// An AudioSourcePlayer which will remove itself from the AudioDeviceManager's
|
||||
// callback list once it finishes playing its source
|
||||
struct AutoRemovingTransportSource : public AudioTransportSource, private Timer
|
||||
{
|
||||
AutoRemovingTransportSource (MixerAudioSource& mixerToUse, AudioTransportSource* ts, bool ownSource,
|
||||
int samplesPerBlock, double sampleRate)
|
||||
: mixer (mixerToUse), transportSource (ts, ownSource)
|
||||
{
|
||||
jassert (ts != nullptr);
|
||||
|
||||
setSource (transportSource);
|
||||
|
||||
prepareToPlay (samplesPerBlock, sampleRate);
|
||||
start();
|
||||
|
||||
mixer.addInputSource (this, true);
|
||||
startTimerHz (10);
|
||||
}
|
||||
|
||||
~AutoRemovingTransportSource()
|
||||
{
|
||||
setSource (nullptr);
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
if (! transportSource->isPlaying())
|
||||
mixer.removeInputSource (this);
|
||||
}
|
||||
|
||||
private:
|
||||
MixerAudioSource& mixer;
|
||||
OptionalScopedPointer<AudioTransportSource> transportSource;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AutoRemovingTransportSource)
|
||||
};
|
||||
|
||||
// An AudioSource which simply outputs a buffer
|
||||
class AudioSampleBufferSource : public PositionableAudioSource
|
||||
{
|
||||
public:
|
||||
AudioSampleBufferSource (AudioSampleBuffer* audioBuffer, bool ownBuffer, bool playOnAllChannels)
|
||||
: buffer (audioBuffer, ownBuffer),
|
||||
position (0), looping (false),
|
||||
playAcrossAllChannels (playOnAllChannels)
|
||||
{}
|
||||
|
||||
//==============================================================================
|
||||
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, double) override {}
|
||||
void releaseResources() override {}
|
||||
|
||||
void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
|
||||
{
|
||||
bufferToFill.clearActiveBufferRegion();
|
||||
|
||||
const int bufferSize = buffer->getNumSamples();
|
||||
const int samplesNeeded = bufferToFill.numSamples;
|
||||
const int samplesToCopy = jmin (bufferSize - position, samplesNeeded);
|
||||
|
||||
if (samplesToCopy > 0)
|
||||
{
|
||||
int maxInChannels = buffer->getNumChannels();
|
||||
int maxOutChannels = bufferToFill.buffer->getNumChannels();
|
||||
|
||||
if (! playAcrossAllChannels)
|
||||
maxOutChannels = jmin (maxOutChannels, maxInChannels);
|
||||
|
||||
for (int i = 0; i < maxOutChannels; ++i)
|
||||
bufferToFill.buffer->copyFrom (i, bufferToFill.startSample, *buffer,
|
||||
i % maxInChannels, position, samplesToCopy);
|
||||
}
|
||||
|
||||
position += samplesNeeded;
|
||||
|
||||
if (looping)
|
||||
position %= bufferSize;
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
OptionalScopedPointer<AudioSampleBuffer> buffer;
|
||||
int position;
|
||||
bool looping, playAcrossAllChannels;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSampleBufferSource)
|
||||
};
|
||||
|
||||
SoundPlayer::SoundPlayer()
|
||||
: sampleRate (44100.0), bufferSize (512)
|
||||
{
|
||||
formatManager.registerBasicFormats();
|
||||
player.setSource (&mixer);
|
||||
}
|
||||
|
||||
SoundPlayer::~SoundPlayer()
|
||||
{
|
||||
mixer.removeAllInputs();
|
||||
player.setSource (nullptr);
|
||||
}
|
||||
|
||||
void SoundPlayer::play (const File& file)
|
||||
{
|
||||
if (file.existsAsFile())
|
||||
play (formatManager.createReaderFor (file), true);
|
||||
}
|
||||
|
||||
void SoundPlayer::play (const void* resourceData, size_t resourceSize)
|
||||
{
|
||||
if (resourceData != nullptr && resourceSize > 0)
|
||||
{
|
||||
MemoryInputStream* mem = new MemoryInputStream (resourceData, resourceSize, false);
|
||||
play (formatManager.createReaderFor (mem), true);
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPlayer::play (AudioFormatReader* reader, bool deleteWhenFinished)
|
||||
{
|
||||
if (reader != nullptr)
|
||||
play (new AudioFormatReaderSource (reader, deleteWhenFinished), true);
|
||||
}
|
||||
|
||||
void SoundPlayer::play (AudioSampleBuffer* buffer, bool deleteWhenFinished, bool playOnAllOutputChannels)
|
||||
{
|
||||
if (buffer != nullptr)
|
||||
play (new AudioSampleBufferSource (buffer, deleteWhenFinished, playOnAllOutputChannels), true);
|
||||
}
|
||||
|
||||
void SoundPlayer::play (PositionableAudioSource* audioSource, bool deleteWhenFinished)
|
||||
{
|
||||
if (audioSource != nullptr)
|
||||
{
|
||||
AudioTransportSource* transport = dynamic_cast<AudioTransportSource*> (audioSource);
|
||||
|
||||
if (transport == nullptr)
|
||||
{
|
||||
if (deleteWhenFinished)
|
||||
{
|
||||
transport = new AudioSourceOwningTransportSource (audioSource);
|
||||
}
|
||||
else
|
||||
{
|
||||
transport = new AudioTransportSource();
|
||||
transport->setSource (audioSource);
|
||||
deleteWhenFinished = true;
|
||||
}
|
||||
}
|
||||
|
||||
transport->start();
|
||||
transport->prepareToPlay (bufferSize, sampleRate);
|
||||
|
||||
new AutoRemovingTransportSource (mixer, transport, deleteWhenFinished, bufferSize, sampleRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (deleteWhenFinished)
|
||||
delete audioSource;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundPlayer::playTestSound()
|
||||
{
|
||||
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);
|
||||
|
||||
play (newSound, true, true);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void SoundPlayer::audioDeviceIOCallback (const float** inputChannelData,
|
||||
int numInputChannels,
|
||||
float** outputChannelData,
|
||||
int numOutputChannels,
|
||||
int numSamples)
|
||||
{
|
||||
player.audioDeviceIOCallback (inputChannelData, numInputChannels,
|
||||
outputChannelData, numOutputChannels,
|
||||
numSamples);
|
||||
}
|
||||
|
||||
void SoundPlayer::audioDeviceAboutToStart (AudioIODevice* device)
|
||||
{
|
||||
if (device != nullptr)
|
||||
{
|
||||
sampleRate = device->getCurrentSampleRate();
|
||||
bufferSize = device->getCurrentBufferSizeSamples();
|
||||
}
|
||||
|
||||
player.audioDeviceAboutToStart (device);
|
||||
}
|
||||
|
||||
void SoundPlayer::audioDeviceStopped()
|
||||
{
|
||||
player.audioDeviceStopped();
|
||||
}
|
||||
|
||||
void SoundPlayer::audioDeviceError (const String& errorMessage)
|
||||
{
|
||||
player.audioDeviceError (errorMessage);
|
||||
}
|
||||
130
modules/juce_audio_utils/players/juce_SoundPlayer.h
Normal file
130
modules/juce_audio_utils/players/juce_SoundPlayer.h
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
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_SOUNDPLAYER_H_INCLUDED
|
||||
#define JUCE_SOUNDPLAYER_H_INCLUDED
|
||||
|
||||
//==============================================================================
|
||||
/**
|
||||
A simple sound player that you can add to the AudioDeviceManager to play
|
||||
simple sounds.
|
||||
|
||||
@see AudioProcessor, AudioProcessorGraph
|
||||
*/
|
||||
class JUCE_API SoundPlayer : public AudioIODeviceCallback
|
||||
{
|
||||
public:
|
||||
//==============================================================================
|
||||
SoundPlayer ();
|
||||
|
||||
/** Destructor. */
|
||||
virtual ~SoundPlayer();
|
||||
|
||||
//==============================================================================
|
||||
/** Plays a sound from a file. */
|
||||
void play (const File& file);
|
||||
|
||||
/** Convenient method to play sound from a JUCE resource. */
|
||||
void play (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 play (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 audioSource the audio source to play
|
||||
@param deleteWhenFinished If this is true then the audio source will
|
||||
be deleted once the device manager has finished
|
||||
playing.
|
||||
*/
|
||||
void play (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.
|
||||
|
||||
If playOnAllOutputChannels is true, then if there are more output channels
|
||||
than buffer channels, then the ones that are available will be re-used on
|
||||
multiple outputs so that something is sent to all output channels. If it
|
||||
is false, then the buffer will just be played on the first output channels.
|
||||
*/
|
||||
void play (AudioSampleBuffer* buffer,
|
||||
bool deleteWhenFinished = false,
|
||||
bool playOnAllOutputChannels = false);
|
||||
|
||||
/** Plays a beep through the current audio device.
|
||||
|
||||
This is here to allow the audio setup UI panels to easily include a "test"
|
||||
button so that the user can check where the audio is coming from.
|
||||
*/
|
||||
void playTestSound();
|
||||
|
||||
//==============================================================================
|
||||
/** @internal */
|
||||
void audioDeviceIOCallback (const float**, int, float**, int, int) override;
|
||||
/** @internal */
|
||||
void audioDeviceAboutToStart (AudioIODevice*) override;
|
||||
/** @internal */
|
||||
void audioDeviceStopped() override;
|
||||
/** @internal */
|
||||
void audioDeviceError (const String& errorMessage) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
AudioFormatManager formatManager;
|
||||
AudioSourcePlayer player;
|
||||
MixerAudioSource mixer;
|
||||
OwnedArray<AudioSource> sources;
|
||||
|
||||
//==============================================================================
|
||||
double sampleRate;
|
||||
int bufferSize;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (SoundPlayer)
|
||||
};
|
||||
|
||||
|
||||
#endif // JUCE_SOUNDPLAYER_H_INCLUDED
|
||||
Loading…
Add table
Add a link
Reference in a new issue