ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Irix/audio_irix.cpp
Revision: 1.6
Committed: 2002-02-07T16:10:56Z (22 years, 9 months ago) by cebix
Branch: MAIN
CVS Tags: nigel-build-12, nigel-build-13
Changes since 1.5: +1 -10 lines
Log Message:
cleaned up pthread attributes [Brian Johnson]

File Contents

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