ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/audio_oss_esd.cpp
Revision: 1.1
Committed: 1999-10-23T17:57:45Z (24 years, 8 months ago) by cebix
Branch: MAIN
Log Message:
- audio_linux.cpp renamed to audio_oss_esd.cpp (now also used under FreeBSD)
  and added support for ESD
- medium removal is allowed for CD-ROM on exit
- added mkinstalldirs to "make install" target

File Contents

# Content
1 /*
2 * audio_oss_esd.cpp - Audio support, implementation for OSS and ESD (Linux and FreeBSD)
3 *
4 * Basilisk II (C) 1997-1999 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 #ifdef __linux__
30 #include <linux/soundcard.h>
31 #endif
32
33 #ifdef __FreeBSD__
34 #include <machine/soundcard.h>
35 #endif
36
37 #include "cpu_emulation.h"
38 #include "main.h"
39 #include "prefs.h"
40 #include "user_strings.h"
41 #include "audio.h"
42 #include "audio_defs.h"
43
44 #if ENABLE_ESD
45 #include <esd.h>
46 #endif
47
48 #define DEBUG 0
49 #include "debug.h"
50
51
52 // Supported sample rates, sizes and channels
53 int audio_num_sample_rates = 1;
54 uint32 audio_sample_rates[] = {44100 << 16};
55 int audio_num_sample_sizes = 1;
56 uint16 audio_sample_sizes[] = {16};
57 int audio_num_channel_counts = 1;
58 uint16 audio_channel_counts[] = {2};
59
60 // Constants
61 #define DSP_NAME "/dev/dsp"
62
63 // Global variables
64 static int audio_fd = -1; // fd of /dev/dsp or ESD
65 static int mixer_fd = -1; // fd of /dev/mixer
66 static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read
67 static bool sem_inited = false; // Flag: audio_irq_done_sem initialized
68 static int sound_buffer_size; // Size of sound buffer in bytes
69 static bool little_endian = false; // Flag: DSP accepts only little-endian 16-bit sound data
70 static pthread_t stream_thread; // Audio streaming thread
71 static pthread_attr_t stream_thread_attr; // Streaming thread attributes
72 static bool stream_thread_active = false; // Flag: streaming thread installed
73 static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread
74
75 // Prototypes
76 static void *stream_func(void *arg);
77
78
79 /*
80 * Initialization
81 */
82
83 // Init using /dev/dsp, returns false on error
84 bool audio_init_dsp(void)
85 {
86 printf("Using " DSP_NAME " audio output\n");
87
88 // Get supported sample formats
89 unsigned long format;
90 ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format);
91 if ((format & (AFMT_U8 | AFMT_S16_BE | AFMT_S16_LE)) == 0) {
92 WarningAlert(GetString(STR_AUDIO_FORMAT_WARN));
93 close(audio_fd);
94 audio_fd = -1;
95 return false;
96 }
97 if (format & (AFMT_S16_BE | AFMT_S16_LE))
98 audio_sample_sizes[0] = 16;
99 else
100 audio_sample_sizes[0] = 8;
101 if (!(format & AFMT_S16_BE))
102 little_endian = true;
103
104 // Set DSP parameters
105 format = AudioStatus.sample_size == 8 ? AFMT_U8 : (little_endian ? AFMT_S16_LE : AFMT_S16_BE);
106 ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
107 int frag = 0x0004000c; // Block size: 4096 frames
108 ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag);
109 int stereo = (AudioStatus.channels == 2);
110 ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
111 int rate = AudioStatus.sample_rate >> 16;
112 ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate);
113
114 // Get sound buffer size
115 ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &audio_frames_per_block);
116 D(bug("DSP_GETBLKSIZE %d\n", audio_frames_per_block));
117 sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block;
118 return true;
119 }
120
121 // Init using ESD, returns false on error
122 bool audio_init_esd(void)
123 {
124 #if ENABLE_ESD
125 printf("Using ESD audio output\n");
126
127 // ESD audio format
128 esd_format_t format = ESD_STREAM | ESD_PLAY;
129 if (AudioStatus.sample_size == 8)
130 format |= ESD_BITS8;
131 else
132 format |= ESD_BITS16;
133 if (AudioStatus.channels == 1)
134 format |= ESD_MONO;
135 else
136 format |= ESD_STEREO;
137
138 #if WORDS_BIGENDIAN
139 little_endian = false;
140 #else
141 little_endian = true;
142 #endif
143
144 // Open connection to ESD server
145 audio_fd = esd_play_stream(format, AudioStatus.sample_rate >> 16, NULL, NULL);
146 if (audio_fd < 0) {
147 char str[256];
148 sprintf(str, GetString(STR_NO_ESD_WARN), strerror(errno));
149 WarningAlert(str);
150 return false;
151 }
152
153 // Sound buffer size = 4096 frames
154 audio_frames_per_block = 4096;
155 sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block;
156 return true;
157 #else
158 ErrorAlert("Basilisk II has been compiled with ESD support disabled.");
159 return false;
160 #endif
161 }
162
163 void AudioInit(void)
164 {
165 char str[256];
166
167 // Init audio status (defaults) and feature flags
168 AudioStatus.sample_rate = audio_sample_rates[0];
169 AudioStatus.sample_size = audio_sample_sizes[0];
170 AudioStatus.channels = audio_channel_counts[0];
171 AudioStatus.mixer = 0;
172 AudioStatus.num_sources = 0;
173 audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut;
174
175 // Sound disabled in prefs? Then do nothing
176 if (PrefsFindBool("nosound"))
177 return;
178
179 // Try to open /dev/dsp
180 audio_fd = open(DSP_NAME, O_WRONLY);
181 if (audio_fd < 0) {
182 #if ENABLE_ESD
183 if (!audio_init_esd())
184 return;
185 #else
186 sprintf(str, GetString(STR_NO_AUDIO_DEV_WARN), DSP_NAME, strerror(errno));
187 WarningAlert(str);
188 return;
189 #endif
190 } else
191 if (!audio_init_dsp())
192 return;
193
194 // Try to open /dev/mixer
195 mixer_fd = open("/dev/mixer", O_RDWR);
196 if (mixer_fd < 0)
197 printf("WARNING: Cannot open /dev/mixer (%s)", strerror(errno));
198
199 // Init semaphore
200 if (sem_init(&audio_irq_done_sem, 0, 0) < 0)
201 return;
202 sem_inited = true;
203
204 // Start streaming thread
205 pthread_attr_init(&stream_thread_attr);
206 #if defined(_POSIX_THREAD_PRIORITY_SCHEDULING)
207 if (geteuid() == 0) {
208 pthread_attr_setinheritsched(&stream_thread_attr, PTHREAD_EXPLICIT_SCHED);
209 pthread_attr_setschedpolicy(&stream_thread_attr, SCHED_FIFO);
210 struct sched_param fifo_param;
211 fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2;
212 pthread_attr_setschedparam(&stream_thread_attr, &fifo_param);
213 }
214 #endif
215 stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
216
217 // Everything OK
218 audio_open = true;
219 }
220
221
222 /*
223 * Deinitialization
224 */
225
226 void AudioExit(void)
227 {
228 // Stop stream and delete semaphore
229 if (stream_thread_active) {
230 stream_thread_cancel = true;
231 #ifdef HAVE_PTHREAD_CANCEL
232 pthread_cancel(stream_thread);
233 #endif
234 pthread_join(stream_thread, NULL);
235 stream_thread_active = false;
236 }
237 if (sem_inited)
238 sem_destroy(&audio_irq_done_sem);
239
240 // Close /dev/dsp
241 if (audio_fd > 0)
242 close(audio_fd);
243
244 // Close /dev/mixer
245 if (mixer_fd > 0)
246 close(mixer_fd);
247 }
248
249
250 /*
251 * First source added, start audio stream
252 */
253
254 void audio_enter_stream()
255 {
256 // Streaming thread is always running to avoid clicking noises
257 }
258
259
260 /*
261 * Last source removed, stop audio stream
262 */
263
264 void audio_exit_stream()
265 {
266 // Streaming thread is always running to avoid clicking noises
267 }
268
269
270 /*
271 * Streaming function
272 */
273
274 static uint32 apple_stream_info; // Mac address of SoundComponentData struct describing next buffer
275
276 static void *stream_func(void *arg)
277 {
278 int16 *silent_buffer = new int16[sound_buffer_size / 2];
279 int16 *last_buffer = new int16[sound_buffer_size / 2];
280 memset(silent_buffer, 0, sound_buffer_size);
281
282 while (!stream_thread_cancel) {
283 if (AudioStatus.num_sources) {
284
285 // Trigger audio interrupt to get new buffer
286 D(bug("stream: triggering irq\n"));
287 SetInterruptFlag(INTFLAG_AUDIO);
288 TriggerInterrupt();
289 D(bug("stream: waiting for ack\n"));
290 sem_wait(&audio_irq_done_sem);
291 D(bug("stream: ack received\n"));
292
293 // Get size of audio data
294 uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
295 if (apple_stream_info) {
296 int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
297 D(bug("stream: work_size %d\n", work_size));
298 if (work_size > sound_buffer_size)
299 work_size = sound_buffer_size;
300 if (work_size == 0)
301 goto silence;
302
303 // Send data to DSP
304 if (work_size == sound_buffer_size && !little_endian)
305 write(audio_fd, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), sound_buffer_size);
306 else {
307 // Last buffer or little-endian DSP
308 if (little_endian) {
309 int16 *p = (int16 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer));
310 for (int i=0; i<work_size/2; i++)
311 last_buffer[i] = ntohs(p[i]);
312 } else
313 memcpy(last_buffer, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), work_size);
314 memset((uint8 *)last_buffer + work_size, 0, sound_buffer_size - work_size);
315 write(audio_fd, last_buffer, sound_buffer_size);
316 }
317 D(bug("stream: data written\n"));
318 } else
319 goto silence;
320
321 } else {
322
323 // Audio not active, play silence
324 silence: write(audio_fd, silent_buffer, sound_buffer_size);
325 }
326 }
327 delete[] silent_buffer;
328 delete[] last_buffer;
329 return NULL;
330 }
331
332
333 /*
334 * MacOS audio interrupt, read next data block
335 */
336
337 void AudioInterrupt(void)
338 {
339 D(bug("AudioInterrupt\n"));
340
341 // Get data from apple mixer
342 if (AudioStatus.mixer) {
343 M68kRegisters r;
344 r.a[0] = audio_data + adatStreamInfo;
345 r.a[1] = AudioStatus.mixer;
346 Execute68k(audio_data + adatGetSourceData, &r);
347 D(bug(" GetSourceData() returns %08lx\n", r.d[0]));
348 } else
349 WriteMacInt32(audio_data + adatStreamInfo, 0);
350
351 // Signal stream function
352 sem_post(&audio_irq_done_sem);
353 D(bug("AudioInterrupt done\n"));
354 }
355
356
357 /*
358 * Set sampling parameters
359 * "index" is an index into the audio_sample_rates[] etc. arrays
360 * It is guaranteed that AudioStatus.num_sources == 0
361 */
362
363 void audio_set_sample_rate(int index)
364 {
365 }
366
367 void audio_set_sample_size(int index)
368 {
369 }
370
371 void audio_set_channels(int index)
372 {
373 }
374
375
376 /*
377 * Get/set volume controls (volume values received/returned have the left channel
378 * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
379 * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
380 */
381
382 bool audio_get_main_mute(void)
383 {
384 return false;
385 }
386
387 uint32 audio_get_main_volume(void)
388 {
389 if (mixer_fd >= 0) {
390 int vol;
391 if (ioctl(mixer_fd, SOUND_MIXER_READ_PCM, &vol) == 0) {
392 int left = vol >> 8;
393 int right = vol & 0xff;
394 return ((left * 256 / 100) << 16) | (right * 256 / 100);
395 }
396 }
397 return 0x01000100;
398 }
399
400 bool audio_get_speaker_mute(void)
401 {
402 return false;
403 }
404
405 uint32 audio_get_speaker_volume(void)
406 {
407 if (mixer_fd >= 0) {
408 int vol;
409 if (ioctl(mixer_fd, SOUND_MIXER_READ_VOLUME, &vol) == 0) {
410 int left = vol >> 8;
411 int right = vol & 0xff;
412 return ((left * 256 / 100) << 16) | (right * 256 / 100);
413 }
414 }
415 return 0x01000100;
416 }
417
418 void audio_set_main_mute(bool mute)
419 {
420 }
421
422 void audio_set_main_volume(uint32 vol)
423 {
424 if (mixer_fd >= 0) {
425 int left = vol >> 16;
426 int right = vol & 0xffff;
427 int p = ((left * 100 / 256) << 8) | (right * 100 / 256);
428 ioctl(mixer_fd, SOUND_MIXER_WRITE_PCM, &p);
429 }
430 }
431
432 void audio_set_speaker_mute(bool mute)
433 {
434 }
435
436 void audio_set_speaker_volume(uint32 vol)
437 {
438 if (mixer_fd >= 0) {
439 int left = vol >> 16;
440 int right = vol & 0xffff;
441 int p = ((left * 100 / 256) << 8) | (right * 100 / 256);
442 ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &p);
443 }
444 }