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 (24 years 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

# Content
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 }