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 | |
20 | using std::mutex; |
21 | using std::lock_guard; |
22 | |
23 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
24 | AudioQueue::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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
49 | uInt32 AudioQueue::capacity() const |
50 | { |
51 | return uInt32(myFragmentQueue.size()); |
52 | } |
53 | |
54 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
55 | uInt32 AudioQueue::size() |
56 | { |
57 | lock_guard<mutex> guard(myMutex); |
58 | |
59 | return mySize; |
60 | } |
61 | |
62 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
63 | bool AudioQueue::isStereo() const |
64 | { |
65 | return myIsStereo; |
66 | } |
67 | |
68 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
69 | uInt32 AudioQueue::fragmentSize() const |
70 | { |
71 | return myFragmentSize; |
72 | } |
73 | |
74 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
75 | Int16* 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
106 | Int16* 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
129 | void 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 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
141 | void AudioQueue::ignoreOverflows(bool shouldIgnoreOverflows) |
142 | { |
143 | myIgnoreOverflows = shouldIgnoreOverflows; |
144 | } |
145 | |