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