1 | /********************************************************************** <BR>
|
---|
2 | This file is part of Crack dot Com's free source code release of
|
---|
3 | Golgotha. <a href="http://www.crack.com/golgotha_release"> <BR> for
|
---|
4 | information about compiling & licensing issues visit this URL</a>
|
---|
5 | <PRE> If that doesn't help, contact Jonathan Clark at
|
---|
6 | golgotha_source@usa.net (Subject should have "GOLG" in it)
|
---|
7 | ***********************************************************************/
|
---|
8 |
|
---|
9 | #include "error/error.hh"
|
---|
10 | #include "error/alert.hh"
|
---|
11 | #include "sound/linux/linux_sound.hh"
|
---|
12 | #include "loaders/wav_load.hh"
|
---|
13 | #include "string/string.hh"
|
---|
14 | #include "file/file.hh"
|
---|
15 |
|
---|
16 | #include <linux/soundcard.h>
|
---|
17 | #include <sys/ioctl.h>
|
---|
18 | #include <fcntl.h>
|
---|
19 | #include <unistd.h>
|
---|
20 | #include <string.h>
|
---|
21 |
|
---|
22 | enum { LINUX_SOUND_BUFFER_BITS = 11 };
|
---|
23 | enum { LINUX_SOUND_SAMPLE_SPEED = 11025 };
|
---|
24 |
|
---|
25 | // Thread State Enumeration
|
---|
26 | enum
|
---|
27 | {
|
---|
28 | LINUX_SOUND_UNINITIALIZED,
|
---|
29 | LINUX_SOUND_RUNNING,
|
---|
30 | LINUX_SOUND_REQUEST_STOP,
|
---|
31 | LINUX_SOUND_STOPPED,
|
---|
32 | };
|
---|
33 |
|
---|
34 | static sw16 volume_table[I4_SOUND_VOLUME_LEVELS][256];
|
---|
35 | static sw32 mix_buffer[1<<LINUX_SOUND_BUFFER_BITS];
|
---|
36 | static sw32 output_buffer[1<<LINUX_SOUND_BUFFER_BITS];
|
---|
37 | linux_sound_class linux_sound;
|
---|
38 |
|
---|
39 | void linux_voice_class::play()
|
---|
40 | {
|
---|
41 | index = 0;
|
---|
42 | active = 1;
|
---|
43 | }
|
---|
44 |
|
---|
45 | void linux_voice_class::stop()
|
---|
46 | {
|
---|
47 | #error implement
|
---|
48 | }
|
---|
49 |
|
---|
50 |
|
---|
51 | void linux_voice_class::set_frequency(i4_frequency freq)
|
---|
52 | {
|
---|
53 | linux_sound_index f(freq);
|
---|
54 |
|
---|
55 | f.value /= LINUX_SOUND_SAMPLE_SPEED;
|
---|
56 | increment = f;
|
---|
57 | }
|
---|
58 |
|
---|
59 |
|
---|
60 | void linux_voice_class::set_volume(i4_volume _vol)
|
---|
61 | {
|
---|
62 | volume = _vol;
|
---|
63 |
|
---|
64 | left_vol = (pan<0) ? ((volume < -pan)? 0 : volume + pan) : volume;
|
---|
65 | right_vol = (pan<0) ? volume : ((volume < pan) ? 0 : volume - pan);
|
---|
66 | }
|
---|
67 |
|
---|
68 |
|
---|
69 | void linux_voice_class::set_pan(i4_pan _pan)
|
---|
70 | {
|
---|
71 | pan = _pan;
|
---|
72 |
|
---|
73 | left_vol = (pan<0) ? ((volume < -pan)? 0 : volume + pan) : volume;
|
---|
74 | right_vol = (pan<0) ? volume : ((volume < pan) ? 0 : volume - pan);
|
---|
75 | }
|
---|
76 |
|
---|
77 |
|
---|
78 | void linux_sound_class::start_thread()
|
---|
79 | {
|
---|
80 | if (thread_state == LINUX_SOUND_UNINITIALIZED)
|
---|
81 | {
|
---|
82 | pthread_t handle;
|
---|
83 | pthread_attr_t attr;
|
---|
84 | pthread_attr_init(&attr);
|
---|
85 | pthread_create(&handle, &attr, linux_sound_mixer, 0);
|
---|
86 |
|
---|
87 | thread_state = LINUX_SOUND_RUNNING;
|
---|
88 | }
|
---|
89 | }
|
---|
90 |
|
---|
91 |
|
---|
92 | void linux_sound_class::stop_thread()
|
---|
93 | {
|
---|
94 | if (thread_state != LINUX_SOUND_UNINITIALIZED)
|
---|
95 | {
|
---|
96 | if (thread_state == LINUX_SOUND_RUNNING)
|
---|
97 | thread_state = LINUX_SOUND_REQUEST_STOP;
|
---|
98 |
|
---|
99 | while (thread_state != LINUX_SOUND_STOPPED && thread_state != LINUX_SOUND_UNINITIALIZED)
|
---|
100 | sched_yield();
|
---|
101 |
|
---|
102 | thread_state = LINUX_SOUND_UNINITIALIZED;
|
---|
103 | }
|
---|
104 | }
|
---|
105 |
|
---|
106 |
|
---|
107 | void linux_sound_class::initialize_volume_table()
|
---|
108 | {
|
---|
109 | for (int level=0; level<I4_SOUND_VOLUME_LEVELS; level++)
|
---|
110 | for (int i=0; i<256; i++)
|
---|
111 | volume_table[level][i] = (i-128) * level;
|
---|
112 | }
|
---|
113 |
|
---|
114 |
|
---|
115 | extern int i4_global_native_argc;
|
---|
116 | extern char **i4_global_native_argv;
|
---|
117 |
|
---|
118 |
|
---|
119 | void linux_sound_class::init()
|
---|
120 | {
|
---|
121 | int i;
|
---|
122 |
|
---|
123 | for (i=0; i<i4_global_native_argc; i++)
|
---|
124 | if (!strcmp(i4_global_native_argv[i],"-nosound"))
|
---|
125 | return ;
|
---|
126 |
|
---|
127 | #if 0
|
---|
128 | fd=open("/dev/mixer",O_WRONLY);
|
---|
129 | if (fd!=-1)
|
---|
130 | {
|
---|
131 | int vol=127;
|
---|
132 | ioctl(fd,MIXER_WRITE(SOUND_MIXER_VOLUME),&vol);
|
---|
133 | close(fd);
|
---|
134 | }
|
---|
135 | else
|
---|
136 | i4_warning("sound driver : Unable to open /dev/mixer, can't set volume\n");
|
---|
137 | #endif
|
---|
138 |
|
---|
139 | fd=open("/dev/dsp",O_WRONLY,0);
|
---|
140 | if (fd<0)
|
---|
141 | {
|
---|
142 | i4_warning("sound driver : Unable to open /dev/dsp, sound effects disabled\n");
|
---|
143 | return;
|
---|
144 | }
|
---|
145 |
|
---|
146 | // 2 fragments of 2^LINUX_SOUND_BUFFER_BITS bytes
|
---|
147 | i = 0x00020000|LINUX_SOUND_BUFFER_BITS;
|
---|
148 | ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &i);
|
---|
149 |
|
---|
150 | i = 16; // samples are 16 bit
|
---|
151 | if (ioctl(fd, SNDCTL_DSP_SAMPLESIZE, &i)<0)
|
---|
152 | {
|
---|
153 | i4_warning("SNDDRV : Sample size 16 failed, sound effects disabled\n");
|
---|
154 | close(fd);
|
---|
155 | return;
|
---|
156 | }
|
---|
157 |
|
---|
158 | i = LINUX_SOUND_SAMPLE_SPEED;
|
---|
159 | if (ioctl(fd, SNDCTL_DSP_SPEED, &i)<0)
|
---|
160 | {
|
---|
161 | i4_warning("SNDDRV : dsp_speed failed, sound effects disabled\n");
|
---|
162 | close(fd);
|
---|
163 | return;
|
---|
164 | }
|
---|
165 |
|
---|
166 | i = 1; // stero
|
---|
167 | if (ioctl(fd, SNDCTL_DSP_STEREO, &i)<0)
|
---|
168 | {
|
---|
169 | i4_warning("SNDDRV : set stereo failed, sound effects disabled\n");
|
---|
170 | close(fd);
|
---|
171 | return;
|
---|
172 | }
|
---|
173 |
|
---|
174 | thread_state = LINUX_SOUND_UNINITIALIZED;
|
---|
175 |
|
---|
176 | initialize_volume_table();
|
---|
177 |
|
---|
178 | start_thread();
|
---|
179 |
|
---|
180 | i4_sound_manager_class::init();
|
---|
181 | }
|
---|
182 |
|
---|
183 |
|
---|
184 | void linux_sound_class::load_sounds(w32 max_sounds)
|
---|
185 | {
|
---|
186 | sound = new linux_sample_class[max_sounds];
|
---|
187 |
|
---|
188 | i4_sound_info info;
|
---|
189 | i4_const_str *sounds=i4_string_man.get_array("sounds");
|
---|
190 |
|
---|
191 | for (w32 count=0; !sounds[count].null(); count++)
|
---|
192 | {
|
---|
193 | i4_file_class *fp=i4_file_man.open(sounds[count]);
|
---|
194 | if (!fp)
|
---|
195 | i4_alert(i4gets("file_missing"),200,&sounds[count]);
|
---|
196 | else
|
---|
197 | {
|
---|
198 | if (i4_load_wav(fp,info))
|
---|
199 | {
|
---|
200 | linux_sample_class *snd = &sound[count];
|
---|
201 |
|
---|
202 | if (info.sample_size==1)
|
---|
203 | {
|
---|
204 | snd->data = (w8*)info.data;
|
---|
205 | snd->sample_rate = linux_sound_index(info.sample_rate);
|
---|
206 | snd->size = linux_sound_index(info.size);
|
---|
207 | }
|
---|
208 | else
|
---|
209 | {
|
---|
210 | snd->size = 0;
|
---|
211 | i4_alert(i4gets("bad_format"),200,&sounds[count]);
|
---|
212 | }
|
---|
213 | }
|
---|
214 | else
|
---|
215 | i4_alert(i4gets("bad_format"),200,&sounds[count]);
|
---|
216 | delete fp;
|
---|
217 | }
|
---|
218 | }
|
---|
219 |
|
---|
220 | i4_free(sounds);
|
---|
221 | }
|
---|
222 |
|
---|
223 | i4_voice_class *linux_sound_class::alloc(i4_sound_id sound_id, const sound_parameters& param)
|
---|
224 | {
|
---|
225 | int i=0;
|
---|
226 |
|
---|
227 | if (!sound[sound_id].data)
|
---|
228 | return 0;
|
---|
229 |
|
---|
230 | while (i<LINUX_SOUND_NUM_VOICE && voice[i].sound)
|
---|
231 | i++;
|
---|
232 |
|
---|
233 | if (i<LINUX_SOUND_NUM_VOICE)
|
---|
234 | {
|
---|
235 | voice[i].looping = param.looping;
|
---|
236 | voice[i].index = 0;
|
---|
237 | voice[i].active = 0;
|
---|
238 |
|
---|
239 | voice[i].set_frequency(param.frequency);
|
---|
240 | voice[i].set_volume(param.volume);
|
---|
241 | voice[i].set_pan(param.pan);
|
---|
242 |
|
---|
243 | voice[i].sound = &sound[sound_id];
|
---|
244 |
|
---|
245 | return &voice[i];
|
---|
246 | }
|
---|
247 | else
|
---|
248 | return 0;
|
---|
249 | }
|
---|
250 |
|
---|
251 |
|
---|
252 | void *linux_sound_mixer(void *arg)
|
---|
253 | {
|
---|
254 | w16 voc,i;
|
---|
255 |
|
---|
256 | while (linux_sound.thread_state == LINUX_SOUND_RUNNING)
|
---|
257 | {
|
---|
258 | memset(mix_buffer, 0, sizeof(mix_buffer));
|
---|
259 | memset(output_buffer, 0, sizeof(output_buffer));
|
---|
260 | for (voc=0; voc<LINUX_SOUND_NUM_VOICE; voc++)
|
---|
261 | {
|
---|
262 | linux_voice_class& v(linux_sound.voice[voc]);
|
---|
263 |
|
---|
264 | if (v.sound && v.active)
|
---|
265 | {
|
---|
266 | for (i=0; i<1<<(LINUX_SOUND_BUFFER_BITS-2); i++)
|
---|
267 | {
|
---|
268 | mix_buffer[i] += volume_table[v.left_vol][ v.sound->data[ w32(v.index) ] ];
|
---|
269 | output_buffer[i] += volume_table[v.right_vol][ v.sound->data[ w32(v.index) ] ];
|
---|
270 | v.index += v.increment;
|
---|
271 |
|
---|
272 | if (v.index >= v.sound->size)
|
---|
273 | {
|
---|
274 | if ( !v.looping && (v.complete == 0 || v.complete(&v)) )
|
---|
275 | {
|
---|
276 | v.sound = 0;
|
---|
277 | break;
|
---|
278 | }
|
---|
279 | else
|
---|
280 | while (v.index >= v.sound->size)
|
---|
281 | v.index -= v.sound->size;
|
---|
282 | }
|
---|
283 | }
|
---|
284 | }
|
---|
285 | }
|
---|
286 | for (i=0; i<1<<(LINUX_SOUND_BUFFER_BITS-2); i++)
|
---|
287 | {
|
---|
288 | sw32 val;
|
---|
289 |
|
---|
290 | val = output_buffer[i];
|
---|
291 | val = (w16)((val<-32768) ? -32768 : ( (val>32767)? 32767 : val ));
|
---|
292 | output_buffer[i] = val;
|
---|
293 | val = mix_buffer[i];
|
---|
294 | val = (w16)((val<-32768) ? -32768 : ( (val>32767)? 32767 : val ));
|
---|
295 | output_buffer[i] |= val<<16;
|
---|
296 | }
|
---|
297 | write(linux_sound.fd, output_buffer, 1<<LINUX_SOUND_BUFFER_BITS);
|
---|
298 | sched_yield();
|
---|
299 | }
|
---|
300 | linux_sound.thread_state = LINUX_SOUND_STOPPED;
|
---|
301 |
|
---|
302 |
|
---|
303 | pthread_exit(0);
|
---|
304 | }
|
---|
305 |
|
---|
306 |
|
---|