NXEngine v1.0.0.4
[NXEngine.git] / sound / pxt.cpp
blobda78cb85bb17fc52f3ff49762f51811cf63a408f
2 // PXT sound file player
3 // see bottom of file for info on how to use this module
5 #include <stdio.h>
6 #include <math.h> // for sin()
7 #include <stdlib.h>
8 #include <string.h>
9 #include <endian.h>
11 #include "../config.h"
12 #include "pxt.h"
13 #include "sslib.h"
15 #include "pxt.fdh"
17 #define MODEL_SIZE 256
18 #define PXCACHE_MAGICK 'PXC1'
20 // gets the next byte from wave "wave", scales it by the waves volume, and places result in "out".
21 // x * (y / z) = (x * y) / z
22 #define GETWAVEBYTE(wave, out) \
23 { \
24 if (wave->model_no != MOD_WHITE) \
25 { \
26 out = wave->model[(unsigned char)wave->phaseacc]; \
27 } \
28 else \
29 { \
30 out = white[wave->white_ptr]; \
31 if (++wave->white_ptr >= WHITE_LEN) wave->white_ptr = 0; \
32 } \
33 out *= wave->volume; \
34 out /= 64; \
38 #define WHITE_LEN 22050
39 int8_t white[WHITE_LEN];
41 // the final sounds ready to play (after pxt_PrepareToPlay)
42 static struct
44 int16_t *buffer;
45 int len;
46 int loops_left;
47 void (*DoneCallback)(int, int);
48 int channel;
49 } sound_fx[256];
50 int load_top;
53 static struct
55 uint8_t table[256];
56 } wave[PXT_NO_MODELS];
59 static unsigned int rng_seed = 0;
60 static unsigned short rand_next(void)
62 rng_seed *= 0x343fd;
63 rng_seed += 0x269ec3;
65 return (rng_seed >> 16) & 0x7fff;
68 static void GenerateSineModel(unsigned char *table)
70 double twopi = 6.283184000f;
71 double ratio = 256.00f;
72 double rat64 = 64.00f;
73 double reg;
74 int i;
76 for(i=0;i<256;i++)
78 reg = (double)i;
79 reg *= twopi;
80 reg /= ratio;
81 reg = sin(reg);
82 reg *= rat64;
84 table[i] = (unsigned char)reg;
89 static void GenerateTriangleModel(unsigned char *table)
91 int i, f;
93 for(i=0;i<64;i++) table[i] = i;
95 f = 0;
96 for(;i<192;i++)
98 table[i] = 0x40 - f;
99 f++;
102 f = 0;
103 for(;i<256;i++)
105 table[i] = f - 0x40;
106 f++;
111 static void GenerateSawUpModel(unsigned char *table)
113 int i;
115 for(i=0;i<256;i++)
116 table[i] = (i >> 1) - 0x40;
120 static void GenerateSawDownModel(unsigned char *table)
122 int i;
124 for(i=0;i<256;i++)
125 table[i] = 0x40 - (i >> 1);
129 static void GenerateSquareModel(unsigned char *table)
131 int i;
133 for(i=0;i<128;i++) table[i] = 0x40;
134 for(;i<256;i++) table[i] = (uint8_t)-0x40;
138 static void GenerateRandModel(unsigned char *table)
140 int i;
141 signed char k;
143 rng_seed = 0;
145 for(i=0;i<256;i++)
147 k = (signed char)rand_next();
149 if (k < 0) k++;
150 table[i] = k >> 1;
154 void GenerateWhiteModel(void)
156 int i;
158 seedrand(0xa42c1911);
160 for(i=0;i<WHITE_LEN;i++)
161 white[i] = random(-63, 63);
164 static void GeneratePulseModel(unsigned char *table)
166 int i;
168 for(i=0;i<192;i++) table[i] = 0x40;
169 for(;i<256;i++) table[i] = (uint8_t)-0x40;
173 // generate the models so we can do synth
174 // must call this before doing any rendering
175 char pxt_init(void)
177 static int inited = 0;
178 int i;
180 if (inited)
182 staterr("pxt_init: pxt module already initialized");
183 return 0;
185 else inited = 1;
187 memset(sound_fx, 0, sizeof(sound_fx));
188 for(i=0;i<256;i++) sound_fx[i].channel = -1;
190 return 0;
193 char pxt_initsynth(void)
195 static int synth_inited = 0;
196 if (synth_inited) return 0; else synth_inited = 1;
198 GenerateSineModel(wave[MOD_SINE].table);
199 GenerateTriangleModel(wave[MOD_TRI].table);
200 GenerateSawUpModel(wave[MOD_SAWUP].table);
201 GenerateSawDownModel(wave[MOD_SAWDOWN].table);
202 GenerateSquareModel(wave[MOD_SQUARE].table);
203 GenerateRandModel(wave[MOD_NOISE].table);
204 GeneratePulseModel(wave[MOD_PULSE].table);
205 GenerateWhiteModel();
206 return 0;
209 char pxt_SetModel(stPXWave *pxwave, int m)
211 if (m >= 0 && m < PXT_NO_MODELS)
213 pxwave->model = (signed char *)wave[m].table;
214 pxwave->model_no = m;
215 return 0;
217 else
219 staterr("pxt_SetModel: invalid sound model '%d'", m);
220 return 1;
225 /*void cout(int val)
227 static int c = 0;
228 char buf[8];
229 int i;
230 sprintf(buf, "%d", val);
231 for(i=0;i<4-strlen(buf);i++) lprintf(" ");
232 lprintf("%s ", buf);
234 if (++c > 16) {c=0; lprintf("\n");}
237 void couthx(int val)
239 static int c = 0;
240 char buf[80];
241 int i;
243 sprintf(buf, "%02x", val);
244 i = strlen(buf) - 2;
245 lprintf("%s ", &buf[i]);
247 if (++c > 24) { c=0; lprintf("\n"); }
252 /*static void display_audio(signed char *buffer, int size_blocks, int centerline, int ysize, char *caption, char is_env, int r, int g, int b)
254 double ratio, yratio;
255 double curpos;
256 int x, y;
257 int lastx, lasty;
258 signed char value;
259 int wd = 315;
260 int xoff = 2;
261 char buf[80];
263 #define scale_sample(value) (centerline + (int)((double)value * yratio))
265 //lprintf("display_audio: displaying buffer of len %d\n", size_blocks);
267 ratio = (double)size_blocks / (double)wd;
268 yratio = (double)ysize / (double)(127+127);
269 //lprintf("ratio = %.2f yratio = %.2f\n", ratio, yratio);
271 DrawSDLLine(SCREEN_WIDTH/2, 0, SCREEN_WIDTH/2, SCREEN_HEIGHT, 18,18,18);
272 DrawSDLLine(xoff, centerline, xoff+wd, centerline, 255,0,0);
273 y = scale_sample(TOPAMP); DrawSDLLine(xoff, y, xoff+wd, y, 68,68,68);
274 y = scale_sample(BTMAMP); DrawSDLLine(xoff, y, xoff+wd, y, 68,68,68);
276 curpos = 0;
277 lastx = -1;
278 for(x=xoff;x<wd+xoff;x++)
280 value = buffer[(int)curpos];
281 curpos += ratio;
283 // envelope is 0-63, so scale it to 0-127
284 if (is_env) value = -value * 2;
286 y = scale_sample(value);
288 if (lastx==-1 || abs(lasty - y) < 2)
290 PlotSDLPixel(x, y, r,g,b);
292 else
294 DrawSDLLine(lastx, lasty, x, y, r,g,b);
297 lastx = x; lasty = y;
300 if (caption[0])
302 sprintf(buf, " %s len = %d", caption, size_blocks);
303 font_draw_shaded(4, ((centerline+(ysize/2))-16), buf, 0, &greenfont);
306 flip();
309 void debugshowsound(stPXSound *snd)
311 uchar r[4] = { 250, 49, 250, 0 };
312 uchar g[4] = { 250, 179, 127, 200 };
313 uchar b[4] = { 0, 49, 127, 180 };
314 int c;
315 char *capt1 = "", *capt2 = "";
317 for(c=3;c>=0;c--)
319 if (snd->chan[c].enabled)
321 display_audio(snd->chan[c].buffer, snd->chan[c].size_blocks, 60, 80, capt1, 0, r[c], g[c], b[c]);
322 display_audio(snd->chan[c].envbuffer, 256, 170, 80, capt2, 1, r[c], g[c], b[c]);
325 if (c==1)
327 capt1 = "Output";
328 capt2 = "Envelope";
332 flip();
336 // sets the given envelope to default values
337 void pxt_SetDefaultEnvelope(stPXEnvelope *env)
339 env->initial = 63;
340 env->time[0] = 64;
341 env->time[1] = 128;
342 env->time[2] = 255;
343 env->val[0] = 63;
344 env->val[1] = 63;
345 env->val[2] = 63;
349 // generate a 256-byte envelope "waveform" containing volume adjustment values from 00-3f
350 // in short it renders the envelope for a sound.
351 // the envelope must be ready before CreateAudio can be used.
352 void GenerateEnvelope(stPXEnvelope *env, char *buffer)
354 double curenv, envinc;
355 int i;
357 curenv = env->initial;
358 envinc = (double)(env->val[0] - env->initial) / env->time[0];
359 for(i=0;i<env->time[0];i++)
361 buffer[i] = (int)curenv;
362 curenv += envinc;
365 curenv = env->val[0];
366 envinc = (double)(env->val[1] - env->val[0]) / (env->time[1] - env->time[0]);
367 for(;i<env->time[1];i++)
369 buffer[i] = (int)curenv;
370 curenv += envinc;
373 curenv = env->val[1];
374 envinc = (double)(env->val[2] - env->val[1]) / (env->time[2] - env->time[1]);
375 for(;i<env->time[2];i++)
377 buffer[i] = (int)curenv;
378 curenv += envinc;
381 // fade to 0 volume if time_c is < end of sound, just like the pretty drawing in PixTone.
382 envinc = (double)(-1 - env->val[2]) / (256 - env->time[2]);
383 curenv = env->val[2];
384 for(;i<256;i++)
386 buffer[i] = (int)curenv;
387 curenv += envinc;
393 // added this for sound editing tools. it will render you a single PXWave,
394 // that is a component of a PXSound, into a buffer you provide.
395 // otherwise it's useless.
396 void pxt_RenderPXWave(stPXWave *pxwave, signed char *buffer, int size_blocks)
398 int i, j, e;
399 int output;
401 // we generate twice the buf size and average it down afterwards for increased precision.
402 // this is what pixtone does, and although I'm not sure if that's why; it really does
403 // seem to make a slight quality increase
404 size_blocks *= 2;
405 char *tempbuffer = (char *)malloc(size_blocks);
407 //lprintf("RenderPXWave: buffer len %d, repeat = %.2f\n", size_blocks, pxwave->repeat);
409 pxwave->phaseinc = ((MODEL_SIZE * pxwave->repeat) / (double)size_blocks);
410 pxwave->phaseacc = (double)pxwave->offset;
411 pxwave->white_ptr = pxwave->offset;
413 for(i=0;i<size_blocks;i++)
415 GETWAVEBYTE(pxwave, output);
417 tempbuffer[i] = output;
418 pxwave->phaseacc += pxwave->phaseinc;
421 // average our doublesampled audio down into the final buffer
422 for(i=j=0;i<size_blocks;i+=2)
424 e = tempbuffer[i] + tempbuffer[i+1];
425 e >>= 1;
426 buffer[j++] = e;
431 // renders a sound channel.
432 // call GenerateEnvelope first.
433 static void CreateAudio(stPXChannel *chan)
435 // store all the commonly-used pointers
436 stPXWave *main = &chan->main;
437 stPXWave *pitch = &chan->pitch;
438 stPXWave *pitch2 = &chan->pitch2;
439 stPXWave *volume = &chan->volume;
440 unsigned char *envbuffer = chan->envbuffer;
441 int size_blocks = chan->size_blocks;
442 // var defs
443 int i, j;
444 int output, bm, bm2, volmod;
445 double phaseval;
446 int e;
447 double env_acc, env_inc;
449 // we generate twice the buf size and average it down afterwards for increased precision.
450 // this is what pixtone does, and although I'm not sure if that's why; it really does
451 // seem to make a slight quality increase
452 size_blocks *= 2;
453 signed char *buffer = (signed char *)malloc(size_blocks);
455 //lprintf("CreateAudio: buffer len %d, repeat = %.2f / %.2f\n", size_blocks, main->repeat, pitch->repeat);
457 // calculate all the phaseinc's
458 main->phaseinc = ((MODEL_SIZE * main->repeat) / (double)size_blocks);
459 pitch->phaseinc = ((MODEL_SIZE * pitch->repeat) / (double)size_blocks);
460 pitch2->phaseinc = ((MODEL_SIZE * pitch2->repeat) / (double)size_blocks);
461 volume->phaseinc = ((MODEL_SIZE * volume->repeat) / (double)size_blocks);
462 env_inc = (MODEL_SIZE / (double)size_blocks);
464 //lprintf("main phaseinc = %.6f\n", main->phaseinc);
465 //lprintf("pitch phaseinc = %.6f\n", pitch->phaseinc);
466 //lprintf("volume phaseinc = %.6f\n", volume->phaseinc);
468 // set the starting positions
469 main->phaseacc = (double)main->offset;
470 pitch->phaseacc = (double)pitch->offset;
471 pitch2->phaseacc = (double)pitch2->offset;
472 volume->phaseacc = (double)volume->offset;
474 main->white_ptr = main->offset;
475 pitch->white_ptr = pitch->offset;
476 pitch2->white_ptr = pitch2->offset;
477 volume->white_ptr = volume->offset;
479 env_acc = 0;
481 for(i=0;i<size_blocks;i++)
483 // get next sample from the main waveform
484 GETWAVEBYTE(main, output);
485 GETWAVEBYTE(volume, volmod);
487 // hack to allow inverting sign of MOD_PULSE-based volume modulators
488 // by just raising the top over 128
489 if (volume->model_no==MOD_PULSE)
491 if (volmod > 127 || volmod < -127)
493 if (volmod > 127)
495 volmod = 256 - volmod;
497 else
499 volmod = -(256 - -volmod);
501 volmod = -volmod;
505 // offset volume modulator such that it completely silences the sound at <= -0x40
506 volmod += 64;
507 if (volmod < 0) volmod = 0;
509 // apply volume modulator to main
510 output = (output * volmod) / 64;
512 // apply the envelope
513 e = envbuffer[(unsigned char)env_acc];
514 output = (output * e) / 64;
516 // save sample to output buffer
517 buffer[i] = output;
520 // get next byte from the frequency modulator waveform
521 GETWAVEBYTE(pitch, bm);
522 GETWAVEBYTE(pitch2, bm2);
524 // clip the values to a signed char if it's a pulse waveform...allows
525 // switch it's signed from up/up/down to down/down/up just by taking
526 // the top over 128. but for other waveforms we actually want the clipping
527 // to not happen right in this case because A. it allows for making larger
528 // range frequency sweeps than otherwise possible and B. pixtone has this
529 // same "glitch" so it's a compatibility thing.
530 if (pitch->model_no==MOD_PULSE) bm = (signed char)bm;
531 if (pitch2->model_no==MOD_PULSE) bm2 = (signed char)bm2;
533 bm += bm2;
536 if (bm >= 0)
537 { // when positive, every 32 clicks doubles the phaseinc.
538 // this is actually "phaseval = (mod / 32) * main->phaseinc;" however
539 // we lose precision by doing the division first, so this is equivalent:
540 phaseval = ((double)bm * main->phaseinc) / 32;
542 // phaseinc is sped up by phaseval
543 main->phaseacc += (main->phaseinc + phaseval);
545 else
546 { // when negative, every 64 clicks half's the phaseinc.
547 // this can be thought of as "phaseval = (mod / 64) * (main->phaseinc * 0.5f);" however
548 // that doesn't actually work because of precision loss, so this instead:
549 phaseval = ((double)(-bm) * main->phaseinc) / 128;
551 // phaseinc is slowed down by phaseval
552 main->phaseacc += (main->phaseinc - phaseval);
556 pitch->phaseacc += pitch->phaseinc;
557 pitch2->phaseacc += pitch2->phaseinc;
558 volume->phaseacc += volume->phaseinc;
560 env_acc += env_inc;
561 // just to make certain the envelope never starts over from the beginning.
562 // although it shouldn't; the precision on env_inc seems to be pretty good.
563 if (env_acc > 255) env_acc = 255;
565 // normally we would have to do this, but we don't as long as model_size is 256
566 // and we cast phaseacc to an unsigned char when we do the lookup; it'll wrap by itself.
567 // neat huh.
568 //while(main->phaseacc >= MODEL_SIZE) main->phaseacc -= MODEL_SIZE;
569 //while(pitch->phaseacc >= MODEL_SIZE) pitch->phaseacc -= MODEL_SIZE;
572 // average our doublesampled audio down into the final buffer
573 signed char *outbuffer = chan->buffer;
574 for(i=j=0;i<size_blocks;i+=2)
576 e = buffer[i] + buffer[i+1];
577 e >>= 1;
578 outbuffer[j++] = e;
583 // allocate all the buffers needed for the given sound
584 static char AllocBuffers(stPXSound *snd)
586 int topbufsize = 64;
587 int i;
589 FreePXTBuf(snd);
591 // allocate buffers for each enabled channel
592 for(i=0;i<PXT_NO_CHANNELS;i++)
594 if (snd->chan[i].enabled)
596 snd->chan[i].buffer = (signed char *)malloc(snd->chan[i].size_blocks);
597 if (!snd->chan[i].buffer)
599 staterr("AllocBuffers (pxt): out of memory (1)!");
600 return -1;
603 if (snd->chan[i].size_blocks > topbufsize)
604 topbufsize = snd->chan[i].size_blocks;
608 // allocate the final buffer
609 snd->final_buffer = (signed char *)malloc(topbufsize);
610 if (!snd->final_buffer)
612 staterr("AllocBuffers (pxt): out of memory (2)!");
613 return -1;
616 snd->final_size = topbufsize;
618 return topbufsize;
622 // generate 8-bit signed PCM audio from a PXT sound, put it in it's final_buffer.
623 char pxt_Render(stPXSound *snd)
625 int i, s;
626 signed short mixed_sample;
627 signed short *middle_buffer;
628 int bufsize;
630 bufsize = AllocBuffers(snd);
631 if (bufsize==-1) return 1; // error
633 // --------------------------------
634 // render all the channels
635 // --------------------------------
636 for(i=0;i<PXT_NO_CHANNELS;i++)
638 if (snd->chan[i].enabled)
640 // memset(snd->chan[i].buffer, 0, sizeof(snd->chan[i].buffer));
641 GenerateEnvelope(&snd->chan[i].envelope, (char *)snd->chan[i].envbuffer);
642 CreateAudio(&snd->chan[i]);
646 // ----------------------------------------------
647 // mix the channels [generate final_buffer]
648 // ----------------------------------------------
649 //lprintf("final_size = %d final_buffer = %08x\n", snd->final_size, snd->final_buffer);
651 middle_buffer = (signed short *)malloc(snd->final_size * 2);
652 memset(middle_buffer, 0, snd->final_size * 2);
654 for(i=0;i<PXT_NO_CHANNELS;i++)
656 if (snd->chan[i].enabled)
658 for(s=0;s<snd->chan[i].size_blocks;s++)
660 middle_buffer[s] += snd->chan[i].buffer[s];
665 for(s=0;s<snd->final_size;s++)
667 mixed_sample = middle_buffer[s];
669 if (mixed_sample > 127) mixed_sample = 127;
670 else if (mixed_sample < -127) mixed_sample = -127;
672 snd->final_buffer[s] = (char)mixed_sample;
675 free(middle_buffer);
676 return 0;
680 // get an already-rendered pxt 'snd' ready for sending to SDL_mixer.
681 // converts the 8-bit signed audio to SDL_mixer's 16-bit stereo format and
682 // sets up the Mix_Chunk.
683 void pxt_PrepareToPlay(stPXSound *snd, int slot)
685 int value;
686 int ap;
687 int i;
688 signed char *buffer = snd->final_buffer;
689 signed short *outbuffer;
690 int malc_size;
692 // convert the buffer from 8-bit mono signed to 16-bit stereo signed
693 malc_size = (snd->final_size * 2 * 2);
694 outbuffer = (signed short *)malloc(malc_size);
696 for(i=ap=0;i<snd->final_size;i++)
698 value = buffer[i];
699 value *= 200;
700 value = htole16(value);
702 outbuffer[ap++] = value; // left ch
703 outbuffer[ap++] = value; // right ch
707 sound_fx[slot].buffer = outbuffer;
708 sound_fx[slot].len = snd->final_size;
709 //lprintf("pxt ready to play in slot %d\n", slot);
712 // quick-and-dirty function to raise or lower the pitch of a sound.
713 // I say quick-and-dirty because it also changes the length.
714 // We need this for the "SSS" (Stream Sound) which is supposed to
715 // have adjustable pitch.
716 void pxt_ChangePitch(stPXSound *snd, double factor)
718 signed char *inbuffer = snd->final_buffer;
719 int insize = snd->final_size;
721 int outsize = (int)((double)insize * factor);
722 signed char *outbuffer = (signed char *)malloc(outsize);
723 if (factor == 0) factor = 0.001;
725 for(int i=0;i<outsize;i++)
726 outbuffer[i] = inbuffer[(int)((double)i / factor)];
728 free(snd->final_buffer);
729 snd->final_buffer = outbuffer;
730 snd->final_size = outsize;
734 // begins playing the pxt in the given slot.
735 // the SSChannel is returned.
736 // on error, returns -1.
737 int pxt_Play(int chan, int slot, char loop)
739 return pxt_PlayWithCallback(chan, slot, loop, NULL);
742 int pxt_PlayWithCallback(int chan, int slot, char loop, void (*FinishedCB)(int, int))
744 if (sound_fx[slot].buffer)
746 // locking the audio here ensures that sound won't finish before we get down
747 // below and finish setting it's params.
748 SSLockAudio();
749 if (loop)
751 chan = SSPlayChunk(chan, sound_fx[slot].buffer, sound_fx[slot].len, slot, pxtLooper);
752 SSEnqueueChunk(chan, sound_fx[slot].buffer, sound_fx[slot].len, slot, pxtLooper);
754 sound_fx[slot].loops_left = (loop==-1) ? -1 : (loop - 1);
756 else
758 chan = SSPlayChunk(chan, sound_fx[slot].buffer, sound_fx[slot].len, slot, pxtSoundDone);
761 sound_fx[slot].DoneCallback = FinishedCB;
762 sound_fx[slot].channel = chan;
763 SSUnlockAudio();
765 if (chan < 0)
767 staterr("pxt_Play: SSPlayChunk returned error");
769 return chan;
771 else
773 staterr("pxt_Play: sound slot 0x%02x not rendered", slot);
774 return -1;
778 static void pxtSoundDone(int chan, int slot)
780 sound_fx[slot].channel = -1;
781 if (sound_fx[slot].DoneCallback)
783 (*sound_fx[slot].DoneCallback)(chan, slot);
787 static void pxtLooper(int chan, int slot)
789 if (sound_fx[slot].loops_left)
791 SSEnqueueChunk(chan, sound_fx[slot].buffer, sound_fx[slot].len, slot, pxtLooper);
793 else
795 pxtSoundDone(chan, slot);
798 if (sound_fx[slot].loops_left > 0) sound_fx[slot].loops_left--;
801 void pxt_Stop(int slot)
802 { /// possible threading issues here? i'm not sure if it's important enough
803 /// i don't want to lock the audio because i'm worried that when the sound is aborted
804 /// it could end up being left locked during the user's sound done callback.
805 if (sound_fx[slot].channel != -1)
807 sound_fx[slot].loops_left = 0;
808 SSAbortChannel(sound_fx[slot].channel);
812 char pxt_IsPlaying(int slot)
814 return (sound_fx[slot].channel != -1);
818 // render all pxt files under "path" up to slot "top".
819 // get them all ready to play in their sound slots.
820 // if cache_name is specified the pcm audio data is cached under the given filename.
821 char pxt_LoadSoundFX(const char *path, const char *cache_name, int top)
823 char fname[80];
824 int slot;
825 stPXSound snd;
826 FILE *fp = NULL;
828 stat("Loading Sound FX...");
829 load_top = top;
831 if (cache_name)
833 // try to load the cache if we can
834 if (LoadFXCache(cache_name, top) == 0)
836 return 0;
839 fp = fileopen(cache_name, "wb");
840 if (!fp)
842 staterr("LoadSoundFX: failed open: '%s'", cache_name);
843 return 1;
846 uint32_t magick = PXCACHE_MAGICK;
847 fwrite(&magick, 4, 1, fp); // fwrite allows us to verify endianness, as well
848 fputi(top, fp);
851 // get ready to do synthesis
852 pxt_initsynth();
854 for(slot=1;slot<=top;slot++)
856 sprintf(fname, "%sfx%02x.pxt", path, slot);
858 if (pxt_load(fname, &snd)) continue;
859 pxt_Render(&snd);
861 // dirty hack; lower the pitch of the Stream Sounds
862 // to match the way they actually sound in the game
863 // with the SSS0400 command.
864 if (slot == 40)
865 pxt_ChangePitch(&snd, 5.0f);
866 if (slot == 41)
867 pxt_ChangePitch(&snd, 6.0f);
869 // save the rendered audio to cache
870 if (fp)
872 fputl(snd.final_size, fp);
873 fputc(slot, fp);
874 fwrite(snd.final_buffer, snd.final_size, 1, fp);
877 // upscale the sound to 16-bit for SDL_mixer then throw away the now unnecessary 8-bit data
878 pxt_PrepareToPlay(&snd, slot);
879 FreePXTBuf(&snd);
882 if (fp)
884 stat(" - created %s; %d bytes", cache_name, ftell(fp));
885 fclose(fp);
888 return 0;
892 // attempts to load all the PXT's out of the given cache file.
893 // if succesful, returns 0.
894 static char LoadFXCache(const char *fname, int top)
896 FILE *fp;
897 int slot;
898 uint32_t magick;
899 stPXSound snd;
901 fp = fileopen(fname, "rb");
902 if (!fp)
904 stat("LoadFXCache: audio cache %s not exist", fname);
905 return 1;
908 // I don't use endian-agnostic fgetl as this file is endian-specific and we
909 // want the check to fail if the file were moved from a little-endian to
910 // big-endian system or vice-versa.
911 fread(&magick, sizeof(magick), 1, fp);
912 if (magick != PXCACHE_MAGICK)
914 stat("LoadFXCache: %s is incorrect format: expected %08x, got %08x", fname, PXCACHE_MAGICK, magick);
915 fclose(fp);
916 return 1;
919 if (fgeti(fp) != top)
921 stat("LoadFXCache: # of sounds has changed since cache creation");
922 fclose(fp);
923 return 1;
926 int allocd_size = 0;
927 snd.final_buffer = NULL;
929 stat("LoadFXCache: restoring pxts from cache");
930 for(;;)
932 snd.final_size = fgetl(fp);
933 slot = fgetc(fp);
934 if (slot == -1) break;
936 if (snd.final_size > allocd_size)
938 allocd_size = (snd.final_size * 10);
940 if (snd.final_buffer) free(snd.final_buffer);
941 snd.final_buffer = (signed char *)malloc(allocd_size);
942 if (!snd.final_buffer)
944 staterr("LoadFXCache: out of memory!");
945 return 1;
949 fread(snd.final_buffer, snd.final_size, 1, fp);
950 pxt_PrepareToPlay(&snd, slot);
953 load_top = slot;
954 free(snd.final_buffer);
955 return 0;
958 void pxt_freeSoundFX(void)
960 int i;
961 for(i=0;i<=load_top;i++)
963 if (sound_fx[i].buffer)
965 free(sound_fx[i].buffer);
966 sound_fx[i].buffer = NULL;
971 void pxt_FreeSound(int slot)
973 if (sound_fx[slot].buffer)
975 free(sound_fx[slot].buffer);
976 sound_fx[slot].buffer = NULL;
981 char pxt_init(void)
983 int start;
985 //lprintf("Loading sound FX...\n");
986 start = SDL_GetTicks();
987 if (LoadSoundFX("pxt/", "fx.pcm", 0xa0)) return 1;
988 lprintf("time = %d\n", SDL_GetTicks() - start);
990 pxt_Play(0x1e);
992 snd = pxt_load("pxt/fx11.pxt");
993 if (snd)
995 render_pxt(snd);
997 pxt_PrepareToPlay(snd, 4);
998 pxt_Play(4);
1000 debugshowsound(snd);
1003 return 0;
1007 // free all the internal buffers of a PXSound
1008 void FreePXTBuf(stPXSound *snd)
1010 if (snd)
1012 int i;
1014 // free up the buffers
1015 for(i=0;i<PXT_NO_CHANNELS;i++)
1017 if (snd->chan[i].buffer)
1019 free(snd->chan[i].buffer);
1020 snd->chan[i].buffer = NULL;
1024 if (snd->final_buffer)
1026 free(snd->final_buffer);
1027 snd->final_buffer = NULL;
1034 // read a .pxt file into memory and return a stPXSound ready to be rendered.
1035 char pxt_load(const char *fname, stPXSound *snd)
1037 FILE *fp;
1038 char load_extended_section = 0;
1039 char ch;
1040 int i, cc;
1042 #define BRACK '{' // my damn IDE is borking up the Function List if i put this inline
1044 fp = fileopen(fname, "rb");
1045 if (!fp) { staterr("pxt_load: file '%s' not found.", fname); return 1; }
1047 //lprintf("pxt_load: reading %s...\n", fname);
1049 // set all buffers to non-existant and zero-length to start
1050 memset(snd, 0, sizeof(stPXSound));
1053 // skip ahead in the file till we find the machine readable data, denoted by '{'
1054 if (ReadToBracket(fp)) return 1;
1056 // load all channels we find
1057 cc = 0;
1058 ch = BRACK;
1059 while(!feof(fp))
1061 if (ch=='>') // extended section
1063 load_extended_section = 1;
1064 break;
1066 else if (ch==BRACK)
1067 { // opening a new channel
1068 if (cc >= PXT_NO_CHANNELS)
1070 staterr("pxt_load: sound '%s' contains too many channels!", fname);
1071 goto error;
1074 snd->chan[cc].enabled = fgeticsv(fp);
1075 snd->chan[cc].size_blocks = fgeticsv(fp);
1077 if (LoadComponent(fp, &snd->chan[cc].main)) goto error;
1078 if (LoadComponent(fp, &snd->chan[cc].pitch)) goto error;
1079 if (LoadComponent(fp, &snd->chan[cc].volume)) goto error;
1081 snd->chan[cc].envelope.initial = fgeticsv(fp);
1082 for(i=0;i<PXENV_NUM_VERTICES;i++)
1084 snd->chan[cc].envelope.time[i] = fgeticsv(fp);
1085 snd->chan[cc].envelope.val[i] = fgeticsv(fp);
1088 cc++;
1091 ch = fgetc(fp);
1094 if (load_extended_section)
1096 stat("pxt_load: extended section found, loading it");
1097 if (ReadToBracket(fp)) return 1;
1098 for(cc=0;cc<PXT_NO_CHANNELS;cc++)
1100 if (LoadComponent(fp, &snd->chan[cc].pitch2)) goto error;
1103 else
1105 //stat("No extended section found; setting compatibility values.");
1106 for(i=0;i<PXT_NO_CHANNELS;i++)
1108 memset(&snd->chan[i].pitch2, 0, sizeof(stPXWave));
1109 pxt_SetModel(&snd->chan[i].pitch2, 0);
1113 //stat("pxt_load: '%s' parsed ok", fname);
1114 fclose(fp);
1115 return 0;
1117 error: ;
1118 for(i=0;i<PXT_NO_CHANNELS;i++)
1120 if (snd->chan[i].buffer)
1122 free(snd->chan[i].buffer);
1123 snd->chan[i].buffer = NULL;
1126 if (fp) fclose(fp);
1128 return 1;
1131 static char LoadComponent(FILE *fp, stPXWave *pxw)
1133 if (pxt_SetModel(pxw, fgeticsv(fp))) return 1;
1135 pxw->repeat = fgetfcsv(fp);
1136 pxw->volume = fgeticsv(fp);
1137 pxw->offset = fgeticsv(fp);
1138 return 0;
1141 static char ReadToBracket(FILE *fp)
1143 uchar ch;
1145 for(;;)
1147 ch = fgetc(fp);
1148 if (ch==BRACK) break;
1150 if (feof(fp))
1152 staterr("pxt_load: file is in incorrect file format [failed to find '{']");
1153 fclose(fp);
1154 return 1;
1157 return 0;
1161 char pxt_save(const char *fname, stPXSound *snd)
1163 FILE *fp;
1164 int i, j;
1166 fp = fileopen(fname, "wb");
1167 if (!fp)
1169 stat("save_pxt: unable to open '%s'", fname);
1170 return 1;
1173 for(i=0;i<PXT_NO_CHANNELS;i++)
1175 fprintf(fp, "use :%d\r\n", snd->chan[i].enabled);
1176 fprintf(fp, "size :%d\r\n", snd->chan[i].size_blocks);
1178 SaveComponent(fp, "main", &snd->chan[i].main);
1179 SaveComponent(fp, "pitch", &snd->chan[i].pitch);
1180 SaveComponent(fp, "volume", &snd->chan[i].volume);
1182 fprintf(fp, "initialY:%d\r\n", snd->chan[i].envelope.initial);
1183 for(j=0;j<PXENV_NUM_VERTICES;j++)
1185 SaveEnvVertice(fp, &snd->chan[i].envelope, j);
1188 fprintf(fp, "\r\n");
1191 // save "machine-readable" section
1192 for(i=0;i<PXT_NO_CHANNELS;i++)
1194 fprintf(fp, "{");
1196 fprintf(fp, "%d,%d,", snd->chan[i].enabled, snd->chan[i].size_blocks);
1198 SaveComponentMachine(fp, &snd->chan[i].main, 1);
1199 SaveComponentMachine(fp, &snd->chan[i].pitch, 1);
1200 SaveComponentMachine(fp, &snd->chan[i].volume, 1);
1202 fprintf(fp, "%d,", snd->chan[i].envelope.initial);
1203 for(j=0;j<PXENV_NUM_VERTICES-1;j++)
1205 fprintf(fp, "%d,%d,", snd->chan[i].envelope.time[j], snd->chan[i].envelope.val[j]);
1207 fprintf(fp, "%d,%d", snd->chan[i].envelope.time[j], snd->chan[i].envelope.val[j]);
1209 fprintf(fp, "},\r\n");
1212 // save "extended" section-- original PixTone seems really picky about
1213 // trying to put anything it doesn't know about into the normal section
1215 // machine readable pitch2
1216 fprintf(fp, "\r\n-> {");
1217 for(i=0;i<PXT_NO_CHANNELS;i++)
1219 SaveComponentMachine(fp, &snd->chan[i].pitch2, (i+1<PXT_NO_CHANNELS));
1221 fprintf(fp, "},\r\n");
1223 // machine readable extra envelope vertices
1224 fprintf(fp, "-> {255,0,255,0,255,0,255,0},\r\n");
1226 // human readable copy
1227 fprintf(fp, "\r\n");
1228 for(i=0;i<PXT_NO_CHANNELS;i++)
1230 SaveComponent(fp, "pitch2", &snd->chan[i].pitch);
1231 fprintf(fp, "dx :255\r\n");
1232 fprintf(fp, "dy :0\r\n");
1233 fprintf(fp, "\r\n");
1236 fclose(fp);
1237 stat("pxt save ok '%s'", fname);
1238 return 0;
1242 static void SaveComponent(FILE *fp, const char *name, stPXWave *pxw)
1244 char spaces[40];
1245 int nspaces;
1247 nspaces = 6 - strlen(name);
1248 if (nspaces) memset(spaces, ' ', nspaces);
1249 spaces[nspaces] = 0;
1251 fprintf(fp, "%s_model %s:%d\r\n", name, spaces, pxw->model_no);
1252 fprintf(fp, "%s_freq %s:%.2f\r\n", name, spaces, pxw->repeat);
1253 fprintf(fp, "%s_top %s:%d\r\n", name, spaces, pxw->volume);
1254 fprintf(fp, "%s_offset%s:%d\r\n", name, spaces, pxw->offset);
1257 static void SaveComponentMachine(FILE *fp, stPXWave *pxw, char trailcomma)
1259 fprintf(fp, "%d,%.2f,%d,%d", pxw->model_no, pxw->repeat, pxw->volume, pxw->offset);
1260 if (trailcomma) fprintf(fp, ",");
1263 static void SaveEnvVertice(FILE *fp, stPXEnvelope *env, int v)
1265 fprintf(fp, "%cx :%d\r\n", v+'a', env->time[v]);
1266 fprintf(fp, "%cy :%d\r\n", v+'a', env->val[v]);
1270 how to use it--it's pretty easy
1272 First you have to load (parse) the pxt file you want to play.
1273 You can do this with pxt_load() which will read a pxt file and set up your stPXSound
1274 structure with the proper values as spec'd in the file.
1276 Then you must synthesize or *render* the sound. First make sure the synthesizer
1277 is initialized by calling pxt_init & pxt_initsynth.
1279 Send your stPXSound through render_pxt. Now it includes 8-bit signed PCM audio
1280 in final_buffer.
1282 But maybe you want it in a format SDL_mixer can play easier? No problem, call pxt_PrepareToPlay.
1283 Give it your sound and a *slot number* from 0-255 which is like a sound id as would be used in
1284 a game. You can free (pxt_freebuffers) that old 8-bit data now if you like and in fact entirely
1285 throw away the stPXSound as it has nothing to do with pxt_Play. When you're ready to
1286 play the sound give pxt_Play the slot number you picked and it will return to you the
1287 SDL_mixer channel number if successful.
1289 Oh yeah, you can also load a whole directory full of pxt's, up to some max slot #,
1290 by using pxt_LoadSoundFX. This function also has the bonus that first, you don't have to
1291 do any of the above stuff, it does it all for you and you can just call pxt_Play straight away.
1292 Secondly, you can give it a filename for a cache file and it will cache all the sounds after
1293 it builds them so that they load quicker next time.