]> Shamusworld >> Repos - thunder/blob - src/sound.cpp
Added save states; updated application icon.
[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         if (YMInit(3579580, SAMPLE_RATE))
47         {
48                 printf("SOUND: Could not init YM2151 emulator!\n");
49                 return;
50         }
51
52         InitPSG(SAMPLE_RATE);
53
54         SDL_AudioSpec desired, obtained;
55         desired.freq = SAMPLE_RATE;
56         desired.format = AUDIO_S16;
57         desired.channels = 1;
58         desired.samples = BUFFER_SIZE * 2;      // Size is in BYTES, so x2
59         desired.callback = SoundFunc;
60         desired.userdata = NULL;
61
62         // 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)
63         if (SDL_OpenAudio(&desired, &obtained) < 0)
64         {
65                 soundInitialized = false;
66                 printf("SOUND: Couldn't open audio: %s\n", SDL_GetError());
67                 return;
68         }
69
70         // Get that audio going!
71         SDL_PauseAudio(0);
72         soundInitialized = true;
73 }
74
75 void SpawnSound(int type, int snd, int channel/* = 0*/)
76 {
77         if (!soundInitialized)
78                 return;
79
80         snd_num = snd;
81
82         // Voice type sounds...
83         if (type == GAMESOUND)
84         {
85                 // Will that do it???  Yes!!!
86                 snd--;
87
88                 if (channel == 0)
89                 {
90                         // 00 nn ss (nn # of repeats of sample ss)
91                         uint32_t st = 0;
92
93                         if (snd & 0x40)
94                         {
95                                 st = 0x10000;
96                                 snd &= 0x0F;
97                         }
98
99                         spos1 = (voice_rom[st + (snd << 1)] << 8) | voice_rom[st + (snd << 1) + 1];
100                         spos1 += st;                                                            // Need to add start somewhere...
101                         prevSamp1 = 128;
102                         sample1 = 0;
103                         chan1_go = true;
104                 }
105                 else
106                 {
107                         uint32_t st = 0;
108
109                         if (snd & 0x40)
110                         {
111                                 st = 0x10000;
112                                 snd &= 0x0F;
113                         }
114
115                         spos2 = (voice_rom[st + (snd << 1)] << 8) | voice_rom[st + (snd << 1) + 1];
116                         spos2 += st;                                                            // Need to add start somewhere...
117                         prevSamp2 = 128;
118                         sample2 = 0;
119                         chan2_go = true;
120                 }
121         }
122         else if (type == USERSOUND)
123         {
124                 spos6 = 0;
125                 end_pos6 = snd_lens[snd_num];                                   // User sound
126                 sndp6 = snd_array[snd_num];                                             // Load pointer
127                 chan6_go = true;
128         }
129 }
130
131 void SoundFunc(void * userdata, Uint8 * buffer, int num)
132 {
133         // We do num / 2 because num is in BYTES, and the buffer uses signed WORDs.
134         YM2151UpdateOne(buffer, num / 2);
135         UpdatePSG(buffer, num / 2);
136
137         // 0-22 different sounds...
138         uint16_t cnt = 0;
139         uint8_t start_samp1, end_samp1, start_samp2, end_samp2;
140         int16_t samp1 = 0, samp2 = 0, samp6 = 0;                        // Zero samples...
141
142         if (!(chan1_go || chan2_go || chan6_go))
143                 return;
144
145         while (cnt != (num / 2))
146         {
147                 if (chan1_go)
148                 {
149                         if (sample1 < 1)
150                         {
151                                 uint8_t voiceSample = voice_rom[spos1++];
152                                 samp1 = ((int16_t)voiceSample - 128) * 160;
153
154                                 // Kill channel 1 if done...
155                                 if (voiceSample == 0xFF)
156                                 {
157                                         chan1_go = false;
158                                         samp1 = 0;
159                                 }
160                                 // RLE compression...
161                                 else if (voiceSample == 0x00)
162                                 {
163                                         // # of repeats
164                                         sample1 += (float)voice_rom[spos1++] * sampleBase;
165                                         // Get last good sample
166                                         samp1   = prevSamp1;
167                                 }
168                                 else
169                                         // Keep fractional part intact
170                                         sample1 += sampleBase;
171                         }
172
173                         prevSamp1 = samp1;              // Save last sample value
174                         sample1 -= 1.0;                 // Decrement repeat counter
175                 }
176
177 // Stretching 5KHz samples to 22KHz:
178 //   numRepeats = 4;
179 //            6KHz -> 22KHz:  22/6 repeats...
180                 if (chan2_go)
181                 {
182                         if (sample2 < 1)
183                         {
184                                 uint8_t voiceSample = voice_rom[spos2++];
185                                 samp2 = ((int16_t)voiceSample - 128) * 160;
186
187                                 if (voiceSample == 0xFF)
188                                 {
189                                         chan2_go = false;
190                                         samp2 = 128;                                    // Kill channel 2 if done...
191                                 }
192                                 else if (voiceSample == 0x00)                           // RLE compression...
193                                 {
194                                         sample2 += (float)voice_rom[spos2++] * sampleBase;      // # of repeats
195                                         samp2   = prevSamp2;
196                                 }
197                                 else
198                                         sample2 += sampleBase;
199                         }
200
201 // Delta-X values were making the samples sound like crap...
202 //                              start_samp2 += delta_x2;
203                         prevSamp2 = samp2;
204                         sample2 -= 1.0;
205                 }
206
207                 if (chan6_go)
208                 {
209                         samp6 = sndp6[spos6++];
210
211                         if (spos6 == end_pos6)
212                         {
213                                 chan6_go = false;
214                                 samp6 = 0;              // Kill channel 6...
215                         }
216                 }
217
218                 // Mix 'em...
219                 int32_t sample = samp1 + samp2 + samp6;
220                 sample += ((int16_t *)buffer)[cnt];
221
222                 // If it overflowed, clip it
223                 if (sample > 32767)
224                         sample = 32767;
225                 else if (sample < -32767)
226                         sample = -32767;
227
228                 ((int16_t *)buffer)[cnt++] = (int16_t)sample;
229         }
230 }