ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Irix/audio_irix.cpp
Revision: 1.10
Committed: 2008-01-01T09:40:33Z (16 years, 10 months ago) by gbeauche
Branch: MAIN
CVS Tags: HEAD
Changes since 1.9: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# Content
1 /*
2 * audio_irix.cpp - Audio support, SGI Irix implementation
3 *
4 * Basilisk II (C) 1997-2008 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 #include <dmedia/audio.h>
30 #include <dmedia/dmedia.h>
31
32 #include "cpu_emulation.h"
33 #include "main.h"
34 #include "prefs.h"
35 #include "user_strings.h"
36 #include "audio.h"
37 #include "audio_defs.h"
38
39 #define DEBUG 0
40 #include "debug.h"
41
42
43 // The currently selected audio parameters (indices in audio_sample_rates[]
44 // etc. vectors)
45 static int audio_sample_rate_index = 0;
46 static int audio_sample_size_index = 0;
47 static int audio_channel_count_index = 0;
48
49 // Global variables
50 static int audio_fd = -1; // fd from audio library
51 static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read
52 static bool sem_inited = false; // Flag: audio_irq_done_sem initialized
53 static int sound_buffer_size; // Size of sound buffer in bytes
54 static int sound_buffer_fill_point; // Fill buffer when this many frames are empty
55 static uint8 silence_byte = 0; // Byte value to use to fill sound buffers with silence
56 static pthread_t stream_thread; // Audio streaming thread
57 static pthread_attr_t stream_thread_attr; // Streaming thread attributes
58 static bool stream_thread_active = false; // Flag: streaming thread installed
59 static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread
60
61 static bool current_main_mute = false; // Flag: output muted
62 static bool current_speaker_mute = false; // Flag: speaker muted
63 static uint32 current_main_volume = 0; // Output volume
64 static uint32 current_speaker_volume = 0; // Speaker volume
65
66 // IRIX libaudio control structures
67 static ALconfig config;
68 static ALport port;
69
70
71 // Prototypes
72 static void *stream_func(void *arg);
73 static uint32 read_volume(void);
74 static bool read_mute(void);
75 static void set_mute(bool mute);
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 bool open_audio(void)
91 {
92 ALpv pv[2];
93
94 printf("Using libaudio audio output\n");
95
96 // Get supported sample formats
97
98 if (audio_sample_sizes.empty()) {
99 // All sample sizes are supported
100 audio_sample_sizes.push_back(8);
101 audio_sample_sizes.push_back(16);
102
103 // Assume at least two channels are supported. Some IRIX boxes
104 // can do 4 or more... MacOS only handles up to 2.
105 audio_channel_counts.push_back(1);
106 audio_channel_counts.push_back(2);
107
108 if (audio_sample_sizes.empty() || audio_channel_counts.empty()) {
109 WarningAlert(GetString(STR_AUDIO_FORMAT_WARN));
110 alClosePort(port);
111 audio_fd = -1;
112 return false;
113 }
114
115 audio_sample_rates.push_back( 8000 << 16);
116 audio_sample_rates.push_back(11025 << 16);
117 audio_sample_rates.push_back(22050 << 16);
118 audio_sample_rates.push_back(44100 << 16);
119
120 // Default to highest supported values
121 audio_sample_rate_index = audio_sample_rates.size() - 1;
122 audio_sample_size_index = audio_sample_sizes.size() - 1;
123 audio_channel_count_index = audio_channel_counts.size() - 1;
124 }
125
126 // Set the sample format
127
128 D(bug("Size %d, channels %d, rate %d\n",
129 audio_sample_sizes[audio_sample_size_index],
130 audio_channel_counts[audio_channel_count_index],
131 audio_sample_rates[audio_sample_rate_index] >> 16));
132 config = alNewConfig();
133 alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP);
134 if (audio_sample_sizes[audio_sample_size_index] == 8) {
135 alSetWidth(config, AL_SAMPLE_8);
136 }
137 else {
138 alSetWidth(config, AL_SAMPLE_16);
139 }
140 alSetChannels(config, audio_channel_counts[audio_channel_count_index]);
141 alSetDevice(config, AL_DEFAULT_OUTPUT); // Allow selecting via prefs?
142
143 // Try to open the audio library
144
145 port = alOpenPort("BasiliskII", "w", config);
146 if (port == NULL) {
147 fprintf(stderr, "ERROR: Cannot open audio port: %s\n",
148 alGetErrorString(oserror()));
149 WarningAlert(GetString(STR_NO_AUDIO_WARN));
150 return false;
151 }
152
153 // Set the sample rate
154
155 pv[0].param = AL_RATE;
156 pv[0].value.ll = alDoubleToFixed(audio_sample_rates[audio_sample_rate_index] >> 16);
157 pv[1].param = AL_MASTER_CLOCK;
158 pv[1].value.i = AL_CRYSTAL_MCLK_TYPE;
159 if (alSetParams(AL_DEFAULT_OUTPUT, pv, 2) < 0) {
160 fprintf(stderr, "ERROR: libaudio setparams failed: %s\n",
161 alGetErrorString(oserror()));
162 alClosePort(port);
163 return false;
164 }
165
166 // Compute sound buffer size and libaudio refill point
167
168 config = alGetConfig(port);
169 audio_frames_per_block = alGetQueueSize(config);
170 if (audio_frames_per_block < 0) {
171 fprintf(stderr, "ERROR: couldn't get queue size: %s\n",
172 alGetErrorString(oserror()));
173 alClosePort(port);
174 return false;
175 }
176 D(bug("alGetQueueSize %d, width %d, channels %d\n",
177 audio_frames_per_block,
178 alGetWidth(config),
179 alGetChannels(config)));
180
181 // Put a limit on the Mac sound buffer size, to decrease delay
182 #define AUDIO_BUFFER_MSEC 50 // milliseconds of sound to buffer
183 int target_frames_per_block =
184 (audio_sample_rates[audio_sample_rate_index] >> 16) *
185 AUDIO_BUFFER_MSEC / 1000;
186 if (audio_frames_per_block > target_frames_per_block)
187 audio_frames_per_block = target_frames_per_block;
188 D(bug("frames per block %d\n", audio_frames_per_block));
189
190 alZeroFrames(port, audio_frames_per_block); // so we don't underflow
191
192 // Try to keep the buffer pretty full
193 sound_buffer_fill_point = alGetQueueSize(config) -
194 2 * audio_frames_per_block;
195 if (sound_buffer_fill_point < 0)
196 sound_buffer_fill_point = alGetQueueSize(config) / 3;
197 D(bug("fill point %d\n", sound_buffer_fill_point));
198
199 sound_buffer_size = (audio_sample_sizes[audio_sample_size_index] >> 3) *
200 audio_channel_counts[audio_channel_count_index] *
201 audio_frames_per_block;
202 set_audio_status_format();
203
204 // Get a file descriptor we can select() on
205
206 audio_fd = alGetFD(port);
207 if (audio_fd < 0) {
208 fprintf(stderr, "ERROR: couldn't get libaudio file descriptor: %s\n",
209 alGetErrorString(oserror()));
210 alClosePort(port);
211 return false;
212 }
213
214 // Initialize volume, mute settings
215 current_main_volume = current_speaker_volume = read_volume();
216 current_main_mute = current_speaker_mute = read_mute();
217
218
219 // Start streaming thread
220 Set_pthread_attr(&stream_thread_attr, 0);
221 stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
222
223 // Everything went fine
224 audio_open = true;
225 return true;
226 }
227
228 void AudioInit(void)
229 {
230 // Init audio status (reasonable defaults) and feature flags
231 AudioStatus.sample_rate = 44100 << 16;
232 AudioStatus.sample_size = 16;
233 AudioStatus.channels = 2;
234 AudioStatus.mixer = 0;
235 AudioStatus.num_sources = 0;
236 audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut;
237
238 // Sound disabled in prefs? Then do nothing
239 if (PrefsFindBool("nosound"))
240 return;
241
242 // Init semaphore
243 if (sem_init(&audio_irq_done_sem, 0, 0) < 0)
244 return;
245 sem_inited = true;
246
247 // Open and initialize audio device
248 open_audio();
249 }
250
251
252 /*
253 * Deinitialization
254 */
255
256 static void close_audio(void)
257 {
258 // Stop stream and delete semaphore
259 if (stream_thread_active) {
260 stream_thread_cancel = true;
261 #ifdef HAVE_PTHREAD_CANCEL
262 pthread_cancel(stream_thread);
263 #endif
264 pthread_join(stream_thread, NULL);
265 stream_thread_active = false;
266 stream_thread_cancel = false;
267 }
268
269 // Close audio library
270 alClosePort(port);
271
272 audio_open = false;
273 }
274
275 void AudioExit(void)
276 {
277 // Close audio device
278 close_audio();
279
280 // Delete semaphore
281 if (sem_inited) {
282 sem_destroy(&audio_irq_done_sem);
283 sem_inited = false;
284 }
285 }
286
287
288 /*
289 * First source added, start audio stream
290 */
291
292 void audio_enter_stream()
293 {
294 // Streaming thread is always running to avoid clicking noises
295 }
296
297
298 /*
299 * Last source removed, stop audio stream
300 */
301
302 void audio_exit_stream()
303 {
304 // Streaming thread is always running to avoid clicking noises
305 }
306
307
308 /*
309 * Streaming function
310 */
311
312 static void *stream_func(void *arg)
313 {
314 int32 *last_buffer = new int32[sound_buffer_size / 4];
315 fd_set audio_fdset;
316 int numfds, was_error;
317
318 numfds = audio_fd + 1;
319 FD_ZERO(&audio_fdset);
320
321 while (!stream_thread_cancel) {
322 if (AudioStatus.num_sources) {
323
324 // Trigger audio interrupt to get new buffer
325 D(bug("stream: triggering irq\n"));
326 SetInterruptFlag(INTFLAG_AUDIO);
327 TriggerInterrupt();
328 D(bug("stream: waiting for ack\n"));
329 sem_wait(&audio_irq_done_sem);
330 D(bug("stream: ack received\n"));
331
332 // Get size of audio data
333 uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
334 if (!current_main_mute &&
335 !current_speaker_mute &&
336 apple_stream_info) {
337 int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
338 D(bug("stream: work_size %d\n", work_size));
339 if (work_size > sound_buffer_size)
340 work_size = sound_buffer_size;
341 if (work_size == 0)
342 goto silence;
343
344 // Send data to audio library. Convert 8-bit data
345 // unsigned->signed, using same algorithm as audio_amiga.cpp.
346 // It works fine for 8-bit mono, but not stereo.
347 if (AudioStatus.sample_size == 8) {
348 uint32 *p = (uint32 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer));
349 uint32 *q = (uint32 *)last_buffer;
350 int r = work_size >> 2;
351 // XXX not quite right....
352 while (r--)
353 *q++ = *p++ ^ 0x80808080;
354 if (work_size != sound_buffer_size)
355 memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
356 alWriteFrames(port, last_buffer, audio_frames_per_block);
357 }
358 else if (work_size == sound_buffer_size)
359 alWriteFrames(port, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), audio_frames_per_block);
360 else {
361 // Last buffer
362 Mac2Host_memcpy(last_buffer, ReadMacInt32(apple_stream_info + scd_buffer), work_size);
363 memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
364 alWriteFrames(port, last_buffer, audio_frames_per_block);
365 }
366 D(bug("stream: data written\n"));
367 } else
368 goto silence;
369
370 } else {
371
372 // Audio not active, play silence
373 silence: // D(bug("stream: silence\n"));
374 alZeroFrames(port, audio_frames_per_block);
375 }
376
377 // Wait for fill point to be reached (may be immediate)
378
379 if (alSetFillPoint(port, sound_buffer_fill_point) < 0) {
380 fprintf(stderr, "ERROR: alSetFillPoint failed: %s\n",
381 alGetErrorString(oserror()));
382 // Should stop the audio here....
383 }
384
385 do {
386 errno = 0;
387 FD_SET(audio_fd, &audio_fdset);
388 was_error = select(numfds, NULL, &audio_fdset, NULL, NULL);
389 } while(was_error < 0 && (errno == EINTR));
390 if (was_error < 0) {
391 fprintf(stderr, "ERROR: select returned %d, errno %d\n",
392 was_error, errno);
393 // Should stop audio here....
394 }
395 }
396 delete[] last_buffer;
397 return NULL;
398 }
399
400
401 /*
402 * Read or set the current output volume using the audio library
403 */
404
405 static uint32 read_volume(void)
406 {
407 ALpv x[2];
408 ALfixed gain[8];
409 double maxgain, mingain;
410 ALparamInfo pi;
411 uint32 ret = 0x01000100; // default, maximum value
412 int dev = alGetDevice(config);
413
414 // Fetch the maximum and minimum gain settings
415
416 alGetParamInfo(dev, AL_GAIN, &pi);
417 maxgain = alFixedToDouble(pi.max.ll);
418 mingain = alFixedToDouble(pi.min.ll);
419 // printf("maxgain = %lf dB, mingain = %lf dB\n", maxgain, mingain);
420
421 // Get the current gain values
422
423 x[0].param = AL_GAIN;
424 x[0].value.ptr = gain;
425 x[0].sizeIn = sizeof(gain) / sizeof(gain[0]);
426 x[1].param = AL_CHANNELS;
427 if (alGetParams(dev, x, 2) < 0) {
428 printf("alGetParams failed: %s\n", alGetErrorString(oserror()));
429 }
430 else {
431 if (x[0].sizeOut < 0) {
432 printf("AL_GAIN was an unrecognized parameter\n");
433 }
434 else {
435 double v;
436 uint32 left, right;
437
438 // Left
439 v = alFixedToDouble(gain[0]);
440 if (v < mingain)
441 v = mingain; // handle gain == -inf
442 v = (v - mingain) / (maxgain - mingain); // scale to 0..1
443 left = (uint32)(v * (double)256); // convert to 8.8 fixed point
444
445 // Right
446 if (x[0].sizeOut <= 1) { // handle a mono interface
447 right = left;
448 }
449 else {
450 v = alFixedToDouble(gain[1]);
451 if (v < mingain)
452 v = mingain; // handle gain == -inf
453 v = (v - mingain) / (maxgain - mingain); // scale to 0..1
454 right = (uint32)(v * (double)256); // convert to 8.8 fixed point
455 }
456
457 ret = (left << 16) | right;
458 }
459 }
460
461 return ret;
462 }
463
464 static void set_volume(uint32 vol)
465 {
466 ALpv x[1];
467 ALfixed gain[2]; // left and right
468 double maxgain, mingain;
469 ALparamInfo pi;
470 int dev = alGetDevice(config);
471
472 // Fetch the maximum and minimum gain settings
473
474 alGetParamInfo(dev, AL_GAIN, &pi);
475 maxgain = alFixedToDouble(pi.max.ll);
476 mingain = alFixedToDouble(pi.min.ll);
477
478 // Set the new gain values
479
480 x[0].param = AL_GAIN;
481 x[0].value.ptr = gain;
482 x[0].sizeIn = sizeof(gain) / sizeof(gain[0]);
483
484 uint32 left = vol >> 16;
485 uint32 right = vol & 0xffff;
486 double lv, rv;
487
488 if (left == 0 && pi.specialVals & AL_NEG_INFINITY_BIT) {
489 lv = AL_NEG_INFINITY;
490 }
491 else {
492 lv = ((double)left / 256) * (maxgain - mingain) + mingain;
493 }
494
495 if (right == 0 && pi.specialVals & AL_NEG_INFINITY_BIT) {
496 rv = AL_NEG_INFINITY;
497 }
498 else {
499 rv = ((double)right / 256) * (maxgain - mingain) + mingain;
500 }
501
502 D(bug("set_volume: left=%lf dB, right=%lf dB\n", lv, rv));
503
504 gain[0] = alDoubleToFixed(lv);
505 gain[1] = alDoubleToFixed(rv);
506
507 if (alSetParams(dev, x, 1) < 0) {
508 printf("alSetParams failed: %s\n", alGetErrorString(oserror()));
509 }
510 }
511
512
513 /*
514 * Read or set the mute setting using the audio library
515 */
516
517 static bool read_mute(void)
518 {
519 bool ret;
520 int dev = alGetDevice(config);
521 ALpv x;
522 x.param = AL_MUTE;
523
524 if (alGetParams(dev, &x, 1) < 0) {
525 printf("alSetParams failed: %s\n", alGetErrorString(oserror()));
526 return current_main_mute; // Or just return false?
527 }
528
529 ret = x.value.i;
530
531 D(bug("read_mute: mute=%d\n", ret));
532 return ret;
533 }
534
535 static void set_mute(bool mute)
536 {
537 D(bug("set_mute: mute=%ld\n", mute));
538
539 int dev = alGetDevice(config);
540 ALpv x;
541 x.param = AL_MUTE;
542 x.value.i = mute;
543
544 if (alSetParams(dev, &x, 1) < 0) {
545 printf("alSetParams failed: %s\n", alGetErrorString(oserror()));
546 }
547 }
548
549
550
551 /*
552 * MacOS audio interrupt, read next data block
553 */
554
555 void AudioInterrupt(void)
556 {
557 D(bug("AudioInterrupt\n"));
558
559 // Get data from apple mixer
560 if (AudioStatus.mixer) {
561 M68kRegisters r;
562 r.a[0] = audio_data + adatStreamInfo;
563 r.a[1] = AudioStatus.mixer;
564 Execute68k(audio_data + adatGetSourceData, &r);
565 D(bug(" GetSourceData() returns %08lx\n", r.d[0]));
566 } else
567 WriteMacInt32(audio_data + adatStreamInfo, 0);
568
569 // Signal stream function
570 sem_post(&audio_irq_done_sem);
571 D(bug("AudioInterrupt done\n"));
572 }
573
574
575 /*
576 * Set sampling parameters
577 * "index" is an index into the audio_sample_rates[] etc. vectors
578 * It is guaranteed that AudioStatus.num_sources == 0
579 */
580
581 bool audio_set_sample_rate(int index)
582 {
583 close_audio();
584 audio_sample_rate_index = index;
585 return open_audio();
586 }
587
588 bool audio_set_sample_size(int index)
589 {
590 close_audio();
591 audio_sample_size_index = index;
592 return open_audio();
593 }
594
595 bool audio_set_channels(int index)
596 {
597 close_audio();
598 audio_channel_count_index = index;
599 return open_audio();
600 }
601
602
603 /*
604 * Get/set volume controls (volume values received/returned have the left channel
605 * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
606 * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
607 */
608
609 bool audio_get_main_mute(void)
610 {
611 D(bug("audio_get_main_mute: mute=%ld\n", current_main_mute));
612
613 return current_main_mute;
614 }
615
616 uint32 audio_get_main_volume(void)
617 {
618 uint32 ret = current_main_volume;
619
620 D(bug("audio_get_main_volume: vol=0x%x\n", ret));
621
622 return ret;
623 }
624
625 bool audio_get_speaker_mute(void)
626 {
627 D(bug("audio_get_speaker_mute: mute=%ld\n", current_speaker_mute));
628
629 return current_speaker_mute;
630 }
631
632 uint32 audio_get_speaker_volume(void)
633 {
634 uint32 ret = current_speaker_volume;
635
636 D(bug("audio_get_speaker_volume: vol=0x%x\n", ret));
637
638 return ret;
639 }
640
641 void audio_set_main_mute(bool mute)
642 {
643 D(bug("audio_set_main_mute: mute=%ld\n", mute));
644
645 if (mute != current_main_mute) {
646 current_main_mute = mute;
647 }
648
649 set_mute(current_main_mute);
650 }
651
652 void audio_set_main_volume(uint32 vol)
653 {
654
655 D(bug("audio_set_main_volume: vol=%x\n", vol));
656
657 current_main_volume = vol;
658
659 set_volume(vol);
660 }
661
662 void audio_set_speaker_mute(bool mute)
663 {
664 D(bug("audio_set_speaker_mute: mute=%ld\n", mute));
665
666 if (mute != current_speaker_mute) {
667 current_speaker_mute = mute;
668 }
669
670 set_mute(current_speaker_mute);
671 }
672
673 void audio_set_speaker_volume(uint32 vol)
674 {
675 D(bug("audio_set_speaker_volume: vol=%x\n", vol));
676
677 current_speaker_volume = vol;
678
679 set_volume(vol);
680 }