--- BasiliskII/src/Unix/audio_oss_esd.cpp 2000/07/13 13:47:06 1.5 +++ BasiliskII/src/Unix/audio_oss_esd.cpp 2004/01/04 16:35:32 1.17 @@ -1,7 +1,7 @@ /* * audio_oss_esd.cpp - Audio support, implementation for OSS and ESD (Linux and FreeBSD) * - * Basilisk II (C) 1997-2000 Christian Bauer + * Basilisk II (C) 1997-2002 Christian Bauer * * 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 @@ -49,20 +49,15 @@ #include "debug.h" -// Supported sample rates, sizes and channels (defaults) -int audio_num_sample_rates = 1; -uint32 audio_sample_rates[] = {44100 << 16}; -int audio_num_sample_sizes = 1; -uint16 audio_sample_sizes[] = {16}; -int audio_num_channel_counts = 1; -uint16 audio_channel_counts[] = {2}; - -// Constants -#define DSP_NAME "/dev/dsp" +// 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 of /dev/dsp or ESD -static int mixer_fd = -1; // fd of /dev/mixer +static bool is_dsp_audio = false; // Flag: is DSP audio +static int audio_fd = -1; // fd of dsp or ESD +static int mixer_fd = -1; // fd of mixer static sem_t audio_irq_done_sem; // Signal from interrupt to streaming thread: data block read static bool sem_inited = false; // Flag: audio_irq_done_sem initialized static int sound_buffer_size; // Size of sound buffer in bytes @@ -84,73 +79,118 @@ 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 using /dev/dsp, returns false on error -bool audio_init_dsp(void) +// Init using the dsp device, returns false on error +static bool open_dsp(void) { - printf("Using " DSP_NAME " audio output\n"); - - // Get supported sample formats - unsigned long format; - ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format); - if ((format & (AFMT_U8 | AFMT_S16_BE | AFMT_S16_LE)) == 0) { - WarningAlert(GetString(STR_AUDIO_FORMAT_WARN)); - close(audio_fd); - audio_fd = -1; + // Open the device + const char *dsp = PrefsFindString("dsp"); + audio_fd = open(dsp, O_WRONLY); + if (audio_fd < 0) { + fprintf(stderr, "WARNING: Cannot open %s (%s)\n", dsp, strerror(errno)); return false; } - if (format & (AFMT_S16_BE | AFMT_S16_LE)) { - audio_sample_sizes[0] = 16; - silence_byte = 0; - } else { - audio_sample_sizes[0] = 8; - silence_byte = 0x80; + + printf("Using %s audio output\n", dsp); + is_dsp_audio = true; + + // Get supported sample formats + if (audio_sample_sizes.empty()) { + unsigned long format; + ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format); + if (format & AFMT_U8) + audio_sample_sizes.push_back(8); + if (format & (AFMT_S16_BE | AFMT_S16_LE)) + audio_sample_sizes.push_back(16); + + int stereo = 0; + if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == 0 && stereo == 0) + audio_channel_counts.push_back(1); + stereo = 1; + if (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) == 0 && stereo == 1) + audio_channel_counts.push_back(2); + + if (audio_sample_sizes.empty() || audio_channel_counts.empty()) { + WarningAlert(GetString(STR_AUDIO_FORMAT_WARN)); + close(audio_fd); + audio_fd = -1; + return false; + } + + audio_sample_rates.push_back(11025 << 16); + audio_sample_rates.push_back(22050 << 16); + int rate = 44100; + ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate); + if (rate > 22050) + audio_sample_rates.push_back(rate << 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; } - if (!(format & AFMT_S16_BE)) - little_endian = true; // Set DSP parameters - format = audio_sample_sizes[0] == 8 ? AFMT_U8 : (little_endian ? AFMT_S16_LE : AFMT_S16_BE); + unsigned long format; + if (audio_sample_sizes[audio_sample_size_index] == 8) { + format = AFMT_U8; + little_endian = false; + silence_byte = 0x80; + } else { + unsigned long sup_format; + ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &sup_format); + if (sup_format & AFMT_S16_BE) { + little_endian = false; + format = AFMT_S16_BE; + } else { + little_endian = true; + format = AFMT_S16_LE; + } + silence_byte = 0; + } ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format); int frag = 0x0004000c; // Block size: 4096 frames ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag); - int stereo = (audio_channel_counts[0] == 2); + int stereo = (audio_channel_counts[audio_channel_count_index] == 2); ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo); - int rate = audio_sample_rates[0] >> 16; + int rate = audio_sample_rates[audio_sample_rate_index] >> 16; ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate); - audio_sample_rates[0] = rate << 16; - - // Set AudioStatus again because we now know more about the sound - // system's capabilities - set_audio_status_format(); // Get sound buffer size ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &audio_frames_per_block); D(bug("DSP_GETBLKSIZE %d\n", audio_frames_per_block)); - sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block; return true; } // Init using ESD, returns false on error -bool audio_init_esd(void) +static bool open_esd(void) { #ifdef ENABLE_ESD - printf("Using ESD audio output\n"); - - // ESD audio format + int rate; esd_format_t format = ESD_STREAM | ESD_PLAY; - if (AudioStatus.sample_size == 8) - format |= ESD_BITS8; - else - format |= ESD_BITS16; - if (AudioStatus.channels == 1) - format |= ESD_MONO; - else - format |= ESD_STEREO; + + if (audio_sample_sizes.empty()) { + + // Default values + rate = 44100; + format |= (ESD_BITS16 | ESD_STEREO); + + } else { + + rate = audio_sample_rates[audio_sample_rate_index] >> 16; + if (audio_sample_sizes[audio_sample_size_index] == 8) + format |= ESD_BITS8; + else + format |= ESD_BITS16; + if (audio_channel_counts[audio_channel_count_index] == 1) + format |= ESD_MONO; + else + format |= ESD_STEREO; + } #if WORDS_BIGENDIAN little_endian = false; @@ -160,30 +200,87 @@ bool audio_init_esd(void) silence_byte = 0; // Is this correct for 8-bit mode? // Open connection to ESD server - audio_fd = esd_play_stream(format, AudioStatus.sample_rate >> 16, NULL, NULL); + audio_fd = esd_play_stream(format, rate, NULL, NULL); if (audio_fd < 0) { - char str[256]; - sprintf(str, GetString(STR_NO_ESD_WARN), strerror(errno)); - WarningAlert(str); + fprintf(stderr, "WARNING: Cannot open ESD connection\n"); return false; } + printf("Using ESD audio output\n"); + + // ESD supports a variety of twisted little audio formats, all different + if (audio_sample_sizes.empty()) { + + // The reason we do this here is that we don't want to add sample + // rates etc. unless the ESD server connection could be opened + // (if ESD fails, dsp might be tried next) + audio_sample_rates.push_back(11025 << 16); + audio_sample_rates.push_back(22050 << 16); + audio_sample_rates.push_back(44100 << 16); + audio_sample_sizes.push_back(8); + audio_sample_sizes.push_back(16); + audio_channel_counts.push_back(1); + audio_channel_counts.push_back(2); + + // 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; + } + // Sound buffer size = 4096 frames audio_frames_per_block = 4096; - sound_buffer_size = (AudioStatus.sample_size >> 3) * AudioStatus.channels * audio_frames_per_block; return true; #else - ErrorAlert("Basilisk II has been compiled with ESD support disabled."); + // ESD is not enabled, shut up the compiler return false; #endif } -void AudioInit(void) +static bool open_audio(void) { - char str[256]; +#ifdef ENABLE_ESD + // If ESPEAKER is set, the user probably wants to use ESD, so try that first + if (getenv("ESPEAKER")) + if (open_esd()) + goto dev_opened; +#endif + + // Try to open dsp + if (open_dsp()) + goto dev_opened; + +#ifdef ENABLE_ESD + // Hm, dsp failed so we try ESD again if ESPEAKER wasn't set + if (!getenv("ESPEAKER")) + if (open_esd()) + goto dev_opened; +#endif + + // No audio device succeeded + WarningAlert(GetString(STR_NO_AUDIO_WARN)); + return false; - // Init audio status (defaults) and feature flags + // Device opened, set AudioStatus +dev_opened: + 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(); + + // 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; +} + +void AudioInit(void) +{ + // 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; @@ -192,46 +289,19 @@ void AudioInit(void) if (PrefsFindBool("nosound")) return; - // Try to open /dev/dsp - audio_fd = open(DSP_NAME, O_WRONLY); - if (audio_fd < 0) { -#ifdef ENABLE_ESD - if (!audio_init_esd()) - return; -#else - sprintf(str, GetString(STR_NO_AUDIO_DEV_WARN), DSP_NAME, strerror(errno)); - WarningAlert(str); - return; -#endif - } else - if (!audio_init_dsp()) - return; - - // Try to open /dev/mixer - mixer_fd = open("/dev/mixer", O_RDWR); - if (mixer_fd < 0) - printf("WARNING: Cannot open /dev/mixer (%s)", strerror(errno)); - // Init semaphore if (sem_init(&audio_irq_done_sem, 0, 0) < 0) return; sem_inited = true; - // Start streaming thread - pthread_attr_init(&stream_thread_attr); -#if defined(_POSIX_THREAD_PRIORITY_SCHEDULING) - if (geteuid() == 0) { - pthread_attr_setinheritsched(&stream_thread_attr, PTHREAD_EXPLICIT_SCHED); - pthread_attr_setschedpolicy(&stream_thread_attr, SCHED_FIFO); - struct sched_param fifo_param; - fifo_param.sched_priority = (sched_get_priority_min(SCHED_FIFO) + sched_get_priority_max(SCHED_FIFO)) / 2; - pthread_attr_setschedparam(&stream_thread_attr, &fifo_param); - } -#endif - stream_thread_active = (pthread_create(&stream_thread, &stream_thread_attr, stream_func, NULL) == 0); + // Try to open the mixer device + const char *mixer = PrefsFindString("mixer"); + mixer_fd = open(mixer, O_RDWR); + if (mixer_fd < 0) + printf("WARNING: Cannot open %s (%s)\n", mixer, strerror(errno)); - // Everything OK - audio_open = true; + // Open and initialize audio device + open_audio(); } @@ -239,7 +309,7 @@ void AudioInit(void) * Deinitialization */ -void AudioExit(void) +static void close_audio(void) { // Stop stream and delete semaphore if (stream_thread_active) { @@ -250,16 +320,37 @@ void AudioExit(void) pthread_join(stream_thread, NULL); stream_thread_active = false; } - if (sem_inited) - sem_destroy(&audio_irq_done_sem); - // Close /dev/dsp - if (audio_fd > 0) + // Close dsp or ESD socket + if (audio_fd >= 0) { close(audio_fd); + audio_fd = -1; + } - // Close /dev/mixer - if (mixer_fd > 0) + audio_open = false; +} + +void AudioExit(void) +{ + // Stop the device immediately. Otherwise, close() sends + // SNDCTL_DSP_SYNC, which may hang + if (is_dsp_audio) + ioctl(audio_fd, SNDCTL_DSP_RESET, 0); + + // Close audio device + close_audio(); + + // Delete semaphore + if (sem_inited) { + sem_destroy(&audio_irq_done_sem); + sem_inited = false; + } + + // Close mixer device + if (mixer_fd >= 0) { close(mixer_fd); + mixer_fd = -1; + } } @@ -287,8 +378,6 @@ void audio_exit_stream() * Streaming function */ -static uint32 apple_stream_info; // Mac address of SoundComponentData struct describing next buffer - static void *stream_func(void *arg) { int16 *silent_buffer = new int16[sound_buffer_size / 2]; @@ -372,20 +461,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 */ -void audio_set_sample_rate(int index) +bool audio_set_sample_rate(int index) { + close_audio(); + audio_sample_rate_index = index; + return open_audio(); } -void audio_set_sample_size(int index) +bool audio_set_sample_size(int index) { + close_audio(); + audio_sample_size_index = index; + return open_audio(); } -void audio_set_channels(int index) +bool audio_set_channels(int index) { + close_audio(); + audio_channel_count_index = index; + return open_audio(); }