macemu/BasiliskII/src/audio.cpp
2000-04-10 18:53:46 +00:00

463 lines
14 KiB
C++

/*
* audio.cpp - Audio support
*
* Basilisk II (C) 1997-2000 Christian Bauer
* Portions (C) 1997-1999 Marc Hellwig
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* SEE ALSO
* Inside Macintosh: Sound, chapter 5 "Sound Components"
*/
#include "sysdeps.h"
#include "cpu_emulation.h"
#include "macos_util.h"
#include "emul_op.h"
#include "main.h"
#include "audio.h"
#include "audio_defs.h"
#define DEBUG 0
#include "debug.h"
// Global variables
struct audio_status AudioStatus; // Current audio status (sample rate etc.)
bool audio_open = false; // Flag: audio is initialized and ready
int audio_frames_per_block; // Number of audio frames per block
uint32 audio_component_flags; // Component feature flags
uint32 audio_data = 0; // Mac address of global data area
static int open_count = 0; // Open/close nesting count
bool AudioAvailable = false; // Flag: audio output available (from the software point of view)
/*
* Get audio info
*/
static int32 AudioGetInfo(uint32 infoPtr, uint32 selector, uint32 sourceID)
{
D(bug(" AudioGetInfo %c%c%c%c, infoPtr %08lx, source ID %08lx\n", selector >> 24, (selector >> 16) & 0xff, (selector >> 8) & 0xff, selector & 0xff, infoPtr, sourceID));
M68kRegisters r;
int i;
switch (selector) {
case siSampleSize:
WriteMacInt16(infoPtr, AudioStatus.sample_size);
break;
case siSampleSizeAvailable: {
r.d[0] = audio_num_sample_sizes * 2;
Execute68kTrap(0xa122, &r); // NewHandle()
uint32 h = r.a[0];
if (h == 0)
return memFullErr;
WriteMacInt16(infoPtr + sil_count, audio_num_sample_sizes);
WriteMacInt32(infoPtr + sil_infoHandle, h);
uint32 sp = ReadMacInt32(h);
for (i=0; i<audio_num_sample_sizes; i++)
WriteMacInt16(sp + i*2, audio_sample_sizes[i]);
break;
}
case siNumberChannels:
WriteMacInt16(infoPtr, AudioStatus.channels);
break;
case siChannelAvailable: {
r.d[0] = audio_num_channel_counts * 2;
Execute68kTrap(0xa122, &r); // NewHandle()
uint32 h = r.a[0];
if (h == 0)
return memFullErr;
WriteMacInt16(infoPtr + sil_count, audio_num_channel_counts);
WriteMacInt32(infoPtr + sil_infoHandle, h);
uint32 sp = ReadMacInt32(h);
for (i=0; i<audio_num_channel_counts; i++)
WriteMacInt16(sp + i*2, audio_channel_counts[i]);
break;
}
case siSampleRate:
WriteMacInt32(infoPtr, AudioStatus.sample_rate);
break;
case siSampleRateAvailable: {
r.d[0] = audio_num_sample_rates * 4;
Execute68kTrap(0xa122, &r); // NewHandle()
uint32 h = r.a[0];
if (h == 0)
return memFullErr;
WriteMacInt16(infoPtr + sil_count, audio_num_sample_rates);
WriteMacInt32(infoPtr + sil_infoHandle, h);
uint32 lp = ReadMacInt32(h);
for (i=0; i<audio_num_sample_rates; i++)
WriteMacInt32(lp + i*4, audio_sample_rates[i]);
break;
}
case siSpeakerMute:
WriteMacInt16(infoPtr, audio_get_speaker_mute());
break;
case siSpeakerVolume:
WriteMacInt32(infoPtr, audio_get_speaker_volume());
break;
case siHeadphoneMute:
WriteMacInt16(infoPtr, 0);
break;
case siHeadphoneVolume:
WriteMacInt32(infoPtr, 0x01000100);
break;
case siHeadphoneVolumeSteps:
WriteMacInt16(infoPtr, 13);
break;
case siHardwareMute:
WriteMacInt16(infoPtr, audio_get_main_mute());
break;
case siHardwareVolume:
WriteMacInt32(infoPtr, audio_get_main_volume());
break;
case siHardwareVolumeSteps:
WriteMacInt16(infoPtr, 13);
break;
case siHardwareBusy:
WriteMacInt16(infoPtr, AudioStatus.num_sources != 0);
break;
default: // Delegate to Apple Mixer
if (AudioStatus.mixer == 0)
return badComponentSelector;
M68kRegisters r;
r.a[0] = infoPtr;
r.d[0] = selector;
r.a[1] = sourceID;
r.a[2] = AudioStatus.mixer;
Execute68k(audio_data + adatGetInfo, &r);
D(bug(" delegated to Apple Mixer, returns %08lx\n", r.d[0]));
return r.d[0];
}
return noErr;
}
/*
* Set audio info
*/
static int32 AudioSetInfo(uint32 infoPtr, uint32 selector, uint32 sourceID)
{
D(bug(" AudioSetInfo %c%c%c%c, infoPtr %08lx, source ID %08lx\n", selector >> 24, (selector >> 16) & 0xff, (selector >> 8) & 0xff, selector & 0xff, infoPtr, sourceID));
M68kRegisters r;
int i;
switch (selector) {
case siSampleSize:
D(bug(" set sample size %08lx\n", infoPtr));
if (AudioStatus.num_sources)
return siDeviceBusyErr;
for (i=0; i<audio_num_sample_sizes; i++)
if (audio_sample_sizes[i] == infoPtr) {
audio_set_sample_size(i);
return noErr;
}
return siInvalidSampleSize;
case siSampleRate:
D(bug(" set sample rate %08lx\n", infoPtr));
if (AudioStatus.num_sources)
return siDeviceBusyErr;
for (i=0; i<audio_num_sample_rates; i++)
if (audio_sample_rates[i] == infoPtr) {
audio_set_sample_rate(i);
return noErr;
}
return siInvalidSampleRate;
case siNumberChannels:
D(bug(" set number of channels %08lx\n", infoPtr));
if (AudioStatus.num_sources)
return siDeviceBusyErr;
for (i=0; i<audio_num_channel_counts; i++)
if (audio_channel_counts[i] == infoPtr) {
audio_set_channels(i);
return noErr;
}
return badChannel;
case siSpeakerMute:
audio_set_speaker_mute((uint16)infoPtr);
break;
case siSpeakerVolume:
D(bug(" set speaker volume %08lx\n", infoPtr));
audio_set_speaker_volume(infoPtr);
break;
case siHeadphoneMute:
case siHeadphoneVolume:
break;
case siHardwareMute:
audio_set_main_mute((uint16)infoPtr);
break;
case siHardwareVolume:
D(bug(" set hardware volume %08lx\n", infoPtr));
audio_set_main_volume(infoPtr);
break;
default: // Delegate to Apple Mixer
if (AudioStatus.mixer == 0)
return badComponentSelector;
r.a[0] = infoPtr;
r.d[0] = selector;
r.a[1] = sourceID;
r.a[2] = AudioStatus.mixer;
Execute68k(audio_data + adatSetInfo, &r);
D(bug(" delegated to Apple Mixer, returns %08lx\n", r.d[0]));
return r.d[0];
}
return noErr;
}
/*
* Sound output component dispatch
*/
int32 AudioDispatch(uint32 params, uint32 globals)
{
D(bug("AudioDispatch params %08lx (size %d), what %d\n", params, ReadMacInt8(params + cp_paramSize), (int16)ReadMacInt16(params + cp_what)));
M68kRegisters r;
uint32 p = params + cp_params;
switch ((int16)ReadMacInt16(params + cp_what)) {
// Basic component functions
case kComponentOpenSelect:
if (audio_data == 0) {
// Allocate global data area
r.d[0] = SIZEOF_adat;
Execute68kTrap(0xa71e, &r); // NewPtrSysClear()
if (r.a[0] == 0)
return memFullErr;
audio_data = r.a[0];
D(bug(" global data at %08lx\n", audio_data));
// Put in 68k routines
int p = audio_data + adatDelegateCall;
WriteMacInt16(p, 0x598f); p += 2; // subq.l #4,sp
WriteMacInt16(p, 0x2f09); p += 2; // move.l a1,-(sp)
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x7024); p += 2; // moveq #$24,d0
WriteMacInt16(p, 0xa82a); p += 2; // ComponentDispatch
WriteMacInt16(p, 0x201f); p += 2; // move.l (sp)+,d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatOpenMixer)
goto adat_error;
WriteMacInt16(p, 0x558f); p += 2; // subq.l #2,sp
WriteMacInt16(p, 0x2f09); p += 2; // move.l a1,-(sp)
WriteMacInt16(p, 0x2f00); p += 2; // move.l d0,-(sp)
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x203c); p += 2; // move.l #$06140018,d0
WriteMacInt32(p, 0x06140018); p+= 4;
WriteMacInt16(p, 0xa800); p += 2; // SoundDispatch
WriteMacInt16(p, 0x301f); p += 2; // move.w (sp)+,d0
WriteMacInt16(p, 0x48c0); p += 2; // ext.l d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatCloseMixer)
goto adat_error;
WriteMacInt16(p, 0x558f); p += 2; // subq.l #2,sp
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x203c); p += 2; // move.l #$02180018,d0
WriteMacInt32(p, 0x02180018); p+= 4;
WriteMacInt16(p, 0xa800); p += 2; // SoundDispatch
WriteMacInt16(p, 0x301f); p += 2; // move.w (sp)+,d0
WriteMacInt16(p, 0x48c0); p += 2; // ext.l d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatGetInfo)
goto adat_error;
WriteMacInt16(p, 0x598f); p += 2; // subq.l #4,sp
WriteMacInt16(p, 0x2f0a); p += 2; // move.l a2,-(sp)
WriteMacInt16(p, 0x2f09); p += 2; // move.l a1,-(sp)
WriteMacInt16(p, 0x2f00); p += 2; // move.l d0,-(sp)
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x2f3c); p += 2; // move.l #$000c0103,-(sp)
WriteMacInt32(p, 0x000c0103); p+= 4;
WriteMacInt16(p, 0x7000); p += 2; // moveq #0,d0
WriteMacInt16(p, 0xa82a); p += 2; // ComponentDispatch
WriteMacInt16(p, 0x201f); p += 2; // move.l (sp)+,d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatSetInfo)
goto adat_error;
WriteMacInt16(p, 0x598f); p += 2; // subq.l #4,sp
WriteMacInt16(p, 0x2f0a); p += 2; // move.l a2,-(sp)
WriteMacInt16(p, 0x2f09); p += 2; // move.l a1,-(sp)
WriteMacInt16(p, 0x2f00); p += 2; // move.l d0,-(sp)
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x2f3c); p += 2; // move.l #$000c0104,-(sp)
WriteMacInt32(p, 0x000c0104); p+= 4;
WriteMacInt16(p, 0x7000); p += 2; // moveq #0,d0
WriteMacInt16(p, 0xa82a); p += 2; // ComponentDispatch
WriteMacInt16(p, 0x201f); p += 2; // move.l (sp)+,d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatPlaySourceBuffer)
goto adat_error;
WriteMacInt16(p, 0x598f); p += 2; // subq.l #4,sp
WriteMacInt16(p, 0x2f0a); p += 2; // move.l a2,-(sp)
WriteMacInt16(p, 0x2f09); p += 2; // move.l a1,-(sp)
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x2f00); p += 2; // move.l d0,-(sp)
WriteMacInt16(p, 0x2f3c); p += 2; // move.l #$000c0108,-(sp)
WriteMacInt32(p, 0x000c0108); p+= 4;
WriteMacInt16(p, 0x7000); p += 2; // moveq #0,d0
WriteMacInt16(p, 0xa82a); p += 2; // ComponentDispatch
WriteMacInt16(p, 0x201f); p += 2; // move.l (sp)+,d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatGetSourceData)
goto adat_error;
WriteMacInt16(p, 0x598f); p += 2; // subq.l #4,sp
WriteMacInt16(p, 0x2f09); p += 2; // move.l a1,-(sp)
WriteMacInt16(p, 0x2f08); p += 2; // move.l a0,-(sp)
WriteMacInt16(p, 0x2f3c); p += 2; // move.l #$00040004,-(sp)
WriteMacInt32(p, 0x00040004); p+= 4;
WriteMacInt16(p, 0x7000); p += 2; // moveq #0,d0
WriteMacInt16(p, 0xa82a); p += 2; // ComponentDispatch
WriteMacInt16(p, 0x201f); p += 2; // move.l (sp)+,d0
WriteMacInt16(p, M68K_RTS); p += 2; // rts
if (p - audio_data != adatData)
goto adat_error;
}
AudioAvailable = true;
if (open_count == 0)
audio_enter_stream();
open_count++;
return noErr;
adat_error: printf("FATAL: audio component data block initialization error\n");
QuitEmulator();
return openErr;
case kComponentCanDoSelect:
case kComponentRegisterSelect:
return noErr;
case kComponentVersionSelect:
return 0x00010003;
case kComponentCloseSelect:
open_count--;
if (open_count == 0) {
if (AudioStatus.mixer) {
// Close Apple Mixer
r.a[0] = AudioStatus.mixer;
Execute68k(audio_data + adatCloseMixer, &r);
AudioStatus.mixer = 0;
return r.d[0];
}
AudioStatus.num_sources = 0;
audio_exit_stream();
}
return noErr;
// Sound component functions
case kSoundComponentInitOutputDeviceSelect:
D(bug(" InitOutputDevice\n"));
if (!audio_open)
return noHardwareErr;
if (AudioStatus.mixer)
return noErr;
// Init sound component data
WriteMacInt32(audio_data + adatData + scd_flags, 0);
WriteMacInt32(audio_data + adatData + scd_format, AudioStatus.sample_size == 16 ? 'twos' : 'raw ');
WriteMacInt16(audio_data + adatData + scd_numChannels, AudioStatus.channels);
WriteMacInt16(audio_data + adatData + scd_sampleSize, AudioStatus.sample_size);
WriteMacInt32(audio_data + adatData + scd_sampleRate, AudioStatus.sample_rate);
WriteMacInt32(audio_data + adatData + scd_sampleCount, audio_frames_per_block);
WriteMacInt32(audio_data + adatData + scd_buffer, 0);
WriteMacInt32(audio_data + adatData + scd_reserved, 0);
WriteMacInt32(audio_data + adatStreamInfo, 0);
// Open Apple Mixer
r.a[0] = audio_data + adatMixer;
r.d[0] = 0;
r.a[1] = audio_data + adatData;
Execute68k(audio_data + adatOpenMixer, &r);
AudioStatus.mixer = ReadMacInt32(audio_data + adatMixer);
D(bug(" OpenMixer() returns %08lx, mixer %08lx\n", r.d[0], AudioStatus.mixer));
return r.d[0];
case kSoundComponentAddSourceSelect:
D(bug(" AddSource\n"));
AudioStatus.num_sources++;
goto delegate;
case kSoundComponentRemoveSourceSelect:
D(bug(" RemoveSource\n"));
AudioStatus.num_sources--;
goto delegate;
case kSoundComponentStopSourceSelect:
D(bug(" StopSource\n"));
goto delegate;
case kSoundComponentPauseSourceSelect:
D(bug(" PauseSource\n"));
delegate: // Delegate call to Apple Mixer
D(bug(" delegating call to Apple Mixer\n"));
r.a[0] = AudioStatus.mixer;
r.a[1] = params;
Execute68k(audio_data + adatDelegateCall, &r);
D(bug(" returns %08lx\n", r.d[0]));
return r.d[0];
case kSoundComponentStartSourceSelect:
D(bug(" StartSource\n"));
return noErr;
case kSoundComponentGetInfoSelect:
return AudioGetInfo(ReadMacInt32(p), ReadMacInt32(p + 4), ReadMacInt32(p + 8));
case kSoundComponentSetInfoSelect:
return AudioSetInfo(ReadMacInt32(p), ReadMacInt32(p + 4), ReadMacInt32(p + 8));
case kSoundComponentPlaySourceBufferSelect:
D(bug(" PlaySourceBuffer\n"));
r.d[0] = ReadMacInt32(p);
r.a[0] = ReadMacInt32(p + 4);
r.a[1] = ReadMacInt32(p + 8);
r.a[2] = AudioStatus.mixer;
Execute68k(audio_data + adatPlaySourceBuffer, &r);
D(bug(" returns %08lx\n", r.d[0]));
return r.d[0];
default:
return badComponentSelector;
}
}