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