| 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 | |