mirror of https://github.com/stella-emu/stella.git
Threadsafe fragment queue.
This commit is contained in:
parent
317f7391c2
commit
be91e6ff21
|
@ -0,0 +1,133 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include "AudioQueue.hxx"
|
||||||
|
|
||||||
|
using std::mutex;
|
||||||
|
using std::lock_guard;
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
AudioQueue::AudioQueue(uInt32 fragmentSize, uInt8 capacity, bool isStereo, uInt16 sampleRate)
|
||||||
|
: myFragmentSize(fragmentSize),
|
||||||
|
myIsStereo(isStereo),
|
||||||
|
myFragmentQueue(capacity),
|
||||||
|
myAllFragments(capacity + 2),
|
||||||
|
mySize(0),
|
||||||
|
myNextFragment(0)
|
||||||
|
{
|
||||||
|
const uInt8 sampleSize = myIsStereo ? 2 : 1;
|
||||||
|
|
||||||
|
myFragmentBuffer = new Int16[myFragmentSize * sampleSize * (capacity + 2)];
|
||||||
|
|
||||||
|
for (uInt8 i = 0; i < capacity; i++)
|
||||||
|
myFragmentQueue[i] = myAllFragments[i] = myFragmentBuffer + i * sampleSize * myFragmentSize;
|
||||||
|
|
||||||
|
myAllFragments[capacity] = myFirstFragmentForEnqueue =
|
||||||
|
myFragmentBuffer + capacity * sampleSize * myFragmentSize;
|
||||||
|
|
||||||
|
myAllFragments[capacity + 1] = myFirstFragmentForDequeue =
|
||||||
|
myFragmentBuffer + (capacity + 1) * sampleSize * myFragmentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
AudioQueue::~AudioQueue()
|
||||||
|
{
|
||||||
|
delete[]myFragmentBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt8 AudioQueue::capacity() const
|
||||||
|
{
|
||||||
|
return myFragmentQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt8 AudioQueue::size()
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(myMutex);
|
||||||
|
|
||||||
|
return mySize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool AudioQueue::isStereo() const
|
||||||
|
{
|
||||||
|
return myIsStereo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt32 AudioQueue::fragmentSize() const
|
||||||
|
{
|
||||||
|
return myFragmentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
uInt16 AudioQueue::sampleRate() const
|
||||||
|
{
|
||||||
|
return mySampleRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Int16* AudioQueue::enqueue(Int16* fragment)
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(myMutex);
|
||||||
|
|
||||||
|
Int16* newFragment;
|
||||||
|
|
||||||
|
if (!fragment) {
|
||||||
|
if (!myFirstFragmentForEnqueue) throw runtime_error("enqueue called empty");
|
||||||
|
|
||||||
|
newFragment = myFirstFragmentForEnqueue;
|
||||||
|
myFirstFragmentForEnqueue = 0;
|
||||||
|
|
||||||
|
return newFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uInt8 capacity = myFragmentQueue.size();
|
||||||
|
const uInt8 fragmentIndex = (myNextFragment + mySize) % capacity;
|
||||||
|
|
||||||
|
newFragment = myFragmentQueue.at(fragmentIndex);
|
||||||
|
myFragmentQueue.at(fragmentIndex) = fragment;
|
||||||
|
|
||||||
|
if (mySize < capacity) mySize++;
|
||||||
|
else myNextFragment = (myNextFragment + 1) % capacity;
|
||||||
|
|
||||||
|
return newFragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
Int16* AudioQueue::dequeue(Int16* fragment)
|
||||||
|
{
|
||||||
|
lock_guard<mutex> guard(myMutex);
|
||||||
|
|
||||||
|
if (mySize == 0) return 0;
|
||||||
|
|
||||||
|
if (!fragment) {
|
||||||
|
if (!myFirstFragmentForDequeue) throw runtime_error("dequeue called empty");
|
||||||
|
|
||||||
|
fragment = myFirstFragmentForDequeue;
|
||||||
|
myFirstFragmentForDequeue = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Int16* nextFragment = myFragmentQueue.at(myNextFragment);
|
||||||
|
myFragmentQueue.at(myNextFragment) = fragment;
|
||||||
|
|
||||||
|
mySize--;
|
||||||
|
myNextFragment = (myNextFragment + 1) % myFragmentQueue.size();
|
||||||
|
|
||||||
|
return nextFragment;
|
||||||
|
}
|
|
@ -0,0 +1,141 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2018 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#ifndef AUDIO_QUEUE_HXX
|
||||||
|
#define AUDIO_QUEUE_HXX
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include "bspf.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
This class implements a an audio queue that acts both like a ring buffer
|
||||||
|
and a pool of audio fragments. The TIA emulation core fills a fragment
|
||||||
|
with samples and then returns it to the queue, receiving a new fragment
|
||||||
|
in return. The sound driver removes fragments for playback from the
|
||||||
|
queue and returns the used fragment in this process.
|
||||||
|
|
||||||
|
The queue needs to be threadsafe as the (SDL) audio driver runs on a
|
||||||
|
separate thread. Samples are stored as signed 16 bit integers
|
||||||
|
(platform endian).
|
||||||
|
*/
|
||||||
|
class AudioQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
Create a new AudioQueue.
|
||||||
|
|
||||||
|
@param fragmentSize The size (in stereo / mono samples) of each fragment
|
||||||
|
@param capacaity The number of fragments that can be queued before wrapping.
|
||||||
|
@param isStereo Whether samples are stereo or mono.
|
||||||
|
@param sampleRate The sample rate. This is not used, but can be queried.
|
||||||
|
*/
|
||||||
|
AudioQueue(uInt32 fragmentSize, uInt8 capacity, bool isStereo, uInt16 sampleRate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
We need a destructor to deallocate the individual fragment buffers.
|
||||||
|
*/
|
||||||
|
~AudioQueue();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Capacity getter.
|
||||||
|
*/
|
||||||
|
uInt8 capacity() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Size getter.
|
||||||
|
*/
|
||||||
|
uInt8 size();
|
||||||
|
|
||||||
|
/**
|
||||||
|
Stereo / mono getter.
|
||||||
|
*/
|
||||||
|
bool isStereo() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Fragment size getter.
|
||||||
|
*/
|
||||||
|
uInt32 fragmentSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Sample rate getter.
|
||||||
|
*/
|
||||||
|
uInt16 sampleRate() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Enqueue a new fragment and get a new fragmen to fill.
|
||||||
|
|
||||||
|
@param fragment The returned fragment. This must be empty on the first call (when
|
||||||
|
there is nothing to return)
|
||||||
|
*/
|
||||||
|
Int16* enqueue(Int16* fragment = 0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dequeue a fragment for playback and return the played fragment. This may
|
||||||
|
* return 0 if there is no queued fragment to return (in this case, the returned
|
||||||
|
* fragment is not enqueued and must be passed in the next invocation).
|
||||||
|
*
|
||||||
|
* @param fragment The returned fragment. This must be empty on the first call (when
|
||||||
|
* there is nothing to return).
|
||||||
|
*/
|
||||||
|
Int16* dequeue(Int16* fragment = 0);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
// The size of an individual fragment (in stereo / mono samples)
|
||||||
|
uInt32 myFragmentSize;
|
||||||
|
|
||||||
|
// Are we using stereo samples?
|
||||||
|
bool myIsStereo;
|
||||||
|
|
||||||
|
// The sample rate
|
||||||
|
uInt16 mySampleRate;
|
||||||
|
|
||||||
|
// The fragment queue
|
||||||
|
vector<Int16*> myFragmentQueue;
|
||||||
|
|
||||||
|
// All fragments, including the two fragments that are in circulation.
|
||||||
|
vector<Int16*> myAllFragments;
|
||||||
|
|
||||||
|
// We allocate a consecutive slice of memory for the fragments.
|
||||||
|
Int16* myFragmentBuffer;
|
||||||
|
|
||||||
|
// The nubmer if queued fragments
|
||||||
|
uInt8 mySize;
|
||||||
|
|
||||||
|
// The next fragment.
|
||||||
|
uInt8 myNextFragment;
|
||||||
|
|
||||||
|
// We need a mutex for thread safety.
|
||||||
|
std::mutex myMutex;
|
||||||
|
|
||||||
|
// The first (empty) enqueue call returns this fragment.
|
||||||
|
Int16* myFirstFragmentForEnqueue;
|
||||||
|
// The first (empty) dequeue call replaces the returned fragment with this fragment.
|
||||||
|
Int16* myFirstFragmentForDequeue;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
AudioQueue() = delete;
|
||||||
|
AudioQueue(const AudioQueue&) = delete;
|
||||||
|
AudioQueue(AudioQueue&&) = delete;
|
||||||
|
AudioQueue& operator=(const AudioQueue&) = delete;
|
||||||
|
AudioQueue& operator=(AudioQueue&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AUDIO_QUEUE_HXX
|
|
@ -12,7 +12,8 @@ MODULE_OBJS := \
|
||||||
src/common/MouseControl.o \
|
src/common/MouseControl.o \
|
||||||
src/common/RewindManager.o \
|
src/common/RewindManager.o \
|
||||||
src/common/StateManager.o \
|
src/common/StateManager.o \
|
||||||
src/common/ZipHandler.o
|
src/common/ZipHandler.o \
|
||||||
|
src/common/AudioQueue.o
|
||||||
|
|
||||||
MODULE_DIRS += \
|
MODULE_DIRS += \
|
||||||
src/common
|
src/common
|
||||||
|
|
Loading…
Reference in New Issue