mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-12-18 17:31:08 +00:00
added format switching and other improvements [Brian Johnson]
This commit is contained in:
parent
0074b3346e
commit
16b21014ef
@ -40,6 +40,12 @@
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
// The currently selected audio parameters (indices in audio_sample_rates[]
|
||||
// etc. vectors)
|
||||
static int audio_sample_rate_index = 0;
|
||||
static int audio_sample_size_index = 0;
|
||||
static int audio_channel_count_index = 0;
|
||||
|
||||
// Global variables
|
||||
static int audio_fd = -1; // fd from audio library
|
||||
static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read
|
||||
@ -52,6 +58,11 @@ static pthread_attr_t stream_thread_attr; // Streaming thread attributes
|
||||
static bool stream_thread_active = false; // Flag: streaming thread installed
|
||||
static volatile bool stream_thread_cancel = false; // Flag: cancel streaming thread
|
||||
|
||||
static bool current_main_mute = false; // Flag: output muted
|
||||
static bool current_speaker_mute = false; // Flag: speaker muted
|
||||
static uint32 current_main_volume = 0; // Output volume
|
||||
static uint32 current_speaker_volume = 0; // Speaker volume
|
||||
|
||||
// IRIX libaudio control structures
|
||||
static ALconfig config;
|
||||
static ALport port;
|
||||
@ -59,6 +70,9 @@ static ALport port;
|
||||
|
||||
// Prototypes
|
||||
static void *stream_func(void *arg);
|
||||
static uint32 read_volume(void);
|
||||
static bool read_mute(void);
|
||||
static void set_mute(bool mute);
|
||||
|
||||
|
||||
/*
|
||||
@ -68,37 +82,78 @@ static void *stream_func(void *arg);
|
||||
// Set AudioStatus to reflect current audio stream format
|
||||
static void set_audio_status_format(void)
|
||||
{
|
||||
AudioStatus.sample_rate = audio_sample_rates[0];
|
||||
AudioStatus.sample_size = audio_sample_sizes[0];
|
||||
AudioStatus.channels = audio_channel_counts[0];
|
||||
AudioStatus.sample_rate = audio_sample_rates[audio_sample_rate_index];
|
||||
AudioStatus.sample_size = audio_sample_sizes[audio_sample_size_index];
|
||||
AudioStatus.channels = audio_channel_counts[audio_channel_count_index];
|
||||
}
|
||||
|
||||
// Init libaudio, returns false on error
|
||||
bool audio_init_al(void)
|
||||
bool open_audio(void)
|
||||
{
|
||||
ALpv pv[2];
|
||||
|
||||
printf("Using libaudio audio output\n");
|
||||
|
||||
// Try to open the audio library
|
||||
// Get supported sample formats
|
||||
|
||||
if (audio_sample_sizes.empty()) {
|
||||
// All sample sizes are supported
|
||||
audio_sample_sizes.push_back(8);
|
||||
audio_sample_sizes.push_back(16);
|
||||
|
||||
// Assume at least two channels are supported. Some IRIX boxes
|
||||
// can do 4 or more... MacOS only handles up to 2.
|
||||
audio_channel_counts.push_back(1);
|
||||
audio_channel_counts.push_back(2);
|
||||
|
||||
if (audio_sample_sizes.empty() || audio_channel_counts.empty()) {
|
||||
WarningAlert(GetString(STR_AUDIO_FORMAT_WARN));
|
||||
alClosePort(port);
|
||||
audio_fd = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
audio_sample_rates.push_back( 8000 << 16);
|
||||
audio_sample_rates.push_back(11025 << 16);
|
||||
audio_sample_rates.push_back(22050 << 16);
|
||||
audio_sample_rates.push_back(44100 << 16);
|
||||
|
||||
// Default to highest supported values
|
||||
audio_sample_rate_index = audio_sample_rates.size() - 1;
|
||||
audio_sample_size_index = audio_sample_sizes.size() - 1;
|
||||
audio_channel_count_index = audio_channel_counts.size() - 1;
|
||||
}
|
||||
|
||||
// Set the sample format
|
||||
|
||||
D(bug("Size %d, channels %d, rate %d\n",
|
||||
audio_sample_sizes[audio_sample_size_index],
|
||||
audio_channel_counts[audio_channel_count_index],
|
||||
audio_sample_rates[audio_sample_rate_index] >> 16));
|
||||
config = alNewConfig();
|
||||
alSetSampFmt(config, AL_SAMPFMT_TWOSCOMP);
|
||||
alSetWidth(config, AL_SAMPLE_16);
|
||||
alSetChannels(config, 2); // stereo
|
||||
if (audio_sample_sizes[audio_sample_size_index] == 8) {
|
||||
alSetWidth(config, AL_SAMPLE_8);
|
||||
}
|
||||
else {
|
||||
alSetWidth(config, AL_SAMPLE_16);
|
||||
}
|
||||
alSetChannels(config, audio_channel_counts[audio_channel_count_index]);
|
||||
alSetDevice(config, AL_DEFAULT_OUTPUT); // Allow selecting via prefs?
|
||||
|
||||
// Try to open the audio library
|
||||
|
||||
port = alOpenPort("BasiliskII", "w", config);
|
||||
if (port == NULL) {
|
||||
fprintf(stderr, "ERROR: Cannot open audio port: %s\n",
|
||||
alGetErrorString(oserror()));
|
||||
WarningAlert(GetString(STR_NO_AUDIO_WARN));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Set the sample rate
|
||||
|
||||
pv[0].param = AL_RATE;
|
||||
pv[0].value.ll = alDoubleToFixed(audio_sample_rates[0] >> 16);
|
||||
pv[0].value.ll = alDoubleToFixed(audio_sample_rates[audio_sample_rate_index] >> 16);
|
||||
pv[1].param = AL_MASTER_CLOCK;
|
||||
pv[1].value.i = AL_CRYSTAL_MCLK_TYPE;
|
||||
if (alSetParams(AL_DEFAULT_OUTPUT, pv, 2) < 0) {
|
||||
@ -108,12 +163,6 @@ bool audio_init_al(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: list all supported sample formats?
|
||||
|
||||
// Set AudioStatus again because we now know more about the sound
|
||||
// system's capabilities
|
||||
set_audio_status_format();
|
||||
|
||||
// Compute sound buffer size and libaudio refill point
|
||||
|
||||
config = alGetConfig(port);
|
||||
@ -124,20 +173,33 @@ bool audio_init_al(void)
|
||||
alClosePort(port);
|
||||
return false;
|
||||
}
|
||||
D(bug("alGetQueueSize %d\n", audio_frames_per_block));
|
||||
D(bug("alGetQueueSize %d, width %d, channels %d\n",
|
||||
audio_frames_per_block,
|
||||
alGetWidth(config),
|
||||
alGetChannels(config)));
|
||||
|
||||
// Put a limit on the Mac sound buffer size, to decrease delay
|
||||
#define AUDIO_BUFFER_MSEC 50 // milliseconds of sound to buffer
|
||||
int target_frames_per_block =
|
||||
(audio_sample_rates[audio_sample_rate_index] >> 16) *
|
||||
AUDIO_BUFFER_MSEC / 1000;
|
||||
if (audio_frames_per_block > target_frames_per_block)
|
||||
audio_frames_per_block = target_frames_per_block;
|
||||
D(bug("frames per block %d\n", audio_frames_per_block));
|
||||
|
||||
alZeroFrames(port, audio_frames_per_block); // so we don't underflow
|
||||
|
||||
// Put a limit on the Mac sound buffer size, to decrease delay
|
||||
if (audio_frames_per_block > 2048)
|
||||
audio_frames_per_block = 2048;
|
||||
// Try to keep the buffer pretty full. 5000 samples of slack works well.
|
||||
sound_buffer_fill_point = alGetQueueSize(config) - 5000;
|
||||
// Try to keep the buffer pretty full
|
||||
sound_buffer_fill_point = alGetQueueSize(config) -
|
||||
2 * audio_frames_per_block;
|
||||
if (sound_buffer_fill_point < 0)
|
||||
sound_buffer_fill_point = alGetQueueSize(config) / 3;
|
||||
D(bug("fill point %d\n", sound_buffer_fill_point));
|
||||
|
||||
sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block;
|
||||
sound_buffer_size = (audio_sample_sizes[audio_sample_size_index] >> 3) *
|
||||
audio_channel_counts[audio_channel_count_index] *
|
||||
audio_frames_per_block;
|
||||
set_audio_status_format();
|
||||
|
||||
// Get a file descriptor we can select() on
|
||||
|
||||
@ -149,21 +211,26 @@ bool audio_init_al(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Initialize volume, mute settings
|
||||
current_main_volume = current_speaker_volume = read_volume();
|
||||
current_main_mute = current_speaker_mute = read_mute();
|
||||
|
||||
|
||||
// Start streaming thread
|
||||
Set_pthread_attr(&stream_thread_attr, 0);
|
||||
stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
|
||||
|
||||
// Everything went fine
|
||||
audio_open = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
void AudioInit(void)
|
||||
{
|
||||
// Init audio status (defaults) and feature flags
|
||||
audio_sample_rates.push_back(44100 << 16);
|
||||
audio_sample_sizes.push_back(16);
|
||||
audio_channel_counts.push_back(2);
|
||||
set_audio_status_format();
|
||||
// Init audio status (reasonable defaults) and feature flags
|
||||
AudioStatus.sample_rate = 44100 << 16;
|
||||
AudioStatus.sample_size = 16;
|
||||
AudioStatus.channels = 2;
|
||||
AudioStatus.mixer = 0;
|
||||
AudioStatus.num_sources = 0;
|
||||
audio_component_flags = cmpWantsRegisterMessage | kStereoOut | k16BitOut;
|
||||
@ -172,21 +239,13 @@ void AudioInit(void)
|
||||
if (PrefsFindBool("nosound"))
|
||||
return;
|
||||
|
||||
// Try to open audio library
|
||||
if (!audio_init_al())
|
||||
return;
|
||||
|
||||
// Init semaphore
|
||||
if (sem_init(&audio_irq_done_sem, 0, 0) < 0)
|
||||
return;
|
||||
sem_inited = true;
|
||||
|
||||
// Start streaming thread
|
||||
Set_pthread_attr(&stream_thread_attr, 0);
|
||||
stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0);
|
||||
|
||||
// Everything OK
|
||||
audio_open = true;
|
||||
// Open and initialize audio device
|
||||
open_audio();
|
||||
}
|
||||
|
||||
|
||||
@ -194,7 +253,7 @@ void AudioInit(void)
|
||||
* Deinitialization
|
||||
*/
|
||||
|
||||
void AudioExit(void)
|
||||
static void close_audio(void)
|
||||
{
|
||||
// Stop stream and delete semaphore
|
||||
if (stream_thread_active) {
|
||||
@ -204,12 +263,25 @@ void AudioExit(void)
|
||||
#endif
|
||||
pthread_join(stream_thread, NULL);
|
||||
stream_thread_active = false;
|
||||
stream_thread_cancel = false;
|
||||
}
|
||||
if (sem_inited)
|
||||
sem_destroy(&audio_irq_done_sem);
|
||||
|
||||
// Close audio library
|
||||
alClosePort(port);
|
||||
|
||||
audio_open = false;
|
||||
}
|
||||
|
||||
void AudioExit(void)
|
||||
{
|
||||
// Close audio device
|
||||
close_audio();
|
||||
|
||||
// Delete semaphore
|
||||
if (sem_inited) {
|
||||
sem_destroy(&audio_irq_done_sem);
|
||||
sem_inited = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -239,7 +311,7 @@ void audio_exit_stream()
|
||||
|
||||
static void *stream_func(void *arg)
|
||||
{
|
||||
int16 *last_buffer = new int16[sound_buffer_size / 2];
|
||||
int32 *last_buffer = new int32[sound_buffer_size / 4];
|
||||
fd_set audio_fdset;
|
||||
int numfds, was_error;
|
||||
|
||||
@ -257,11 +329,11 @@ static void *stream_func(void *arg)
|
||||
sem_wait(&audio_irq_done_sem);
|
||||
D(bug("stream: ack received\n"));
|
||||
|
||||
uint32 apple_stream_info; // Mac address of SoundComponentData struct describing next buffer
|
||||
// Get size of audio data
|
||||
apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
|
||||
|
||||
if (apple_stream_info) {
|
||||
uint32 apple_stream_info = ReadMacInt32(audio_data + adatStreamInfo);
|
||||
if (!current_main_mute &&
|
||||
!current_speaker_mute &&
|
||||
apple_stream_info) {
|
||||
int work_size = ReadMacInt32(apple_stream_info + scd_sampleCount) * (AudioStatus.sample_size >> 3) * AudioStatus.channels;
|
||||
D(bug("stream: work_size %d\n", work_size));
|
||||
if (work_size > sound_buffer_size)
|
||||
@ -269,8 +341,21 @@ static void *stream_func(void *arg)
|
||||
if (work_size == 0)
|
||||
goto silence;
|
||||
|
||||
// Send data to audio library
|
||||
if (work_size == sound_buffer_size)
|
||||
// Send data to audio library. Convert 8-bit data
|
||||
// unsigned->signed, using same algorithm as audio_amiga.cpp.
|
||||
// It works fine for 8-bit mono, but not stereo.
|
||||
if (AudioStatus.sample_size == 8) {
|
||||
uint32 *p = (uint32 *)Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer));
|
||||
uint32 *q = (uint32 *)last_buffer;
|
||||
int r = work_size >> 2;
|
||||
// XXX not quite right....
|
||||
while (r--)
|
||||
*q++ = *p++ ^ 0x80808080;
|
||||
if (work_size != sound_buffer_size)
|
||||
memset((uint8 *)last_buffer + work_size, silence_byte, sound_buffer_size - work_size);
|
||||
alWriteFrames(port, last_buffer, audio_frames_per_block);
|
||||
}
|
||||
else if (work_size == sound_buffer_size)
|
||||
alWriteFrames(port, Mac2HostAddr(ReadMacInt32(apple_stream_info + scd_buffer)), audio_frames_per_block);
|
||||
else {
|
||||
// Last buffer
|
||||
@ -313,6 +398,156 @@ static void *stream_func(void *arg)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read or set the current output volume using the audio library
|
||||
*/
|
||||
|
||||
static uint32 read_volume(void)
|
||||
{
|
||||
ALpv x[2];
|
||||
ALfixed gain[8];
|
||||
double maxgain, mingain;
|
||||
ALparamInfo pi;
|
||||
uint32 ret = 0x01000100; // default, maximum value
|
||||
int dev = alGetDevice(config);
|
||||
|
||||
// Fetch the maximum and minimum gain settings
|
||||
|
||||
alGetParamInfo(dev, AL_GAIN, &pi);
|
||||
maxgain = alFixedToDouble(pi.max.ll);
|
||||
mingain = alFixedToDouble(pi.min.ll);
|
||||
// printf("maxgain = %lf dB, mingain = %lf dB\n", maxgain, mingain);
|
||||
|
||||
// Get the current gain values
|
||||
|
||||
x[0].param = AL_GAIN;
|
||||
x[0].value.ptr = gain;
|
||||
x[0].sizeIn = sizeof(gain) / sizeof(gain[0]);
|
||||
x[1].param = AL_CHANNELS;
|
||||
if (alGetParams(dev, x, 2) < 0) {
|
||||
printf("alGetParams failed: %s\n", alGetErrorString(oserror()));
|
||||
}
|
||||
else {
|
||||
if (x[0].sizeOut < 0) {
|
||||
printf("AL_GAIN was an unrecognized parameter\n");
|
||||
}
|
||||
else {
|
||||
double v;
|
||||
uint32 left, right;
|
||||
|
||||
// Left
|
||||
v = alFixedToDouble(gain[0]);
|
||||
if (v < mingain)
|
||||
v = mingain; // handle gain == -inf
|
||||
v = (v - mingain) / (maxgain - mingain); // scale to 0..1
|
||||
left = (uint32)(v * (double)256); // convert to 8.8 fixed point
|
||||
|
||||
// Right
|
||||
if (x[0].sizeOut <= 1) { // handle a mono interface
|
||||
right = left;
|
||||
}
|
||||
else {
|
||||
v = alFixedToDouble(gain[1]);
|
||||
if (v < mingain)
|
||||
v = mingain; // handle gain == -inf
|
||||
v = (v - mingain) / (maxgain - mingain); // scale to 0..1
|
||||
right = (uint32)(v * (double)256); // convert to 8.8 fixed point
|
||||
}
|
||||
|
||||
ret = (left << 16) | right;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_volume(uint32 vol)
|
||||
{
|
||||
ALpv x[1];
|
||||
ALfixed gain[2]; // left and right
|
||||
double maxgain, mingain;
|
||||
ALparamInfo pi;
|
||||
int dev = alGetDevice(config);
|
||||
|
||||
// Fetch the maximum and minimum gain settings
|
||||
|
||||
alGetParamInfo(dev, AL_GAIN, &pi);
|
||||
maxgain = alFixedToDouble(pi.max.ll);
|
||||
mingain = alFixedToDouble(pi.min.ll);
|
||||
|
||||
// Set the new gain values
|
||||
|
||||
x[0].param = AL_GAIN;
|
||||
x[0].value.ptr = gain;
|
||||
x[0].sizeIn = sizeof(gain) / sizeof(gain[0]);
|
||||
|
||||
uint32 left = vol >> 16;
|
||||
uint32 right = vol & 0xffff;
|
||||
double lv, rv;
|
||||
|
||||
if (left == 0 && pi.specialVals & AL_NEG_INFINITY_BIT) {
|
||||
lv = AL_NEG_INFINITY;
|
||||
}
|
||||
else {
|
||||
lv = ((double)left / 256) * (maxgain - mingain) + mingain;
|
||||
}
|
||||
|
||||
if (right == 0 && pi.specialVals & AL_NEG_INFINITY_BIT) {
|
||||
rv = AL_NEG_INFINITY;
|
||||
}
|
||||
else {
|
||||
rv = ((double)right / 256) * (maxgain - mingain) + mingain;
|
||||
}
|
||||
|
||||
D(bug("set_volume: left=%lf dB, right=%lf dB\n", lv, rv));
|
||||
|
||||
gain[0] = alDoubleToFixed(lv);
|
||||
gain[1] = alDoubleToFixed(rv);
|
||||
|
||||
if (alSetParams(dev, x, 1) < 0) {
|
||||
printf("alSetParams failed: %s\n", alGetErrorString(oserror()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read or set the mute setting using the audio library
|
||||
*/
|
||||
|
||||
static bool read_mute(void)
|
||||
{
|
||||
bool ret;
|
||||
int dev = alGetDevice(config);
|
||||
ALpv x;
|
||||
x.param = AL_MUTE;
|
||||
|
||||
if (alGetParams(dev, &x, 1) < 0) {
|
||||
printf("alSetParams failed: %s\n", alGetErrorString(oserror()));
|
||||
return current_main_mute; // Or just return false?
|
||||
}
|
||||
|
||||
ret = x.value.i;
|
||||
|
||||
D(bug("read_mute: mute=%d\n", ret));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_mute(bool mute)
|
||||
{
|
||||
D(bug("set_mute: mute=%ld\n", mute));
|
||||
|
||||
int dev = alGetDevice(config);
|
||||
ALpv x;
|
||||
x.param = AL_MUTE;
|
||||
x.value.i = mute;
|
||||
|
||||
if (alSetParams(dev, &x, 1) < 0) {
|
||||
printf("alSetParams failed: %s\n", alGetErrorString(oserror()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* MacOS audio interrupt, read next data block
|
||||
*/
|
||||
@ -339,23 +574,29 @@ void AudioInterrupt(void)
|
||||
|
||||
/*
|
||||
* Set sampling parameters
|
||||
* "index" is an index into the audio_sample_rates[] etc. arrays
|
||||
* "index" is an index into the audio_sample_rates[] etc. vectors
|
||||
* It is guaranteed that AudioStatus.num_sources == 0
|
||||
*/
|
||||
|
||||
bool audio_set_sample_rate(int index)
|
||||
{
|
||||
return true;
|
||||
close_audio();
|
||||
audio_sample_rate_index = index;
|
||||
return open_audio();
|
||||
}
|
||||
|
||||
bool audio_set_sample_size(int index)
|
||||
{
|
||||
return true;
|
||||
close_audio();
|
||||
audio_sample_size_index = index;
|
||||
return open_audio();
|
||||
}
|
||||
|
||||
bool audio_set_channels(int index)
|
||||
{
|
||||
return true;
|
||||
close_audio();
|
||||
audio_channel_count_index = index;
|
||||
return open_audio();
|
||||
}
|
||||
|
||||
|
||||
@ -367,36 +608,73 @@ bool audio_set_channels(int index)
|
||||
|
||||
bool audio_get_main_mute(void)
|
||||
{
|
||||
return false;
|
||||
D(bug("audio_get_main_mute: mute=%ld\n", current_main_mute));
|
||||
|
||||
return current_main_mute;
|
||||
}
|
||||
|
||||
uint32 audio_get_main_volume(void)
|
||||
{
|
||||
return 0x01000100;
|
||||
uint32 ret = current_main_volume;
|
||||
|
||||
D(bug("audio_get_main_volume: vol=0x%x\n", ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool audio_get_speaker_mute(void)
|
||||
{
|
||||
return false;
|
||||
D(bug("audio_get_speaker_mute: mute=%ld\n", current_speaker_mute));
|
||||
|
||||
return current_speaker_mute;
|
||||
}
|
||||
|
||||
uint32 audio_get_speaker_volume(void)
|
||||
{
|
||||
return 0x01000100;
|
||||
uint32 ret = current_speaker_volume;
|
||||
|
||||
D(bug("audio_get_speaker_volume: vol=0x%x\n", ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void audio_set_main_mute(bool mute)
|
||||
{
|
||||
D(bug("audio_set_main_mute: mute=%ld\n", mute));
|
||||
|
||||
if (mute != current_main_mute) {
|
||||
current_main_mute = mute;
|
||||
}
|
||||
|
||||
set_mute(current_main_mute);
|
||||
}
|
||||
|
||||
void audio_set_main_volume(uint32 vol)
|
||||
{
|
||||
|
||||
D(bug("audio_set_main_volume: vol=%x\n", vol));
|
||||
|
||||
current_main_volume = vol;
|
||||
|
||||
set_volume(vol);
|
||||
}
|
||||
|
||||
void audio_set_speaker_mute(bool mute)
|
||||
{
|
||||
D(bug("audio_set_speaker_mute: mute=%ld\n", mute));
|
||||
|
||||
if (mute != current_speaker_mute) {
|
||||
current_speaker_mute = mute;
|
||||
}
|
||||
|
||||
set_mute(current_speaker_mute);
|
||||
}
|
||||
|
||||
void audio_set_speaker_volume(uint32 vol)
|
||||
{
|
||||
D(bug("audio_set_speaker_volume: vol=%x\n", vol));
|
||||
|
||||
current_speaker_volume = vol;
|
||||
|
||||
set_volume(vol);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user