1 /* sound.c */
2
3 /* Copyright Hugh Robinson 2006-2008.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17
18 #include <SDL/SDL_mixer.h>
19 #include <math.h>
20 #include "asylum.h"
21
22 extern asylum_options options;
23 char sound_available;
24
bidforsoundforce(int r0,char r1,char r2,int r3,int r4,int r5,char r6,int r7,Mix_Chunk * chunk)25 void bidforsoundforce(int r0, char r1, char r2, int r3, int r4, int r5, char r6, int r7, Mix_Chunk* chunk)
26 {
27 if (options.soundtype && sound_available)
28 {
29 r2 = (r2 > options.soundvol) ? options.soundvol : r2;
30 soundclaim(r0&7, r1, r2, r3, r4, r5, r6, r7, chunk);
31 }
32 }
33
bidforsound(int r0,char r1,char r2,int r3,int r4,int r5,char r6,int r7,Mix_Chunk * chunk)34 void bidforsound(int r0, char r1, char r2, int r3, int r4, int r5, char r6, int r7, Mix_Chunk* chunk)
35 {
36 if (options.soundtype && sound_available)
37 {
38 r2 = (r2 > options.soundvol) ? options.soundvol : r2;
39 if ((r0&7)==_Explochannel)
40 soundclaimexplo(r0&7, r1, r2, r3, r4, r5, r6, r7, chunk);
41 else
42 soundclaimmaybe(r0&7, r1, r2, r3, r4, r5, r6, r7, chunk);
43 }
44 }
45
soundclaimmaybe(int r0,char r1,char r2,int r3,int r4,int r5,char r6,int r7,Mix_Chunk * chunk)46 void soundclaimmaybe(int r0, char r1, char r2, int r3, int r4, int r5, char r6, int r7, Mix_Chunk* chunk)
47 {
48 //soundtab=soundtabofs+(r0<<soundtabshift);
49 if ((!Mix_Playing(r0)) || (Mix_GetChunk(r0)->volume < r2)) soundclaim(r0, r1, r2, r3, r4, r5, r6, r7, chunk);
50 }
51
soundclaimexplo(int r0,char r1,char r2,int r3,int r4,int r5,char r6,int r7,Mix_Chunk * chunk)52 void soundclaimexplo(int r0, char r1, char r2, int r3, int r4, int r5, char r6, int r7, Mix_Chunk* chunk)
53 {
54 //soundtab=soundtabofs+(r0<<soundtabshift);
55 bidforexplo:
56 if ((!Mix_Playing(3)) || (Mix_GetChunk(3)->volume < r2)) soundclaim(3, r1, r2, r3, r4, r5, r6, r7, chunk);
57 else if ((!Mix_Playing(4)) || (Mix_GetChunk(4)->volume < r2)) soundclaim(4, r1, r2, r3, r4, r5, r6, r7, chunk);
58 else if ((!Mix_Playing(5)) || (Mix_GetChunk(5)->volume < r2)) soundclaim(5, r1, r2, r3, r4, r5, r6, r7, chunk);
59 else if ((!Mix_Playing(6)) || (Mix_GetChunk(6)->volume < r2)) soundclaim(6, r1, r2, r3, r4, r5, r6, r7, chunk);
60 else if ((!Mix_Playing(7)) || (Mix_GetChunk(7)->volume < r2)) soundclaim(7, r1, r2, r3, r4, r5, r6, r7, chunk);
61 }
62
63 typedef struct
64 {
65 int time;
66 int tempo;
67 char* tune;
68 int pitch[4];
69 int inst[4];
70 int start_time[4];
71 char* section;
72 int pointer;
73 } music_state;
74 music_state music;
75 char voice[32][30000];
76 char tuneload[4][30000];
77 Sint16 mulaw[256];
78 Mix_Music* oggmusic[4];
79
init_audio()80 void init_audio()
81 {
82 sound_available = !Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024);
83 if (!sound_available) fprintf(stderr, "Sound disabled: opening audio device failed: %s\n", Mix_GetError());
84 }
85
init_mulaw()86 void init_mulaw()
87 {
88 Sint16 exp[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
89 for (int i = 0; i < 256; i++)
90 mulaw[i] = ((i&1) ? -1 : 1)*(exp[i>>5]+((i&0x1e)<<(2+(i>>5))));
91 }
92
make_sound(char samp,int initpitch,int volslide,int pitchslide,char frames)93 Mix_Chunk* make_sound(char samp, int initpitch, int volslide, int pitchslide, char frames)
94 {
95 int numsamples = (int)(frames*22050.0/50.0*2); // frames are 50Hz?
96 Mix_Chunk* mc = (Mix_Chunk*)malloc(sizeof(Mix_Chunk));
97 Uint16* s = (Uint16*)malloc(numsamples*2*sizeof(Uint16));
98
99 mc->abuf = (Uint8*)s;
100 mc->alen = numsamples*2*sizeof(Uint16);
101 mc->allocated = 0; // don't automatically free buffer
102 mc->volume = 0xff; // XXX volume
103 int time = 0;
104 double ps = ((Sint16)(pitchslide&0xffff))*22/22050.0;
105 double vs = ((Sint16)(volslide&0xffff))/22050.0;
106 Sint16 psmax = pitchslide>>16;
107 double matching = 0;
108 fprintf(stderr, "."); fflush(stderr);
109 for (int i = 0; i < numsamples*2*sizeof(Uint16); i += 4, time++, s += 2)
110 {
111 Uint16 mono = 0;
112
113 double pitch = initpitch+ps*time; // XXX pitchslide
114 //if ((ps>0)&&(pitch>psmax)) pitch=psmax;
115 //if ((ps<0)&&(pitch<psmax)) pitch=psmax;
116 int off = time;
117 double old_sample_rate = 500.0*pow(2, pitch/4096.0);
118 matching += old_sample_rate/22050;
119 for (int j = (int)matching-32; j <= (int)matching+32; j++)
120 {
121 if ((j < 0)) continue;
122 int jj = j;
123 uint32_t len = read_littleendian(((uint32_t*)voice[samp])+6);
124 uint32_t gap = read_littleendian(((uint32_t*)voice[samp])+7); // ???
125 uint32_t rep = read_littleendian(((uint32_t*)voice[samp])+8);
126 if (jj >= len) if (rep == 0) continue;else jj = (len-rep)+((jj-len)%rep);double w;
127 if (jj >= len-gap)
128 w = ((jj-(len-gap))*(double)mulaw[voice[samp][jj+44-rep]]
129 +(len-jj)*(double)mulaw[voice[samp][jj+44]])/gap/4;
130 else w = mulaw[voice[samp][jj+44]]/4;
131 double vscale = 0x7f+vs*time;
132 if (vscale > 0xff) vscale = 0xff;
133 if (vscale > 0) w *= (vscale/0x7f);else w = 0;
134 double x = M_PI*(j-matching);
135 if (x == 0) mono += (Sint16)(w/1.3);
136 else mono += (Sint16)(w*sinf(x)/(1.3*x));
137 }
138 s[1] = *s = mono; // XXX stereo
139 }
140 return mc;
141 }
142
soundclaim(int c,char samp,char initvol,int initpitch,int volslide,int pitchslide,char frames,int stereo,Mix_Chunk * static_chunk)143 void soundclaim(int c, char samp, char initvol, int initpitch, int volslide, int pitchslide, char frames, int stereo, Mix_Chunk* static_chunk)
144 {
145 //int* argv = (int*)vargv;
146 //int c = argv[0]; char samp = argv[1]; char initvol = argv[2]; int initpitch = argv[3];
147 //int volslide = argv[4]; int pitchslide = argv[5]; char frames = argv[6]; int stereo = argv[7];
148 Mix_Chunk* old_chunk = Mix_GetChunk(c);
149 Mix_Chunk* chunk = static_chunk ? static_chunk : make_sound(samp, initpitch, volslide, pitchslide, frames);
150
151 Mix_HaltChannel(c);
152 Mix_Volume(c, initvol);
153 Mix_SetPanning(c, 254-stereo, stereo);
154 Mix_PlayChannel(c, chunk, 0);
155 //if (old_chunk) Mix_FreeChunk(old_chunk); XXX memory leak
156 }
157
158 FILE* musicdumpfile;
159
sdl_music_hook(void * udata,Uint8 * stream,int len)160 void sdl_music_hook(void* udata, Uint8* stream, int len)
161 {
162 music_state* m = (music_state*)udata;
163 char* tuneload = m->tune;
164 Sint16* s = (Sint16*)stream;
165 int window = (len < 0) ? 16 : 4;
166 unsigned long ln = (len < 0) ? -ftell(musicdumpfile) : 0;
167
168 for (int i = 0; (len < 0) || (i < len); i += 4, m->time++, (len < 0) || (s += 2))
169 {
170 if (0 == (m->time%m->tempo))
171 {
172 // new beat
173 if (len == -2)
174 {
175 ln += ftell(musicdumpfile); fseek(musicdumpfile, 8, 0);
176 fputc(ln>>24, musicdumpfile); fputc((ln&0xff0000)>>16, musicdumpfile);
177 fputc((ln&0xff00)>>8, musicdumpfile); fputc(ln&0xff, musicdumpfile);
178 break;
179 }
180 int inst = 0;
181 int pitch = 8;
182 char first;
183 char second;
184 do
185 {
186 first = tuneload[m->pointer++];
187 second = tuneload[m->pointer++];
188 if (first)
189 { // pitch word
190 inst = first&0x1f;
191 pitch = second&0x3f;
192 }
193 else
194 {
195 m->pitch[second&3] = pitch;
196 m->inst[second&3] = inst;
197 m->start_time[second&3] = m->time;
198 inst = 0; pitch = 8;
199 }
200 }
201 while ((second&0xc0) == 0);
202 if (second&0x80)
203 {
204 m->section++;
205 if (0x80&*m->section)
206 {
207 m->section = tuneload+8; if (len < 0) len = -2;
208 }
209 m->pointer = read_littleendian(((uint32_t*)tuneload)+0x42+*m->section);
210 }
211 }
212 *s = 0; s[1] = 0;
213 for (int v = 0; v < 4; v++)
214 {
215 if (m->inst[v] == 0) continue;
216 int pitch = m->pitch[v];
217 int off = m->time-m->start_time[v];
218 double old_sample_rate = 4000.0*pow(2, pitch/12.0);
219 double matching = off*old_sample_rate/22050;
220 for (int j = (int)matching-window; j <= (int)matching+window; j++)
221 {
222 if ((j < 0)) continue;
223 int jj = j;
224 uint32_t len = read_littleendian(((uint32_t*)voice[m->inst[v]])+6);
225 uint32_t gap = read_littleendian(((uint32_t*)voice[m->inst[v]])+7); // ???
226 uint32_t rep = read_littleendian(((uint32_t*)voice[m->inst[v]])+8);
227 /*if (m->inst[v]==16)
228 while (jj>=6960+1000) jj-=6960;
229 else
230 if (jj>=8192) continue;*/
231 if (jj >= len) jj = rep ? ((len-rep)+((jj-len)%rep)) : 0;
232 double w;
233 if (jj >= len-gap)
234 w = ((jj-(len-gap))*(double)mulaw[voice[m->inst[v]][jj+44-rep]]
235 +(len-jj)*(double)mulaw[voice[m->inst[v]][jj+44]])/gap/4;
236 else w = mulaw[voice[m->inst[v]][jj+44]]/4;
237 double x = M_PI*(j-matching);
238 if (x == 0) s[v&1] += (Sint16)(w/1.3);
239 else s[v&1] += (Sint16)(w*sinf(x)/(1.3*x));
240 }
241 }
242 if (len < 0)
243 {
244 /* .au is big-endian format */
245 fputc(s[0]>>8, musicdumpfile);
246 fputc(s[0]&0xff, musicdumpfile);
247 fputc(s[1]>>8, musicdumpfile);
248 fputc(s[1]&0xff, musicdumpfile);
249 }
250 }
251 }
252
load_voices()253 void load_voices()
254 {
255 if (!sound_available) return;
256 load_voice(1, "./Voices/Jump");
257 load_voice(2, "./Voices/Bonus");
258 load_voice(3, "./Voices/Explo");
259 load_voice(4, "./Voices/AtomExplo");
260 load_voice(5, "./Voices/Cannon");
261 load_voice(6, "./Voices/Rocket");
262 load_voice(7, "./Voices/Hiss");
263 load_voice(8, "./Voices/Stunned");
264 load_voice(9, "./Voices/SmallZap");
265 load_voice(10, "./Voices/BigZap");
266 load_voice(16, "./Voices/Organ");
267 load_voice(17, "./Voices/Plink");
268 load_voice(18, "./Voices/PlinkHard");
269 load_voice(19, "./Voices/Raver");
270 load_voice(20, "./Voices/Dome");
271 load_voice(21, "./Voices/NasalStr");
272 load_voice(22, "./Voices/BassHollow");
273 load_voice(23, "./Voices/NasalBass");
274 load_voice(24, "./Voices/BassDrum");
275 load_voice(25, "./Voices/Snare");
276 load_voice(26, "./Voices/Cymbal");
277 }
278
init_sounds()279 void init_sounds()
280 {
281 if (!sound_available) return;
282 init_mulaw();
283 fprintf(stderr, "Building sound effects ");
284 init_chunk_bullet();
285 init_chunk_player();
286 init_chunk_alien();
287 init_chunk_maze();
288 fprintf(stderr, " done.\n");
289 }
290
load_voice(int v,const char * filename)291 void load_voice(int v, const char* filename)
292 {
293 SDL_RWops* file = SDL_RWFromFile(filename, "r");
294
295 if (file == NULL)
296 {
297 fprintf(stderr, "Loading sound effect %s failed\n", filename);
298 return;
299 }
300 SDL_RWseek(file, 0, SEEK_END);
301 int file_len = SDL_RWtell(file) /*-44*/;
302 SDL_RWseek(file, 0 /*44*/, SEEK_SET);
303 SDL_RWread(file, voice[v], 1, file_len);
304 }
305
306
initialize_music(int a)307 void initialize_music(int a)
308 {
309 swi_sound_qtempo(0x1000);
310 music.time = 0;
311 music.tune = tuneload[a];
312 music.section = tuneload[a]+8;
313 music.pointer = read_littleendian(((uint32_t*)(tuneload[a]))+0x42+*music.section);
314 for (int v = 0; v < 4; v++)
315 {
316 music.pitch[v] = 0; music.inst[v] = 0; music.start_time[v] = 0;
317 }
318 }
319
dumpmusic(int argc,char ** argv)320 void dumpmusic(int argc,char** argv)
321 {
322 init_mulaw();
323 Uint8 wav[4];
324 char musicdumppath[1024] = "";
325 char* musicinputpath = argv[2];
326 strncat(musicdumppath, musicinputpath, 1000);
327 strncat(musicdumppath, ".au", 20);
328 fprintf(stderr, "Dumping music ");
329 //swi_bodgemusic_load(1,musicinputpath);
330 musicdumpfile = fopen(musicdumppath, "w");
331 SDL_RWops* tune = SDL_RWFromFile(musicinputpath, "r");
332 SDL_RWread(tune, tuneload+1, 1, 30000);
333 initialize_music(1);
334 if (argc > 3) if (!strcmp(argv[3], "--slower")) swi_sound_qtempo(0x980);
335 fputs(".snd", musicdumpfile);
336 fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(32, musicdumpfile); // data start
337 fputc(255, musicdumpfile); fputc(255, musicdumpfile); fputc(255, musicdumpfile); fputc(255, musicdumpfile); // length
338 fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(3, musicdumpfile); // 16-bit linear
339 fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(22050>>8, musicdumpfile); fputc(22050&0xff, musicdumpfile);
340 fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(0, musicdumpfile); fputc(2, musicdumpfile); // stereo
341 fputs("blotwell", musicdumpfile);
342 sdl_music_hook(&music, wav+2, -1 /* dump entire track into buffer */);
343 fclose(musicdumpfile);
344 exit(0);
345 }
346
maketestsound(int r1)347 void maketestsound(int r1)
348 {
349 swi_stasis_link(1, 1);
350 swi_sound_control(1, 0x100|r1, 0x20, 0xfe);
351 //swi_bodgemusic_volume(musicvol);
352 }
swi_bodgemusic_start(int a,int b)353 void swi_bodgemusic_start(int a, int b)
354 {
355 if (!sound_available) return;
356 swi_bodgemusic_stop();
357 if (oggmusic[a]) Mix_PlayMusic(oggmusic[a], -1);
358 else
359 {
360 initialize_music(a);
361 Mix_HookMusic(sdl_music_hook, &music);
362 }
363 }
swi_bodgemusic_stop()364 void swi_bodgemusic_stop()
365 {
366 if (!sound_available) return;
367 Mix_HaltMusic(); Mix_HookMusic(NULL, NULL);
368 }
swi_bodgemusic_volume(int v)369 void swi_bodgemusic_volume(int v)
370 {
371 Mix_VolumeMusic(v);
372 }
swi_bodgemusic_load(int a,char * b)373 void swi_bodgemusic_load(int a, char* b)
374 {
375 char name[1024] = "";
376
377 strncpy(name, b, 1000);
378 strncat(name, ".ogg", 20);
379 if (oggmusic[a]) Mix_FreeMusic(oggmusic[a]);
380 oggmusic[a] = Mix_LoadMUS(name);
381 if (oggmusic[a]) return;
382 SDL_RWops* tune = SDL_RWFromFile(b, "r");
383 //SDL_RWseek(tune, 8+256+64, SEEK_SET);
384 SDL_RWread(tune, tuneload+a, 1, 30000);
385 }
swi_sound_qtempo(int t)386 void swi_sound_qtempo(int t)
387 {
388 music.tempo = (2756*0x1000)/t;
389 }
swi_sound_control(int c,int a,int p,int d)390 void swi_sound_control(int c, int a, int p, int d)
391 {
392 ;
393 }
swi_sound_speaker(int s)394 int swi_sound_speaker(int s)
395 {
396 ;
397 }
swi_stasis_link(int a,int b)398 void swi_stasis_link(int a, int b)
399 {
400 ;
401 }
swi_stasis_control(int a,int b)402 void swi_stasis_control(int a, int b)
403 {
404 ;
405 }
swi_stasis_volslide(int a,int b,int c)406 void swi_stasis_volslide(int a, int b, int c)
407 {
408 ;
409 }
410