Skip to content

Commit

Permalink
Fix data race in AudioFilePlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
anthonyalfimov committed Nov 11, 2022
1 parent f88a47b commit ddca327
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 21 deletions.
39 changes: 23 additions & 16 deletions Source/AudioFilePlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
AudioFilePlayer::AudioFilePlayer()
{
transportSource.addChangeListener (this);

//==========================================================================
// Check that atomic bool is lock-free
static_assert (std::atomic<bool>::is_always_lock_free,
"std::atomic for type bool must be always lock free");
}

//==============================================================================
Expand All @@ -24,17 +29,18 @@ void AudioFilePlayer::setAudioFormatReader (AudioFormatReader* reader)
// Pass reader ownership to newSource:
auto newSource = std::make_unique<AudioFormatReaderSource> (reader, true);

// Set input source for the transportSource object:
transportSource.setSource (newSource.get(), 0, nullptr, reader->sampleRate);
{
SpinLock::ScopedLockType readerLoopingLock (readerLoopingMutex);

// Transfer memory ownership of the audio source to readerSource ptr:
readerSource.reset (newSource.release());
// Set input source for the transportSource object:
transportSource.setSource (newSource.get(), 0, nullptr, reader->sampleRate);

// Update looping status of the new readerSource:
readerSource->setLooping (looping);
// Transfer memory ownership of the audio source to readerSource ptr:
readerSource.reset (newSource.release());

// Stop transport:
changeState (TransportState::Stopped);
// Update transport state:
changeState (TransportState::Stopped);
}
}

//==========================================================================
Expand All @@ -45,10 +51,11 @@ void AudioFilePlayer::prepareToPlay (int samplesPerBlockExpected, double sampleR

void AudioFilePlayer::getNextAudioBlock (const AudioSourceChannelInfo& info)
{
if (readerSource == nullptr)
{
info.clearActiveBufferRegion();
return;
SpinLock::ScopedTryLockType readerLoopingLock (readerLoopingMutex);

if (readerLoopingLock.isLocked() && readerSource != nullptr)
readerSource->setLooping (looping.load());
}

transportSource.getNextAudioBlock (info);
Expand Down Expand Up @@ -93,12 +100,12 @@ void AudioFilePlayer::stop()
changeState (TransportState::Stopping);
}

void AudioFilePlayer::setLooping (bool shouldLoop)
//==============================================================================
double AudioFilePlayer::getCurrentPosition() const
{
looping = shouldLoop;

if (readerSource.get() != nullptr)
readerSource->setLooping (looping);
// getCurrentPosition() method accesses readerSource, so we need to lock:
SpinLock::ScopedLockType readerLoopingLock (readerLoopingMutex);
return transportSource.getCurrentPosition();
}

//==============================================================================
Expand Down
11 changes: 6 additions & 5 deletions Source/AudioFilePlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@

#include <JuceHeader.h>

// TODO: Pre-buffer the file that is playing?
// TODO: Examine thread-safety of this class (use thread sanitiser?)
// TODO: Pre-buffer the file that is playing

class AudioFilePlayer : public AudioSource,
public ChangeListener
Expand All @@ -35,13 +34,13 @@ class AudioFilePlayer : public AudioSource,
// Player transport controls
void playPause();
void stop();
void setLooping (bool shouldLoop);
void setLooping (bool shouldLoop) { looping.store (shouldLoop); }

//==========================================================================
// Player transport state
bool isPlaying() const { return transportSource.isPlaying(); }
bool isLooping() const { return transportSource.isLooping(); }
double getCurrentPosition() const { return transportSource.getCurrentPosition(); }
double getCurrentPosition() const;

//==========================================================================
// Transport state change callbacks
Expand All @@ -66,13 +65,15 @@ class AudioFilePlayer : public AudioSource,

void changeState (TransportState newState);

bool looping = false;
std::atomic<bool> looping = false;
bool shouldFadeIn = true;

std::unique_ptr<AudioFormatReaderSource> readerSource;
AudioTransportSource transportSource;
TransportState state = TransportState::Stopped;

SpinLock readerLoopingMutex;

//==========================================================================
// Change listener callback
void changeListenerCallback (ChangeBroadcaster* source) override;
Expand Down

0 comments on commit ddca327

Please sign in to comment.