1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#include "AudioQueue.hxx"
19
20using std::mutex;
21using std::lock_guard;
22
23// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
24AudioQueue::AudioQueue(uInt32 fragmentSize, uInt32 capacity, bool isStereo)
25 : myFragmentSize(fragmentSize),
26 myIsStereo(isStereo),
27 myFragmentQueue(capacity),
28 myAllFragments(capacity + 2),
29 mySize(0),
30 myNextFragment(0),
31 myIgnoreOverflows(true),
32 myOverflowLogger("audio buffer overflow", Logger::Level::INFO)
33{
34 const uInt8 sampleSize = myIsStereo ? 2 : 1;
35
36 myFragmentBuffer = make_unique<Int16[]>(myFragmentSize * sampleSize * (capacity + 2));
37
38 for (uInt32 i = 0; i < capacity; ++i)
39 myFragmentQueue[i] = myAllFragments[i] = myFragmentBuffer.get() + i * sampleSize * myFragmentSize;
40
41 myAllFragments[capacity] = myFirstFragmentForEnqueue =
42 myFragmentBuffer.get() + capacity * sampleSize * myFragmentSize;
43
44 myAllFragments[capacity + 1] = myFirstFragmentForDequeue =
45 myFragmentBuffer.get() + (capacity + 1) * sampleSize * myFragmentSize;
46}
47
48// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
49uInt32 AudioQueue::capacity() const
50{
51 return uInt32(myFragmentQueue.size());
52}
53
54// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
55uInt32 AudioQueue::size()
56{
57 lock_guard<mutex> guard(myMutex);
58
59 return mySize;
60}
61
62// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
63bool AudioQueue::isStereo() const
64{
65 return myIsStereo;
66}
67
68// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
69uInt32 AudioQueue::fragmentSize() const
70{
71 return myFragmentSize;
72}
73
74// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
75Int16* AudioQueue::enqueue(Int16* fragment)
76{
77 lock_guard<mutex> guard(myMutex);
78
79 Int16* newFragment;
80
81 if (!fragment) {
82 if (!myFirstFragmentForEnqueue) throw runtime_error("enqueue called empty");
83
84 newFragment = myFirstFragmentForEnqueue;
85 myFirstFragmentForEnqueue = nullptr;
86
87 return newFragment;
88 }
89
90 const uInt8 capacity = uInt8(myFragmentQueue.size());
91 const uInt8 fragmentIndex = (myNextFragment + mySize) % capacity;
92
93 newFragment = myFragmentQueue.at(fragmentIndex);
94 myFragmentQueue.at(fragmentIndex) = fragment;
95
96 if (mySize < capacity) ++mySize;
97 else {
98 myNextFragment = (myNextFragment + 1) % capacity;
99 if (!myIgnoreOverflows) myOverflowLogger.log();
100 }
101
102 return newFragment;
103}
104
105// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
106Int16* AudioQueue::dequeue(Int16* fragment)
107{
108 lock_guard<mutex> guard(myMutex);
109
110 if (mySize == 0) return nullptr;
111
112 if (!fragment) {
113 if (!myFirstFragmentForDequeue) throw runtime_error("dequeue called empty");
114
115 fragment = myFirstFragmentForDequeue;
116 myFirstFragmentForDequeue = nullptr;
117 }
118
119 Int16* nextFragment = myFragmentQueue.at(myNextFragment);
120 myFragmentQueue.at(myNextFragment) = fragment;
121
122 --mySize;
123 myNextFragment = (myNextFragment + 1) % myFragmentQueue.size();
124
125 return nextFragment;
126}
127
128// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129void AudioQueue::closeSink(Int16* fragment)
130{
131 lock_guard<mutex> guard(myMutex);
132
133 if (myFirstFragmentForDequeue && fragment)
134 throw new runtime_error("attempt to return unknown buffer on closeSink");
135
136 if (!myFirstFragmentForDequeue)
137 myFirstFragmentForDequeue = fragment;
138}
139
140// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
141void AudioQueue::ignoreOverflows(bool shouldIgnoreOverflows)
142{
143 myIgnoreOverflows = shouldIgnoreOverflows;
144}
145