nkeynes@66: /** nkeynes@463: * $Id: audio.c,v 1.10 2007-10-24 21:24:09 nkeynes Exp $ 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@66: #include "glib/gmem.h" nkeynes@66: #include "dream.h" nkeynes@66: #include nkeynes@66: #include nkeynes@66: nkeynes@66: #define NUM_BUFFERS 3 nkeynes@434: #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@66: struct audio_channel channels[64]; 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: extern char *arm_mem; nkeynes@66: nkeynes@66: /** 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@111: gboolean audio_set_driver( audio_driver_t driver, nkeynes@111: uint32_t samplerate, int format ) 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@111: if( driver == NULL ) nkeynes@111: driver = &audio_null_driver; nkeynes@111: if( driver != audio_driver ) { nkeynes@111: if( !driver->set_output_format( samplerate, format ) ) nkeynes@111: return FALSE; nkeynes@111: audio_driver = driver; nkeynes@111: } nkeynes@111: } nkeynes@111: nkeynes@66: if( format & AUDIO_FMT_16BIT ) nkeynes@66: bytes_per_sample = 2; nkeynes@66: if( format & AUDIO_FMT_STEREO ) nkeynes@66: bytes_per_sample <<= 1; nkeynes@66: if( samplerate == audio.output_rate && nkeynes@66: bytes_per_sample == audio.output_sample_size ) nkeynes@431: return TRUE; nkeynes@66: samples_per_buffer = (samplerate * MS_PER_BUFFER / 1000); nkeynes@66: for( i=0; ilength = samples_per_buffer * bytes_per_sample; nkeynes@66: audio.output_buffers[i]->posn = 0; nkeynes@66: audio.output_buffers[i]->status = BUFFER_EMPTY; nkeynes@66: } nkeynes@66: audio.output_format = format; nkeynes@66: audio.output_rate = samplerate; 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@66: audio_driver->process_buffer( current ) ) { nkeynes@66: audio_next_read_buffer(); nkeynes@66: } nkeynes@66: audio.write_buffer = NEXT_BUFFER(); nkeynes@66: result = audio.output_buffers[audio.write_buffer]; nkeynes@66: if( result->status == BUFFER_FULL ) nkeynes@66: return NULL; nkeynes@66: else { nkeynes@66: result->status = BUFFER_WRITING; nkeynes@66: 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@66: assert( current->status == BUFFER_FULL ); nkeynes@66: current->status = BUFFER_EMPTY; nkeynes@66: current->posn = 0; nkeynes@66: audio.read_buffer++; nkeynes@66: if( audio.read_buffer == NUM_BUFFERS ) nkeynes@66: audio.read_buffer = 0; nkeynes@66: nkeynes@66: current = audio.output_buffers[audio.read_buffer]; nkeynes@66: if( current->status == BUFFER_FULL ) nkeynes@66: return current; nkeynes@66: else return NULL; 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@66: if (value > 32767) \ nkeynes@66: value = 32767; \ nkeynes@66: else if (value < -32768) \ nkeynes@66: value = -32768; \ nkeynes@66: nkeynes@66: static const int yamaha_indexscale[] = { nkeynes@66: 230, 230, 230, 230, 307, 409, 512, 614, nkeynes@66: 230, 230, 230, 230, 307, 409, 512, 614 nkeynes@66: }; nkeynes@66: nkeynes@66: static const int yamaha_difflookup[] = { nkeynes@66: 1, 3, 5, 7, 9, 11, 13, 15, nkeynes@66: -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@66: 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@66: for( i=0; i < 64; i++ ) { nkeynes@66: audio_channel_t channel = &audio.channels[i]; nkeynes@66: if( channel->active ) { nkeynes@66: int32_t sample; nkeynes@82: int vol_left = (channel->vol * (32 - channel->pan)) >> 5; nkeynes@82: int vol_right = (channel->vol * (channel->pan + 1)) >> 5; nkeynes@66: switch( channel->sample_format ) { nkeynes@66: case AUDIO_FMT_16BIT: nkeynes@73: for( j=0; jstart))[channel->posn]; nkeynes@82: result_buf[j][0] += sample * vol_left; nkeynes@82: result_buf[j][1] += sample * vol_right; nkeynes@73: nkeynes@73: channel->posn_left += channel->sample_rate; nkeynes@73: while( channel->posn_left > audio.output_rate ) { nkeynes@73: channel->posn_left -= audio.output_rate; nkeynes@73: channel->posn++; nkeynes@73: nkeynes@73: if( channel->posn == channel->end ) { nkeynes@463: if( channel->loop ) { nkeynes@73: channel->posn = channel->loop_start; nkeynes@463: channel->loop = LOOP_LOOPED; nkeynes@463: } else { nkeynes@73: audio_stop_channel(i); nkeynes@73: j = num_samples; nkeynes@73: break; nkeynes@73: } nkeynes@73: } nkeynes@73: } nkeynes@73: } nkeynes@66: break; nkeynes@66: case AUDIO_FMT_8BIT: nkeynes@73: for( j=0; jstart))[channel->posn] << 8; nkeynes@82: result_buf[j][0] += sample * vol_left; nkeynes@82: result_buf[j][1] += sample * vol_right; nkeynes@73: nkeynes@73: channel->posn_left += channel->sample_rate; nkeynes@73: while( channel->posn_left > audio.output_rate ) { nkeynes@73: channel->posn_left -= audio.output_rate; nkeynes@73: channel->posn++; nkeynes@73: nkeynes@73: if( channel->posn == channel->end ) { nkeynes@463: if( channel->loop ) { nkeynes@73: channel->posn = channel->loop_start; nkeynes@463: channel->loop = LOOP_LOOPED; nkeynes@463: } else { nkeynes@73: audio_stop_channel(i); nkeynes@73: j = num_samples; nkeynes@73: break; nkeynes@73: } nkeynes@73: } nkeynes@73: } nkeynes@73: } nkeynes@66: break; nkeynes@66: case AUDIO_FMT_ADPCM: nkeynes@73: for( j=0; jadpcm_predict; nkeynes@82: result_buf[j][0] += sample * vol_left; nkeynes@82: result_buf[j][1] += sample * vol_right; nkeynes@73: channel->posn_left += channel->sample_rate; nkeynes@73: while( channel->posn_left > audio.output_rate ) { nkeynes@73: channel->posn_left -= audio.output_rate; nkeynes@434: channel->posn++; nkeynes@434: if( channel->posn == channel->end ) { nkeynes@434: if( channel->loop ) { nkeynes@434: channel->posn = channel->loop_start; nkeynes@463: channel->loop = LOOP_LOOPED; nkeynes@434: channel->adpcm_predict = 0; nkeynes@434: channel->adpcm_step = 0; nkeynes@434: } else { nkeynes@434: audio_stop_channel(i); nkeynes@434: j = num_samples; nkeynes@73: break; nkeynes@73: } nkeynes@434: } nkeynes@434: uint8_t data = ((uint8_t *)(arm_mem + channel->start))[channel->posn>>1]; nkeynes@434: if( channel->posn&1 ) { nkeynes@434: adpcm_yamaha_decode_nibble( channel, (data >> 4) & 0x0F ); nkeynes@434: } else { nkeynes@73: adpcm_yamaha_decode_nibble( channel, data & 0x0F ); nkeynes@73: } nkeynes@73: } nkeynes@73: } nkeynes@73: break; nkeynes@66: default: nkeynes@73: break; nkeynes@66: } nkeynes@66: } nkeynes@66: } nkeynes@73: nkeynes@66: /* Down-render to the final output format */ nkeynes@73: nkeynes@66: if( audio.output_format & AUDIO_FMT_16BIT ) { nkeynes@73: audio_buffer_t buf = audio.output_buffers[audio.write_buffer]; nkeynes@73: uint16_t *data = (uint16_t *)&buf->data[buf->posn]; nkeynes@73: for( j=0; j < num_samples; j++ ) { nkeynes@82: *data++ = (int16_t)(result_buf[j][0] >> 6); nkeynes@82: *data++ = (int16_t)(result_buf[j][1] >> 6); nkeynes@73: buf->posn += 4; nkeynes@73: if( buf->posn == buf->length ) { nkeynes@73: audio_next_write_buffer(); nkeynes@73: buf = audio.output_buffers[audio.write_buffer]; nkeynes@73: data = (uint16_t *)&buf->data[0]; nkeynes@73: } nkeynes@73: } nkeynes@66: } else { nkeynes@73: audio_buffer_t buf = audio.output_buffers[audio.write_buffer]; nkeynes@73: uint8_t *data = (uint8_t *)&buf->data[buf->posn]; nkeynes@73: for( j=0; j < num_samples; j++ ) { nkeynes@73: *data++ = (uint8_t)(result_buf[j][0] >> 16); nkeynes@73: *data++ = (uint8_t)(result_buf[j][1] >> 16); nkeynes@73: buf->posn += 2; nkeynes@73: if( buf->posn == buf->length ) { nkeynes@73: audio_next_write_buffer(); nkeynes@73: buf = audio.output_buffers[audio.write_buffer]; nkeynes@73: data = (uint8_t *)&buf->data[0]; nkeynes@73: } nkeynes@73: } 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@434: if( !start ) { nkeynes@434: audio_stop_channel(channel); nkeynes@434: } nkeynes@434: } else if( start ) { nkeynes@434: 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@434: audio.channels[channel].adpcm_step = 0; nkeynes@434: audio.channels[channel].adpcm_predict = 0; nkeynes@434: uint8_t data = ((uint8_t *)(arm_mem + audio.channels[channel].start))[0]; nkeynes@434: adpcm_yamaha_decode_nibble( &audio.channels[channel], data & 0x0F ); nkeynes@434: } nkeynes@66: }