ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Irix/audio_irix.cpp
Revision: 1.8
Committed: 2004-01-12T15:54:44Z (20 years, 10 months ago) by cebix
Branch: MAIN
CVS Tags: nigel-build-16, nigel-build-15
Changes since 1.7: +1 -1 lines
Log Message:
Happy New Year!

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * audio_irix.cpp - Audio support, SGI Irix implementation
3     *
4 cebix 1.8 * Basilisk II (C) 1997-2004 Christian Bauer
5 cebix 1.1 *
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 cebix 1.7 // 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 cebix 1.1 // 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 cebix 1.7 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 cebix 1.1 // IRIX libaudio control structures
67     static ALconfig config;
68     static ALport port;
69    
70    
71     // Prototypes
72     static void *stream_func(void *arg);
73 cebix 1.7 static uint32 read_volume(void);
74     static bool read_mute(void);
75     static void set_mute(bool mute);
76 cebix 1.1
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 cebix 1.7 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 cebix 1.1 }
89    
90 cebix 1.7 bool open_audio(void)
91 cebix 1.1 {
92     ALpv pv[2];
93    
94     printf("Using libaudio audio output\n");
95    
96 cebix 1.7 // 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 cebix 1.1
128 cebix 1.7 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 cebix 1.1 config = alNewConfig();
133     alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP);
134 cebix 1.7 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 cebix 1.1 alSetDevice(config, AL_DEFAULT_OUTPUT); // Allow selecting via prefs?
142    
143 cebix 1.7 // Try to open the audio library
144    
145 cebix 1.1 port = alOpenPort("BasiliskII", "w", config);
146     if (port == NULL) {
147     fprintf(stderr, "ERROR: Cannot open audio port: %s\n",
148     alGetErrorString(oserror()));
149 cebix 1.7 WarningAlert(GetString(STR_NO_AUDIO_WARN));
150 cebix 1.1 return false;
151     }
152 cebix 1.7
153 cebix 1.1 // Set the sample rate
154    
155     pv[0].param = AL_RATE;
156 cebix 1.7 pv[0].value.ll = alDoubleToFixed(audio_sample_rates[audio_sample_rate_index] >> 16);
157 cebix 1.1 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 cebix 1.7 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 cebix 1.1
190     alZeroFrames(port, audio_frames_per_block); // so we don't underflow
191    
192 cebix 1.7 // Try to keep the buffer pretty full
193     sound_buffer_fill_point = alGetQueueSize(config) -
194     2 * audio_frames_per_block;
195 cebix 1.1 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 cebix 1.7 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 cebix 1.1
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 cebix 1.7 // 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 cebix 1.1 return true;
226     }
227    
228     void AudioInit(void)
229     {
230 cebix 1.7 // Init audio status (reasonable defaults) and feature flags
231     AudioStatus.sample_rate = 44100 << 16;
232     AudioStatus.sample_size = 16;
233     AudioStatus.channels = 2;
234 cebix 1.1 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 cebix 1.7 // Open and initialize audio device
248     open_audio();
249 cebix 1.1 }
250    
251    
252     /*
253     * Deinitialization
254     */
255    
256 cebix 1.7 static void close_audio(void)
257 cebix 1.1 {
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 cebix 1.7 stream_thread_cancel = false;
267 cebix 1.1 }
268    
269     // Close audio library
270     alClosePort(port);
271 cebix 1.7
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 cebix 1.1 }
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 cebix 1.7 int32 *last_buffer = new int32[sound_buffer_size / 4];
315 cebix 1.1 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 cebix 1.7 uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
334     if (!current_main_mute &&
335     !current_speaker_mute &&
336     apple_stream_info) {
337 cebix 1.1 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 cebix 1.7 // 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 cebix 1.1 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 cebix 1.7 * 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 cebix 1.1 * 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 cebix 1.7 * "index" is an index into the audio_sample_rates[] etc. vectors
578 cebix 1.1 * It is guaranteed that AudioStatus.num_sources == 0
579     */
580    
581 cebix 1.4 bool audio_set_sample_rate(int index)
582 cebix 1.1 {
583 cebix 1.7 close_audio();
584     audio_sample_rate_index = index;
585     return open_audio();
586 cebix 1.1 }
587    
588 cebix 1.4 bool audio_set_sample_size(int index)
589 cebix 1.1 {
590 cebix 1.7 close_audio();
591     audio_sample_size_index = index;
592     return open_audio();
593 cebix 1.1 }
594    
595 cebix 1.4 bool audio_set_channels(int index)
596 cebix 1.1 {
597 cebix 1.7 close_audio();
598     audio_channel_count_index = index;
599     return open_audio();
600 cebix 1.1 }
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 cebix 1.7 D(bug("audio_get_main_mute: mute=%ld\n", current_main_mute));
612    
613     return current_main_mute;
614 cebix 1.1 }
615    
616     uint32 audio_get_main_volume(void)
617     {
618 cebix 1.7 uint32 ret = current_main_volume;
619    
620     D(bug("audio_get_main_volume: vol=0x%x\n", ret));
621    
622     return ret;
623 cebix 1.1 }
624    
625     bool audio_get_speaker_mute(void)
626     {
627 cebix 1.7 D(bug("audio_get_speaker_mute: mute=%ld\n", current_speaker_mute));
628    
629     return current_speaker_mute;
630 cebix 1.1 }
631    
632     uint32 audio_get_speaker_volume(void)
633     {
634 cebix 1.7 uint32 ret = current_speaker_volume;
635    
636     D(bug("audio_get_speaker_volume: vol=0x%x\n", ret));
637    
638     return ret;
639 cebix 1.1 }
640    
641     void audio_set_main_mute(bool mute)
642     {
643 cebix 1.7 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 cebix 1.1 }
651    
652     void audio_set_main_volume(uint32 vol)
653     {
654 cebix 1.7
655     D(bug("audio_set_main_volume: vol=%x\n", vol));
656    
657     current_main_volume = vol;
658    
659     set_volume(vol);
660 cebix 1.1 }
661    
662     void audio_set_speaker_mute(bool mute)
663     {
664 cebix 1.7 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 cebix 1.1 }
672    
673     void audio_set_speaker_volume(uint32 vol)
674     {
675 cebix 1.7 D(bug("audio_set_speaker_volume: vol=%x\n", vol));
676    
677     current_speaker_volume = vol;
678    
679     set_volume(vol);
680 cebix 1.1 }