1 /* TIATracker, (c) 2016 Andre "Kylearan" Wichmann.
2  * Website: https://bitbucket.org/kylearan/tiatracker
3  * Email: andre.wichmann@gmx.de
4  * See the file "license.txt" for information on usage and redistribution
5  * of this file.
6  */
7 
8 #include "instrument.h"
9 
10 #include <QMutex>
11 
12 #include <stdexcept>
13 #include <iostream>
14 #include <QJsonArray>
15 #include <QJsonObject>
16 #include "mainwindow.h"
17 #include "instrumentstab.h"
18 
19 
20 namespace Track {
21 
22 /* TODO: More sensible empty detection, like a bool flag that gets
23  * changed whenever the instrument data gets updated by the GUI...
24  */
isEmpty()25 bool Instrument::isEmpty() {
26     bool empty = true;
27     if (name != "---"
28             || envelopeLength > 2
29             || frequencies.size() > 2
30             || volumes[0] != 0 || volumes[1] != 0
31             || frequencies[0] != 0 || frequencies[1] != 0
32             || sustainStart != 0 || releaseStart != 1
33             || baseDistortion != TiaSound::Distortion::PURE_COMBINED
34             ) {
35         empty = false;
36     }
37 
38     return empty;
39 }
40 
41 /*************************************************************************/
42 
toJson(QJsonObject & json)43 void Instrument::toJson(QJsonObject &json) {
44     json["version"] = MainWindow::version;
45     json["name"] = name;
46     json["waveform"] = static_cast<int>(baseDistortion);
47     json["envelopeLength"] = envelopeLength;
48     json["sustainStart"] = sustainStart;
49     json["releaseStart"] = releaseStart;
50 
51     QJsonArray freqArray;
52     for (int iFreq = 0; iFreq < envelopeLength; ++iFreq) {
53         freqArray.append(QJsonValue(frequencies[iFreq]));
54     }
55     json["frequencies"] = freqArray;
56 
57     QJsonArray volArray;
58     for (int iVol = 0; iVol < envelopeLength; ++iVol) {
59         volArray.append(QJsonValue(volumes[iVol]));
60     }
61     json["volumes"] = volArray;
62 }
63 
64 /*************************************************************************/
65 
import(const QJsonObject & json)66 bool Instrument::import(const QJsonObject &json) {
67     int version = json["version"].toInt();
68     if (version > MainWindow::version) {
69         MainWindow::displayMessage("An instrument is from a later version of TIATracker!");
70         return false;
71     }
72 
73     QString newName = json["name"].toString();
74     int newWaveform = json["waveform"].toInt();
75     int newEnvelopeLength = json["envelopeLength"].toInt();
76     int newSustainStart = json["sustainStart"].toInt();
77     int newReleaseStart = json["releaseStart"].toInt();
78     QJsonArray freqArray = json["frequencies"].toArray();
79     QJsonArray volArray = json["volumes"].toArray();
80 
81     // Check for data validity
82     if (newName.length() > InstrumentsTab::maxInstrumentNameLength) {
83         MainWindow::displayMessage("An instrument has an invalid name: " + newName);
84         return false;
85     }
86     if (newWaveform < 0 || newWaveform > 16) {
87         MainWindow::displayMessage("An instrument has an invalid waveform: " + newName);
88         return false;
89     }
90     if (newEnvelopeLength < 2 || newEnvelopeLength > 99
91             || newSustainStart < 0 || newSustainStart >= newEnvelopeLength - 1
92             || newReleaseStart <= newSustainStart || newReleaseStart > newEnvelopeLength - 1) {
93         MainWindow::displayMessage("An instrument has an invalid envelope structure: " + newName);
94         return false;
95     }
96     if (newEnvelopeLength != freqArray.size() || newEnvelopeLength != volArray.size()) {
97         MainWindow::displayMessage("An instrument has an invalid frequency envelope: " + newName);
98         return false;
99     }
100 
101     // Copy data. Adjust volumes or frequencies if necessary.
102     frequencies.clear();
103     volumes.clear();
104     for (int frame = 0; frame < newEnvelopeLength; ++frame) {
105         int newVolume = volArray[frame].toInt();
106         if (newVolume < 0) {
107             newVolume = 0;
108         }
109         if (newVolume > 15) {
110             newVolume = 15;
111         }
112         volumes.append(newVolume);
113         int newFrequency = freqArray[frame].toInt();
114         if (newFrequency < -8) {
115             newFrequency = -8;
116         }
117         if (newFrequency > 7) {
118             newFrequency = 7;
119         }
120         frequencies.append(newFrequency);
121     }
122     name = newName;
123     baseDistortion = TiaSound::distortions[newWaveform];
124     envelopeLength = newEnvelopeLength;
125     sustainStart = newSustainStart;
126     releaseStart = newReleaseStart;
127 
128     return true;
129 }
130 
131 /*************************************************************************/
132 
setEnvelopeLength(int newSize)133 void Instrument::setEnvelopeLength(int newSize) {
134     if (newSize > volumes.size()) {
135         // grow
136         int lastVolume = volumes[volumes.size() - 1];
137         int lastFrequency = frequencies[volumes.size() - 1];
138         while (volumes.size() != newSize) {
139             volumes.append(lastVolume);
140             frequencies.append(lastFrequency);
141         }
142     }
143     envelopeLength = newSize;
144     validateSustainReleaseValues();
145 }
146 
147 /*************************************************************************/
148 
getAudCValue(int frequency)149 int Instrument::getAudCValue(int frequency) {
150     int result;
151 
152     if (baseDistortion != TiaSound::Distortion::PURE_COMBINED) {
153         result = (static_cast<int>(baseDistortion));
154     } else {
155         if (frequency < 32) {
156             result = (static_cast<int>(TiaSound::Distortion::PURE_HIGH));
157         } else {
158             result = (static_cast<int>(TiaSound::Distortion::PURE_LOW));
159         }
160     }
161 
162     return result;
163 }
164 
165 /*************************************************************************/
166 
validateSustainReleaseValues()167 void Instrument::validateSustainReleaseValues() {
168     if (releaseStart >= envelopeLength) {
169         releaseStart = envelopeLength - 1;
170     }
171     if (sustainStart >= releaseStart) {
172         sustainStart = releaseStart - 1;
173     }
174 }
175 
176 /*************************************************************************/
177 
setSustainAndRelease(int newSustainStart,int newReleaseStart)178 void Instrument::setSustainAndRelease(int newSustainStart, int newReleaseStart) {
179     // TODO: change intro assert
180     if (newReleaseStart <= newSustainStart
181             || newSustainStart >= envelopeLength - 1
182             || newReleaseStart >= envelopeLength) {
183         throw std::invalid_argument("Invalid new sustain/release index values!");
184     }
185     sustainStart = newSustainStart;
186     releaseStart = newReleaseStart;
187 }
188 
189 /*************************************************************************/
190 
deleteInstrument()191 void Instrument::deleteInstrument()
192 {
193     name = "---";
194     while (volumes.size() > 2) {
195         volumes.removeLast();
196         frequencies.removeLast();
197     }
198     volumes[0] = 0;
199     volumes[1] = 0;
200     frequencies[0] = 0;
201     frequencies[1] = 0;
202     envelopeLength = 2;
203     sustainStart = 0;
204     releaseStart = 1;
205     baseDistortion = TiaSound::Distortion::PURE_COMBINED;
206 }
207 
208 /*************************************************************************/
209 
getMinVolume()210 int Instrument::getMinVolume() {
211     int min = volumes[0];
212     for (int i = 1; i < volumes.size(); ++i) {
213         min = qMin(min, volumes[i]);
214     }
215     return min;
216 }
217 
218 /*************************************************************************/
219 
getMaxVolume()220 int Instrument::getMaxVolume() {
221     int max = volumes[0];
222     for (int i = 1; i < volumes.size(); ++i) {
223         max = qMax(max, volumes[i]);
224     }
225     return max;
226 }
227 
228 /*************************************************************************/
229 
correctSustainReleaseForInsert(int frame)230 void Instrument::correctSustainReleaseForInsert(int frame) {
231     if (frame < sustainStart) {
232         // in AD
233         sustainStart++;
234         releaseStart++;
235     } else if (frame < releaseStart) {
236         // in Sustain
237         releaseStart++;
238     }
239 }
240 
insertFrameBefore(int frame)241 void Instrument::insertFrameBefore(int frame) {
242     if (envelopeLength == maxEnvelopeLength) {
243         return;
244     }
245     envelopeLength++;
246 
247     correctSustainReleaseForInsert(frame);
248 
249     // Interpolate
250     int volBefore = 0;
251     int freqBefore = 0;
252     if (frame != 0) {
253         volBefore = volumes[frame - 1];
254         freqBefore = frequencies[frame - 1];
255     }
256     int volAfter = volumes[frame];
257     int newVol = int((volAfter + volBefore)/2);
258     int freqAfter = frequencies[frame];
259     int freqNew = int((freqAfter + freqBefore)/2);
260 
261     volumes.insert(frame, newVol);
262     frequencies.insert(frame, freqNew);
263 }
264 
265 /*************************************************************************/
266 
insertFrameAfter(int frame)267 void Instrument::insertFrameAfter(int frame) {
268     if (envelopeLength == maxEnvelopeLength) {
269         return;
270     }
271     envelopeLength++;
272 
273     correctSustainReleaseForInsert(frame);
274 
275     // Interpolate
276     int volBefore = volumes[frame];
277     int freqBefore = frequencies[frame];
278     int volAfter = 0;
279     int freqAfter = 0;
280     // -1 b/c we increased length already
281     if (frame + 1 != envelopeLength - 1) {
282         volAfter = volumes[frame + 1];
283         freqAfter = frequencies[frame + 1];
284     }
285     int newVol = int((volAfter + volBefore)/2);
286     int newFreq = int((freqAfter + freqBefore)/2);
287 
288     volumes.insert(frame + 1, newVol);
289     frequencies.insert(frame + 1, newFreq);
290 }
291 
292 /*************************************************************************/
293 
deleteFrame(int frame)294 void Instrument::deleteFrame(int frame) {
295     if (envelopeLength == 2) {
296         return;
297     }
298     envelopeLength--;
299 
300     // Correct sustain and release
301     if (frame < sustainStart) {
302         // in AD
303         sustainStart--;
304         releaseStart--;
305     } else if (frame < releaseStart) {
306         // in Sustain
307         if (releaseStart - sustainStart > 1) {
308             releaseStart--;
309         } else {
310             // Trying to delete single sustain frame.
311             if (sustainStart > 0) {
312                 // Move sustain one back
313                 sustainStart--;
314                 releaseStart--;
315             } else {
316                 // keep sustain at this position
317             }
318         }
319     } else {
320         // in Release
321         if (releaseStart - sustainStart == 1) {
322             // Move Release back one frame
323             sustainStart--;
324             releaseStart--;
325         }
326     }
327 
328     // Delete frames
329     volumes.removeAt(frame);
330     frequencies.removeAt(frame);
331 }
332 
333 /*************************************************************************/
334 
getSustainStart() const335 int Instrument::getSustainStart() const {
336     return sustainStart;
337 }
338 
getReleaseStart() const339 int Instrument::getReleaseStart() const {
340     return releaseStart;
341 }
342 
343 /*************************************************************************/
344 
calcEffectiveSize()345 int Instrument::calcEffectiveSize() {
346     int realSize = envelopeLength;
347     while (realSize > (releaseStart + 1) && frequencies[realSize - 1] == 0 && volumes[realSize - 1] == 0) {
348         realSize--;
349     }
350     return realSize + 1;    // +1 for "end of instrument" 0 byte
351 }
352 
353 /*************************************************************************/
354 
getEnvelopeLength()355 int Instrument::getEnvelopeLength() {
356     return envelopeLength;
357 }
358 
359 }
360