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-2021 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 "Audio.hxx"
19 #include "AudioQueue.hxx"
20
21 #include <cmath>
22
23 namespace {
24 constexpr double R_MAX = 30.;
25 constexpr double R = 1.;
26
mixingTableEntry(uInt8 v,uInt8 vMax)27 Int16 mixingTableEntry(uInt8 v, uInt8 vMax)
28 {
29 return static_cast<Int16>(
30 floor(0x7fff * double(v) / double(vMax) * (R_MAX + R * double(vMax)) / (R_MAX + R * double(v)))
31 );
32 }
33 }
34
35 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Audio()36 Audio::Audio()
37 {
38 for (uInt8 i = 0; i <= 0x1e; ++i) myMixingTableSum[i] = mixingTableEntry(i, 0x1e);
39 for (uInt8 i = 0; i <= 0x0f; ++i) myMixingTableIndividual[i] = mixingTableEntry(i, 0x0f);
40
41 reset();
42 }
43
44 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
reset()45 void Audio::reset()
46 {
47 myCounter = 0;
48 mySampleIndex = 0;
49
50 myChannel0.reset();
51 myChannel1.reset();
52 }
53
54 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
setAudioQueue(const shared_ptr<AudioQueue> & queue)55 void Audio::setAudioQueue(const shared_ptr<AudioQueue>& queue)
56 {
57 myAudioQueue = queue;
58
59 myCurrentFragment = myAudioQueue->enqueue();
60 mySampleIndex = 0;
61 }
62
63 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
tick()64 void Audio::tick()
65 {
66 switch (myCounter) {
67 case 9:
68 case 81:
69 myChannel0.phase0();
70 myChannel1.phase0();
71
72 break;
73
74 case 37:
75 case 149:
76 phase1();
77 break;
78 }
79
80 if (++myCounter == 228) myCounter = 0;
81 }
82
83 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
phase1()84 void Audio::phase1()
85 {
86 uInt8 sample0 = myChannel0.phase1();
87 uInt8 sample1 = myChannel1.phase1();
88
89 addSample(sample0, sample1);
90 #ifdef GUI_SUPPORT
91 if(myRewindMode)
92 mySamples.push_back(sample0 | (sample1 << 4));
93 #endif
94 }
95
96 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
addSample(uInt8 sample0,uInt8 sample1)97 void Audio::addSample(uInt8 sample0, uInt8 sample1)
98 {
99 if(!myAudioQueue) return;
100
101 if(myAudioQueue->isStereo()) {
102 myCurrentFragment[2 * mySampleIndex] = myMixingTableIndividual[sample0];
103 myCurrentFragment[2 * mySampleIndex + 1] = myMixingTableIndividual[sample1];
104 }
105 else {
106 myCurrentFragment[mySampleIndex] = myMixingTableSum[sample0 + sample1];
107 }
108
109 if(++mySampleIndex == myAudioQueue->fragmentSize()) {
110 mySampleIndex = 0;
111 myCurrentFragment = myAudioQueue->enqueue(myCurrentFragment);
112 }
113 }
114
115 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
channel0()116 AudioChannel& Audio::channel0()
117 {
118 return myChannel0;
119 }
120
121 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
channel1()122 AudioChannel& Audio::channel1()
123 {
124 return myChannel1;
125 }
126
127 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
save(Serializer & out) const128 bool Audio::save(Serializer& out) const
129 {
130 try
131 {
132 out.putByte(myCounter);
133
134 // The queue starts out pristine after loading, so we don't need to save
135 // any other parts of our state
136
137 if (!myChannel0.save(out)) return false;
138 if (!myChannel1.save(out)) return false;
139 #ifdef GUI_SUPPORT
140 out.putLong(uInt64(mySamples.size()));
141 out.putByteArray(mySamples.data(), mySamples.size());
142
143 // TODO: check if this improves sound of playback for larger state gaps
144 //out.putInt(mySampleIndex);
145 //out.putShortArray((uInt16*)myCurrentFragment, myAudioQueue->fragmentSize());
146
147 mySamples.clear();
148 #endif
149 }
150 catch(...)
151 {
152 cerr << "ERROR: TIA_Audio::save" << endl;
153 return false;
154 }
155
156 return true;
157 }
158
159 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
load(Serializer & in)160 bool Audio::load(Serializer& in)
161 {
162 try
163 {
164 myCounter = in.getByte();
165
166 if (!myChannel0.load(in)) return false;
167 if (!myChannel1.load(in)) return false;
168 #ifdef GUI_SUPPORT
169 uInt64 sampleSize = in.getLong();
170 unique_ptr<uInt8[]> samples = make_unique<uInt8[]>(sampleSize);
171 in.getByteArray(samples.get(), sampleSize);
172
173 //mySampleIndex = in.getInt();
174 //in.getShortArray((uInt16*)myCurrentFragment, myAudioQueue->fragmentSize());
175
176 // Feed all loaded samples into the audio queue
177 for(size_t i = 0; i < sampleSize; i++)
178 {
179 uInt8 sample = samples[i];
180 uInt8 sample0 = sample & 0x0f;
181 uInt8 sample1 = sample >> 4;
182
183 addSample(sample0, sample1);
184 }
185 #endif
186 }
187 catch(...)
188 {
189 cerr << "ERROR: TIA_Audio::load" << endl;
190 return false;
191 }
192
193 return true;
194 }
195