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 (25 years, 1 month 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

# User Rev Content
1 cebix 1.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     }