1 /* Based on */
2 /* SIMPLE CAVE STORY MUSIC PLAYER (Organya) */
3 /* Written by Joel Yliluoma -- http://iki.fi/bisqwit/ */
4 /* https://bisqwit.iki.fi/jutut/kuvat/programming_examples/doukutsu-org/orgplay.cc */
5
6 #include "Pixtone.h"
7
8 #include "../ResourceManager.h"
9 #include "../common/misc.h"
10 #include "../Utils/Logger.h"
11 #include "../config.h"
12
13 #include <SDL.h>
14 #include <SDL_mixer.h>
15 #include <cmath>
16 #include <cstdio>
17 #include <cstdlib>
18 #include <cstring>
19 #include <functional>
20 #include <iomanip>
21 #include <sstream>
22 #include <vector>
23
24 // using std::fgetc;
25
26 namespace NXE
27 {
28 namespace Sound
29 {
30
31 static struct
32 {
33 int8_t table[256];
34 } wave[PXT_NO_MODELS];
35
fgetv(FILE * fp)36 double fgetv(FILE *fp) // Load a numeric value from text file; one per line.
37 {
38 char Buf[4096], *p = Buf;
39 Buf[4095] = '\0';
40 if (!std::fgets(Buf, sizeof(Buf) - 1, fp))
41 return 0.0;
42 // Ignore empty lines. If the line was empty, try next line.
43 if (!Buf[0] || Buf[0] == '\r' || Buf[0] == '\n')
44 return fgetv(fp);
45 while (*p && *p++ != ':')
46 {
47 } // Skip until a colon character.
48 return std::strtod(p, 0); // Parse the value and return it.
49 }
50
load(const std::string & fname)51 bool stPXSound::load(const std::string &fname)
52 {
53 FILE *fp;
54
55 fp = myfopen(widen(fname).c_str(), widen("rb").c_str());
56 if (!fp)
57 {
58 LOG_WARN("pxt->load: file '{}' not found.", fname);
59 return false;
60 }
61
62 auto f = [=]() { return (int32_t)fgetv(fp); };
63 auto fu = [=]() { return (uint32_t)fgetv(fp); };
64
65 for (auto &c : channels)
66 {
67 c = {
68 f() != 0,
69 fu(), // enabled, length
70 {wave[f() % 6].table, fgetv(fp), f(), f()}, // carrier wave
71 {wave[f() % 6].table, fgetv(fp), f(), f()}, // frequency wave
72 {wave[f() % 6].table, fgetv(fp), f(), f()}, // amplitude wave
73 {f(), {{f(), f()}, {f(), f()}, {f(), f()}}}, // envelope
74 nullptr
75 };
76 }
77 fclose(fp);
78 return true;
79 }
80
freeBuf()81 void stPXSound::freeBuf()
82 {
83 uint32_t i;
84
85 // free up the buffers
86 for (i = 0; i < PXT_NO_CHANNELS; i++)
87 {
88 if (this->channels[i].buffer)
89 {
90 free(this->channels[i].buffer);
91 this->channels[i].buffer = nullptr;
92 }
93 }
94
95 if (this->final_buffer)
96 {
97 free(this->final_buffer);
98 this->final_buffer = nullptr;
99 }
100 }
101
allocBuf()102 int32_t stPXSound::allocBuf()
103 {
104 uint32_t topbufsize = 64;
105 uint32_t i;
106
107 freeBuf();
108
109 // allocate buffers for each enabled channel
110 for (i = 0; i < PXT_NO_CHANNELS; i++)
111 {
112 if (this->channels[i].enabled)
113 {
114 this->channels[i].buffer = (signed char *)malloc(this->channels[i].nsamples);
115 if (!this->channels[i].buffer)
116 {
117 LOG_ERROR("AllocBuffers (pxt): out of memory (channels)!");
118 return -1;
119 }
120
121 if (this->channels[i].nsamples > topbufsize)
122 topbufsize = this->channels[i].nsamples;
123 }
124 }
125
126 // allocate the final buffer
127 this->final_buffer = (signed char *)malloc(topbufsize);
128 if (!this->final_buffer)
129 {
130 LOG_ERROR("AllocBuffers (pxt): out of memory (finalbuffer)!");
131 return -1;
132 }
133
134 this->final_size = topbufsize;
135
136 return topbufsize;
137 }
138
render()139 bool stPXSound::render()
140 {
141 uint32_t i, s;
142 int16_t mixed_sample;
143 int16_t *middle_buffer;
144 int32_t bufsize;
145
146 bufsize = this->allocBuf();
147 if (bufsize == -1)
148 return 1; // error
149
150 // --------------------------------
151 // render all the channels
152 // --------------------------------
153 for (i = 0; i < PXT_NO_CHANNELS; i++)
154 {
155 if (this->channels[i].enabled)
156 {
157 this->channels[i].synth();
158 }
159 }
160
161 // ----------------------------------------------
162 // mix the channels [generate final_buffer]
163 // ----------------------------------------------
164 // lprintf("final_size = %d final_buffer = %08x\n", snd->final_size, snd->final_buffer);
165
166 middle_buffer = (int16_t *)malloc(this->final_size * 2);
167
168 memset(middle_buffer, 0, this->final_size * 2);
169
170 for (i = 0; i < PXT_NO_CHANNELS; i++)
171 {
172 if (this->channels[i].enabled)
173 {
174 for (s = 0; s < this->channels[i].nsamples; s++)
175 {
176 middle_buffer[s] += this->channels[i].buffer[s];
177 }
178 }
179 }
180
181 for (s = 0; s < this->final_size; s++)
182 {
183 mixed_sample = middle_buffer[s];
184
185 if (mixed_sample > 127)
186 mixed_sample = 127;
187 else if (mixed_sample < -127)
188 mixed_sample = -127;
189
190 this->final_buffer[s] = (char)mixed_sample;
191 }
192
193 free(middle_buffer);
194 return 0;
195 }
196
evaluate(int32_t i) const197 int32_t stPXEnvelope::evaluate(int32_t i) const // Linearly interpolate between the key points:
198 {
199 int32_t prevval = initial, prevtime = 0;
200 int32_t nextval = 0, nexttime = 256;
201 for (int32_t j = 2; j >= 0; --j)
202 if (i < p[j].time)
203 {
204 nexttime = p[j].time;
205 nextval = p[j].val;
206 }
207 for (int32_t j = 0; j <= 2; ++j)
208 if (i >= p[j].time)
209 {
210 prevtime = p[j].time;
211 prevval = p[j].val;
212 }
213 if (nexttime <= prevtime)
214 return prevval;
215 return (i - prevtime) * (nextval - prevval) / (nexttime - prevtime) + prevval;
216 }
217
synth()218 void stPXChannel::synth()
219 {
220 if (!enabled)
221 return;
222
223 auto &c = carrier, &f = frequency, &a = amplitude;
224 double mainpos = c.offset, maindelta = 256 * c.pitch / nsamples;
225 for (size_t i = 0; i < nsamples; ++i)
226 {
227 auto s = [=](double p = 1) { return 256 * p * i / nsamples; };
228 // Take sample from each of the three signal generators:
229 int freqval = f.wave[0xFF & int(f.offset + s(f.pitch))] * f.level;
230 int ampval = a.wave[0xFF & int(a.offset + s(a.pitch))] * a.level;
231 int mainval = c.wave[0xFF & int(mainpos)] * c.level;
232 // Apply amplitude & envelope to the main signal level:
233 buffer[i] = mainval * (ampval + 4096) / 4096 * envelope.evaluate(s()) / 4096;
234 // Apply frequency modulation to maindelta:
235 mainpos += maindelta * (1 + (freqval / (freqval < 0 ? 8192. : 2048.)));
236 }
237 }
238
Pixtone()239 Pixtone::Pixtone() {}
~Pixtone()240 Pixtone::~Pixtone() {}
241
getInstance()242 Pixtone *Pixtone::getInstance()
243 {
244 return Singleton<Pixtone>::get();
245 }
246
247 std::function<void(int chan)> sfxCallback;
248
mySfxCallback(int chan)249 void mySfxCallback(int chan)
250 {
251 sfxCallback(chan);
252 }
253
init()254 bool Pixtone::init()
255 {
256 if (_inited)
257 {
258 LOG_ERROR("pxt_init: pxt module already initialized");
259 return false;
260 }
261 else
262 _inited = true;
263
264 for (uint16_t i = 0; i < 256; i++)
265 _sound_fx[i].channel = -1;
266
267 for (uint32_t seed = 0, i = 0; i < 256; ++i)
268 {
269 seed = (seed * 214013) + 2531011; // Linear congruential generator
270 wave[MOD_SINE].table[i] = 0x40 * std::sin(i * 3.1416 / 0x80); // Sine
271 wave[MOD_TRI].table[i] = ((0x40 + i) & 0x80) ? 0x80 - i : i; // Triangle
272 wave[MOD_SAWUP].table[i] = -0x40 + i / 2; // Sawtooth up
273 wave[MOD_SAWDOWN].table[i] = 0x40 - i / 2; // Sawtooth down
274 wave[MOD_SQUARE].table[i] = 0x40 - (i & 0x80); // Square
275 wave[MOD_NOISE].table[i] = (signed char)(seed >> 16) / 2; // Pseudorandom
276 }
277
278 uint32_t slot;
279
280 LOG_INFO("Loading Sound FX...");
281
282 std::string path = ResourceManager::getInstance()->getPathForDir("pxt/");
283 for (slot = 1; slot <= NUM_SOUNDS; slot++)
284 {
285 std::ostringstream filename;
286 filename << path << "fx" << std::hex << std::setw(2) << std::setfill('0') << slot << ".pxt";
287 stPXSound snd;
288
289 if (!snd.load(filename.str()))
290 continue;
291 snd.render();
292
293 // upscale the sound to 16-bit for SDL_mixer then throw away the now unnecessary 8-bit data
294 _prepareToPlay(&snd, slot);
295 snd.freeBuf();
296 }
297
298 sfxCallback = std::bind(&Pixtone::pxtSoundDone, this, std::placeholders::_1);
299 Mix_ChannelFinished(mySfxCallback);
300
301 return true;
302 }
303
shutdown()304 void Pixtone::shutdown()
305 {
306 for (uint32_t i = 0; i <= NUM_SOUNDS; i++)
307 {
308 if (_sound_fx[i].chunk)
309 {
310 SDL_free(_sound_fx[i].chunk->abuf);
311 Mix_FreeChunk(_sound_fx[i].chunk);
312 _sound_fx[i].chunk = nullptr;
313 }
314 for (int i = 0; i < NUM_RESAMPLED_BUFFERS; i ++)
315 {
316 if (_sound_fx[i].resampled[i])
317 {
318 SDL_free(_sound_fx[i].resampled[i]->abuf);
319 Mix_FreeChunk(_sound_fx[i].resampled[i]);
320 _sound_fx[i].resampled[i] = nullptr;
321 }
322 }
323 }
324 }
325
play(int32_t chan,int32_t slot,int32_t loop)326 int Pixtone::play(int32_t chan, int32_t slot, int32_t loop)
327 {
328 if (_sound_fx[slot].chunk)
329 {
330 chan = Mix_PlayChannel(chan, _sound_fx[slot].chunk, loop);
331 _sound_fx[slot].channel = chan;
332 _slots[chan] = slot;
333
334 if (chan < 0)
335 {
336 LOG_ERROR("Pixtone::play: Mix_PlayChannel returned error");
337 }
338 return chan;
339 }
340 else
341 {
342 LOG_ERROR("Pixtone::play: sound slot {} not rendered", slot);
343 return -1;
344 }
345 }
346
playResampled(int32_t chan,int32_t slot,int32_t loop,uint32_t percent)347 int Pixtone::playResampled(int32_t chan, int32_t slot, int32_t loop, uint32_t percent)
348 {
349 if (_sound_fx[slot].chunk)
350 {
351 uint32_t resampled_rate = SAMPLE_RATE * (percent / 100);
352
353 int i;
354 int idx = -1;
355 int rslot = 0;
356
357 for (i = 0; i < NUM_RESAMPLED_BUFFERS; i++)
358 {
359 if (resampled_rate == _sound_fx[slot].resampled_rate[i])
360 {
361 idx = i; // found
362 }
363 if (_sound_fx[slot].resampled[i] == NULL)
364 {
365 if (rslot == 0)
366 rslot = i;
367 }
368 }
369
370 if (idx == -1)
371 {
372 SDL_AudioCVT cvt;
373
374 if (SDL_BuildAudioCVT(&cvt, AUDIO_S16, 2, SAMPLE_RATE, AUDIO_S16, 2, resampled_rate) == -1)
375 {
376 LOG_ERROR("SDL_BuildAudioCVT: {}", SDL_GetError());
377 }
378 cvt.len = _sound_fx[slot].chunk->alen;
379 cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult);
380 SDL_memcpy(cvt.buf, _sound_fx[slot].chunk->abuf, _sound_fx[slot].chunk->alen);
381
382 if (SDL_ConvertAudio(&cvt) == -1)
383 {
384 LOG_ERROR("SDL_ConvertAudio: {}", SDL_GetError());
385 }
386
387 /* if (_sound_fx[slot].resampled != NULL)
388 {
389 SDL_free(_sound_fx[slot].resampled->abuf);
390 SDL_free(_sound_fx[slot].resampled);
391 _sound_fx[slot].resampled = NULL;
392 }*/
393
394 Uint8 *sound_buf = (Uint8 *)SDL_malloc(cvt.len_cvt);
395 SDL_memcpy(sound_buf, (Uint8 *)cvt.buf, cvt.len_cvt);
396 SDL_free(cvt.buf);
397
398 _sound_fx[slot].resampled[rslot] = Mix_QuickLoad_RAW(sound_buf, cvt.len_cvt);
399 _sound_fx[slot].resampled_rate[rslot] = resampled_rate;
400 idx = rslot;
401 }
402
403 chan = Mix_PlayChannel(chan, _sound_fx[slot].resampled[idx], loop);
404 _sound_fx[slot].channel = chan;
405 _slots[chan] = slot;
406
407 if (chan < 0)
408 {
409 LOG_ERROR("Pixtone::playResampled: Mix_PlayChannel returned error");
410 }
411 return chan;
412 }
413 else
414 {
415 LOG_ERROR("Pixtone::playResampled: sound slot {} not rendered", slot);
416 return -1;
417 }
418 }
419
prepareResampled(int32_t slot,uint32_t percent)420 int Pixtone::prepareResampled(int32_t slot, uint32_t percent)
421 {
422 if (_sound_fx[slot].chunk)
423 {
424 uint32_t resampled_rate = SAMPLE_RATE * (percent / 100);
425
426 int i;
427 int idx = -1;
428 int rslot = 0;
429
430 for (i = 0; i < NUM_RESAMPLED_BUFFERS; i++)
431 {
432 if (resampled_rate == _sound_fx[slot].resampled_rate[i])
433 {
434 idx = i; // found
435 }
436 if (_sound_fx[slot].resampled[i] == NULL)
437 {
438 if (rslot == 0)
439 rslot = i;
440 }
441 }
442
443 if (idx == -1) // not found
444 {
445 SDL_AudioCVT cvt;
446
447 if (SDL_BuildAudioCVT(&cvt, AUDIO_S16, 2, SAMPLE_RATE, AUDIO_S16, 2, resampled_rate) == -1)
448 {
449 LOG_ERROR("SDL_BuildAudioCVT: {}", SDL_GetError());
450 }
451 cvt.len = _sound_fx[slot].chunk->alen;
452 cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult);
453 SDL_memcpy(cvt.buf, _sound_fx[slot].chunk->abuf, _sound_fx[slot].chunk->alen);
454
455 if (SDL_ConvertAudio(&cvt) == -1)
456 {
457 LOG_ERROR("SDL_ConvertAudio: {}", SDL_GetError());
458 }
459
460 /*
461 if (_sound_fx[slot].resampled != NULL)
462 {
463 SDL_free(_sound_fx[slot].resampled->abuf);
464 SDL_free(_sound_fx[slot].resampled);
465 _sound_fx[slot].resampled = NULL;
466 }
467 */
468
469 Uint8 *sound_buf = (Uint8 *)SDL_malloc(cvt.len_cvt);
470 SDL_memcpy(sound_buf, (Uint8 *)cvt.buf, cvt.len_cvt);
471 SDL_free(cvt.buf);
472
473 _sound_fx[slot].resampled[rslot] = Mix_QuickLoad_RAW(sound_buf, cvt.len_cvt);
474 _sound_fx[slot].resampled_rate[rslot] = resampled_rate;
475 }
476 }
477 else
478 {
479 LOG_ERROR("Pixtone::prepareResampled: sound slot {} not rendered", slot);
480 return -1;
481 }
482 return 0;
483 }
484
stop(int32_t slot)485 void Pixtone::stop(int32_t slot)
486 {
487 if (_sound_fx[slot].channel != -1)
488 {
489 Mix_HaltChannel(_sound_fx[slot].channel);
490 if (_sound_fx[slot].channel != -1)
491 {
492 _slots[_sound_fx[slot].channel] = -1;
493 }
494 }
495 }
496
pxtSoundDone(int channel)497 void Pixtone::pxtSoundDone(int channel)
498 {
499 if (_slots[channel] != -1)
500 {
501 _sound_fx[_slots[channel]].channel = -1;
502 }
503 }
504
_prepareToPlay(stPXSound * snd,int32_t slot)505 void Pixtone::_prepareToPlay(stPXSound *snd, int32_t slot)
506 {
507 // convert the buffer from 8-bit mono signed to 16-bit stereo signed
508 SDL_AudioCVT cvt;
509
510 if (SDL_BuildAudioCVT(&cvt, AUDIO_S8, 1, 22050, AUDIO_S16, 2, SAMPLE_RATE) == -1)
511 {
512 LOG_ERROR("SDL_BuildAudioCVT: {}", SDL_GetError());
513 }
514
515 cvt.len = snd->final_size;
516
517 cvt.buf = (Uint8 *)SDL_malloc(cvt.len * cvt.len_mult);
518 memcpy(cvt.buf, snd->final_buffer, snd->final_size);
519
520 if (SDL_ConvertAudio(&cvt) == -1)
521 {
522 LOG_ERROR("SDL_ConvertAudio: {}", SDL_GetError());
523 }
524
525 Uint8 *sound_buf = (Uint8 *)SDL_malloc(cvt.len_cvt);
526 SDL_memcpy(sound_buf, (Uint8 *)cvt.buf, cvt.len_cvt);
527 SDL_free(cvt.buf);
528
529 _sound_fx[slot].chunk = Mix_QuickLoad_RAW(sound_buf, cvt.len_cvt);
530 }
531
532 } // namespace Sound
533 } // namespace NXE