ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Irix/audio_irix.cpp
Revision: 1.1
Committed: 2000-11-02T14:45:17Z (23 years, 10 months ago) by cebix
Branch: MAIN
Log Message:
- added audio support for IRIX [Brian J. Johnson]
- improved Delay_usec() under FreeBSD and IRIX
- fixed typo ("HAVE_PTHREDS") in video_x.cpp

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * audio_irix.cpp - Audio support, SGI Irix implementation
3     *
4     * Basilisk II (C) 1997-2000 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     // Supported sample rates, sizes and channels (defaults)
44     int audio_num_sample_rates = 1;
45     uint32 audio_sample_rates[] = {44100 << 16};
46     int audio_num_sample_sizes = 1;
47     uint16 audio_sample_sizes[] = {16};
48     int audio_num_channel_counts = 1;
49     uint16 audio_channel_counts[] = {2};
50    
51     // Global variables
52     static int audio_fd = -1; // fd from audio library
53     static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read
54     static bool sem_inited = false; // Flag: audio_irq_done_sem initialized
55     static int sound_buffer_size; // Size of sound buffer in bytes
56     static int sound_buffer_fill_point; // Fill buffer when this many frames are empty
57     static uint8 silence_byte = 0; // Byte value to use to fill sound buffers with silence
58     static pthread_t stream_thread; // Audio streaming thread
59     static pthread_attr_t stream_thread_attr; // Streaming thread attributes
60     static bool stream_thread_active = false; // Flag: streaming thread installed
61     static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread
62    
63     // IRIX libaudio control structures
64     static ALconfig config;
65     static ALport port;
66    
67    
68     // Prototypes
69     static void *stream_func(void *arg);
70    
71    
72     /*
73     * Initialization
74     */
75    
76     // Set AudioStatus to reflect current audio stream format
77     static void set_audio_status_format(void)
78     {
79     AudioStatus.sample_rate = audio_sample_rates[0];
80     AudioStatus.sample_size = audio_sample_sizes[0];
81     AudioStatus.channels = audio_channel_counts[0];
82     }
83    
84     // Init libaudio, returns false on error
85     bool audio_init_al(void)
86     {
87     ALpv pv[2];
88    
89     printf("Using libaudio audio output\n");
90    
91     // Try to open the audio library
92    
93     config = alNewConfig();
94     alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP);
95     alSetWidth(config, AL_SAMPLE_16);
96     alSetChannels(config, 2); // stereo
97     alSetDevice(config, AL_DEFAULT_OUTPUT); // Allow selecting via prefs?
98    
99     port = alOpenPort("BasiliskII", "w", config);
100     if (port == NULL) {
101     fprintf(stderr, "ERROR: Cannot open audio port: %s\n",
102     alGetErrorString(oserror()));
103     return false;
104     }
105    
106     // Set the sample rate
107    
108     pv[0].param = AL_RATE;
109     pv[0].value.ll = alDoubleToFixed(audio_sample_rates[0] >> 16);
110     pv[1].param = AL_MASTER_CLOCK;
111     pv[1].value.i = AL_CRYSTAL_MCLK_TYPE;
112     if (alSetParams(AL_DEFAULT_OUTPUT, pv, 2) < 0) {
113     fprintf(stderr, "ERROR: libaudio setparams failed: %s\n",
114     alGetErrorString(oserror()));
115     alClosePort(port);
116     return false;
117     }
118    
119     // TODO: list all supported sample formats?
120    
121     // Set AudioStatus again because we now know more about the sound
122     // system's capabilities
123     set_audio_status_format();
124    
125     // Compute sound buffer size and libaudio refill point
126    
127     config = alGetConfig(port);
128     audio_frames_per_block = alGetQueueSize(config);
129     if (audio_frames_per_block < 0) {
130     fprintf(stderr, "ERROR: couldn't get queue size: %s\n",
131     alGetErrorString(oserror()));
132     alClosePort(port);
133     return false;
134     }
135     D(bug("alGetQueueSize %d\n", audio_frames_per_block));
136    
137     alZeroFrames(port, audio_frames_per_block); // so we don't underflow
138    
139     // Put a limit on the Mac sound buffer size, to decrease delay
140     if (audio_frames_per_block > 2048)
141     audio_frames_per_block = 2048;
142     // Try to keep the buffer pretty full. 5000 samples of slack works well.
143     sound_buffer_fill_point = alGetQueueSize(config) - 5000;
144     if (sound_buffer_fill_point < 0)
145     sound_buffer_fill_point = alGetQueueSize(config) / 3;
146     D(bug("fill point %d\n", sound_buffer_fill_point));
147    
148     sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block;
149    
150     // Get a file descriptor we can select() on
151    
152     audio_fd = alGetFD(port);
153     if (audio_fd < 0) {
154     fprintf(stderr, "ERROR: couldn't get libaudio file descriptor: %s\n",
155     alGetErrorString(oserror()));
156     alClosePort(port);
157     return false;
158     }
159    
160     return true;
161     }
162    
163    
164     /*
165     * Initialization
166     */
167    
168     void AudioInit(void)
169     {
170     // Init audio status (defaults) and feature flags
171     set_audio_status_format();
172     AudioStatus.mixer = 0;
173     AudioStatus.num_sources = 0;
174     audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut;
175    
176     // Sound disabled in prefs? Then do nothing
177     if (PrefsFindBool("nosound"))
178     return;
179    
180     // Try to open audio library
181     if (!audio_init_al())
182     return;
183    
184     // Init semaphore
185     if (sem_init(&audio_irq_done_sem, 0, 0) < 0)
186     return;
187     sem_inited = true;
188    
189     // Start streaming thread
190     pthread_attr_init(&stream_thread_attr);
191     #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
192     if (geteuid() == 0) {
193     pthread_attr_setinheritsched(&stream_thread_attr, PTHREAD_EXPLICIT_SCHED);
194     pthread_attr_setschedpolicy(&stream_thread_attr, SCHED_FIFO);
195     struct sched_param fifo_param;
196     fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2;
197     pthread_attr_setschedparam(&stream_thread_attr, &fifo_param);
198     }
199     #endif
200     stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
201    
202     // Everything OK
203     audio_open = true;
204     }
205    
206    
207     /*
208     * Deinitialization
209     */
210    
211     void AudioExit(void)
212     {
213     // Stop stream and delete semaphore
214     if (stream_thread_active) {
215     stream_thread_cancel = true;
216     #ifdef HAVE_PTHREAD_CANCEL
217     pthread_cancel(stream_thread);
218     #endif
219     pthread_join(stream_thread, NULL);
220     stream_thread_active = false;
221     }
222     if (sem_inited)
223     sem_destroy(&audio_irq_done_sem);
224    
225     // Close audio library
226     alClosePort(port);
227     }
228    
229    
230     /*
231     * First source added, start audio stream
232     */
233    
234     void audio_enter_stream()
235     {
236     // Streaming thread is always running to avoid clicking noises
237     }
238    
239    
240     /*
241     * Last source removed, stop audio stream
242     */
243    
244     void audio_exit_stream()
245     {
246     // Streaming thread is always running to avoid clicking noises
247     }
248    
249    
250     /*
251     * Streaming function
252     */
253    
254     static void *stream_func(void *arg)
255     {
256     int16 *last_buffer = new int16[sound_buffer_size / 2];
257     fd_set audio_fdset;
258     int numfds, was_error;
259    
260     numfds = audio_fd + 1;
261     FD_ZERO(&audio_fdset);
262    
263     while (!stream_thread_cancel) {
264     if (AudioStatus.num_sources) {
265    
266     // Trigger audio interrupt to get new buffer
267     D(bug("stream: triggering irq\n"));
268     SetInterruptFlag(INTFLAG_AUDIO);
269     TriggerInterrupt();
270     D(bug("stream: waiting for ack\n"));
271     sem_wait(&audio_irq_done_sem);
272     D(bug("stream: ack received\n"));
273    
274     uint32 apple_stream_info; // Mac address of SoundComponentData struct describing next buffer
275     // Get size of audio data
276     apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
277    
278     if (apple_stream_info) {
279     int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
280     D(bug("stream: work_size %d\n", work_size));
281     if (work_size > sound_buffer_size)
282     work_size = sound_buffer_size;
283     if (work_size == 0)
284     goto silence;
285    
286     // Send data to audio library
287     if (work_size == sound_buffer_size)
288     alWriteFrames(port, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), audio_frames_per_block);
289     else {
290     // Last buffer
291     Mac2Host_memcpy(last_buffer, ReadMacInt32(apple_stream_info + scd_buffer), work_size);
292     memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
293     alWriteFrames(port, last_buffer, audio_frames_per_block);
294     }
295     D(bug("stream: data written\n"));
296     } else
297     goto silence;
298    
299     } else {
300    
301     // Audio not active, play silence
302     silence: // D(bug("stream: silence\n"));
303     alZeroFrames(port, audio_frames_per_block);
304     }
305    
306     // Wait for fill point to be reached (may be immediate)
307    
308     if (alSetFillPoint(port, sound_buffer_fill_point) < 0) {
309     fprintf(stderr, "ERROR: alSetFillPoint failed: %s\n",
310     alGetErrorString(oserror()));
311     // Should stop the audio here....
312     }
313    
314     do {
315     errno = 0;
316     FD_SET(audio_fd, &audio_fdset);
317     was_error = select(numfds, NULL, &audio_fdset, NULL, NULL);
318     } while(was_error < 0 && (errno == EINTR));
319     if (was_error < 0) {
320     fprintf(stderr, "ERROR: select returned %d, errno %d\n",
321     was_error, errno);
322     // Should stop audio here....
323     }
324     }
325     delete[] last_buffer;
326     return NULL;
327     }
328    
329    
330     /*
331     * MacOS audio interrupt, read next data block
332     */
333    
334     void AudioInterrupt(void)
335     {
336     D(bug("AudioInterrupt\n"));
337    
338     // Get data from apple mixer
339     if (AudioStatus.mixer) {
340     M68kRegisters r;
341     r.a[0] = audio_data + adatStreamInfo;
342     r.a[1] = AudioStatus.mixer;
343     Execute68k(audio_data + adatGetSourceData, &r);
344     D(bug(" GetSourceData() returns %08lx\n", r.d[0]));
345     } else
346     WriteMacInt32(audio_data + adatStreamInfo, 0);
347    
348     // Signal stream function
349     sem_post(&audio_irq_done_sem);
350     D(bug("AudioInterrupt done\n"));
351     }
352    
353    
354     /*
355     * Set sampling parameters
356     * "index" is an index into the audio_sample_rates[] etc. arrays
357     * It is guaranteed that AudioStatus.num_sources == 0
358     */
359    
360     void audio_set_sample_rate(int index)
361     {
362     }
363    
364     void audio_set_sample_size(int index)
365     {
366     }
367    
368     void audio_set_channels(int index)
369     {
370     }
371    
372    
373     /*
374     * Get/set volume controls (volume values received/returned have the left channel
375     * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
376     * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
377     */
378    
379     bool audio_get_main_mute(void)
380     {
381     return false;
382     }
383    
384     uint32 audio_get_main_volume(void)
385     {
386     return 0x01000100;
387     }
388    
389     bool audio_get_speaker_mute(void)
390     {
391     return false;
392     }
393    
394     uint32 audio_get_speaker_volume(void)
395     {
396     return 0x01000100;
397     }
398    
399     void audio_set_main_mute(bool mute)
400     {
401     }
402    
403     void audio_set_main_volume(uint32 vol)
404     {
405     }
406    
407     void audio_set_speaker_mute(bool mute)
408     {
409     }
410    
411     void audio_set_speaker_volume(uint32 vol)
412     {
413     }