ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/audio_oss_esd.cpp
Revision: 1.15
Committed: 2002-10-15T16:25:04Z (21 years, 8 months ago) by cebix
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.14: +18 -19 lines
Log Message:
Unix: new prefs options "dsp" and "mixer" to set the OSS device names instead
of the hardcoded '/dev/dsp' and '/dev/mixer'

File Contents

# Content
1 /*
2 * audio_oss_esd.cpp - Audio support, implementation for OSS and ESD (Linux and FreeBSD)
3 *
4 * Basilisk II (C) 1997-2002 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 #include "sysdeps.h"
22
23 #include <sys/ioctl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <pthread.h>
27 #include <semaphore.h>
28
29 #ifdef __linux__
30 #include <linux/soundcard.h>
31 #endif
32
33 #ifdef __FreeBSD__
34 #include <machine/soundcard.h>
35 #endif
36
37 #include "cpu_emulation.h"
38 #include "main.h"
39 #include "prefs.h"
40 #include "user_strings.h"
41 #include "audio.h"
42 #include "audio_defs.h"
43
44 #ifdef ENABLE_ESD
45 #include <esd.h>
46 #endif
47
48 #define DEBUG 0
49 #include "debug.h"
50
51
52 // The currently selected audio parameters (indices in audio_sample_rates[] etc. vectors)
53 static int audio_sample_rate_index = 0;
54 static int audio_sample_size_index = 0;
55 static int audio_channel_count_index = 0;
56
57 // Global variables
58 static int audio_fd = -1; // fd of dsp or ESD
59 static int mixer_fd = -1; // fd of mixer
60 static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read
61 static bool sem_inited = false; // Flag: audio_irq_done_sem initialized
62 static int sound_buffer_size; // Size of sound buffer in bytes
63 static bool little_endian = false; // Flag: DSP accepts only little-endian 16-bit sound data
64 static uint8 silence_byte; // Byte value to use to fill sound buffers with silence
65 static pthread_t stream_thread; // Audio streaming thread
66 static pthread_attr_t stream_thread_attr; // Streaming thread attributes
67 static bool stream_thread_active = false; // Flag: streaming thread installed
68 static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread
69
70 // Prototypes
71 static void *stream_func(void *arg);
72
73
74 /*
75 * Initialization
76 */
77
78 // Set AudioStatus to reflect current audio stream format
79 static void set_audio_status_format(void)
80 {
81 AudioStatus.sample_rate = audio_sample_rates[audio_sample_rate_index];
82 AudioStatus.sample_size = audio_sample_sizes[audio_sample_size_index];
83 AudioStatus.channels = audio_channel_counts[audio_channel_count_index];
84 }
85
86 // Init using the dsp device, returns false on error
87 static bool open_dsp(void)
88 {
89 // Open the device
90 const char *dsp = PrefsFindString("dsp");
91 audio_fd = open(dsp, O_WRONLY);
92 if (audio_fd < 0) {
93 fprintf(stderr, "WARNING: Cannot open %s (%s)\n", dsp, strerror(errno));
94 return false;
95 }
96
97 printf("Using %s audio output\n", dsp);
98
99 // Get supported sample formats
100 if (audio_sample_sizes.empty()) {
101 unsigned long format;
102 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format);
103 if (format & AFMT_U8)
104 audio_sample_sizes.push_back(8);
105 if (format & (AFMT_S16_BE | AFMT_S16_LE))
106 audio_sample_sizes.push_back(16);
107
108 int stereo = 0;
109 if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == 0 && stereo == 0)
110 audio_channel_counts.push_back(1);
111 stereo = 1;
112 if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == 0 && stereo == 1)
113 audio_channel_counts.push_back(2);
114
115 if (audio_sample_sizes.empty() || audio_channel_counts.empty()) {
116 WarningAlert(GetString(STR_AUDIO_FORMAT_WARN));
117 close(audio_fd);
118 audio_fd = -1;
119 return false;
120 }
121
122 audio_sample_rates.push_back(11025 << 16);
123 audio_sample_rates.push_back(22050 << 16);
124 int rate = 44100;
125 ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
126 if (rate > 22050)
127 audio_sample_rates.push_back(rate << 16);
128
129 // Default to highest supported values
130 audio_sample_rate_index = audio_sample_rates.size() - 1;
131 audio_sample_size_index = audio_sample_sizes.size() - 1;
132 audio_channel_count_index = audio_channel_counts.size() - 1;
133 }
134
135 // Set DSP parameters
136 unsigned long format;
137 if (audio_sample_sizes[audio_sample_size_index] == 8) {
138 format = AFMT_U8;
139 little_endian = false;
140 silence_byte = 0x80;
141 } else {
142 unsigned long sup_format;
143 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &sup_format);
144 if (sup_format & AFMT_S16_BE) {
145 little_endian = false;
146 format = AFMT_S16_BE;
147 } else {
148 little_endian = true;
149 format = AFMT_S16_LE;
150 }
151 silence_byte = 0;
152 }
153 ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
154 int frag = 0x0004000c; // Block size: 4096 frames
155 ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag);
156 int stereo = (audio_channel_counts[audio_channel_count_index] == 2);
157 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
158 int rate = audio_sample_rates[audio_sample_rate_index] >> 16;
159 ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
160
161 // Get sound buffer size
162 ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &audio_frames_per_block);
163 D(bug("DSP_GETBLKSIZE %d\n", audio_frames_per_block));
164 return true;
165 }
166
167 // Init using ESD, returns false on error
168 static bool open_esd(void)
169 {
170 #ifdef ENABLE_ESD
171 int rate;
172 esd_format_t format = ESD_STREAM | ESD_PLAY;
173
174 if (audio_sample_sizes.empty()) {
175
176 // Default values
177 rate = 44100;
178 format |= (ESD_BITS16 | ESD_STEREO);
179
180 } else {
181
182 rate = audio_sample_rates[audio_sample_rate_index] >> 16;
183 if (audio_sample_sizes[audio_sample_size_index] == 8)
184 format |= ESD_BITS8;
185 else
186 format |= ESD_BITS16;
187 if (audio_channel_counts[audio_channel_count_index] == 1)
188 format |= ESD_MONO;
189 else
190 format |= ESD_STEREO;
191 }
192
193 #if WORDS_BIGENDIAN
194 little_endian = false;
195 #else
196 little_endian = true;
197 #endif
198 silence_byte = 0; // Is this correct for 8-bit mode?
199
200 // Open connection to ESD server
201 audio_fd = esd_play_stream(format, rate, NULL, NULL);
202 if (audio_fd < 0) {
203 fprintf(stderr, "WARNING: Cannot open ESD connection\n");
204 return false;
205 }
206
207 printf("Using ESD audio output\n");
208
209 // ESD supports a variety of twisted little audio formats, all different
210 if (audio_sample_sizes.empty()) {
211
212 // The reason we do this here is that we don't want to add sample
213 // rates etc. unless the ESD server connection could be opened
214 // (if ESD fails, dsp might be tried next)
215 audio_sample_rates.push_back(11025 << 16);
216 audio_sample_rates.push_back(22050 << 16);
217 audio_sample_rates.push_back(44100 << 16);
218 audio_sample_sizes.push_back(8);
219 audio_sample_sizes.push_back(16);
220 audio_channel_counts.push_back(1);
221 audio_channel_counts.push_back(2);
222
223 // Default to highest supported values
224 audio_sample_rate_index = audio_sample_rates.size() - 1;
225 audio_sample_size_index = audio_sample_sizes.size() - 1;
226 audio_channel_count_index = audio_channel_counts.size() - 1;
227 }
228
229 // Sound buffer size = 4096 frames
230 audio_frames_per_block = 4096;
231 return true;
232 #else
233 // ESD is not enabled, shut up the compiler
234 return false;
235 #endif
236 }
237
238 static bool open_audio(void)
239 {
240 #ifdef ENABLE_ESD
241 // If ESPEAKER is set, the user probably wants to use ESD, so try that first
242 if (getenv("ESPEAKER"))
243 if (open_esd())
244 goto dev_opened;
245 #endif
246
247 // Try to open dsp
248 if (open_dsp())
249 goto dev_opened;
250
251 #ifdef ENABLE_ESD
252 // Hm, dsp failed so we try ESD again if ESPEAKER wasn't set
253 if (!getenv("ESPEAKER"))
254 if (open_esd())
255 goto dev_opened;
256 #endif
257
258 // No audio device succeeded
259 WarningAlert(GetString(STR_NO_AUDIO_WARN));
260 return false;
261
262 // Device opened, set AudioStatus
263 dev_opened:
264 sound_buffer_size = (audio_sample_sizes[audio_sample_size_index] >> 3) * audio_channel_counts[audio_channel_count_index] * audio_frames_per_block;
265 set_audio_status_format();
266
267 // Start streaming thread
268 Set_pthread_attr(&stream_thread_attr, 0);
269 stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
270
271 // Everything went fine
272 audio_open = true;
273 return true;
274 }
275
276 void AudioInit(void)
277 {
278 // Init audio status (reasonable defaults) and feature flags
279 AudioStatus.sample_rate = 44100 << 16;
280 AudioStatus.sample_size = 16;
281 AudioStatus.channels = 2;
282 AudioStatus.mixer = 0;
283 AudioStatus.num_sources = 0;
284 audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut;
285
286 // Sound disabled in prefs? Then do nothing
287 if (PrefsFindBool("nosound"))
288 return;
289
290 // Init semaphore
291 if (sem_init(&audio_irq_done_sem, 0, 0) < 0)
292 return;
293 sem_inited = true;
294
295 // Try to open the mixer device
296 const char *mixer = PrefsFindString("mixer");
297 mixer_fd = open(mixer, O_RDWR);
298 if (mixer_fd < 0)
299 printf("WARNING: Cannot open %s (%s)\n", mixer, strerror(errno));
300
301 // Open and initialize audio device
302 open_audio();
303 }
304
305
306 /*
307 * Deinitialization
308 */
309
310 static void close_audio(void)
311 {
312 // Stop stream and delete semaphore
313 if (stream_thread_active) {
314 stream_thread_cancel = true;
315 #ifdef HAVE_PTHREAD_CANCEL
316 pthread_cancel(stream_thread);
317 #endif
318 pthread_join(stream_thread, NULL);
319 stream_thread_active = false;
320 }
321
322 // Close dsp or ESD socket
323 if (audio_fd >= 0) {
324 close(audio_fd);
325 audio_fd = -1;
326 }
327
328 audio_open = false;
329 }
330
331 void AudioExit(void)
332 {
333 // Close audio device
334 close_audio();
335
336 // Delete semaphore
337 if (sem_inited) {
338 sem_destroy(&audio_irq_done_sem);
339 sem_inited = false;
340 }
341
342 // Close mixer device
343 if (mixer_fd >= 0) {
344 close(mixer_fd);
345 mixer_fd = -1;
346 }
347 }
348
349
350 /*
351 * First source added, start audio stream
352 */
353
354 void audio_enter_stream()
355 {
356 // Streaming thread is always running to avoid clicking noises
357 }
358
359
360 /*
361 * Last source removed, stop audio stream
362 */
363
364 void audio_exit_stream()
365 {
366 // Streaming thread is always running to avoid clicking noises
367 }
368
369
370 /*
371 * Streaming function
372 */
373
374 static void *stream_func(void *arg)
375 {
376 int16 *silent_buffer = new int16[sound_buffer_size / 2];
377 int16 *last_buffer = new int16[sound_buffer_size / 2];
378 memset(silent_buffer, silence_byte, sound_buffer_size);
379
380 while (!stream_thread_cancel) {
381 if (AudioStatus.num_sources) {
382
383 // Trigger audio interrupt to get new buffer
384 D(bug("stream: triggering irq\n"));
385 SetInterruptFlag(INTFLAG_AUDIO);
386 TriggerInterrupt();
387 D(bug("stream: waiting for ack\n"));
388 sem_wait(&audio_irq_done_sem);
389 D(bug("stream: ack received\n"));
390
391 // Get size of audio data
392 uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
393 if (apple_stream_info) {
394 int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
395 D(bug("stream: work_size %d\n", work_size));
396 if (work_size > sound_buffer_size)
397 work_size = sound_buffer_size;
398 if (work_size == 0)
399 goto silence;
400
401 // Send data to DSP
402 if (work_size == sound_buffer_size && !little_endian)
403 write(audio_fd, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), sound_buffer_size);
404 else {
405 // Last buffer or little-endian DSP
406 if (little_endian) {
407 int16 *p = (int16 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer));
408 for (int i=0; i<work_size/2; i++)
409 last_buffer[i] = ntohs(p[i]);
410 } else
411 Mac2Host_memcpy(last_buffer, ReadMacInt32(apple_stream_info + scd_buffer), work_size);
412 memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
413 write(audio_fd, last_buffer, sound_buffer_size);
414 }
415 D(bug("stream: data written\n"));
416 } else
417 goto silence;
418
419 } else {
420
421 // Audio not active, play silence
422 silence: write(audio_fd, silent_buffer, sound_buffer_size);
423 }
424 }
425 delete[] silent_buffer;
426 delete[] last_buffer;
427 return NULL;
428 }
429
430
431 /*
432 * MacOS audio interrupt, read next data block
433 */
434
435 void AudioInterrupt(void)
436 {
437 D(bug("AudioInterrupt\n"));
438
439 // Get data from apple mixer
440 if (AudioStatus.mixer) {
441 M68kRegisters r;
442 r.a[0] = audio_data + adatStreamInfo;
443 r.a[1] = AudioStatus.mixer;
444 Execute68k(audio_data + adatGetSourceData, &r);
445 D(bug(" GetSourceData() returns %08lx\n", r.d[0]));
446 } else
447 WriteMacInt32(audio_data + adatStreamInfo, 0);
448
449 // Signal stream function
450 sem_post(&audio_irq_done_sem);
451 D(bug("AudioInterrupt done\n"));
452 }
453
454
455 /*
456 * Set sampling parameters
457 * "index" is an index into the audio_sample_rates[] etc. vectors
458 * It is guaranteed that AudioStatus.num_sources == 0
459 */
460
461 bool audio_set_sample_rate(int index)
462 {
463 close_audio();
464 audio_sample_rate_index = index;
465 return open_audio();
466 }
467
468 bool audio_set_sample_size(int index)
469 {
470 close_audio();
471 audio_sample_size_index = index;
472 return open_audio();
473 }
474
475 bool audio_set_channels(int index)
476 {
477 close_audio();
478 audio_channel_count_index = index;
479 return open_audio();
480 }
481
482
483 /*
484 * Get/set volume controls (volume values received/returned have the left channel
485 * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
486 * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
487 */
488
489 bool audio_get_main_mute(void)
490 {
491 return false;
492 }
493
494 uint32 audio_get_main_volume(void)
495 {
496 if (mixer_fd >= 0) {
497 int vol;
498 if (ioctl(mixer_fd, SOUND_MIXER_READ_PCM, &vol) == 0) {
499 int left = vol >> 8;
500 int right = vol & 0xff;
501 return ((left * 256 / 100) << 16) | (right * 256 / 100);
502 }
503 }
504 return 0x01000100;
505 }
506
507 bool audio_get_speaker_mute(void)
508 {
509 return false;
510 }
511
512 uint32 audio_get_speaker_volume(void)
513 {
514 if (mixer_fd >= 0) {
515 int vol;
516 if (ioctl(mixer_fd, SOUND_MIXER_READ_VOLUME, &vol) == 0) {
517 int left = vol >> 8;
518 int right = vol & 0xff;
519 return ((left * 256 / 100) << 16) | (right * 256 / 100);
520 }
521 }
522 return 0x01000100;
523 }
524
525 void audio_set_main_mute(bool mute)
526 {
527 }
528
529 void audio_set_main_volume(uint32 vol)
530 {
531 if (mixer_fd >= 0) {
532 int left = vol >> 16;
533 int right = vol & 0xffff;
534 int p = ((left * 100 / 256) << 8) | (right * 100 / 256);
535 ioctl(mixer_fd, SOUND_MIXER_WRITE_PCM, &p);
536 }
537 }
538
539 void audio_set_speaker_mute(bool mute)
540 {
541 }
542
543 void audio_set_speaker_volume(uint32 vol)
544 {
545 if (mixer_fd >= 0) {
546 int left = vol >> 16;
547 int right = vol & 0xffff;
548 int p = ((left * 100 / 256) << 8) | (right * 100 / 256);
549 ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &p);
550 }
551 }