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/RewindManager.o \
|
||||
src/common/StateManager.o \
|
||||
src/common/ZipHandler.o
|
||||
src/common/ZipHandler.o \
|
||||
src/common/AudioQueue.o
|
||||
|
||||
MODULE_DIRS += \
|
||||
src/common
|
||||
|
|
Loading…
Reference in New Issue