ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/AmigaOS/audio_amiga.cpp
Revision: 1.11
Committed: 2002-06-23T08:27:05Z (22 years, 5 months ago) by jlachmann
Branch: MAIN
CVS Tags: nigel-build-19, nigel-build-12, nigel-build-13, nigel-build-16, nigel-build-17, nigel-build-15
Changes since 1.10: +240 -34 lines
Log Message:
Adapted to OO video scheme; Audio volume/muting/sample rate now settable

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * audio_amiga.cpp - Audio support, AmigaOS implementation using AHI
3     *
4 jlachmann 1.11 * Basilisk II (C) 1997-2001 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 <exec/types.h>
24     #include <exec/memory.h>
25     #include <devices/ahi.h>
26 jlachmann 1.11 #define __USE_SYSBASE
27 cebix 1.1 #include <proto/exec.h>
28     #include <proto/ahi.h>
29 jlachmann 1.11 #include <inline/exec.h>
30     #include <inline/ahi.h>
31 cebix 1.1
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 jlachmann 1.11 #define D1(x) ;
43    
44 cebix 1.1
45     // Global variables
46     static ULONG ahi_id = AHI_DEFAULT_ID; // AHI audio ID
47     static struct AHIAudioCtrl *ahi_ctrl = NULL;
48     static struct AHISampleInfo sample[2]; // Two sample infos for double-buffering
49     static struct Hook sf_hook;
50     static int play_buf = 0; // Number of currently played buffer
51     static long sound_buffer_size; // Size of one audio buffer in bytes
52     static int audio_block_fetched = 0; // Number of audio blocks fetched by interrupt routine
53    
54 jlachmann 1.11 static bool main_mute = false;
55     static bool speaker_mute = false;
56     static ULONG supports_volume_changes = false;
57     static ULONG supports_stereo_panning = false;
58     static ULONG current_main_volume;
59     static ULONG current_speaker_volume;
60    
61 cebix 1.1
62     // Prototypes
63 cebix 1.4 static __saveds __attribute__((regparm(3))) ULONG audio_callback(struct Hook *hook /*a0*/, struct AHISoundMessage *msg /*a1*/, struct AHIAudioCtrl *ahi_ctrl /*a2*/);
64 jlachmann 1.11 void audio_set_sample_rate_byval(uint32 value);
65     void audio_set_sample_size_byval(uint32 value);
66     void audio_set_channels_byval(uint32 value);
67 cebix 1.1
68    
69     /*
70     * Initialization
71     */
72    
73 cebix 1.9 // Set AudioStatus to reflect current audio stream format
74 jlachmann 1.11 static void set_audio_status_format(int sample_rate_index)
75 cebix 1.9 {
76 jlachmann 1.11 AudioStatus.sample_rate = audio_sample_rates[sample_rate_index];
77 cebix 1.9 AudioStatus.sample_size = audio_sample_sizes[0];
78     AudioStatus.channels = audio_channel_counts[0];
79     }
80    
81 cebix 1.1 void AudioInit(void)
82     {
83     sample[0].ahisi_Address = sample[1].ahisi_Address = NULL;
84    
85     // Init audio status and feature flags
86 cebix 1.9 audio_channel_counts.push_back(2);
87 jlachmann 1.11 // set_audio_status_format();
88 cebix 1.1 AudioStatus.mixer = 0;
89     AudioStatus.num_sources = 0;
90     audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut;
91    
92     // Sound disabled in prefs? Then do nothing
93     if (PrefsFindBool("nosound"))
94     return;
95    
96     // AHI available?
97     if (AHIBase == NULL) {
98     WarningAlert(GetString(STR_NO_AHI_WARN));
99     return;
100     }
101    
102     // Initialize callback hook
103     sf_hook.h_Entry = (HOOKFUNC)audio_callback;
104    
105     // Read "sound" preferences
106     const char *str = PrefsFindString("sound");
107     if (str)
108     sscanf(str, "ahi/%08lx", &ahi_id);
109    
110     // Open audio control structure
111     if ((ahi_ctrl = AHI_AllocAudio(
112     AHIA_AudioID, ahi_id,
113     AHIA_MixFreq, AudioStatus.sample_rate >> 16,
114     AHIA_Channels, 1,
115     AHIA_Sounds, 2,
116     AHIA_SoundFunc, (ULONG)&sf_hook,
117     TAG_END)) == NULL) {
118     WarningAlert(GetString(STR_NO_AHI_CTRL_WARN));
119     return;
120     }
121    
122 jlachmann 1.11 ULONG max_channels, sample_rate, frequencies, sample_rate_index;
123    
124     AHI_GetAudioAttrs(ahi_id, ahi_ctrl,
125     AHIDB_MaxChannels, (ULONG) &max_channels,
126     AHIDB_Frequencies, (ULONG) &frequencies,
127     TAG_END);
128    
129     D(bug("AudioInit: max_channels=%ld frequencies=%ld\n", max_channels, frequencies));
130    
131     for (int n=0; n<frequencies; n++)
132     {
133     AHI_GetAudioAttrs(ahi_id, ahi_ctrl,
134     AHIDB_FrequencyArg, n,
135     AHIDB_Frequency, (ULONG) &sample_rate,
136     TAG_END);
137    
138     D(bug("AudioInit: f=%ld Hz\n", sample_rate));
139     audio_sample_rates.push_back(sample_rate << 16);
140     }
141    
142     ULONG sample_size_bits = 16;
143    
144     D(bug("AudioInit: sampe_rates=%ld\n", audio_sample_rates.size() ));
145    
146     // get index of sample rate closest to 22050 Hz
147     AHI_GetAudioAttrs(ahi_id, ahi_ctrl,
148     AHIDB_IndexArg, 22050,
149     AHIDB_Bits, (ULONG) &sample_size_bits,
150     AHIDB_Index, (ULONG) &sample_rate_index,
151     AHIDB_Volume, (ULONG) &supports_volume_changes,
152     AHIDB_Panning, (ULONG) &supports_stereo_panning,
153     TAG_END);
154    
155     audio_sample_sizes.push_back(16);
156    
157     set_audio_status_format(sample_rate_index);
158    
159 cebix 1.1 // 2048 frames per block
160     audio_frames_per_block = 2048;
161     sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block;
162    
163     // Prepare SampleInfos and load sounds (two sounds for double buffering)
164     sample[0].ahisi_Type = AudioStatus.sample_size == 16 ? AHIST_S16S : AHIST_S8S;
165     sample[0].ahisi_Length = audio_frames_per_block;
166     sample[0].ahisi_Address = AllocVec(sound_buffer_size, MEMF_PUBLIC | MEMF_CLEAR);
167     sample[1].ahisi_Type = AudioStatus.sample_size == 16 ? AHIST_S16S : AHIST_S8S;
168     sample[1].ahisi_Length = audio_frames_per_block;
169     sample[1].ahisi_Address = AllocVec(sound_buffer_size, MEMF_PUBLIC | MEMF_CLEAR);
170     if (sample[0].ahisi_Address == NULL || sample[1].ahisi_Address == NULL)
171     return;
172 jlachmann 1.11
173 cebix 1.1 AHI_LoadSound(0, AHIST_DYNAMICSAMPLE, &sample[0], ahi_ctrl);
174     AHI_LoadSound(1, AHIST_DYNAMICSAMPLE, &sample[1], ahi_ctrl);
175    
176     // Set parameters
177     play_buf = 0;
178 jlachmann 1.11 current_main_volume = current_speaker_volume = 0x10000;
179     AHI_SetVol(0, current_speaker_volume, 0x8000, ahi_ctrl, AHISF_IMM);
180    
181 cebix 1.1 AHI_SetFreq(0, AudioStatus.sample_rate >> 16, ahi_ctrl, AHISF_IMM);
182     AHI_SetSound(0, play_buf, 0, 0, ahi_ctrl, AHISF_IMM);
183    
184     // Everything OK
185     audio_open = true;
186     }
187    
188    
189     /*
190     * Deinitialization
191     */
192    
193     void AudioExit(void)
194     {
195     // Free everything
196     if (ahi_ctrl != NULL) {
197     AHI_ControlAudio(ahi_ctrl, AHIC_Play, FALSE, TAG_END);
198     AHI_FreeAudio(ahi_ctrl);
199     }
200    
201     FreeVec(sample[0].ahisi_Address);
202     FreeVec(sample[1].ahisi_Address);
203     }
204    
205    
206     /*
207     * First source added, start audio stream
208     */
209    
210     void audio_enter_stream()
211     {
212     AHI_ControlAudio(ahi_ctrl, AHIC_Play, TRUE, TAG_END);
213     }
214    
215    
216     /*
217     * Last source removed, stop audio stream
218     */
219    
220     void audio_exit_stream()
221     {
222     AHI_ControlAudio(ahi_ctrl, AHIC_Play, FALSE, TAG_END);
223     }
224    
225    
226     /*
227     * AHI sound callback, request next buffer
228     */
229    
230 cebix 1.4 static __saveds __attribute__((regparm(3))) ULONG audio_callback(struct Hook *hook /*a0*/, struct AHISoundMessage *msg /*a1*/, struct AHIAudioCtrl *ahi_ctrl /*a2*/)
231 cebix 1.1 {
232     play_buf ^= 1;
233    
234     // New buffer available?
235 jlachmann 1.11 if (audio_block_fetched)
236     {
237 cebix 1.1 audio_block_fetched--;
238    
239 jlachmann 1.11 if (main_mute || speaker_mute)
240     {
241     memset(sample[play_buf].ahisi_Address, 0, sound_buffer_size);
242     }
243     else
244     {
245     // Get size of audio data
246     uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
247     if (apple_stream_info) {
248     int32 sample_count = ReadMacInt32(apple_stream_info + scd_sampleCount);
249    
250     uint32 num_channels = ReadMacInt16(apple_stream_info + scd_numChannels);
251     uint32 sample_size = ReadMacInt16(apple_stream_info + scd_sampleSize);
252     uint32 sample_rate = ReadMacInt32(apple_stream_info + scd_sampleRate);
253    
254     D(bug("stream: sample_count=%ld num_channels=%ld sample_size=%ld sample_rate=%ld\n", sample_count, num_channels, sample_size, sample_rate >> 16));
255    
256     // Yes, this can happen.
257     if(sample_count != 0) {
258     if(sample_rate != AudioStatus.sample_rate) {
259     audio_set_sample_rate_byval(sample_rate);
260     }
261     if(num_channels != AudioStatus.channels) {
262     audio_set_channels_byval(num_channels);
263     }
264     if(sample_size != AudioStatus.sample_size) {
265     audio_set_sample_size_byval(sample_size);
266     }
267     }
268    
269     if (sample_count < 0)
270     sample_count = 0;
271    
272     int work_size = sample_count * num_channels * (sample_size>>3);
273     D(bug("stream: work_size=%ld sound_buffer_size=%ld\n", work_size, sound_buffer_size));
274    
275     if (work_size > sound_buffer_size)
276     work_size = sound_buffer_size;
277    
278     // Put data into AHI buffer (convert 8-bit data unsigned->signed)
279     if (AudioStatus.sample_size == 16)
280     Mac2Host_memcpy(sample[play_buf].ahisi_Address, ReadMacInt32(apple_stream_info + scd_buffer), work_size);
281     else {
282     uint32 *p = (uint32 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer));
283     uint32 *q = (uint32 *)sample[play_buf].ahisi_Address;
284     int r = work_size >> 2;
285     while (r--)
286     *q++ = *p++ ^ 0x80808080;
287     }
288     if (work_size != sound_buffer_size)
289     memset((uint8 *)sample[play_buf].ahisi_Address + work_size, 0, sound_buffer_size - work_size);
290 cebix 1.1 }
291     }
292    
293 jlachmann 1.11 }
294     else
295 cebix 1.1 memset(sample[play_buf].ahisi_Address, 0, sound_buffer_size);
296    
297     // Play next buffer
298     AHI_SetSound(0, play_buf, 0, 0, ahi_ctrl, 0);
299    
300     // Trigger audio interrupt to get new buffer
301     if (AudioStatus.num_sources) {
302 jlachmann 1.11 D1(bug("stream: triggering irq\n"));
303 cebix 1.1 SetInterruptFlag(INTFLAG_AUDIO);
304     TriggerInterrupt();
305     }
306     return 0;
307     }
308    
309    
310     /*
311     * MacOS audio interrupt, read next data block
312     */
313    
314     void AudioInterrupt(void)
315     {
316 jlachmann 1.11 D1(bug("AudioInterrupt\n"));
317 cebix 1.1
318     // Get data from apple mixer
319     if (AudioStatus.mixer) {
320     M68kRegisters r;
321     r.a[0] = audio_data + adatStreamInfo;
322     r.a[1] = AudioStatus.mixer;
323     Execute68k(audio_data + adatGetSourceData, &r);
324 jlachmann 1.11 D1(bug(" GetSourceData() returns %08lx\n", r.d[0]));
325 cebix 1.1 } else
326     WriteMacInt32(audio_data + adatStreamInfo, 0);
327    
328     // Signal stream function
329     audio_block_fetched++;
330 jlachmann 1.11 D1(bug("AudioInterrupt done\n"));
331 cebix 1.1 }
332    
333    
334     /*
335     * Set sampling parameters
336     * "index" is an index into the audio_sample_rates[] etc. arrays
337     * It is guaranteed that AudioStatus.num_sources == 0
338     */
339    
340 jlachmann 1.11 void audio_set_sample_rate_byval(uint32 value)
341     {
342     bool changed = (AudioStatus.sample_rate != value);
343     if(changed)
344     {
345     ULONG sample_rate_index;
346    
347     // get index of sample rate closest to <value> Hz
348     AHI_GetAudioAttrs(ahi_id, ahi_ctrl,
349     AHIDB_IndexArg, value >> 16,
350     AHIDB_Index, (ULONG) &sample_rate_index,
351     TAG_END);
352    
353     D(bug(" audio_set_sample_rate_byval requested rate=%ld Hz\n", value >> 16));
354    
355     AudioStatus.sample_rate = audio_sample_rates[sample_rate_index];
356    
357     AHI_SetFreq(0, AudioStatus.sample_rate >> 16, ahi_ctrl, 0);
358     }
359    
360     D(bug(" audio_set_sample_rate_byval rate=%ld Hz\n", AudioStatus.sample_rate >> 16));
361     }
362    
363     void audio_set_sample_size_byval(uint32 value)
364     {
365     bool changed = (AudioStatus.sample_size != value);
366     if(changed) {
367     // AudioStatus.sample_size = value;
368     // update_sound_parameters();
369     // WritePrivateProfileInt( "Audio", "SampleSize", AudioStatus.sample_size, ini_file_name );
370     }
371     D(bug(" audio_set_sample_size_byval %d\n", AudioStatus.sample_size));
372     }
373    
374     void audio_set_channels_byval(uint32 value)
375     {
376     bool changed = (AudioStatus.channels != value);
377     if(changed) {
378     // AudioStatus.channels = value;
379     // update_sound_parameters();
380     // WritePrivateProfileInt( "Audio", "Channels", AudioStatus.channels, ini_file_name );
381     }
382     D(bug(" audio_set_channels_byval %d\n", AudioStatus.channels));
383     }
384    
385 cebix 1.9 bool audio_set_sample_rate(int index)
386 cebix 1.1 {
387 jlachmann 1.11 if(index >= 0 && index < audio_sample_rates.size() ) {
388     audio_set_sample_rate_byval( audio_sample_rates[index] );
389     D(bug(" audio_set_sample_rate index=%ld rate=%ld\n", index, AudioStatus.sample_rate >> 16));
390     }
391    
392 cebix 1.9 return true;
393 cebix 1.1 }
394    
395 cebix 1.9 bool audio_set_sample_size(int index)
396 cebix 1.1 {
397 jlachmann 1.11 if(index >= 0 && index < audio_sample_sizes.size() ) {
398     audio_set_sample_size_byval( audio_sample_sizes[index] );
399     D(bug(" audio_set_sample_size %d,%d\n", index,AudioStatus.sample_size));
400     }
401    
402 cebix 1.9 return true;
403 cebix 1.1 }
404    
405 cebix 1.9 bool audio_set_channels(int index)
406 cebix 1.1 {
407 jlachmann 1.11 if(index >= 0 && index < audio_channel_counts.size() ) {
408     audio_set_channels_byval( audio_channel_counts[index] );
409     D(bug(" audio_set_channels %d,%d\n", index,AudioStatus.channels));
410     }
411    
412 cebix 1.9 return true;
413 cebix 1.1 }
414    
415    
416     /*
417     * Get/set volume controls (volume values received/returned have the left channel
418     * volume in the upper 16 bits and the right channel volume in the lower 16 bits;
419     * both volumes are 8.8 fixed point values with 0x0100 meaning "maximum volume"))
420     */
421    
422     bool audio_get_main_mute(void)
423     {
424 jlachmann 1.11 D(bug("audio_get_main_mute: mute=%ld\n", main_mute));
425    
426     return main_mute;
427 cebix 1.1 }
428    
429     uint32 audio_get_main_volume(void)
430     {
431 jlachmann 1.11 D(bug("audio_get_main_volume\n"));
432    
433     ULONG volume = current_main_volume >> 8; // 0x10000 => 0x100
434    
435     D(bug("audio_get_main_volume: volume=%08lx\n", volume));
436    
437     return (volume << 16) + volume;
438    
439 cebix 1.1 return 0x01000100;
440     }
441    
442 cebix 1.3 bool audio_get_speaker_mute(void)
443 cebix 1.1 {
444 jlachmann 1.11 D(bug("audio_get_speaker_mute: mute=%ld\n", speaker_mute));
445    
446     return speaker_mute;
447 cebix 1.1 }
448    
449 cebix 1.3 uint32 audio_get_speaker_volume(void)
450 cebix 1.1 {
451 jlachmann 1.11 D(bug("audio_get_speaker_volume: \n"));
452    
453     if (audio_open)
454     {
455     ULONG volume = current_speaker_volume >> 8; // 0x10000 => 0x100
456    
457     D(bug("audio_get_speaker_volume: volume=%08lx\n", volume));
458    
459     return (volume << 16) + volume;
460     }
461    
462 cebix 1.1 return 0x01000100;
463     }
464    
465     void audio_set_main_mute(bool mute)
466     {
467 jlachmann 1.11 D(bug("audio_set_main_mute: mute=%ld\n", mute));
468    
469     if (mute != main_mute)
470     {
471     main_mute = mute;
472     }
473 cebix 1.1 }
474    
475     void audio_set_main_volume(uint32 vol)
476     {
477 jlachmann 1.11 D(bug("audio_set_main_volume: vol=%08lx\n", vol));
478    
479     if (audio_open && supports_volume_changes)
480     {
481     ULONG volume = 0x80 * ((vol >> 16) + (vol & 0xffff));
482    
483     D(bug("audio_set_main_volume: volume=%08lx\n", volume));
484    
485     current_main_volume = volume;
486    
487     AHI_SetVol(0, volume, 0x8000, ahi_ctrl, AHISF_IMM);
488     }
489 cebix 1.1 }
490    
491 cebix 1.3 void audio_set_speaker_mute(bool mute)
492 cebix 1.1 {
493 jlachmann 1.11 D(bug("audio_set_speaker_mute: mute=%ld\n", mute));
494    
495     if (mute != speaker_mute)
496     {
497     speaker_mute = mute;
498     }
499 cebix 1.1 }
500    
501 cebix 1.3 void audio_set_speaker_volume(uint32 vol)
502 cebix 1.1 {
503 jlachmann 1.11 D(bug("audio_set_speaker_volume: vol=%08lx\n", vol));
504    
505     if (audio_open && supports_volume_changes)
506     {
507     ULONG volume = 0x80 * ((vol >> 16) + (vol & 0xffff));
508    
509     D(bug("audio_set_speaker_volume: volume=%08lx\n", volume));
510    
511     current_speaker_volume = volume;
512    
513     AHI_SetVol(0, volume, 0x8000, ahi_ctrl, AHISF_IMM);
514     }
515 cebix 1.1 }