nkeynes@66: /** nkeynes@561: * $Id$ nkeynes@66: * nkeynes@66: * Audio mixer core. Combines all the active streams into a single sound nkeynes@66: * buffer for output. nkeynes@66: * nkeynes@66: * Copyright (c) 2005 Nathan Keynes. nkeynes@66: * nkeynes@66: * This program is free software; you can redistribute it and/or modify nkeynes@66: * it under the terms of the GNU General Public License as published by nkeynes@66: * the Free Software Foundation; either version 2 of the License, or nkeynes@66: * (at your option) any later version. nkeynes@66: * nkeynes@66: * This program is distributed in the hope that it will be useful, nkeynes@66: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@66: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@66: * GNU General Public License for more details. nkeynes@66: */ nkeynes@66: nkeynes@66: #include "aica/aica.h" nkeynes@66: #include "aica/audio.h" nkeynes@700: #include nkeynes@66: #include "dream.h" nkeynes@66: #include nkeynes@66: #include nkeynes@66: nkeynes@1024: #define MAX_AUDIO_DRIVERS 16 nkeynes@697: extern struct audio_driver audio_null_driver; nkeynes@697: extern struct audio_driver audio_osx_driver; nkeynes@697: extern struct audio_driver audio_pulse_driver; nkeynes@697: extern struct audio_driver audio_esd_driver; nkeynes@697: extern struct audio_driver audio_alsa_driver; nkeynes@989: extern struct audio_driver audio_sdl_driver; nkeynes@697: nkeynes@1024: static int audio_driver_count = 0; nkeynes@1024: static audio_driver_t audio_driver_list[MAX_AUDIO_DRIVERS] = {}; nkeynes@531: nkeynes@66: #define NUM_BUFFERS 3 nkeynes@700: #define MS_PER_BUFFER 100 nkeynes@66: nkeynes@66: #define BUFFER_EMPTY 0 nkeynes@66: #define BUFFER_WRITING 1 nkeynes@66: #define BUFFER_FULL 2 nkeynes@66: nkeynes@66: struct audio_state { nkeynes@66: audio_buffer_t output_buffers[NUM_BUFFERS]; nkeynes@66: int write_buffer; nkeynes@66: int read_buffer; nkeynes@66: uint32_t output_format; nkeynes@66: uint32_t output_rate; nkeynes@66: uint32_t output_sample_size; nkeynes@465: struct audio_channel channels[AUDIO_CHANNEL_COUNT]; nkeynes@66: } audio; nkeynes@66: nkeynes@66: audio_driver_t audio_driver = NULL; nkeynes@66: nkeynes@66: #define NEXT_BUFFER() ((audio.write_buffer == NUM_BUFFERS-1) ? 0 : audio.write_buffer+1) nkeynes@66: nkeynes@66: /** nkeynes@465: * Preserve audio channel state only - don't bother saving the buffers nkeynes@465: */ nkeynes@465: void audio_save_state( FILE *f ) nkeynes@465: { nkeynes@465: fwrite( &audio.channels[0], sizeof(struct audio_channel), AUDIO_CHANNEL_COUNT, f ); nkeynes@465: } nkeynes@465: nkeynes@465: int audio_load_state( FILE *f ) nkeynes@465: { nkeynes@465: int read = fread( &audio.channels[0], sizeof(struct audio_channel), AUDIO_CHANNEL_COUNT, f ); nkeynes@465: return (read == AUDIO_CHANNEL_COUNT ? 0 : -1 ); nkeynes@465: } nkeynes@465: nkeynes@1024: static int audio_driver_priority_compare(const void *a, const void *b) nkeynes@1024: { nkeynes@1024: audio_driver_t ada = *(audio_driver_t *)a; nkeynes@1024: audio_driver_t adb = *(audio_driver_t *)b; nkeynes@1024: return ada->priority - adb->priority; nkeynes@1024: } nkeynes@1024: nkeynes@1024: static int audio_driver_name_compare(const void *a, const void *b) nkeynes@1024: { nkeynes@1024: audio_driver_t ada = *(audio_driver_t *)a; nkeynes@1024: audio_driver_t adb = *(audio_driver_t *)b; nkeynes@1024: return strcasecmp( ada->name, adb->name ); nkeynes@1024: } nkeynes@1024: nkeynes@1024: nkeynes@1024: nkeynes@1024: gboolean audio_register_driver( audio_driver_t driver ) nkeynes@1024: { nkeynes@1024: if( audio_driver_count >= MAX_AUDIO_DRIVERS ) { nkeynes@1024: return FALSE; nkeynes@1024: } nkeynes@1024: audio_driver_list[audio_driver_count++] = driver; nkeynes@1024: qsort( audio_driver_list, audio_driver_count, sizeof( audio_driver_t ), audio_driver_priority_compare ); nkeynes@1024: return TRUE; nkeynes@1024: } nkeynes@1024: nkeynes@531: audio_driver_t get_audio_driver_by_name( const char *name ) nkeynes@531: { nkeynes@531: int i; nkeynes@531: if( name == NULL ) { nkeynes@697: return audio_driver_list[0]; nkeynes@531: } nkeynes@1024: for( i=0; i < audio_driver_count; i++ ) { nkeynes@697: if( strcasecmp( audio_driver_list[i]->name, name ) == 0 ) { nkeynes@697: return audio_driver_list[i]; nkeynes@697: } nkeynes@531: } nkeynes@531: nkeynes@531: return NULL; nkeynes@531: } nkeynes@531: nkeynes@700: void print_audio_drivers( FILE * out ) nkeynes@700: { nkeynes@700: int i; nkeynes@1024: audio_driver_t temp_list[MAX_AUDIO_DRIVERS]; nkeynes@1024: memcpy( temp_list, audio_driver_list, audio_driver_count*sizeof(audio_driver_t) ); nkeynes@1024: qsort( temp_list, audio_driver_count, sizeof(audio_driver_t), audio_driver_name_compare ); nkeynes@700: fprintf( out, "Available audio drivers:\n" ); nkeynes@1024: for( i=0; i < audio_driver_count; i++ ) { nkeynes@1024: fprintf( out, " %-8s %s\n", temp_list[i]->name, nkeynes@1024: gettext(temp_list[i]->description) ); nkeynes@700: } nkeynes@700: } nkeynes@700: nkeynes@697: audio_driver_t audio_init_driver( const char *preferred_driver ) nkeynes@697: { nkeynes@697: audio_driver_t audio_driver = get_audio_driver_by_name(preferred_driver); nkeynes@697: if( audio_driver == NULL ) { nkeynes@697: ERROR( "Audio driver '%s' not found, aborting.", preferred_driver ); nkeynes@697: exit(2); nkeynes@697: } else if( audio_set_driver( audio_driver ) == FALSE ) { nkeynes@779: int i; nkeynes@1024: for( i=0; i < audio_driver_count; i++ ) { nkeynes@779: if( audio_driver_list[i] != audio_driver && nkeynes@779: audio_set_driver( audio_driver_list[i] ) ) { nkeynes@779: ERROR( "Failed to initialize audio driver %s, falling back to %s", nkeynes@779: audio_driver->name, audio_driver_list[i]->name ); nkeynes@779: return audio_driver_list[i]; nkeynes@779: } nkeynes@779: } nkeynes@779: ERROR( "Unable to intialize any audio driver, aborting." ); nkeynes@779: exit(2); nkeynes@759: } nkeynes@759: return audio_driver; nkeynes@697: } nkeynes@697: nkeynes@1024: void audio_start_driver(void) nkeynes@1024: { nkeynes@1024: if( audio_driver != NULL && audio_driver->start != NULL ) { nkeynes@1024: audio_driver->start(); nkeynes@1024: } nkeynes@1024: } nkeynes@1024: nkeynes@1024: void audio_stop_driver(void) nkeynes@1024: { nkeynes@1024: if( audio_driver != NULL && audio_driver->stop != NULL ) { nkeynes@1024: audio_driver->stop(); nkeynes@1024: } nkeynes@1024: } nkeynes@1024: nkeynes@465: /** nkeynes@66: * Set the output driver, sample rate and format. Also initializes the nkeynes@66: * output buffers, flushing any current data and reallocating as nkeynes@66: * necessary. nkeynes@66: */ nkeynes@697: gboolean audio_set_driver( audio_driver_t driver ) nkeynes@66: { nkeynes@66: uint32_t bytes_per_sample = 1; nkeynes@66: uint32_t samples_per_buffer; nkeynes@66: int i; nkeynes@66: nkeynes@111: if( audio_driver == NULL || driver != NULL ) { nkeynes@697: if( driver == NULL ) nkeynes@697: driver = &audio_null_driver; nkeynes@697: if( driver != audio_driver ) { nkeynes@697: if( !driver->init() ) nkeynes@697: return FALSE; nkeynes@697: audio_driver = driver; nkeynes@697: } nkeynes@111: } nkeynes@111: nkeynes@697: switch( driver->sample_format & AUDIO_FMT_SAMPLE_MASK ) { nkeynes@697: case AUDIO_FMT_8BIT: nkeynes@697: bytes_per_sample = 1; nkeynes@697: break; nkeynes@697: case AUDIO_FMT_16BIT: nkeynes@697: bytes_per_sample = 2; nkeynes@697: break; nkeynes@697: case AUDIO_FMT_FLOAT: nkeynes@697: bytes_per_sample = 4; nkeynes@697: break; nkeynes@697: } nkeynes@697: nkeynes@697: if( driver->sample_format & AUDIO_FMT_STEREO ) nkeynes@697: bytes_per_sample <<= 1; nkeynes@697: if( driver->sample_rate == audio.output_rate && nkeynes@697: bytes_per_sample == audio.output_sample_size ) nkeynes@697: return TRUE; nkeynes@697: samples_per_buffer = (driver->sample_rate * MS_PER_BUFFER / 1000); nkeynes@66: for( i=0; ilength = samples_per_buffer * bytes_per_sample; nkeynes@697: audio.output_buffers[i]->posn = 0; nkeynes@697: audio.output_buffers[i]->status = BUFFER_EMPTY; nkeynes@66: } nkeynes@697: audio.output_format = driver->sample_format; nkeynes@697: audio.output_rate = driver->sample_rate; nkeynes@66: audio.output_sample_size = bytes_per_sample; nkeynes@66: audio.write_buffer = 0; nkeynes@66: audio.read_buffer = 0; nkeynes@66: nkeynes@111: return TRUE; nkeynes@66: } nkeynes@66: nkeynes@66: /** nkeynes@66: * Mark the current write buffer as full and prepare the next buffer for nkeynes@66: * writing. Returns the next buffer to write to. nkeynes@66: * If all buffers are full, returns NULL. nkeynes@66: */ nkeynes@66: audio_buffer_t audio_next_write_buffer( ) nkeynes@66: { nkeynes@66: audio_buffer_t result = NULL; nkeynes@66: audio_buffer_t current = audio.output_buffers[audio.write_buffer]; nkeynes@66: current->status = BUFFER_FULL; nkeynes@66: if( audio.read_buffer == audio.write_buffer && nkeynes@697: audio_driver->process_buffer( current ) ) { nkeynes@697: audio_next_read_buffer(); nkeynes@66: } nkeynes@697: int next_buffer = NEXT_BUFFER(); nkeynes@697: result = audio.output_buffers[next_buffer]; nkeynes@66: if( result->status == BUFFER_FULL ) nkeynes@697: return NULL; nkeynes@66: else { nkeynes@697: audio.write_buffer = next_buffer; nkeynes@697: result->status = BUFFER_WRITING; nkeynes@697: return result; nkeynes@66: } nkeynes@66: } nkeynes@66: nkeynes@66: /** nkeynes@66: * Mark the current read buffer as empty and return the next buffer for nkeynes@66: * reading. If there is no next buffer yet, returns NULL. nkeynes@66: */ nkeynes@66: audio_buffer_t audio_next_read_buffer( ) nkeynes@66: { nkeynes@66: audio_buffer_t current = audio.output_buffers[audio.read_buffer]; nkeynes@697: if( current->status == BUFFER_FULL ) { nkeynes@697: // Current read buffer has data, which we've just emptied nkeynes@697: current->status = BUFFER_EMPTY; nkeynes@697: current->posn = 0; nkeynes@697: audio.read_buffer++; nkeynes@697: if( audio.read_buffer == NUM_BUFFERS ) nkeynes@697: audio.read_buffer = 0; nkeynes@697: nkeynes@697: current = audio.output_buffers[audio.read_buffer]; nkeynes@697: if( current->status == BUFFER_FULL ) { nkeynes@697: current->posn = 0; nkeynes@697: return current; nkeynes@697: } nkeynes@697: else return NULL; nkeynes@697: } else { nkeynes@697: return NULL; nkeynes@697: } nkeynes@697: nkeynes@66: } nkeynes@66: nkeynes@66: /*************************** ADPCM ***********************************/ nkeynes@66: nkeynes@66: /** nkeynes@66: * The following section borrows heavily from ffmpeg, which is nkeynes@66: * copyright (c) 2001-2003 by the fine folks at the ffmpeg project, nkeynes@66: * distributed under the GPL version 2 or later. nkeynes@66: */ nkeynes@66: nkeynes@66: #define CLAMP_TO_SHORT(value) \ nkeynes@736: if (value > 32767) \ nkeynes@66: value = 32767; \ nkeynes@736: else if (value < -32768) \ nkeynes@66: value = -32768; \ nkeynes@66: nkeynes@66: static const int yamaha_indexscale[] = { nkeynes@736: 230, 230, 230, 230, 307, 409, 512, 614, nkeynes@736: 230, 230, 230, 230, 307, 409, 512, 614 nkeynes@66: }; nkeynes@66: nkeynes@66: static const int yamaha_difflookup[] = { nkeynes@736: 1, 3, 5, 7, 9, 11, 13, 15, nkeynes@736: -1, -3, -5, -7, -9, -11, -13, -15 nkeynes@66: }; nkeynes@66: nkeynes@66: static inline short adpcm_yamaha_decode_nibble( audio_channel_t c, nkeynes@736: unsigned char nibble ) nkeynes@66: { nkeynes@66: if( c->adpcm_step == 0 ) { nkeynes@66: c->adpcm_predict = 0; nkeynes@66: c->adpcm_step = 127; nkeynes@66: } nkeynes@66: nkeynes@66: c->adpcm_predict += (c->adpcm_step * yamaha_difflookup[nibble]) >> 3; nkeynes@66: CLAMP_TO_SHORT(c->adpcm_predict); nkeynes@66: c->adpcm_step = (c->adpcm_step * yamaha_indexscale[nibble]) >> 8; nkeynes@66: c->adpcm_step = CLAMP(c->adpcm_step, 127, 24567); nkeynes@66: return c->adpcm_predict; nkeynes@66: } nkeynes@66: nkeynes@66: /*************************** Sample mixer *****************************/ nkeynes@66: nkeynes@66: /** nkeynes@66: * Mix a single output sample. nkeynes@66: */ nkeynes@73: void audio_mix_samples( int num_samples ) nkeynes@66: { nkeynes@66: int i, j; nkeynes@73: int32_t result_buf[num_samples][2]; nkeynes@73: nkeynes@73: memset( &result_buf, 0, sizeof(result_buf) ); nkeynes@66: nkeynes@465: for( i=0; i < AUDIO_CHANNEL_COUNT; i++ ) { nkeynes@697: audio_channel_t channel = &audio.channels[i]; nkeynes@697: if( channel->active ) { nkeynes@697: int32_t sample; nkeynes@697: int vol_left = (channel->vol * (32 - channel->pan)) >> 5; nkeynes@697: int vol_right = (channel->vol * (channel->pan + 1)) >> 5; nkeynes@697: switch( channel->sample_format ) { nkeynes@697: case AUDIO_FMT_16BIT: nkeynes@697: for( j=0; jstart + channel->posn*2)&AUDIO_MEM_MASK)); nkeynes@697: result_buf[j][0] += sample * vol_left; nkeynes@697: result_buf[j][1] += sample * vol_right; nkeynes@697: nkeynes@697: channel->posn_left += channel->sample_rate; nkeynes@697: while( channel->posn_left > audio.output_rate ) { nkeynes@697: channel->posn_left -= audio.output_rate; nkeynes@697: channel->posn++; nkeynes@697: nkeynes@697: if( channel->posn == channel->end ) { nkeynes@697: if( channel->loop ) { nkeynes@697: channel->posn = channel->loop_start; nkeynes@697: channel->loop = LOOP_LOOPED; nkeynes@697: } else { nkeynes@697: audio_stop_channel(i); nkeynes@697: j = num_samples; nkeynes@697: break; nkeynes@697: } nkeynes@697: } nkeynes@697: } nkeynes@697: } nkeynes@697: break; nkeynes@697: case AUDIO_FMT_8BIT: nkeynes@697: for( j=0; jstart + channel->posn)&AUDIO_MEM_MASK))) << 8; nkeynes@697: result_buf[j][0] += sample * vol_left; nkeynes@697: result_buf[j][1] += sample * vol_right; nkeynes@697: nkeynes@697: channel->posn_left += channel->sample_rate; nkeynes@697: while( channel->posn_left > audio.output_rate ) { nkeynes@697: channel->posn_left -= audio.output_rate; nkeynes@697: channel->posn++; nkeynes@697: nkeynes@697: if( channel->posn == channel->end ) { nkeynes@697: if( channel->loop ) { nkeynes@697: channel->posn = channel->loop_start; nkeynes@697: channel->loop = LOOP_LOOPED; nkeynes@697: } else { nkeynes@697: audio_stop_channel(i); nkeynes@697: j = num_samples; nkeynes@697: break; nkeynes@697: } nkeynes@697: } nkeynes@697: } nkeynes@697: } nkeynes@697: break; nkeynes@697: case AUDIO_FMT_ADPCM: nkeynes@697: for( j=0; jadpcm_predict; nkeynes@697: result_buf[j][0] += sample * vol_left; nkeynes@697: result_buf[j][1] += sample * vol_right; nkeynes@697: channel->posn_left += channel->sample_rate; nkeynes@697: while( channel->posn_left > audio.output_rate ) { nkeynes@697: channel->posn_left -= audio.output_rate; nkeynes@697: channel->posn++; nkeynes@697: if( channel->posn == channel->end ) { nkeynes@697: if( channel->loop ) { nkeynes@697: channel->posn = channel->loop_start; nkeynes@697: channel->loop = LOOP_LOOPED; nkeynes@697: channel->adpcm_predict = 0; nkeynes@697: channel->adpcm_step = 0; nkeynes@697: } else { nkeynes@697: audio_stop_channel(i); nkeynes@697: j = num_samples; nkeynes@697: break; nkeynes@697: } nkeynes@697: } nkeynes@1089: uint8_t data = *(uint8_t *)(aica_main_ram + ((channel->start + (channel->posn>>1))&AUDIO_MEM_MASK)); nkeynes@697: if( channel->posn&1 ) { nkeynes@697: adpcm_yamaha_decode_nibble( channel, (data >> 4) & 0x0F ); nkeynes@697: } else { nkeynes@697: adpcm_yamaha_decode_nibble( channel, data & 0x0F ); nkeynes@697: } nkeynes@697: } nkeynes@697: } nkeynes@697: break; nkeynes@697: default: nkeynes@697: break; nkeynes@697: } nkeynes@697: } nkeynes@66: } nkeynes@736: nkeynes@66: /* Down-render to the final output format */ nkeynes@697: audio_buffer_t buf = audio.output_buffers[audio.write_buffer]; nkeynes@697: if( buf->status == BUFFER_FULL ) { nkeynes@697: buf = audio_next_write_buffer(); nkeynes@697: if( buf == NULL ) { // no available space nkeynes@697: return; nkeynes@697: } nkeynes@697: } nkeynes@736: nkeynes@697: switch( audio.output_format & AUDIO_FMT_SAMPLE_MASK ) { nkeynes@697: case AUDIO_FMT_FLOAT: { nkeynes@697: float scale = 1.0/SHRT_MAX; nkeynes@697: float *data = (float *)&buf->data[buf->posn]; nkeynes@697: for( j=0; j> 6); nkeynes@697: *data++ = scale * (result_buf[j][1] >> 6); nkeynes@697: buf->posn += 8; nkeynes@697: if( buf->posn == buf->length ) { nkeynes@697: buf = audio_next_write_buffer(); nkeynes@697: if( buf == NULL ) { nkeynes@697: break; nkeynes@697: } nkeynes@697: data = (float *)&buf->data[0]; nkeynes@697: } nkeynes@697: } nkeynes@697: break; nkeynes@697: } nkeynes@697: case AUDIO_FMT_16BIT: { nkeynes@697: int16_t *data = (int16_t *)&buf->data[buf->posn]; nkeynes@697: for( j=0; j < num_samples; j++ ) { nkeynes@697: *data++ = (int16_t)(result_buf[j][0] >> 6); nkeynes@697: *data++ = (int16_t)(result_buf[j][1] >> 6); nkeynes@697: buf->posn += 4; nkeynes@697: if( buf->posn == buf->length ) { nkeynes@697: buf = audio_next_write_buffer(); nkeynes@697: if( buf == NULL ) { nkeynes@697: // All buffers are full nkeynes@697: break; nkeynes@697: } nkeynes@697: data = (int16_t *)&buf->data[0]; nkeynes@697: } nkeynes@697: } nkeynes@697: break; nkeynes@697: } nkeynes@697: case AUDIO_FMT_8BIT: { nkeynes@700: int8_t *data = (int8_t *)&buf->data[buf->posn]; nkeynes@697: for( j=0; j < num_samples; j++ ) { nkeynes@697: *data++ = (int8_t)(result_buf[j][0] >> 16); nkeynes@697: *data++ = (int8_t)(result_buf[j][1] >> 16); nkeynes@697: buf->posn += 2; nkeynes@697: if( buf->posn == buf->length ) { nkeynes@697: buf = audio_next_write_buffer(); nkeynes@697: if( buf == NULL ) { nkeynes@697: // All buffers are full nkeynes@697: break; nkeynes@697: } nkeynes@697: buf = audio.output_buffers[audio.write_buffer]; nkeynes@700: data = (int8_t *)&buf->data[0]; nkeynes@697: } nkeynes@697: } nkeynes@697: break; nkeynes@697: } nkeynes@66: } nkeynes@66: } nkeynes@66: nkeynes@66: /********************** Internal AICA calls ***************************/ nkeynes@66: nkeynes@66: audio_channel_t audio_get_channel( int channel ) nkeynes@66: { nkeynes@66: return &audio.channels[channel]; nkeynes@66: } nkeynes@66: nkeynes@434: void audio_start_stop_channel( int channel, gboolean start ) nkeynes@434: { nkeynes@434: if( audio.channels[channel].active ) { nkeynes@736: if( !start ) { nkeynes@736: audio_stop_channel(channel); nkeynes@736: } nkeynes@434: } else if( start ) { nkeynes@736: audio_start_channel(channel); nkeynes@434: } nkeynes@434: } nkeynes@434: nkeynes@66: void audio_stop_channel( int channel ) nkeynes@66: { nkeynes@66: audio.channels[channel].active = FALSE; nkeynes@66: } nkeynes@66: nkeynes@66: nkeynes@66: void audio_start_channel( int channel ) nkeynes@66: { nkeynes@66: audio.channels[channel].posn = 0; nkeynes@66: audio.channels[channel].posn_left = 0; nkeynes@66: audio.channels[channel].active = TRUE; nkeynes@434: if( audio.channels[channel].sample_format == AUDIO_FMT_ADPCM ) { nkeynes@736: audio.channels[channel].adpcm_step = 0; nkeynes@736: audio.channels[channel].adpcm_predict = 0; nkeynes@934: uint8_t data = ((uint8_t *)(aica_main_ram + audio.channels[channel].start))[0]; nkeynes@736: adpcm_yamaha_decode_nibble( &audio.channels[channel], data & 0x0F ); nkeynes@434: } nkeynes@66: }