1 |
cebix |
1.1 |
/* |
2 |
|
|
* audio_irix.cpp - Audio support, SGI Irix implementation |
3 |
|
|
* |
4 |
gbeauche |
1.9 |
* Basilisk II (C) 1997-2005 Christian Bauer |
5 |
cebix |
1.1 |
* |
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 |
cebix |
1.7 |
// The currently selected audio parameters (indices in audio_sample_rates[] |
44 |
|
|
// etc. vectors) |
45 |
|
|
static int audio_sample_rate_index = 0; |
46 |
|
|
static int audio_sample_size_index = 0; |
47 |
|
|
static int audio_channel_count_index = 0; |
48 |
|
|
|
49 |
cebix |
1.1 |
// Global variables |
50 |
|
|
static int audio_fd = -1; // fd from audio library |
51 |
|
|
static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read |
52 |
|
|
static bool sem_inited = false; // Flag: audio_irq_done_sem initialized |
53 |
|
|
static int sound_buffer_size; // Size of sound buffer in bytes |
54 |
|
|
static int sound_buffer_fill_point; // Fill buffer when this many frames are empty |
55 |
|
|
static uint8 silence_byte = 0; // Byte value to use to fill sound buffers with silence |
56 |
|
|
static pthread_t stream_thread; // Audio streaming thread |
57 |
|
|
static pthread_attr_t stream_thread_attr; // Streaming thread attributes |
58 |
|
|
static bool stream_thread_active = false; // Flag: streaming thread installed |
59 |
|
|
static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread |
60 |
|
|
|
61 |
cebix |
1.7 |
static bool current_main_mute = false; // Flag: output muted |
62 |
|
|
static bool current_speaker_mute = false; // Flag: speaker muted |
63 |
|
|
static uint32 current_main_volume = 0; // Output volume |
64 |
|
|
static uint32 current_speaker_volume = 0; // Speaker volume |
65 |
|
|
|
66 |
cebix |
1.1 |
// IRIX libaudio control structures |
67 |
|
|
static ALconfig config; |
68 |
|
|
static ALport port; |
69 |
|
|
|
70 |
|
|
|
71 |
|
|
// Prototypes |
72 |
|
|
static void *stream_func(void *arg); |
73 |
cebix |
1.7 |
static uint32 read_volume(void); |
74 |
|
|
static bool read_mute(void); |
75 |
|
|
static void set_mute(bool mute); |
76 |
cebix |
1.1 |
|
77 |
|
|
|
78 |
|
|
/* |
79 |
|
|
* Initialization |
80 |
|
|
*/ |
81 |
|
|
|
82 |
|
|
// Set AudioStatus to reflect current audio stream format |
83 |
|
|
static void set_audio_status_format(void) |
84 |
|
|
{ |
85 |
cebix |
1.7 |
AudioStatus.sample_rate = audio_sample_rates[audio_sample_rate_index]; |
86 |
|
|
AudioStatus.sample_size = audio_sample_sizes[audio_sample_size_index]; |
87 |
|
|
AudioStatus.channels = audio_channel_counts[audio_channel_count_index]; |
88 |
cebix |
1.1 |
} |
89 |
|
|
|
90 |
cebix |
1.7 |
bool open_audio(void) |
91 |
cebix |
1.1 |
{ |
92 |
|
|
ALpv pv[2]; |
93 |
|
|
|
94 |
|
|
printf("Using libaudio audio output\n"); |
95 |
|
|
|
96 |
cebix |
1.7 |
// Get supported sample formats |
97 |
|
|
|
98 |
|
|
if (audio_sample_sizes.empty()) { |
99 |
|
|
// All sample sizes are supported |
100 |
|
|
audio_sample_sizes.push_back(8); |
101 |
|
|
audio_sample_sizes.push_back(16); |
102 |
|
|
|
103 |
|
|
// Assume at least two channels are supported. Some IRIX boxes |
104 |
|
|
// can do 4 or more... MacOS only handles up to 2. |
105 |
|
|
audio_channel_counts.push_back(1); |
106 |
|
|
audio_channel_counts.push_back(2); |
107 |
|
|
|
108 |
|
|
if (audio_sample_sizes.empty() || audio_channel_counts.empty()) { |
109 |
|
|
WarningAlert(GetString(STR_AUDIO_FORMAT_WARN)); |
110 |
|
|
alClosePort(port); |
111 |
|
|
audio_fd = -1; |
112 |
|
|
return false; |
113 |
|
|
} |
114 |
|
|
|
115 |
|
|
audio_sample_rates.push_back( 8000 << 16); |
116 |
|
|
audio_sample_rates.push_back(11025 << 16); |
117 |
|
|
audio_sample_rates.push_back(22050 << 16); |
118 |
|
|
audio_sample_rates.push_back(44100 << 16); |
119 |
|
|
|
120 |
|
|
// Default to highest supported values |
121 |
|
|
audio_sample_rate_index = audio_sample_rates.size() - 1; |
122 |
|
|
audio_sample_size_index = audio_sample_sizes.size() - 1; |
123 |
|
|
audio_channel_count_index = audio_channel_counts.size() - 1; |
124 |
|
|
} |
125 |
|
|
|
126 |
|
|
// Set the sample format |
127 |
cebix |
1.1 |
|
128 |
cebix |
1.7 |
D(bug("Size %d, channels %d, rate %d\n", |
129 |
|
|
audio_sample_sizes[audio_sample_size_index], |
130 |
|
|
audio_channel_counts[audio_channel_count_index], |
131 |
|
|
audio_sample_rates[audio_sample_rate_index] >> 16)); |
132 |
cebix |
1.1 |
config = alNewConfig(); |
133 |
|
|
alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP); |
134 |
cebix |
1.7 |
if (audio_sample_sizes[audio_sample_size_index] == 8) { |
135 |
|
|
alSetWidth(config, AL_SAMPLE_8); |
136 |
|
|
} |
137 |
|
|
else { |
138 |
|
|
alSetWidth(config, AL_SAMPLE_16); |
139 |
|
|
} |
140 |
|
|
alSetChannels(config, audio_channel_counts[audio_channel_count_index]); |
141 |
cebix |
1.1 |
alSetDevice(config, AL_DEFAULT_OUTPUT); // Allow selecting via prefs? |
142 |
|
|
|
143 |
cebix |
1.7 |
// Try to open the audio library |
144 |
|
|
|
145 |
cebix |
1.1 |
port = alOpenPort("BasiliskII", "w", config); |
146 |
|
|
if (port == NULL) { |
147 |
|
|
fprintf(stderr, "ERROR: Cannot open audio port: %s\n", |
148 |
|
|
alGetErrorString(oserror())); |
149 |
cebix |
1.7 |
WarningAlert(GetString(STR_NO_AUDIO_WARN)); |
150 |
cebix |
1.1 |
return false; |
151 |
|
|
} |
152 |
cebix |
1.7 |
|
153 |
cebix |
1.1 |
// Set the sample rate |
154 |
|
|
|
155 |
|
|
pv[0].param = AL_RATE; |
156 |
cebix |
1.7 |
pv[0].value.ll = alDoubleToFixed(audio_sample_rates[audio_sample_rate_index] >> 16); |
157 |
cebix |
1.1 |
pv[1].param = AL_MASTER_CLOCK; |
158 |
|
|
pv[1].value.i = AL_CRYSTAL_MCLK_TYPE; |
159 |
|
|
if (alSetParams(AL_DEFAULT_OUTPUT, pv, 2) < 0) { |
160 |
|
|
fprintf(stderr, "ERROR: libaudio setparams failed: %s\n", |
161 |
|
|
alGetErrorString(oserror())); |
162 |
|
|
alClosePort(port); |
163 |
|
|
return false; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
// Compute sound buffer size and libaudio refill point |
167 |
|
|
|
168 |
|
|
config = alGetConfig(port); |
169 |
|
|
audio_frames_per_block = alGetQueueSize(config); |
170 |
|
|
if (audio_frames_per_block < 0) { |
171 |
|
|
fprintf(stderr, "ERROR: couldn't get queue size: %s\n", |
172 |
|
|
alGetErrorString(oserror())); |
173 |
|
|
alClosePort(port); |
174 |
|
|
return false; |
175 |
|
|
} |
176 |
cebix |
1.7 |
D(bug("alGetQueueSize %d, width %d, channels %d\n", |
177 |
|
|
audio_frames_per_block, |
178 |
|
|
alGetWidth(config), |
179 |
|
|
alGetChannels(config))); |
180 |
|
|
|
181 |
|
|
// Put a limit on the Mac sound buffer size, to decrease delay |
182 |
|
|
#define AUDIO_BUFFER_MSEC 50 // milliseconds of sound to buffer |
183 |
|
|
int target_frames_per_block = |
184 |
|
|
(audio_sample_rates[audio_sample_rate_index] >> 16) * |
185 |
|
|
AUDIO_BUFFER_MSEC / 1000; |
186 |
|
|
if (audio_frames_per_block > target_frames_per_block) |
187 |
|
|
audio_frames_per_block = target_frames_per_block; |
188 |
|
|
D(bug("frames per block %d\n", audio_frames_per_block)); |
189 |
cebix |
1.1 |
|
190 |
|
|
alZeroFrames(port, audio_frames_per_block); // so we don't underflow |
191 |
|
|
|
192 |
cebix |
1.7 |
// Try to keep the buffer pretty full |
193 |
|
|
sound_buffer_fill_point = alGetQueueSize(config) - |
194 |
|
|
2 * audio_frames_per_block; |
195 |
cebix |
1.1 |
if (sound_buffer_fill_point < 0) |
196 |
|
|
sound_buffer_fill_point = alGetQueueSize(config) / 3; |
197 |
|
|
D(bug("fill point %d\n", sound_buffer_fill_point)); |
198 |
|
|
|
199 |
cebix |
1.7 |
sound_buffer_size = (audio_sample_sizes[audio_sample_size_index] >> 3) * |
200 |
|
|
audio_channel_counts[audio_channel_count_index] * |
201 |
|
|
audio_frames_per_block; |
202 |
|
|
set_audio_status_format(); |
203 |
cebix |
1.1 |
|
204 |
|
|
// Get a file descriptor we can select() on |
205 |
|
|
|
206 |
|
|
audio_fd = alGetFD(port); |
207 |
|
|
if (audio_fd < 0) { |
208 |
|
|
fprintf(stderr, "ERROR: couldn't get libaudio file descriptor: %s\n", |
209 |
|
|
alGetErrorString(oserror())); |
210 |
|
|
alClosePort(port); |
211 |
|
|
return false; |
212 |
|
|
} |
213 |
|
|
|
214 |
cebix |
1.7 |
// Initialize volume, mute settings |
215 |
|
|
current_main_volume = current_speaker_volume = read_volume(); |
216 |
|
|
current_main_mute = current_speaker_mute = read_mute(); |
217 |
|
|
|
218 |
|
|
|
219 |
|
|
// Start streaming thread |
220 |
|
|
Set_pthread_attr(&stream_thread_attr, 0); |
221 |
|
|
stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0); |
222 |
|
|
|
223 |
|
|
// Everything went fine |
224 |
|
|
audio_open = true; |
225 |
cebix |
1.1 |
return true; |
226 |
|
|
} |
227 |
|
|
|
228 |
|
|
void AudioInit(void) |
229 |
|
|
{ |
230 |
cebix |
1.7 |
// Init audio status (reasonable defaults) and feature flags |
231 |
|
|
AudioStatus.sample_rate = 44100 << 16; |
232 |
|
|
AudioStatus.sample_size = 16; |
233 |
|
|
AudioStatus.channels = 2; |
234 |
cebix |
1.1 |
AudioStatus.mixer = 0; |
235 |
|
|
AudioStatus.num_sources = 0; |
236 |
|
|
audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut; |
237 |
|
|
|
238 |
|
|
// Sound disabled in prefs? Then do nothing |
239 |
|
|
if (PrefsFindBool("nosound")) |
240 |
|
|
return; |
241 |
|
|
|
242 |
|
|
// Init semaphore |
243 |
|
|
if (sem_init(&audio_irq_done_sem, 0, 0) < 0) |
244 |
|
|
return; |
245 |
|
|
sem_inited = true; |
246 |
|
|
|
247 |
cebix |
1.7 |
// Open and initialize audio device |
248 |
|
|
open_audio(); |
249 |
cebix |
1.1 |
} |
250 |
|
|
|
251 |
|
|
|
252 |
|
|
/* |
253 |
|
|
* Deinitialization |
254 |
|
|
*/ |
255 |
|
|
|
256 |
cebix |
1.7 |
static void close_audio(void) |
257 |
cebix |
1.1 |
{ |
258 |
|
|
// Stop stream and delete semaphore |
259 |
|
|
if (stream_thread_active) { |
260 |
|
|
stream_thread_cancel = true; |
261 |
|
|
#ifdef HAVE_PTHREAD_CANCEL |
262 |
|
|
pthread_cancel(stream_thread); |
263 |
|
|
#endif |
264 |
|
|
pthread_join(stream_thread, NULL); |
265 |
|
|
stream_thread_active = false; |
266 |
cebix |
1.7 |
stream_thread_cancel = false; |
267 |
cebix |
1.1 |
} |
268 |
|
|
|
269 |
|
|
// Close audio library |
270 |
|
|
alClosePort(port); |
271 |
cebix |
1.7 |
|
272 |
|
|
audio_open = false; |
273 |
|
|
} |
274 |
|
|
|
275 |
|
|
void AudioExit(void) |
276 |
|
|
{ |
277 |
|
|
// Close audio device |
278 |
|
|
close_audio(); |
279 |
|
|
|
280 |
|
|
// Delete semaphore |
281 |
|
|
if (sem_inited) { |
282 |
|
|
sem_destroy(&audio_irq_done_sem); |
283 |
|
|
sem_inited = false; |
284 |
|
|
} |
285 |
cebix |
1.1 |
} |
286 |
|
|
|
287 |
|
|
|
288 |
|
|
/* |
289 |
|
|
* First source added, start audio stream |
290 |
|
|
*/ |
291 |
|
|
|
292 |
|
|
void audio_enter_stream() |
293 |
|
|
{ |
294 |
|
|
// Streaming thread is always running to avoid clicking noises |
295 |
|
|
} |
296 |
|
|
|
297 |
|
|
|
298 |
|
|
/* |
299 |
|
|
* Last source removed, stop audio stream |
300 |
|
|
*/ |
301 |
|
|
|
302 |
|
|
void audio_exit_stream() |
303 |
|
|
{ |
304 |
|
|
// Streaming thread is always running to avoid clicking noises |
305 |
|
|
} |
306 |
|
|
|
307 |
|
|
|
308 |
|
|
/* |
309 |
|
|
* Streaming function |
310 |
|
|
*/ |
311 |
|
|
|
312 |
|
|
static void *stream_func(void *arg) |
313 |
|
|
{ |
314 |
cebix |
1.7 |
int32 *last_buffer = new int32[sound_buffer_size / 4]; |
315 |
cebix |
1.1 |
fd_set audio_fdset; |
316 |
|
|
int numfds, was_error; |
317 |
|
|
|
318 |
|
|
numfds = audio_fd + 1; |
319 |
|
|
FD_ZERO(&audio_fdset); |
320 |
|
|
|
321 |
|
|
while (!stream_thread_cancel) { |
322 |
|
|
if (AudioStatus.num_sources) { |
323 |
|
|
|
324 |
|
|
// Trigger audio interrupt to get new buffer |
325 |
|
|
D(bug("stream: triggering irq\n")); |
326 |
|
|
SetInterruptFlag(INTFLAG_AUDIO); |
327 |
|
|
TriggerInterrupt(); |
328 |
|
|
D(bug("stream: waiting for ack\n")); |
329 |
|
|
sem_wait(&audio_irq_done_sem); |
330 |
|
|
D(bug("stream: ack received\n")); |
331 |
|
|
|
332 |
|
|
// Get size of audio data |
333 |
cebix |
1.7 |
uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo); |
334 |
|
|
if (!current_main_mute && |
335 |
|
|
!current_speaker_mute && |
336 |
|
|
apple_stream_info) { |
337 |
cebix |
1.1 |
int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels; |
338 |
|
|
D(bug("stream: work_size %d\n", work_size)); |
339 |
|
|
if (work_size > sound_buffer_size) |
340 |
|
|
work_size = sound_buffer_size; |
341 |
|
|
if (work_size == 0) |
342 |
|
|
goto silence; |
343 |
|
|
|
344 |
cebix |
1.7 |
// Send data to audio library. Convert 8-bit data |
345 |
|
|
// unsigned->signed, using same algorithm as audio_amiga.cpp. |
346 |
|
|
// It works fine for 8-bit mono, but not stereo. |
347 |
|
|
if (AudioStatus.sample_size == 8) { |
348 |
|
|
uint32 *p = (uint32 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)); |
349 |
|
|
uint32 *q = (uint32 *)last_buffer; |
350 |
|
|
int r = work_size >> 2; |
351 |
|
|
// XXX not quite right.... |
352 |
|
|
while (r--) |
353 |
|
|
*q++ = *p++ ^ 0x80808080; |
354 |
|
|
if (work_size != sound_buffer_size) |
355 |
|
|
memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size); |
356 |
|
|
alWriteFrames(port, last_buffer, audio_frames_per_block); |
357 |
|
|
} |
358 |
|
|
else if (work_size == sound_buffer_size) |
359 |
cebix |
1.1 |
alWriteFrames(port, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), audio_frames_per_block); |
360 |
|
|
else { |
361 |
|
|
// Last buffer |
362 |
|
|
Mac2Host_memcpy(last_buffer, ReadMacInt32(apple_stream_info + scd_buffer), work_size); |
363 |
|
|
memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size); |
364 |
|
|
alWriteFrames(port, last_buffer, audio_frames_per_block); |
365 |
|
|
} |
366 |
|
|
D(bug("stream: data written\n")); |
367 |
|
|
} else |
368 |
|
|
goto silence; |
369 |
|
|
|
370 |
|
|
} else { |
371 |
|
|
|
372 |
|
|
// Audio not active, play silence |
373 |
|
|
silence: // D(bug("stream: silence\n")); |
374 |
|
|
alZeroFrames(port, audio_frames_per_block); |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
// Wait for fill point to be reached (may be immediate) |
378 |
|
|
|
379 |
|
|
if (alSetFillPoint(port, sound_buffer_fill_point) < 0) { |
380 |
|
|
fprintf(stderr, "ERROR: alSetFillPoint failed: %s\n", |
381 |
|
|
alGetErrorString(oserror())); |
382 |
|
|
// Should stop the audio here.... |
383 |
|
|
} |
384 |
|
|
|
385 |
|
|
do { |
386 |
|
|
errno = 0; |
387 |
|
|
FD_SET(audio_fd, &audio_fdset); |
388 |
|
|
was_error = select(numfds, NULL, &audio_fdset, NULL, NULL); |
389 |
|
|
} while(was_error < 0 && (errno == EINTR)); |
390 |
|
|
if (was_error < 0) { |
391 |
|
|
fprintf(stderr, "ERROR: select returned %d, errno %d\n", |
392 |
|
|
was_error, errno); |
393 |
|
|
// Should stop audio here.... |
394 |
|
|
} |
395 |
|
|
} |
396 |
|
|
delete[] last_buffer; |
397 |
|
|
return NULL; |
398 |
|
|
} |
399 |
|
|
|
400 |
|
|
|
401 |
|
|
/* |
402 |
cebix |
1.7 |
* Read or set the current output volume using the audio library |
403 |
|
|
*/ |
404 |
|
|
|
405 |
|
|
static uint32 read_volume(void) |
406 |
|
|
{ |
407 |
|
|
ALpv x[2]; |
408 |
|
|
ALfixed gain[8]; |
409 |
|
|
double maxgain, mingain; |
410 |
|
|
ALparamInfo pi; |
411 |
|
|
uint32 ret = 0x01000100; // default, maximum value |
412 |
|
|
int dev = alGetDevice(config); |
413 |
|
|
|
414 |
|
|
// Fetch the maximum and minimum gain settings |
415 |
|
|
|
416 |
|
|
alGetParamInfo(dev, AL_GAIN, &pi); |
417 |
|
|
maxgain = alFixedToDouble(pi.max.ll); |
418 |
|
|
mingain = alFixedToDouble(pi.min.ll); |
419 |
|
|
// printf("maxgain = %lf dB, mingain = %lf dB\n", maxgain, mingain); |
420 |
|
|
|
421 |
|
|
// Get the current gain values |
422 |
|
|
|
423 |
|
|
x[0].param = AL_GAIN; |
424 |
|
|
x[0].value.ptr = gain; |
425 |
|
|
x[0].sizeIn = sizeof(gain) / sizeof(gain[0]); |
426 |
|
|
x[1].param = AL_CHANNELS; |
427 |
|
|
if (alGetParams(dev, x, 2) < 0) { |
428 |
|
|
printf("alGetParams failed: %s\n", alGetErrorString(oserror())); |
429 |
|
|
} |
430 |
|
|
else { |
431 |
|
|
if (x[0].sizeOut < 0) { |
432 |
|
|
printf("AL_GAIN was an unrecognized parameter\n"); |
433 |
|
|
} |
434 |
|
|
else { |
435 |
|
|
double v; |
436 |
|
|
uint32 left, right; |
437 |
|
|
|
438 |
|
|
// Left |
439 |
|
|
v = alFixedToDouble(gain[0]); |
440 |
|
|
if (v < mingain) |
441 |
|
|
v = mingain; // handle gain == -inf |
442 |
|
|
v = (v - mingain) / (maxgain - mingain); // scale to 0..1 |
443 |
|
|
left = (uint32)(v * (double)256); // convert to 8.8 fixed point |
444 |
|
|
|
445 |
|
|
// Right |
446 |
|
|
if (x[0].sizeOut <= 1) { // handle a mono interface |
447 |
|
|
right = left; |
448 |
|
|
} |
449 |
|
|
else { |
450 |
|
|
v = alFixedToDouble(gain[1]); |
451 |
|
|
if (v < mingain) |
452 |
|
|
v = mingain; // handle gain == -inf |
453 |
|
|
v = (v - mingain) / (maxgain - mingain); // scale to 0..1 |
454 |
|
|
right = (uint32)(v * (double)256); // convert to 8.8 fixed point |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
ret = (left << 16) | right; |
458 |
|
|
} |
459 |
|
|
} |
460 |
|
|
|
461 |
|
|
return ret; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
static void set_volume(uint32 vol) |
465 |
|
|
{ |
466 |
|
|
ALpv x[1]; |
467 |
|
|
ALfixed gain[2]; // left and right |
468 |
|
|
double maxgain, mingain; |
469 |
|
|
ALparamInfo pi; |
470 |
|
|
int dev = alGetDevice(config); |
471 |
|
|
|
472 |
|
|
// Fetch the maximum and minimum gain settings |
473 |
|
|
|
474 |
|
|
alGetParamInfo(dev, AL_GAIN, &pi); |
475 |
|
|
maxgain = alFixedToDouble(pi.max.ll); |
476 |
|
|
mingain = alFixedToDouble(pi.min.ll); |
477 |
|
|
|
478 |
|
|
// Set the new gain values |
479 |
|
|
|
480 |
|
|
x[0].param = AL_GAIN; |
481 |
|
|
x[0].value.ptr = gain; |
482 |
|
|
x[0].sizeIn = sizeof(gain) / sizeof(gain[0]); |
483 |
|
|
|
484 |
|
|
uint32 left = vol >> 16; |
485 |
|
|
uint32 right = vol & 0xffff; |
486 |
|
|
double lv, rv; |
487 |
|
|
|
488 |
|
|
if (left == 0 && pi.specialVals & AL_NEG_INFINITY_BIT) { |
489 |
|
|
lv = AL_NEG_INFINITY; |
490 |
|
|
} |
491 |
|
|
else { |
492 |
|
|
lv = ((double)left / 256) * (maxgain - mingain) + mingain; |
493 |
|
|
} |
494 |
|
|
|
495 |
|
|
if (right == 0 && pi.specialVals & AL_NEG_INFINITY_BIT) { |
496 |
|
|
rv = AL_NEG_INFINITY; |
497 |
|
|
} |
498 |
|
|
else { |
499 |
|
|
rv = ((double)right / 256) * (maxgain - mingain) + mingain; |
500 |
|
|
} |
501 |
|
|
|
502 |
|
|
D(bug("set_volume: left=%lf dB, right=%lf dB\n", lv, rv)); |
503 |
|
|
|
504 |
|
|
gain[0] = alDoubleToFixed(lv); |
505 |
|
|
gain[1] = alDoubleToFixed(rv); |
506 |
|
|
|
507 |
|
|
if (alSetParams(dev, x, 1) < 0) { |
508 |
|
|
printf("alSetParams failed: %s\n", alGetErrorString(oserror())); |
509 |
|
|
} |
510 |
|
|
} |
511 |
|
|
|
512 |
|
|
|
513 |
|
|
/* |
514 |
|
|
* Read or set the mute setting using the audio library |
515 |
|
|
*/ |
516 |
|
|
|
517 |
|
|
static bool read_mute(void) |
518 |
|
|
{ |
519 |
|
|
bool ret; |
520 |
|
|
int dev = alGetDevice(config); |
521 |
|
|
ALpv x; |
522 |
|
|
x.param = AL_MUTE; |
523 |
|
|
|
524 |
|
|
if (alGetParams(dev, &x, 1) < 0) { |
525 |
|
|
printf("alSetParams failed: %s\n", alGetErrorString(oserror())); |
526 |
|
|
return current_main_mute; // Or just return false? |
527 |
|
|
} |
528 |
|
|
|
529 |
|
|
ret = x.value.i; |
530 |
|
|
|
531 |
|
|
D(bug("read_mute: mute=%d\n", ret)); |
532 |
|
|
return ret; |
533 |
|
|
} |
534 |
|
|
|
535 |
|
|
static void set_mute(bool mute) |
536 |
|
|
{ |
537 |
|
|
D(bug("set_mute: mute=%ld\n", mute)); |
538 |
|
|
|
539 |
|
|
int dev = alGetDevice(config); |
540 |
|
|
ALpv x; |
541 |
|
|
x.param = AL_MUTE; |
542 |
|
|
x.value.i = mute; |
543 |
|
|
|
544 |
|
|
if (alSetParams(dev, &x, 1) < 0) { |
545 |
|
|
printf("alSetParams failed: %s\n", alGetErrorString(oserror())); |
546 |
|
|
} |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
|
550 |
|
|
|
551 |
|
|
/* |
552 |
cebix |
1.1 |
* MacOS audio interrupt, read next data block |
553 |
|
|
*/ |
554 |
|
|
|
555 |
|
|
void AudioInterrupt(void) |
556 |
|
|
{ |
557 |
|
|
D(bug("AudioInterrupt\n")); |
558 |
|
|
|
559 |
|
|
// Get data from apple mixer |
560 |
|
|
if (AudioStatus.mixer) { |
561 |
|
|
M68kRegisters r; |
562 |
|
|
r.a[0] = audio_data + adatStreamInfo; |
563 |
|
|
r.a[1] = AudioStatus.mixer; |
564 |
|
|
Execute68k(audio_data + adatGetSourceData, &r); |
565 |
|
|
D(bug(" GetSourceData() returns %08lx\n", r.d[0])); |
566 |
|
|
} else |
567 |
|
|
WriteMacInt32(audio_data + adatStreamInfo, 0); |
568 |
|
|
|
569 |
|
|
// Signal stream function |
570 |
|
|
sem_post(&audio_irq_done_sem); |
571 |
|
|
D(bug("AudioInterrupt done\n")); |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
|
575 |
|
|
/* |
576 |
|
|
* Set sampling parameters |
577 |
cebix |
1.7 |
* "index" is an index into the audio_sample_rates[] etc. vectors |
578 |
cebix |
1.1 |
* It is guaranteed that AudioStatus.num_sources == 0 |
579 |
|
|
*/ |
580 |
|
|
|
581 |
cebix |
1.4 |
bool audio_set_sample_rate(int index) |
582 |
cebix |
1.1 |
{ |
583 |
cebix |
1.7 |
close_audio(); |
584 |
|
|
audio_sample_rate_index = index; |
585 |
|
|
return open_audio(); |
586 |
cebix |
1.1 |
} |
587 |
|
|
|
588 |
cebix |
1.4 |
bool audio_set_sample_size(int index) |
589 |
cebix |
1.1 |
{ |
590 |
cebix |
1.7 |
close_audio(); |
591 |
|
|
audio_sample_size_index = index; |
592 |
|
|
return open_audio(); |
593 |
cebix |
1.1 |
} |
594 |
|
|
|
595 |
cebix |
1.4 |
bool audio_set_channels(int index) |
596 |
cebix |
1.1 |
{ |
597 |
cebix |
1.7 |
close_audio(); |
598 |
|
|
audio_channel_count_index = index; |
599 |
|
|
return open_audio(); |
600 |
cebix |
1.1 |
} |
601 |
|
|
|
602 |
|
|
|
603 |
|
|
/* |
604 |
|
|
* Get/set volume controls (volume values received/returned have the left channel |
605 |
|
|
* volume in the upper 16 bits and the right channel volume in the lower 16 bits; |
606 |
|
|
* both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume")) |
607 |
|
|
*/ |
608 |
|
|
|
609 |
|
|
bool audio_get_main_mute(void) |
610 |
|
|
{ |
611 |
cebix |
1.7 |
D(bug("audio_get_main_mute: mute=%ld\n", current_main_mute)); |
612 |
|
|
|
613 |
|
|
return current_main_mute; |
614 |
cebix |
1.1 |
} |
615 |
|
|
|
616 |
|
|
uint32 audio_get_main_volume(void) |
617 |
|
|
{ |
618 |
cebix |
1.7 |
uint32 ret = current_main_volume; |
619 |
|
|
|
620 |
|
|
D(bug("audio_get_main_volume: vol=0x%x\n", ret)); |
621 |
|
|
|
622 |
|
|
return ret; |
623 |
cebix |
1.1 |
} |
624 |
|
|
|
625 |
|
|
bool audio_get_speaker_mute(void) |
626 |
|
|
{ |
627 |
cebix |
1.7 |
D(bug("audio_get_speaker_mute: mute=%ld\n", current_speaker_mute)); |
628 |
|
|
|
629 |
|
|
return current_speaker_mute; |
630 |
cebix |
1.1 |
} |
631 |
|
|
|
632 |
|
|
uint32 audio_get_speaker_volume(void) |
633 |
|
|
{ |
634 |
cebix |
1.7 |
uint32 ret = current_speaker_volume; |
635 |
|
|
|
636 |
|
|
D(bug("audio_get_speaker_volume: vol=0x%x\n", ret)); |
637 |
|
|
|
638 |
|
|
return ret; |
639 |
cebix |
1.1 |
} |
640 |
|
|
|
641 |
|
|
void audio_set_main_mute(bool mute) |
642 |
|
|
{ |
643 |
cebix |
1.7 |
D(bug("audio_set_main_mute: mute=%ld\n", mute)); |
644 |
|
|
|
645 |
|
|
if (mute != current_main_mute) { |
646 |
|
|
current_main_mute = mute; |
647 |
|
|
} |
648 |
|
|
|
649 |
|
|
set_mute(current_main_mute); |
650 |
cebix |
1.1 |
} |
651 |
|
|
|
652 |
|
|
void audio_set_main_volume(uint32 vol) |
653 |
|
|
{ |
654 |
cebix |
1.7 |
|
655 |
|
|
D(bug("audio_set_main_volume: vol=%x\n", vol)); |
656 |
|
|
|
657 |
|
|
current_main_volume = vol; |
658 |
|
|
|
659 |
|
|
set_volume(vol); |
660 |
cebix |
1.1 |
} |
661 |
|
|
|
662 |
|
|
void audio_set_speaker_mute(bool mute) |
663 |
|
|
{ |
664 |
cebix |
1.7 |
D(bug("audio_set_speaker_mute: mute=%ld\n", mute)); |
665 |
|
|
|
666 |
|
|
if (mute != current_speaker_mute) { |
667 |
|
|
current_speaker_mute = mute; |
668 |
|
|
} |
669 |
|
|
|
670 |
|
|
set_mute(current_speaker_mute); |
671 |
cebix |
1.1 |
} |
672 |
|
|
|
673 |
|
|
void audio_set_speaker_volume(uint32 vol) |
674 |
|
|
{ |
675 |
cebix |
1.7 |
D(bug("audio_set_speaker_volume: vol=%x\n", vol)); |
676 |
|
|
|
677 |
|
|
current_speaker_volume = vol; |
678 |
|
|
|
679 |
|
|
set_volume(vol); |
680 |
cebix |
1.1 |
} |