ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Irix/audio_irix.cpp
Revision: 1.5
Committed: 2002-01-15T14:58:38Z (22 years, 10 months ago) by cebix
Branch: MAIN
CVS Tags: snapshot-15012002
Changes since 1.4: +1 -1 lines
Log Message:
- documentation updates
- 2001 -> 2002
- version 0.9 -> 1.0

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 pthread_attr_init(&stream_thread_attr);
186 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
187 if (geteuid() == 0) {
188 pthread_attr_setinheritsched(&stream_thread_attr, PTHREAD_EXPLICIT_SCHED);
189 pthread_attr_setschedpolicy(&stream_thread_attr, SCHED_FIFO);
190 struct sched_param fifo_param;
191 fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2;
192 pthread_attr_setschedparam(&stream_thread_attr, &fifo_param);
193 }
194 #endif
195 stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
196
197 // Everything OK
198 audio_open = true;
199 }
200
201
202 /*
203 * Deinitialization
204 */
205
206 void AudioExit(void)
207 {
208 // Stop stream and delete semaphore
209 if (stream_thread_active) {
210 stream_thread_cancel = true;
211 #ifdef HAVE_PTHREAD_CANCEL
212 pthread_cancel(stream_thread);
213 #endif
214 pthread_join(stream_thread, NULL);
215 stream_thread_active = false;
216 }
217 if (sem_inited)
218 sem_destroy(&audio_irq_done_sem);
219
220 // Close audio library
221 alClosePort(port);
222 }
223
224
225 /*
226 * First source added, start audio stream
227 */
228
229 void audio_enter_stream()
230 {
231 // Streaming thread is always running to avoid clicking noises
232 }
233
234
235 /*
236 * Last source removed, stop audio stream
237 */
238
239 void audio_exit_stream()
240 {
241 // Streaming thread is always running to avoid clicking noises
242 }
243
244
245 /*
246 * Streaming function
247 */
248
249 static void *stream_func(void *arg)
250 {
251 int16 *last_buffer = new int16[sound_buffer_size / 2];
252 fd_set audio_fdset;
253 int numfds, was_error;
254
255 numfds = audio_fd + 1;
256 FD_ZERO(&audio_fdset);
257
258 while (!stream_thread_cancel) {
259 if (AudioStatus.num_sources) {
260
261 // Trigger audio interrupt to get new buffer
262 D(bug("stream: triggering irq\n"));
263 SetInterruptFlag(INTFLAG_AUDIO);
264 TriggerInterrupt();
265 D(bug("stream: waiting for ack\n"));
266 sem_wait(&audio_irq_done_sem);
267 D(bug("stream: ack received\n"));
268
269 uint32 apple_stream_info; // Mac address of SoundComponentData struct describing next buffer
270 // Get size of audio data
271 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
272
273 if (apple_stream_info) {
274 int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
275 D(bug("stream: work_size %d\n", work_size));
276 if (work_size > sound_buffer_size)
277 work_size = sound_buffer_size;
278 if (work_size == 0)
279 goto silence;
280
281 // Send data to audio library
282 if (work_size == sound_buffer_size)
283 alWriteFrames(port, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), audio_frames_per_block);
284 else {
285 // Last buffer
286 Mac2Host_memcpy(last_buffer, ReadMacInt32(apple_stream_info + scd_buffer), work_size);
287 memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
288 alWriteFrames(port, last_buffer, audio_frames_per_block);
289 }
290 D(bug("stream: data written\n"));
291 } else
292 goto silence;
293
294 } else {
295
296 // Audio not active, play silence
297 silence: // D(bug("stream: silence\n"));
298 alZeroFrames(port, audio_frames_per_block);
299 }
300
301 // Wait for fill point to be reached (may be immediate)
302
303 if (alSetFillPoint(port, sound_buffer_fill_point) < 0) {
304 fprintf(stderr, "ERROR: alSetFillPoint failed: %s\n",
305 alGetErrorString(oserror()));
306 // Should stop the audio here....
307 }
308
309 do {
310 errno = 0;
311 FD_SET(audio_fd, &audio_fdset);
312 was_error = select(numfds, NULL, &audio_fdset, NULL, NULL);
313 } while(was_error < 0 && (errno == EINTR));
314 if (was_error < 0) {
315 fprintf(stderr, "ERROR: select returned %d, errno %d\n",
316 was_error, errno);
317 // Should stop audio here....
318 }
319 }
320 delete[] last_buffer;
321 return NULL;
322 }
323
324
325 /*
326 * MacOS audio interrupt, read next data block
327 */
328
329 void AudioInterrupt(void)
330 {
331 D(bug("AudioInterrupt\n"));
332
333 // Get data from apple mixer
334 if (AudioStatus.mixer) {
335 M68kRegisters r;
336 r.a[0] = audio_data + adatStreamInfo;
337 r.a[1] = AudioStatus.mixer;
338 Execute68k(audio_data + adatGetSourceData, &r);
339 D(bug(" GetSourceData() returns %08lx\n", r.d[0]));
340 } else
341 WriteMacInt32(audio_data + adatStreamInfo, 0);
342
343 // Signal stream function
344 sem_post(&audio_irq_done_sem);
345 D(bug("AudioInterrupt done\n"));
346 }
347
348
349 /*
350 * Set sampling parameters
351 * "index" is an index into the audio_sample_rates[] etc. arrays
352 * It is guaranteed that AudioStatus.num_sources == 0
353 */
354
355 bool audio_set_sample_rate(int index)
356 {
357 return true;
358 }
359
360 bool audio_set_sample_size(int index)
361 {
362 return true;
363 }
364
365 bool audio_set_channels(int index)
366 {
367 return true;
368 }
369
370
371 /*
372 * Get/set volume controls (volume values received/returned have the left channel
373 * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
374 * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
375 */
376
377 bool audio_get_main_mute(void)
378 {
379 return false;
380 }
381
382 uint32 audio_get_main_volume(void)
383 {
384 return 0x01000100;
385 }
386
387 bool audio_get_speaker_mute(void)
388 {
389 return false;
390 }
391
392 uint32 audio_get_speaker_volume(void)
393 {
394 return 0x01000100;
395 }
396
397 void audio_set_main_mute(bool mute)
398 {
399 }
400
401 void audio_set_main_volume(uint32 vol)
402 {
403 }
404
405 void audio_set_speaker_mute(bool mute)
406 {
407 }
408
409 void audio_set_speaker_volume(uint32 vol)
410 {
411 }