ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/audio_oss_esd.cpp
Revision: 1.7
Committed: 2001-07-05T20:30:51Z (22 years, 11 months ago) by cebix
Branch: MAIN
Changes since 1.6: +178 -90 lines
Log Message:
- supported audio formats are now kept in STL vectors
- added run-time audio parameter switching for OSS/ESD audio output
- setting ESPEAKER env variable causes B2 to try ESD before OSS

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-2001 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 // Constants
58 #define DSP_NAME "/dev/dsp"
59
60 // Global variables
61 static int audio_fd = -1; // fd of /dev/dsp or ESD
62 static int mixer_fd = -1; // fd of /dev/mixer
63 static bool formats_known = false; // Flag: available audio formats have been probed
64 static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read
65 static bool sem_inited = false; // Flag: audio_irq_done_sem initialized
66 static int sound_buffer_size; // Size of sound buffer in bytes
67 static bool little_endian = false; // Flag: DSP accepts only little-endian 16-bit sound data
68 static uint8 silence_byte; // Byte value to use to fill sound buffers with silence
69 static pthread_t stream_thread; // Audio streaming thread
70 static pthread_attr_t stream_thread_attr; // Streaming thread attributes
71 static bool stream_thread_active = false; // Flag: streaming thread installed
72 static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread
73
74 // Prototypes
75 static void *stream_func(void *arg);
76
77
78 /*
79 * Initialization
80 */
81
82 // Set AudioStatus to reflect current audio stream format
83 static void set_audio_status_format(void)
84 {
85 AudioStatus.sample_rate = audio_sample_rates[audio_sample_rate_index];
86 AudioStatus.sample_size = audio_sample_sizes[audio_sample_size_index];
87 AudioStatus.channels = audio_channel_counts[audio_channel_count_index];
88 }
89
90 // Init using /dev/dsp, returns false on error
91 static bool open_dsp(void)
92 {
93 // Open /dev/dsp
94 audio_fd = open(DSP_NAME, O_WRONLY);
95 if (audio_fd < 0) {
96 fprintf(stderr, "WARNING: Cannot open %s (%s)\n", DSP_NAME, strerror(errno));
97 return false;
98 }
99
100 printf("Using " DSP_NAME " audio output\n");
101
102 // Get supported sample formats
103 if (!formats_known) {
104 unsigned long format;
105 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format);
106 if (format & AFMT_U8)
107 audio_sample_sizes.push_back(8);
108 if (format & (AFMT_S16_BE | AFMT_S16_LE))
109 audio_sample_sizes.push_back(16);
110
111 int stereo = 0;
112 if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == 0 && stereo == 0)
113 audio_channel_counts.push_back(1);
114 stereo = 1;
115 if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == 0 && stereo == 1)
116 audio_channel_counts.push_back(2);
117
118 if (audio_sample_sizes.empty() || audio_channel_counts.empty()) {
119 WarningAlert(GetString(STR_AUDIO_FORMAT_WARN));
120 close(audio_fd);
121 audio_fd = -1;
122 return false;
123 }
124
125 audio_sample_rates.push_back(11025 << 16);
126 audio_sample_rates.push_back(22050 << 16);
127 int rate = 44100;
128 ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
129 if (rate > 22050)
130 audio_sample_rates.push_back(rate << 16);
131
132 // Default to highest supported values
133 audio_sample_rate_index = audio_sample_rates.size() - 1;
134 audio_sample_size_index = audio_sample_sizes.size() - 1;
135 audio_channel_count_index = audio_channel_counts.size() - 1;
136 formats_known = true;
137 }
138
139 // Set DSP parameters
140 unsigned long format;
141 if (audio_sample_sizes[audio_sample_size_index] == 8) {
142 format = AFMT_U8;
143 little_endian = false;
144 silence_byte = 0x80;
145 } else {
146 unsigned long sup_format;
147 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format);
148 if (sup_format & AFMT_S16_BE) {
149 little_endian = false;
150 format = AFMT_S16_BE;
151 } else {
152 little_endian = true;
153 format = AFMT_S16_LE;
154 }
155 silence_byte = 0;
156 }
157 ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
158 int frag = 0x0004000c; // Block size: 4096 frames
159 ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag);
160 int stereo = (audio_channel_counts[audio_channel_count_index] == 2);
161 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
162 int rate = audio_sample_rates[audio_sample_rate_index] >> 16;
163 ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
164
165 // Get sound buffer size
166 ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &audio_frames_per_block);
167 D(bug("DSP_GETBLKSIZE %d\n", audio_frames_per_block));
168 return true;
169 }
170
171 // Init using ESD, returns false on error
172 static bool open_esd(void)
173 {
174 #ifdef ENABLE_ESD
175 // ESD supports a variety of audio formats
176 if (!formats_known) {
177 audio_sample_rates.push_back(11025 << 16);
178 audio_sample_rates.push_back(22050 << 16);
179 audio_sample_rates.push_back(44100 << 16);
180 audio_sample_sizes.push_back(8);
181 audio_sample_sizes.push_back(16);
182 audio_channel_counts.push_back(1);
183 audio_channel_counts.push_back(2);
184
185 // Default to 44.1kHz, 16-bit, stereo
186 audio_sample_rate_index = 2;
187 audio_sample_size_index = 1;
188 audio_channel_count_index = 1;
189 formats_known = true;
190 }
191
192 // ESD audio format
193 esd_format_t format = ESD_STREAM | ESD_PLAY;
194 if (audio_sample_sizes[audio_sample_size_index] == 8)
195 format |= ESD_BITS8;
196 else
197 format |= ESD_BITS16;
198 if (audio_channel_counts[audio_channel_count_index] == 1)
199 format |= ESD_MONO;
200 else
201 format |= ESD_STEREO;
202
203 #if WORDS_BIGENDIAN
204 little_endian = false;
205 #else
206 little_endian = true;
207 #endif
208 silence_byte = 0; // Is this correct for 8-bit mode?
209
210 // Open connection to ESD server
211 audio_fd = esd_play_stream(format, audio_sample_rates[audio_sample_rate_index] >> 16, NULL, NULL);
212 if (audio_fd < 0) {
213 fprintf(stderr, "WARNING: Cannot open ESD connection\n");
214 formats_known = false;
215 return false;
216 }
217
218 printf("Using ESD audio output\n");
219
220 // Sound buffer size = 4096 frames
221 audio_frames_per_block = 4096;
222 return true;
223 #endif
224 }
225
226 static bool open_audio(void)
227 {
228 #ifdef ENABLE_ESD
229 // If ESPEAKER is set, the user probably wants to use ESD, so try that first
230 if (getenv("ESPEAKER"))
231 if (open_esd())
232 goto dev_opened;
233 #endif
234
235 // Try to open /dev/dsp
236 if (open_dsp())
237 goto dev_opened;
238
239 #ifdef ENABLE_ESD
240 // Hm, /dev/dsp failed so we try ESD again if ESPEAKER wasn't set
241 if (!getenv("ESPEAKER"))
242 if (open_esd())
243 goto dev_opened;
244 #endif
245
246 // No audio device succeeded
247 WarningAlert(GetString(STR_NO_AUDIO_WARN));
248 return false;
249
250 // Device opened, set AudioStatus
251 dev_opened:
252 sound_buffer_size = (audio_sample_sizes[audio_sample_size_index] >> 3) * audio_channel_counts[audio_channel_count_index] * audio_frames_per_block;
253 set_audio_status_format();
254
255 // Start streaming thread
256 pthread_attr_init(&stream_thread_attr);
257 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
258 if (geteuid() == 0) {
259 pthread_attr_setinheritsched(&stream_thread_attr, PTHREAD_EXPLICIT_SCHED);
260 pthread_attr_setschedpolicy(&stream_thread_attr, SCHED_FIFO);
261 struct sched_param fifo_param;
262 fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2;
263 pthread_attr_setschedparam(&stream_thread_attr, &fifo_param);
264 }
265 #endif
266 stream_thread_cancel = false;
267 stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
268
269 // Everything went fine
270 audio_open = true;
271 return true;
272 }
273
274 void AudioInit(void)
275 {
276 char str[256];
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 /dev/mixer
296 mixer_fd = open("/dev/mixer", O_RDWR);
297 if (mixer_fd < 0)
298 printf("WARNING: Cannot open /dev/mixer (%s)", strerror(errno));
299
300 // Open and initialize audio device
301 open_audio();
302 }
303
304
305 /*
306 * Deinitialization
307 */
308
309 static void close_audio(void)
310 {
311 // Stop stream and delete semaphore
312 if (stream_thread_active) {
313 stream_thread_cancel = true;
314 #ifdef HAVE_PTHREAD_CANCEL
315 pthread_cancel(stream_thread);
316 #endif
317 pthread_join(stream_thread, NULL);
318 stream_thread_active = false;
319 }
320
321 // Close /dev/dsp or ESD socket
322 if (audio_fd >= 0) {
323 close(audio_fd);
324 audio_fd = -1;
325 }
326
327 audio_open = false;
328 }
329
330 void AudioExit(void)
331 {
332 if (sem_inited) {
333 sem_destroy(&audio_irq_done_sem);
334 sem_inited = false;
335 }
336
337 // Close /dev/mixer
338 if (mixer_fd >= 0) {
339 close(mixer_fd);
340 mixer_fd = -1;
341 }
342 }
343
344
345 /*
346 * First source added, start audio stream
347 */
348
349 void audio_enter_stream()
350 {
351 // Streaming thread is always running to avoid clicking noises
352 }
353
354
355 /*
356 * Last source removed, stop audio stream
357 */
358
359 void audio_exit_stream()
360 {
361 // Streaming thread is always running to avoid clicking noises
362 }
363
364
365 /*
366 * Streaming function
367 */
368
369 static uint32 apple_stream_info; // Mac address of SoundComponentData struct describing next buffer
370
371 static void *stream_func(void *arg)
372 {
373 int16 *silent_buffer = new int16[sound_buffer_size / 2];
374 int16 *last_buffer = new int16[sound_buffer_size / 2];
375 memset(silent_buffer, silence_byte, sound_buffer_size);
376
377 while (!stream_thread_cancel) {
378 if (AudioStatus.num_sources) {
379
380 // Trigger audio interrupt to get new buffer
381 D(bug("stream: triggering irq\n"));
382 SetInterruptFlag(INTFLAG_AUDIO);
383 TriggerInterrupt();
384 D(bug("stream: waiting for ack\n"));
385 sem_wait(&audio_irq_done_sem);
386 D(bug("stream: ack received\n"));
387
388 // Get size of audio data
389 uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
390 if (apple_stream_info) {
391 int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
392 D(bug("stream: work_size %d\n", work_size));
393 if (work_size > sound_buffer_size)
394 work_size = sound_buffer_size;
395 if (work_size == 0)
396 goto silence;
397
398 // Send data to DSP
399 if (work_size == sound_buffer_size && !little_endian)
400 write(audio_fd, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), sound_buffer_size);
401 else {
402 // Last buffer or little-endian DSP
403 if (little_endian) {
404 int16 *p = (int16 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer));
405 for (int i=0; i<work_size/2; i++)
406 last_buffer[i] = ntohs(p[i]);
407 } else
408 Mac2Host_memcpy(last_buffer, ReadMacInt32(apple_stream_info + scd_buffer), work_size);
409 memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
410 write(audio_fd, last_buffer, sound_buffer_size);
411 }
412 D(bug("stream: data written\n"));
413 } else
414 goto silence;
415
416 } else {
417
418 // Audio not active, play silence
419 silence: write(audio_fd, silent_buffer, sound_buffer_size);
420 }
421 }
422 delete[] silent_buffer;
423 delete[] last_buffer;
424 return NULL;
425 }
426
427
428 /*
429 * MacOS audio interrupt, read next data block
430 */
431
432 void AudioInterrupt(void)
433 {
434 D(bug("AudioInterrupt\n"));
435
436 // Get data from apple mixer
437 if (AudioStatus.mixer) {
438 M68kRegisters r;
439 r.a[0] = audio_data + adatStreamInfo;
440 r.a[1] = AudioStatus.mixer;
441 Execute68k(audio_data + adatGetSourceData, &r);
442 D(bug(" GetSourceData() returns %08lx\n", r.d[0]));
443 } else
444 WriteMacInt32(audio_data + adatStreamInfo, 0);
445
446 // Signal stream function
447 sem_post(&audio_irq_done_sem);
448 D(bug("AudioInterrupt done\n"));
449 }
450
451
452 /*
453 * Set sampling parameters
454 * "index" is an index into the audio_sample_rates[] etc. vectors
455 * It is guaranteed that AudioStatus.num_sources == 0
456 */
457
458 bool audio_set_sample_rate(int index)
459 {
460 close_audio();
461 audio_sample_rate_index = index;
462 return open_audio();
463 }
464
465 bool audio_set_sample_size(int index)
466 {
467 close_audio();
468 audio_sample_size_index = index;
469 return open_audio();
470 }
471
472 bool audio_set_channels(int index)
473 {
474 close_audio();
475 audio_channel_count_index = index;
476 return open_audio();
477 }
478
479
480 /*
481 * Get/set volume controls (volume values received/returned have the left channel
482 * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
483 * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
484 */
485
486 bool audio_get_main_mute(void)
487 {
488 return false;
489 }
490
491 uint32 audio_get_main_volume(void)
492 {
493 if (mixer_fd >= 0) {
494 int vol;
495 if (ioctl(mixer_fd, SOUND_MIXER_READ_PCM, &vol) == 0) {
496 int left = vol >> 8;
497 int right = vol & 0xff;
498 return ((left * 256 / 100) << 16) | (right * 256 / 100);
499 }
500 }
501 return 0x01000100;
502 }
503
504 bool audio_get_speaker_mute(void)
505 {
506 return false;
507 }
508
509 uint32 audio_get_speaker_volume(void)
510 {
511 if (mixer_fd >= 0) {
512 int vol;
513 if (ioctl(mixer_fd, SOUND_MIXER_READ_VOLUME, &vol) == 0) {
514 int left = vol >> 8;
515 int right = vol & 0xff;
516 return ((left * 256 / 100) << 16) | (right * 256 / 100);
517 }
518 }
519 return 0x01000100;
520 }
521
522 void audio_set_main_mute(bool mute)
523 {
524 }
525
526 void audio_set_main_volume(uint32 vol)
527 {
528 if (mixer_fd >= 0) {
529 int left = vol >> 16;
530 int right = vol & 0xffff;
531 int p = ((left * 100 / 256) << 8) | (right * 100 / 256);
532 ioctl(mixer_fd, SOUND_MIXER_WRITE_PCM, &p);
533 }
534 }
535
536 void audio_set_speaker_mute(bool mute)
537 {
538 }
539
540 void audio_set_speaker_volume(uint32 vol)
541 {
542 if (mixer_fd >= 0) {
543 int left = vol >> 16;
544 int right = vol & 0xffff;
545 int p = ((left * 100 / 256) << 8) | (right * 100 / 256);
546 ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &p);
547 }
548 }