]> Shamusworld >> Repos - thunder/blob - src/sound.cpp
f2af13a3a07ba5bca5a2a5749847493a67a626cb
[thunder] / src / sound.cpp
1 //
2 // Sound Handler
3 //
4 // by James Hammons
5 // (C) 2014 Underground Software
6 //
7 // JLH = James Hammons <jlhamm@acm.org>
8 //
9 // Who  When        What
10 // ---  ----------  -----------------------------------------------------------
11 // JLH  04/18/2014  Created this file
12 //
13
14 #include "sound.h"
15 #include <SDL2/SDL.h>
16 #include "psg.h"
17 #include "resource.h"
18 #include "ym2151.h"
19
20 #define SAMPLE_RATE  48000
21 #define BUFFER_SIZE  512
22
23 // Function prototypes
24 void SoundFunc(void *, Uint8 *, int);
25
26 // Vars
27 extern uint8_t voice_rom[];                     // PCM data pointer
28
29 static bool soundInitialized = false;
30 const float sampleBase = (float)SAMPLE_RATE / 6000.0;  // Voice is between 5512.5 and 6000 Hz
31 bool chan1_go = false, chan2_go = false;
32 bool chan6_go = false;
33 uint8_t * sndp1, * sndp2, * sndp6;
34 uint32_t spos1, end_pos1;
35 uint32_t spos2, end_pos2;
36 uint32_t spos6, end_pos6;
37 float sample1, sample2;
38 int16_t prevSamp1, prevSamp2;
39 uint16_t snd_num;
40 // From RESOURCE.H
41 uint8_t * snd_array[3] = { sunknown, scya, scamera };
42 uint32_t snd_lens[3]   = { sunknownlen, scyalen, scameralen };
43
44 void InitSound(void)
45 {
46         // params 1, 4 & 5 are useless
47 //      if (YMInit(1, 3579580, SAMPLE_RATE, 16, 512))
48         if (YMInit(3579580, SAMPLE_RATE))
49         {
50                 printf("SOUND: Could not init YM2151 emulator!\n");
51                 return;
52         }
53
54         InitPSG(SAMPLE_RATE);
55
56         SDL_AudioSpec desired, obtained;
57         desired.freq = SAMPLE_RATE;
58         desired.format = AUDIO_S16;
59         desired.channels = 1;
60         desired.samples = BUFFER_SIZE * 2;      // Size is in BYTES, so x2
61         desired.callback = SoundFunc;
62         desired.userdata = NULL;
63
64         // Also, should check to see if it got the hardware it needed, correct sample size, etc. (actually, SDL guarantees we get what we asked for--I think)
65         if (SDL_OpenAudio(&desired, &obtained) < 0)
66         {
67                 soundInitialized = false;
68                 printf("SOUND: Couldn't open audio: %s\n", SDL_GetError());
69                 return;
70         }
71
72         // Get that audio going!
73         SDL_PauseAudio(0);
74         soundInitialized = true;
75 }
76
77 void SpawnSound(int type, int snd, int channel/* = 0*/)
78 {
79         if (!soundInitialized)
80                 return;
81
82         snd_num = snd;
83
84         // Voice type sounds...
85         if (type == GAMESOUND)
86         {
87                 // Will that do it???  Yes!!!
88                 snd--;
89
90                 if (channel == 0)
91                 {
92                         // 00 nn ss (nn # of repeats of sample ss)
93                         uint32_t st = 0;
94
95                         if (snd & 0x40)
96                         {
97                                 st = 0x10000;
98                                 snd &= 0x0F;
99                         }
100
101                         spos1 = (voice_rom[st + (snd << 1)] << 8) | voice_rom[st + (snd << 1) + 1];
102                         spos1 += st;                                                            // Need to add start somewhere...
103                         prevSamp1 = 128;
104                         sample1 = 0;
105                         chan1_go = true;
106                 }
107                 else
108                 {
109                         uint32_t st = 0;
110
111                         if (snd & 0x40)
112                         {
113                                 st = 0x10000;
114                                 snd &= 0x0F;
115                         }
116
117                         spos2 = (voice_rom[st + (snd << 1)] << 8) | voice_rom[st + (snd << 1) + 1];
118                         spos2 += st;                                                            // Need to add start somewhere...
119                         prevSamp2 = 128;
120                         sample2 = 0;
121                         chan2_go = true;
122                 }
123         }
124         else if (type == USERSOUND)
125         {
126                 spos6 = 0;
127                 end_pos6 = snd_lens[snd_num];                                   // User sound
128                 sndp6 = snd_array[snd_num];                                             // Load pointer
129                 chan6_go = true;
130         }
131 }
132
133 void SoundFunc(void * userdata, Uint8 * buffer, int num)
134 {
135         // We do num / 2 because num is in BYTES, and the buffer uses signed WORDs.
136         YM2151UpdateOne(buffer, num / 2);
137         UpdatePSG(buffer, num / 2);
138
139         // 0-22 different sounds...
140         uint16_t cnt = 0;
141         uint8_t start_samp1, end_samp1, start_samp2, end_samp2;
142         int16_t samp1 = 0, samp2 = 0, samp6 = 0;                        // Zero samples...
143
144         if (!(chan1_go || chan2_go || chan6_go))
145                 return;
146
147         while (cnt != (num / 2))
148         {
149                 if (chan1_go)
150                 {
151                         if (sample1 < 1)
152                         {
153                                 uint8_t voiceSample = voice_rom[spos1++];
154                                 samp1 = ((int16_t)voiceSample - 128) * 160;
155
156                                 // Kill channel 1 if done...
157                                 if (voiceSample == 0xFF)
158                                 {
159                                         chan1_go = false;
160                                         samp1 = 0;
161                                 }
162                                 // RLE compression...
163                                 else if (voiceSample == 0x00)
164                                 {
165                                         // # of repeats
166                                         sample1 += (float)voice_rom[spos1++] * sampleBase;
167                                         // Get last good sample
168                                         samp1   = prevSamp1;
169                                 }
170                                 else
171                                         // Keep fractional part intact
172                                         sample1 += sampleBase;
173                         }
174
175                         prevSamp1 = samp1;              // Save last sample value
176                         sample1 -= 1.0;                 // Decrement repeat counter
177                 }
178
179 // Stretching 5KHz samples to 22KHz:
180 //   numRepeats = 4;
181 //            6KHz -> 22KHz:  22/6 repeats...
182                 if (chan2_go)
183                 {
184                         if (sample2 < 1)
185                         {
186                                 uint8_t voiceSample = voice_rom[spos2++];
187                                 samp2 = ((int16_t)voiceSample - 128) * 160;
188
189                                 if (voiceSample == 0xFF)
190                                 {
191                                         chan2_go = false;
192                                         samp2 = 128;                                    // Kill channel 2 if done...
193                                 }
194                                 else if (voiceSample == 0x00)                           // RLE compression...
195                                 {
196                                         sample2 += (float)voice_rom[spos2++] * sampleBase;      // # of repeats
197                                         samp2   = prevSamp2;
198                                 }
199                                 else
200                                         sample2 += sampleBase;
201                         }
202
203 // Delta-X values were making the samples sound like crap...
204 //                              start_samp2 += delta_x2;
205                         prevSamp2 = samp2;
206                         sample2 -= 1.0;
207                 }
208
209                 if (chan6_go)
210                 {
211                         samp6 = sndp6[spos6++];
212
213                         if (spos6 == end_pos6)
214                         {
215                                 chan6_go = false;
216                                 samp6 = 0;              // Kill channel 6...
217                         }
218                 }
219
220                 // Mix 'em...
221                 int32_t sample = samp1 + samp2 + samp6;
222                 sample += ((int16_t *)buffer)[cnt];
223
224                 // If it overflowed, clip it
225                 if (sample > 32767)
226                         sample = 32767;
227                 else if (sample < -32767)
228                         sample = -32767;
229
230                 ((int16_t *)buffer)[cnt++] = (int16_t)sample;
231         }
232 }