2018-01-17 18:28:49 +00:00
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
2024-01-01 16:08:25 +00:00
|
|
|
// Copyright (c) 1995-2024 by Bradford W. Mott, Stephen Anthony
|
2018-01-17 18:28:49 +00:00
|
|
|
// 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"
|
2018-12-14 22:57:12 +00:00
|
|
|
#include "StaggeredLogger.hxx"
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
/**
|
2022-10-09 23:58:35 +00:00
|
|
|
This class implements an audio queue that acts both like a ring buffer
|
2018-05-11 23:31:40 +00:00
|
|
|
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).
|
|
|
|
*/
|
2018-01-17 18:28:49 +00:00
|
|
|
class AudioQueue
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
/**
|
|
|
|
Create a new AudioQueue.
|
|
|
|
|
2018-05-11 23:31:40 +00:00
|
|
|
@param fragmentSize The size (in stereo / mono samples) of each fragment
|
|
|
|
@param capacity The number of fragments that can be queued before wrapping.
|
|
|
|
@param isStereo Whether samples are stereo or mono.
|
2018-01-17 18:28:49 +00:00
|
|
|
*/
|
2019-05-01 21:13:17 +00:00
|
|
|
AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo);
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Capacity getter.
|
|
|
|
*/
|
2018-05-04 22:47:48 +00:00
|
|
|
uInt32 capacity() const;
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Size getter.
|
|
|
|
*/
|
2020-12-20 15:36:10 +00:00
|
|
|
uInt32 size() const;
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
Stereo / mono getter.
|
|
|
|
*/
|
|
|
|
bool isStereo() const;
|
|
|
|
|
|
|
|
/**
|
|
|
|
Fragment size getter.
|
|
|
|
*/
|
|
|
|
uInt32 fragmentSize() 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)
|
|
|
|
*/
|
2018-05-11 23:31:40 +00:00
|
|
|
Int16* enqueue(Int16* fragment = nullptr);
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
/**
|
2018-05-11 23:31:40 +00:00
|
|
|
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).
|
2018-01-28 22:23:14 +00:00
|
|
|
|
2018-05-11 23:31:40 +00:00
|
|
|
@param fragment The returned fragment. This must be empty on the first call (when
|
|
|
|
there is nothing to return).
|
2018-01-17 18:28:49 +00:00
|
|
|
*/
|
2018-05-11 23:31:40 +00:00
|
|
|
Int16* dequeue(Int16* fragment = nullptr);
|
2018-01-17 18:28:49 +00:00
|
|
|
|
2018-01-28 22:23:14 +00:00
|
|
|
/**
|
|
|
|
Return the currently playing fragment without drawing a new one. This is called
|
|
|
|
if the sink is closed and prepares the queue to be reopened.
|
|
|
|
*/
|
|
|
|
void closeSink(Int16* fragment);
|
|
|
|
|
2018-06-24 20:48:28 +00:00
|
|
|
/**
|
|
|
|
Should we ignore overflows?
|
|
|
|
*/
|
|
|
|
void ignoreOverflows(bool shouldIgnoreOverflows);
|
|
|
|
|
2018-01-17 18:28:49 +00:00
|
|
|
private:
|
|
|
|
|
|
|
|
// The size of an individual fragment (in stereo / mono samples)
|
2019-12-29 02:06:40 +00:00
|
|
|
uInt32 myFragmentSize{0};
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
// Are we using stereo samples?
|
2019-12-29 02:06:40 +00:00
|
|
|
bool myIsStereo{false};
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
// 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.
|
2018-05-11 23:56:22 +00:00
|
|
|
unique_ptr<Int16[]> myFragmentBuffer;
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
// The nubmer if queued fragments
|
2019-12-29 02:06:40 +00:00
|
|
|
uInt32 mySize{0};
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
// The next fragment.
|
2019-12-29 02:06:40 +00:00
|
|
|
uInt32 myNextFragment{0};
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
// We need a mutex for thread safety.
|
2020-12-20 15:36:10 +00:00
|
|
|
mutable std::mutex myMutex;
|
2018-01-17 18:28:49 +00:00
|
|
|
|
|
|
|
// The first (empty) enqueue call returns this fragment.
|
2019-12-29 02:06:40 +00:00
|
|
|
Int16* myFirstFragmentForEnqueue{nullptr};
|
2018-01-17 18:28:49 +00:00
|
|
|
// The first (empty) dequeue call replaces the returned fragment with this fragment.
|
2019-12-29 02:06:40 +00:00
|
|
|
Int16* myFirstFragmentForDequeue{nullptr};
|
2018-01-17 18:28:49 +00:00
|
|
|
|
2018-06-24 20:48:28 +00:00
|
|
|
// Log overflows?
|
2019-12-29 02:06:40 +00:00
|
|
|
bool myIgnoreOverflows{true};
|
2018-06-24 20:48:28 +00:00
|
|
|
|
2019-12-29 02:06:40 +00:00
|
|
|
StaggeredLogger myOverflowLogger{"audio buffer overflow", Logger::Level::INFO};
|
2018-12-14 22:57:12 +00:00
|
|
|
|
2018-01-17 18:28:49 +00:00
|
|
|
private:
|
|
|
|
|
|
|
|
AudioQueue() = delete;
|
|
|
|
AudioQueue(const AudioQueue&) = delete;
|
|
|
|
AudioQueue(AudioQueue&&) = delete;
|
|
|
|
AudioQueue& operator=(const AudioQueue&) = delete;
|
|
|
|
AudioQueue& operator=(AudioQueue&&) = delete;
|
|
|
|
};
|
|
|
|
|
|
|
|
#endif // AUDIO_QUEUE_HXX
|