revision 66:2ec5b6eb75e5
summary |
tree |
shortlog |
changelog |
graph |
changeset |
raw | bz2 | zip | gz changeset | 66:2ec5b6eb75e5 |
parent | 65:9f124c245fc6 |
child | 67:1f59399bbf17 |
author | nkeynes |
date | Tue Jan 10 13:56:54 2006 +0000 (17 years ago) |
Go go gadget audio!
Slow, but it works :)
Slow, but it works :)
1.1 --- a/src/aica/aica.c Tue Jan 03 12:21:45 2006 +00001.2 +++ b/src/aica/aica.c Tue Jan 10 13:56:54 2006 +00001.3 @@ -1,5 +1,5 @@1.4 /**1.5 - * $Id: aica.c,v 1.10 2006-01-02 14:50:12 nkeynes Exp $1.6 + * $Id: aica.c,v 1.11 2006-01-10 13:56:54 nkeynes Exp $1.7 *1.8 * This is the core sound system (ie the bit which does the actual work)1.9 *1.10 @@ -19,9 +19,11 @@1.11 #define MODULE aica_module1.13 #include "dream.h"1.14 +#include "dreamcast.h"1.15 #include "mem.h"1.16 #include "aica.h"1.17 #include "armcore.h"1.18 +#include "audio.h"1.19 #define MMIO_IMPL1.20 #include "aica.h"1.22 @@ -37,6 +39,7 @@1.23 int aica_load_state( FILE *f );1.24 uint32_t aica_run_slice( uint32_t );1.26 +#define IS_TIMER_ENABLED() (MMIO_READ( AICA2, AICA_TCR ) & 0x40)1.28 struct dreamcast_module aica_module = { "AICA", aica_init, aica_reset,1.29 aica_start, aica_run_slice, aica_stop,1.30 @@ -51,12 +54,14 @@1.31 MMIO_NOTRACE(AICA0);1.32 MMIO_NOTRACE(AICA1);1.33 arm_mem_init();1.34 - arm_reset();1.35 + aica_reset();1.36 + audio_set_output( &esd_audio_driver, 44100, AUDIO_FMT_16BIT|AUDIO_FMT_STEREO );1.37 }1.39 void aica_reset( void )1.40 {1.41 arm_reset();1.42 + aica_event(2); /* Pre-deliver a timer interrupt */1.43 }1.45 void aica_start( void )1.46 @@ -64,15 +69,36 @@1.48 }1.50 +/**1.51 + * Keep track of what we've done so far this second, to try to keep the1.52 + * precision of samples/second.1.53 + */1.54 +int samples_done = 0;1.55 +uint32_t nanosecs_done = 0;1.56 +1.57 uint32_t aica_run_slice( uint32_t nanosecs )1.58 {1.59 /* Run arm instructions */1.60 int reset = MMIO_READ( AICA2, AICA_RESET );1.61 - if( (reset & 1) == 0 ) {1.62 - /* Running */1.63 - nanosecs = arm_run_slice( nanosecs );1.64 + if( (reset & 1) == 0 ) { /* Running */1.65 + int num_samples = (nanosecs_done + nanosecs) / AICA_SAMPLE_RATE - samples_done;1.66 + int i;1.67 + for( i=0; i<num_samples; i++ ) {1.68 + nanosecs = arm_run_slice( AICA_SAMPLE_PERIOD );1.69 + audio_mix_sample();1.70 + if( IS_TIMER_ENABLED() ) {1.71 + uint8_t val = MMIO_READ( AICA2, AICA_TIMER );1.72 + val++;1.73 + if( val == 0 )1.74 + aica_event( AICA_EVENT_TIMER );1.75 + MMIO_WRITE( AICA2, AICA_TIMER, val );1.76 + }1.77 + if( !dreamcast_is_running() )1.78 + break;1.79 + }1.80 + samples_done += num_samples;1.81 + nanosecs_done += nanosecs;1.82 }1.83 - /* Generate audio buffer */1.84 return nanosecs;1.85 }1.87 @@ -132,6 +158,7 @@1.88 armr.int_pending &= ~CPSR_F;1.89 }1.90 }1.91 +1.92 /** Channel register structure:1.93 * 00 4 Channel config1.94 * 04 4 Waveform address lo (16 bits)1.95 @@ -155,20 +182,22 @@1.96 /* Write to channels 0-31 */1.97 void mmio_region_AICA0_write( uint32_t reg, uint32_t val )1.98 {1.99 - // aica_write_channel( reg >> 7, reg % 128, val );1.100 MMIO_WRITE( AICA0, reg, val );1.101 + aica_write_channel( reg >> 7, reg % 128, val );1.102 // DEBUG( "AICA0 Write %08X => %08X", val, reg );1.103 }1.105 /* Write to channels 32-64 */1.106 void mmio_region_AICA1_write( uint32_t reg, uint32_t val )1.107 {1.108 - // aica_write_channel( (reg >> 7) + 32, reg % 128, val );1.109 MMIO_WRITE( AICA1, reg, val );1.110 + aica_write_channel( (reg >> 7) + 32, reg % 128, val );1.111 // DEBUG( "AICA1 Write %08X => %08X", val, reg );1.112 }1.114 -/* General registers */1.115 +/**1.116 + * AICA control registers1.117 + */1.118 void mmio_region_AICA2_write( uint32_t reg, uint32_t val )1.119 {1.120 uint32_t tmp;1.121 @@ -179,6 +208,8 @@1.122 /* ARM enabled - execute a core reset */1.123 DEBUG( "ARM enabled" );1.124 arm_reset();1.125 + samples_done = 0;1.126 + nanosecs_done = 0;1.127 } else if( (tmp&1) == 0 && (val&1) == 1 ) {1.128 DEBUG( "ARM disabled" );1.129 }1.130 @@ -192,3 +223,91 @@1.131 break;1.132 }1.133 }1.134 +1.135 +/**1.136 + * Translate the channel frequency to a sample rate. The frequency is a1.137 + * 14-bit floating point number, where bits 0..9 is the mantissa,1.138 + * 11..14 is the signed exponent (-8 to +7). Bit 10 appears to1.139 + * be unused.1.140 + *1.141 + * @return sample rate in samples per second.1.142 + */1.143 +uint32_t aica_frequency_to_sample_rate( uint32_t freq )1.144 +{1.145 + uint32_t exponent = (freq & 0x3800) >> 11;1.146 + uint32_t mantissa = freq & 0x03FF;1.147 + if( freq & 0x4000 ) {1.148 + /* neg exponent - rate < 44100 */1.149 + exponent = 8 - exponent;1.150 + return (44100 >> exponent) +1.151 + ((44100 * mantissa) >> (10+exponent));1.152 + } else {1.153 + /* pos exponent - rate > 44100 */1.154 + return (44100 << exponent) +1.155 + ((44100 * mantissa) >> (10-exponent));1.156 + }1.157 +}1.158 +1.159 +void aica_write_channel( int channelNo, uint32_t reg, uint32_t val )1.160 +{1.161 + val &= 0x0000FFFF;1.162 + audio_channel_t channel = audio_get_channel(channelNo);1.163 + switch( reg ) {1.164 + case 0x00: /* Config + high address bits*/1.165 + channel->start = (channel->start & 0xFFFF) | ((val&0x1F) << 16);1.166 + if( val & 0x200 )1.167 + channel->loop_count = -1;1.168 + else1.169 + channel->loop_count = 0;1.170 + switch( (val >> 7) & 0x03 ) {1.171 + case 0:1.172 + channel->sample_format = AUDIO_FMT_16BIT;1.173 + break;1.174 + case 1:1.175 + channel->sample_format = AUDIO_FMT_8BIT;1.176 + break;1.177 + case 2:1.178 + case 3:1.179 + channel->sample_format = AUDIO_FMT_ADPCM;1.180 + break;1.181 + }1.182 + switch( (val >> 14) & 0x03 ) {1.183 + case 2:1.184 + audio_stop_channel( channelNo );1.185 + break;1.186 + case 3:1.187 + audio_start_channel( channelNo );1.188 + break;1.189 + default:1.190 + break;1.191 + /* Hrmm... */1.192 + }1.193 + break;1.194 + case 0x04: /* Low 16 address bits */1.195 + channel->start = (channel->start & 0x001F0000) | val;1.196 + break;1.197 + case 0x08: /* Loop start */1.198 + channel->loop_start = val;1.199 + break;1.200 + case 0x0C: /* Loop end */1.201 + channel->loop_end = channel->end = val;1.202 + break;1.203 + case 0x10: /* Envelope register 1 */1.204 + break;1.205 + case 0x14: /* Envelope register 2 */1.206 + break;1.207 + case 0x18: /* Frequency */1.208 + channel->sample_rate = aica_frequency_to_sample_rate ( val );1.209 + break;1.210 + case 0x1C: /* ??? */1.211 + case 0x20: /* ??? */1.212 + case 0x24: /* Volume? /pan */1.213 + break;1.214 + case 0x28: /* Volume */1.215 + channel->vol_left = channel->vol_right = val & 0xFF;1.216 + break;1.217 + default: /* ??? */1.218 + break;1.219 + }1.220 +1.221 +}
2.1 --- a/src/aica/aica.h Tue Jan 03 12:21:45 2006 +00002.2 +++ b/src/aica/aica.h Tue Jan 10 13:56:54 2006 +00002.3 @@ -1,5 +1,5 @@2.4 /**2.5 - * $Id: aica.h,v 1.6 2006-01-02 23:06:37 nkeynes Exp $2.6 + * $Id: aica.h,v 1.7 2006-01-10 13:56:54 nkeynes Exp $2.7 *2.8 * MMIO definitions for the AICA sound chip. Note that the regions defined2.9 * here are relative to the SH4 memory map (0x00700000 based), rather than2.10 @@ -33,14 +33,14 @@2.11 LONG_PORT( 0x044, CDDA_VOL_R, PORT_MRW, 0, "CDDA Volume right" )2.12 LONG_PORT( 0x800, VOL_MASTER, PORT_MRW, UNDEFINED, "Master volume" )2.13 LONG_PORT( 0x808, AICA_UNK7, PORT_MRW, 0, "AICA ??? 7" )2.14 -LONG_PORT( 0x880, AICA_TIMER1, PORT_MRW, 0, "AICA Timer 1" )2.15 -LONG_PORT( 0x890, AICA_TIMER2, PORT_MRW, 0, "AICA Timer 2" )2.16 +LONG_PORT( 0x880, AICA_UNK6, PORT_MRW, 0, "AICA ??? 6" )2.17 +LONG_PORT( 0x890, AICA_TIMER, PORT_MRW, 0, "AICA Timer" )2.18 LONG_PORT( 0x89C, AICA_UNK1, PORT_MRW, 0, "AICA ??? 1" )2.19 -LONG_PORT( 0x8A4, AICA_UNK2, PORT_MRW, 0, "AICA ??? 2" )2.20 +LONG_PORT( 0x8A4, AICA_TCR, PORT_MRW, 0, "AICA Timer Control?" )2.21 BYTE_PORT( 0x8A8, AICA_UNK3, PORT_MRW, 0, "AICA ??? 3" )2.22 BYTE_PORT( 0x8AC, AICA_UNK4, PORT_MRW, 0, "AICA ??? 4" )2.23 BYTE_PORT( 0x8B0, AICA_UNK5, PORT_MRW, 0, "AICA ??? 5" )2.24 -LONG_PORT( 0xC00, AICA_RESET,PORT_MRW, 1, "AICA reset" )2.25 +LONG_PORT( 0xC00, AICA_RESET,PORT_MRW, 0, "AICA reset" )2.26 LONG_PORT( 0xD00, AICA_IRQ, PORT_MR, 0, "AICA IRQ Pending" )2.27 LONG_PORT( 0xD04, AICA_IRQCLEAR, PORT_MRW, 0, "AICA IRQ Clear" )2.28 MMIO_REGION_END2.29 @@ -58,3 +58,16 @@2.30 #define AICA_EVENT_OTHER 52.32 void aica_event( int event );2.33 +void aica_write_channel( int channel, uint32_t addr, uint32_t val );2.34 +2.35 +/**2.36 + * The AICA core runs at 44100 samples/second, regardless of what we're2.37 + * actually outputing.2.38 + */2.39 +#define AICA_SAMPLE_RATE 441002.40 +2.41 +/**2.42 + * This is only used to determine number of instructions to execute2.43 + * per sample, which isn't cycle accurate at the moment.2.44 + */2.45 +#define AICA_SAMPLE_PERIOD (1000000000 / 44100)
3.1 --- a/src/aica/armcore.c Tue Jan 03 12:21:45 2006 +00003.2 +++ b/src/aica/armcore.c Tue Jan 10 13:56:54 2006 +00003.3 @@ -1,5 +1,5 @@3.4 /**3.5 - * $Id: armcore.c,v 1.15 2006-01-02 23:07:17 nkeynes Exp $3.6 + * $Id: armcore.c,v 1.16 2006-01-10 13:56:54 nkeynes Exp $3.7 *3.8 * ARM7TDMI CPU emulation core.3.9 *3.10 @@ -155,6 +155,25 @@3.11 }3.13 /**3.14 + * Return a pointer to the specified register in the user bank,3.15 + * regardless of the active bank3.16 + */3.17 +static uint32_t *arm_user_reg( int reg )3.18 +{3.19 + if( IS_EXCEPTION_MODE() ) {3.20 + if( reg == 13 || reg == 14 )3.21 + return &armr.user_r[reg-8];3.22 + if( IS_FIQ_MODE() ) {3.23 + if( reg >= 8 || reg <= 12 )3.24 + return &armr.user_r[reg-8];3.25 + }3.26 + }3.27 + return &armr.r[reg];3.28 +}3.29 +3.30 +#define USER_R(n) *arm_user_reg(n)3.31 +3.32 +/**3.33 * Set the CPSR to the specified value.3.34 *3.35 * @param value values to set in CPSR3.36 @@ -661,6 +680,7 @@3.37 uint32_t pc;3.38 uint32_t ir;3.39 uint32_t operand, operand2, tmp, tmp2, cond;3.40 + int i;3.42 tmp = armr.int_pending & (~armr.cpsr);3.43 if( tmp ) {3.44 @@ -1121,95 +1141,181 @@3.45 }3.46 armr.r[15] = pc + 4 + operand;3.47 } else { /* Load/store multiple */3.48 - int prestep, poststep;3.49 - if( PFLAG(ir) ) {3.50 - prestep = UFLAG(ir) ? 4 : -4;3.51 - poststep = 0 ;3.52 - } else {3.53 - prestep = 0;3.54 - poststep = UFLAG(ir) ? 4 : -4;3.55 - }3.56 operand = RN(ir);3.57 - if( BFLAG(ir) ) {3.58 - /* Actually S - bit 22. Means "make massively complicated" */3.59 - if( LFLAG(ir) && (ir&0x00008000) ) {3.60 - /* LDM (3). Much like normal LDM but also copies SPSR3.61 - * back to CPSR */3.62 - for( tmp=0; tmp < 16; tmp++ ) {3.63 - if( (ir & (1<<tmp)) ) {3.64 - operand += prestep;3.65 - armr.r[tmp] = arm_read_long(operand);3.66 - operand += poststep;3.67 +3.68 + switch( (ir & 0x01D00000) >> 20 ) {3.69 + case 0: /* STMDA */3.70 + for( i=15; i>= 0; i-- ) {3.71 + if( (ir & (1<<i)) ) {3.72 + arm_write_long( operand, armr.r[i] );3.73 + operand -= 4;3.74 + }3.75 + }3.76 + break;3.77 + case 1: /* LDMDA */3.78 + for( i=15; i>= 0; i-- ) {3.79 + if( (ir & (1<<i)) ) {3.80 + armr.r[i] = arm_read_long( operand );3.81 + operand -= 4;3.82 + }3.83 + }3.84 + break;3.85 + case 4: /* STMDA (S) */3.86 + for( i=15; i>= 0; i-- ) {3.87 + if( (ir & (1<<i)) ) {3.88 + arm_write_long( operand, USER_R(i) );3.89 + operand -= 4;3.90 + }3.91 + }3.92 + break;3.93 + case 5: /* LDMDA (S) */3.94 + if( (ir&0x00008000) ) { /* Load PC */3.95 + for( i=15; i>= 0; i-- ) {3.96 + if( (ir & (1<<i)) ) {3.97 + armr.r[i] = arm_read_long( operand );3.98 + operand -= 4;3.99 }3.100 }3.101 - if( WFLAG(ir) )3.102 - LRN(ir) = operand;3.103 arm_restore_cpsr();3.104 - if( armr.t ) PC &= 0xFFFFFFFE;3.105 - else PC &= 0xFFFFFFFC;3.106 } else {3.107 - /* LDM/STM (2). As normal LDM but accesses the User banks3.108 - * instead of the active ones. Aka the truly evil case3.109 - */3.110 - int bank_start;3.111 - if( IS_FIQ_MODE() )3.112 - bank_start = 8;3.113 - else if( IS_EXCEPTION_MODE() )3.114 - bank_start = 13;3.115 - else bank_start = 15;3.116 - for( tmp=0; tmp<bank_start; tmp++ ) {3.117 - if( (ir & (1<<tmp)) ) {3.118 - operand += prestep;3.119 - if( LFLAG(ir) ) {3.120 - armr.r[tmp] = arm_read_long(operand);3.121 - } else {3.122 - arm_write_long( operand, armr.r[tmp] );3.123 - }3.124 - operand += poststep;3.125 + for( i=15; i>= 0; i-- ) {3.126 + if( (ir & (1<<i)) ) {3.127 + USER_R(i) = arm_read_long( operand );3.128 + operand -= 4;3.129 }3.130 }3.131 - for( ; tmp < 15; tmp ++ ) {3.132 - if( (ir & (1<<tmp)) ) {3.133 - operand += prestep;3.134 - if( LFLAG(ir) ) {3.135 - armr.user_r[tmp-8] = arm_read_long(operand);3.136 - } else {3.137 - arm_write_long( operand, armr.user_r[tmp-8] );3.138 - }3.139 - operand += poststep;3.140 + }3.141 + break;3.142 + case 8: /* STMIA */3.143 + for( i=0; i< 16; i++ ) {3.144 + if( (ir & (1<<i)) ) {3.145 + arm_write_long( operand, armr.r[i] );3.146 + operand += 4;3.147 + }3.148 + }3.149 + break;3.150 + case 9: /* LDMIA */3.151 + for( i=0; i< 16; i++ ) {3.152 + if( (ir & (1<<i)) ) {3.153 + armr.r[i] = arm_read_long( operand );3.154 + operand += 4;3.155 + }3.156 + }3.157 + break;3.158 + case 12: /* STMIA (S) */3.159 + for( i=0; i< 16; i++ ) {3.160 + if( (ir & (1<<i)) ) {3.161 + arm_write_long( operand, USER_R(i) );3.162 + operand += 4;3.163 + }3.164 + }3.165 + break;3.166 + case 13: /* LDMIA (S) */3.167 + if( (ir&0x00008000) ) { /* Load PC */3.168 + for( i=0; i < 16; i++ ) {3.169 + if( (ir & (1<<i)) ) {3.170 + armr.r[i] = arm_read_long( operand );3.171 + operand += 4;3.172 }3.173 }3.174 - if( ir & 0x8000 ) {3.175 - operand += prestep;3.176 - if( LFLAG(ir) ) {3.177 - /* Actually can't happen, but anyway... */3.178 - armr.r[15] = arm_read_long(operand);3.179 - } else {3.180 - arm_write_long( operand, armr.r[15]+ STM_R15_OFFSET - 4 );3.181 + arm_restore_cpsr();3.182 + } else {3.183 + for( i=0; i < 16; i++ ) {3.184 + if( (ir & (1<<i)) ) {3.185 + USER_R(i) = arm_read_long( operand );3.186 + operand += 4;3.187 }3.188 - operand += poststep;3.189 }3.190 }3.191 - } else {3.192 - /* Normal LDM/STM */3.193 - for( tmp=0; tmp < 16; tmp++ ) {3.194 - if( (ir & (1<<tmp)) ) {3.195 - operand += prestep;3.196 - if( LFLAG(ir) ) {3.197 - armr.r[tmp] = arm_read_long(operand);3.198 - } else {3.199 - if( tmp == 15 )3.200 - arm_write_long( operand,3.201 - armr.r[15] + STM_R15_OFFSET - 4 );3.202 - else3.203 - arm_write_long( operand, armr.r[tmp] );3.204 - }3.205 - operand += poststep;3.206 + break;3.207 + case 16: /* STMDB */3.208 + for( i=15; i>= 0; i-- ) {3.209 + if( (ir & (1<<i)) ) {3.210 + operand -= 4;3.211 + arm_write_long( operand, armr.r[i] );3.212 }3.213 }3.214 - if( WFLAG(ir) )3.215 - LRN(ir) = operand;3.216 + break;3.217 + case 17: /* LDMDB */3.218 + for( i=15; i>= 0; i-- ) {3.219 + if( (ir & (1<<i)) ) {3.220 + operand -= 4;3.221 + armr.r[i] = arm_read_long( operand );3.222 + }3.223 + }3.224 + break;3.225 + case 20: /* STMDB (S) */3.226 + for( i=15; i>= 0; i-- ) {3.227 + if( (ir & (1<<i)) ) {3.228 + operand -= 4;3.229 + arm_write_long( operand, USER_R(i) );3.230 + }3.231 + }3.232 + break;3.233 + case 21: /* LDMDB (S) */3.234 + if( (ir&0x00008000) ) { /* Load PC */3.235 + for( i=15; i>= 0; i-- ) {3.236 + if( (ir & (1<<i)) ) {3.237 + operand -= 4;3.238 + armr.r[i] = arm_read_long( operand );3.239 + }3.240 + }3.241 + arm_restore_cpsr();3.242 + } else {3.243 + for( i=15; i>= 0; i-- ) {3.244 + if( (ir & (1<<i)) ) {3.245 + operand -= 4;3.246 + USER_R(i) = arm_read_long( operand );3.247 + }3.248 + }3.249 + }3.250 + break;3.251 + case 24: /* STMIB */3.252 + for( i=0; i< 16; i++ ) {3.253 + if( (ir & (1<<i)) ) {3.254 + operand += 4;3.255 + arm_write_long( operand, armr.r[i] );3.256 + }3.257 + }3.258 + break;3.259 + case 25: /* LDMIB */3.260 + for( i=0; i< 16; i++ ) {3.261 + if( (ir & (1<<i)) ) {3.262 + operand += 4;3.263 + armr.r[i] = arm_read_long( operand );3.264 + }3.265 + }3.266 + break;3.267 + case 28: /* STMIB (S) */3.268 + for( i=0; i< 16; i++ ) {3.269 + if( (ir & (1<<i)) ) {3.270 + operand += 4;3.271 + arm_write_long( operand, USER_R(i) );3.272 + }3.273 + }3.274 + break;3.275 + case 29: /* LDMIB (S) */3.276 + if( (ir&0x00008000) ) { /* Load PC */3.277 + for( i=0; i < 16; i++ ) {3.278 + if( (ir & (1<<i)) ) {3.279 + operand += 4;3.280 + armr.r[i] = arm_read_long( operand );3.281 + }3.282 + }3.283 + arm_restore_cpsr();3.284 + } else {3.285 + for( i=0; i < 16; i++ ) {3.286 + if( (ir & (1<<i)) ) {3.287 + operand += 4;3.288 + USER_R(i) = arm_read_long( operand );3.289 + }3.290 + }3.291 + }3.292 + break;3.293 }3.294 +3.295 + if( WFLAG(ir) )3.296 + LRN(ir) = operand;3.297 }3.298 break;3.299 case 3: /* Copro */
4.1 --- a/src/aica/armcore.h Tue Jan 03 12:21:45 2006 +00004.2 +++ b/src/aica/armcore.h Tue Jan 10 13:56:54 2006 +00004.3 @@ -1,5 +1,5 @@4.4 /**4.5 - * $Id: armcore.h,v 1.11 2005-12-28 22:49:26 nkeynes Exp $4.6 + * $Id: armcore.h,v 1.12 2006-01-10 13:56:54 nkeynes Exp $4.7 *4.8 * Interface definitions for the ARM CPU emulation core proper.4.9 *4.10 @@ -23,7 +23,7 @@4.11 #include <stdint.h>4.12 #include <stdio.h>4.14 -#define ARM_BASE_RATE 33 /* MHZ */4.15 +#define ARM_BASE_RATE 25 /* MHZ */4.16 extern uint32_t arm_cpu_freq;4.17 extern uint32_t arm_cpu_period;
5.1 --- a/src/aica/armmem.c Tue Jan 03 12:21:45 2006 +00005.2 +++ b/src/aica/armmem.c Tue Jan 10 13:56:54 2006 +00005.3 @@ -1,5 +1,5 @@5.4 /**5.5 - * $Id: armmem.c,v 1.6 2005-12-26 10:48:20 nkeynes Exp $5.6 + * $Id: armmem.c,v 1.7 2006-01-10 13:56:54 nkeynes Exp $5.7 *5.8 * Implements the ARM's memory map.5.9 *5.10 @@ -19,6 +19,7 @@5.11 #include <stdlib.h>5.12 #include "dream.h"5.13 #include "mem.h"5.14 +#include "aica.h"5.16 char *arm_mem = NULL;5.17 char *arm_mem_scratch = NULL;5.18 @@ -38,13 +39,20 @@5.19 return *(int32_t *)(arm_mem + addr);5.20 /* Main sound ram */5.21 } else {5.22 + uint32_t val;5.23 switch( addr & 0xFFFFF000 ) {5.24 case 0x00800000:5.25 - return mmio_region_AICA0_read(addr);5.26 + val = mmio_region_AICA0_read(addr&0x0FFF);5.27 + // DEBUG( "ARM long read from %08X => %08X", addr, val );5.28 + return val;5.29 case 0x00801000:5.30 - return mmio_region_AICA1_read(addr);5.31 + val = mmio_region_AICA1_read(addr&0x0FFF);5.32 + // DEBUG( "ARM long read from %08X => %08X", addr, val );5.33 + return val;5.34 case 0x00802000:5.35 - return mmio_region_AICA2_read(addr);5.36 + val = mmio_region_AICA2_read(addr&0x0FFF);5.37 + // DEBUG( "ARM long read from %08X => %08X", addr, val );5.38 + return val;5.39 case 0x00803000:5.40 case 0x00804000:5.41 return *(int32_t *)(arm_mem_scratch + addr - 0x00803000);5.42 @@ -72,13 +80,16 @@5.43 } else {5.44 switch( addr & 0xFFFFF000 ) {5.45 case 0x00800000:5.46 - mmio_region_AICA0_write(addr, value);5.47 + // DEBUG( "ARM long write to %08X <= %08X", addr, value );5.48 + mmio_region_AICA0_write(addr&0x0FFF, value);5.49 break;5.50 case 0x00801000:5.51 - mmio_region_AICA1_write(addr, value);5.52 + // DEBUG( "ARM long write to %08X <= %08X", addr, value );5.53 + mmio_region_AICA1_write(addr&0x0FFF, value);5.54 break;5.55 case 0x00802000:5.56 - mmio_region_AICA2_write(addr, value);5.57 + // DEBUG( "ARM long write to %08X <= %08X", addr, value );5.58 + mmio_region_AICA2_write(addr&0x0FFF, value);5.59 break;5.60 case 0x00803000:5.61 case 0x00804000:5.62 @@ -93,21 +104,42 @@5.63 return 0;5.64 }5.66 +uint32_t arm_combine_byte( uint32_t addr, uint32_t val, uint8_t byte )5.67 +{5.68 + switch( addr & 0x03 ) {5.69 + case 0:5.70 + return (val & 0xFFFFFF00) | byte;5.71 + case 1:5.72 + return (val & 0xFFFF00FF) | (byte<<8);5.73 + case 2:5.74 + return (val & 0xFF00FFFF) | (byte<<16);5.75 + case 3:5.76 + return (val & 0x00FFFFFF) | (byte<<24);5.77 + }5.78 +}5.79 +5.80 void arm_write_byte( uint32_t addr, uint32_t value )5.81 {5.82 if( addr < 0x00200000 ) {5.83 /* Main sound ram */5.84 *(uint8_t *)(arm_mem + addr) = (uint8_t)value;5.85 } else {5.86 + uint32_t tmp;5.87 switch( addr & 0xFFFFF000 ) {5.88 case 0x00800000:5.89 - mmio_region_AICA0_write(addr, value);5.90 + tmp = MMIO_READ( AICA0, addr & 0x0FFC );5.91 + value = arm_combine_byte( addr, tmp, value );5.92 + mmio_region_AICA0_write(addr&0x0FFC, value);5.93 break;5.94 case 0x00801000:5.95 - mmio_region_AICA1_write(addr, value);5.96 + tmp = MMIO_READ( AICA1, addr & 0x0FFC );5.97 + value = arm_combine_byte( addr, tmp, value );5.98 + mmio_region_AICA1_write(addr&0x0FFC, value);5.99 break;5.100 case 0x00802000:5.101 - mmio_region_AICA2_write(addr, value);5.102 + tmp = MMIO_READ( AICA2, addr & 0x0FFC );5.103 + value = arm_combine_byte( addr, tmp, value );5.104 + mmio_region_AICA2_write(addr&0x0FFC, value);5.105 break;5.106 case 0x00803000:5.107 case 0x00804000:
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00006.2 +++ b/src/aica/audio.c Tue Jan 10 13:56:54 2006 +00006.3 @@ -0,0 +1,282 @@6.4 +/**6.5 + * $Id: audio.c,v 1.1 2006-01-10 13:56:54 nkeynes Exp $6.6 + *6.7 + * Audio mixer core. Combines all the active streams into a single sound6.8 + * buffer for output.6.9 + *6.10 + * Copyright (c) 2005 Nathan Keynes.6.11 + *6.12 + * This program is free software; you can redistribute it and/or modify6.13 + * it under the terms of the GNU General Public License as published by6.14 + * the Free Software Foundation; either version 2 of the License, or6.15 + * (at your option) any later version.6.16 + *6.17 + * This program is distributed in the hope that it will be useful,6.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of6.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the6.20 + * GNU General Public License for more details.6.21 + */6.22 +6.23 +#include "aica/aica.h"6.24 +#include "aica/audio.h"6.25 +#include "glib/gmem.h"6.26 +#include "dream.h"6.27 +#include <assert.h>6.28 +#include <string.h>6.29 +6.30 +#define NUM_BUFFERS 36.31 +#define MS_PER_BUFFER 40006.32 +6.33 +#define BUFFER_EMPTY 06.34 +#define BUFFER_WRITING 16.35 +#define BUFFER_FULL 26.36 +6.37 +struct audio_state {6.38 + audio_buffer_t output_buffers[NUM_BUFFERS];6.39 + int write_buffer;6.40 + int read_buffer;6.41 + uint32_t output_format;6.42 + uint32_t output_rate;6.43 + uint32_t output_sample_size;6.44 + struct audio_channel channels[64];6.45 +} audio;6.46 +6.47 +audio_driver_t audio_driver = NULL;6.48 +6.49 +#define NEXT_BUFFER() ((audio.write_buffer == NUM_BUFFERS-1) ? 0 : audio.write_buffer+1)6.50 +6.51 +extern char *arm_mem;6.52 +6.53 +/**6.54 + * Set the output driver, sample rate and format. Also initializes the6.55 + * output buffers, flushing any current data and reallocating as6.56 + * necessary.6.57 + */6.58 +void audio_set_output( audio_driver_t driver,6.59 + uint32_t samplerate, int format )6.60 +{6.61 + uint32_t bytes_per_sample = 1;6.62 + uint32_t samples_per_buffer;6.63 + int i;6.64 +6.65 + if( format & AUDIO_FMT_16BIT )6.66 + bytes_per_sample = 2;6.67 + if( format & AUDIO_FMT_STEREO )6.68 + bytes_per_sample <<= 1;6.69 + if( samplerate == audio.output_rate &&6.70 + bytes_per_sample == audio.output_sample_size )6.71 + return;6.72 + samples_per_buffer = (samplerate * MS_PER_BUFFER / 1000);6.73 + for( i=0; i<NUM_BUFFERS; i++ ) {6.74 + if( audio.output_buffers[i] != NULL )6.75 + free(audio.output_buffers[i]);6.76 + audio.output_buffers[i] = g_malloc0( sizeof(struct audio_buffer) + samples_per_buffer * bytes_per_sample );6.77 + audio.output_buffers[i]->length = samples_per_buffer;6.78 + audio.output_buffers[i]->posn = 0;6.79 + audio.output_buffers[i]->status = BUFFER_EMPTY;6.80 + }6.81 + audio.output_format = format;6.82 + audio.output_rate = samplerate;6.83 + audio.output_sample_size = bytes_per_sample;6.84 + audio.write_buffer = 0;6.85 + audio.read_buffer = 0;6.86 +6.87 + if( driver == NULL )6.88 + driver = &null_audio_driver;6.89 + audio_driver = driver;6.90 + audio_driver->set_output_format( samplerate, format );6.91 +}6.92 +6.93 +/**6.94 + * Mark the current write buffer as full and prepare the next buffer for6.95 + * writing. Returns the next buffer to write to.6.96 + * If all buffers are full, returns NULL.6.97 + */6.98 +audio_buffer_t audio_next_write_buffer( )6.99 +{6.100 + audio_buffer_t result = NULL;6.101 + audio_buffer_t current = audio.output_buffers[audio.write_buffer];6.102 + current->status = BUFFER_FULL;6.103 + if( audio.read_buffer == audio.write_buffer &&6.104 + audio_driver->process_buffer( current ) ) {6.105 + audio_next_read_buffer();6.106 + }6.107 + audio.write_buffer = NEXT_BUFFER();6.108 + result = audio.output_buffers[audio.write_buffer];6.109 + if( result->status == BUFFER_FULL )6.110 + return NULL;6.111 + else {6.112 + result->status = BUFFER_WRITING;6.113 + return result;6.114 + }6.115 +}6.116 +6.117 +/**6.118 + * Mark the current read buffer as empty and return the next buffer for6.119 + * reading. If there is no next buffer yet, returns NULL.6.120 + */6.121 +audio_buffer_t audio_next_read_buffer( )6.122 +{6.123 + audio_buffer_t current = audio.output_buffers[audio.read_buffer];6.124 + assert( current->status == BUFFER_FULL );6.125 + current->status = BUFFER_EMPTY;6.126 + current->posn = 0;6.127 + audio.read_buffer++;6.128 + if( audio.read_buffer == NUM_BUFFERS )6.129 + audio.read_buffer = 0;6.130 +6.131 + current = audio.output_buffers[audio.read_buffer];6.132 + if( current->status == BUFFER_FULL )6.133 + return current;6.134 + else return NULL;6.135 +}6.136 +6.137 +/*************************** ADPCM ***********************************/6.138 +6.139 +/**6.140 + * The following section borrows heavily from ffmpeg, which is6.141 + * copyright (c) 2001-2003 by the fine folks at the ffmpeg project,6.142 + * distributed under the GPL version 2 or later.6.143 + */6.144 +6.145 +#define CLAMP_TO_SHORT(value) \6.146 +if (value > 32767) \6.147 + value = 32767; \6.148 +else if (value < -32768) \6.149 + value = -32768; \6.150 +6.151 +static const int yamaha_indexscale[] = {6.152 + 230, 230, 230, 230, 307, 409, 512, 614,6.153 + 230, 230, 230, 230, 307, 409, 512, 6146.154 +};6.155 +6.156 +static const int yamaha_difflookup[] = {6.157 + 1, 3, 5, 7, 9, 11, 13, 15,6.158 + -1, -3, -5, -7, -9, -11, -13, -156.159 +};6.160 +6.161 +static inline short adpcm_yamaha_decode_nibble( audio_channel_t c,6.162 + unsigned char nibble )6.163 +{6.164 + if( c->adpcm_step == 0 ) {6.165 + c->adpcm_predict = 0;6.166 + c->adpcm_step = 127;6.167 + }6.168 +6.169 + c->adpcm_predict += (c->adpcm_step * yamaha_difflookup[nibble]) >> 3;6.170 + CLAMP_TO_SHORT(c->adpcm_predict);6.171 + c->adpcm_step = (c->adpcm_step * yamaha_indexscale[nibble]) >> 8;6.172 + c->adpcm_step = CLAMP(c->adpcm_step, 127, 24567);6.173 + return c->adpcm_predict;6.174 +}6.175 +6.176 +/*************************** Sample mixer *****************************/6.177 +6.178 +/**6.179 + * Mix a single output sample.6.180 + */6.181 +void audio_mix_sample( )6.182 +{6.183 + int i, j;6.184 + int32_t result_left = 0, result_right = 0;6.185 +6.186 + for( i=0; i < 64; i++ ) {6.187 + audio_channel_t channel = &audio.channels[i];6.188 + if( channel->active ) {6.189 + int32_t sample;6.190 + switch( channel->sample_format ) {6.191 + case AUDIO_FMT_16BIT:6.192 + sample = *(int16_t *)(arm_mem + channel->posn + channel->start);6.193 + break;6.194 + case AUDIO_FMT_8BIT:6.195 + sample = (*(int8_t *)(arm_mem + channel->posn + channel->start)) << 8;6.196 + break;6.197 + case AUDIO_FMT_16BIT|AUDIO_FMT_UNSIGNED:6.198 + sample = (int8_t)((*(uint16_t *)(arm_mem + channel->posn + channel->start)) - 0x8000);6.199 + break;6.200 + case AUDIO_FMT_8BIT|AUDIO_FMT_UNSIGNED:6.201 + sample = (int8_t)((*(uint8_t *)(arm_mem + channel->posn + channel->start)) - 0x80);6.202 + break;6.203 + case AUDIO_FMT_ADPCM:6.204 + sample = (int16_t)channel->adpcm_predict;6.205 + default:6.206 + sample = 0; /* Unsupported */6.207 + }6.208 + result_left += sample * channel->vol_left;6.209 + result_right += sample * channel->vol_right;6.210 +6.211 + channel->posn_left += channel->sample_rate;6.212 + while( channel->posn_left > audio.output_rate ) {6.213 + channel->posn_left -= audio.output_rate;6.214 + if( channel->sample_format == AUDIO_FMT_ADPCM &&6.215 + channel->adpcm_nibble == 0 ) {6.216 + uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start);6.217 + adpcm_yamaha_decode_nibble( channel, (data >> 4) & 0x0F );6.218 + channel->adpcm_nibble = 1;6.219 + continue;6.220 + }6.221 +6.222 + channel->posn++;6.223 +6.224 + if( channel->loop_count != 0 &&6.225 + channel->posn >= channel->loop_end ) {6.226 + channel->posn = channel->loop_start;6.227 + if( channel->loop_count != -1 )6.228 + channel->loop_count --;6.229 + } else if( channel->posn >= channel->end ) {6.230 + audio_stop_channel( i );6.231 + break;6.232 + }6.233 +6.234 + if( channel->sample_format == AUDIO_FMT_ADPCM ) {6.235 + uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start);6.236 + adpcm_yamaha_decode_nibble( channel, data & 0x0F );6.237 + channel->adpcm_nibble = 0;6.238 + }6.239 + }6.240 + }6.241 + }6.242 +6.243 + /* Down-render to the final output format */6.244 + audio_buffer_t buf =6.245 + audio.output_buffers[audio.write_buffer];6.246 + if( audio.output_format & AUDIO_FMT_16BIT ) {6.247 + uint16_t *data = (uint16_t *)&buf->data[buf->posn*audio.output_sample_size];6.248 + *data++ = (int16_t)(result_left >> 8);6.249 + *data++ = (int16_t)(result_right >> 8);6.250 + } else {6.251 + audio_buffer_t buf =6.252 + audio.output_buffers[audio.write_buffer];6.253 + uint8_t *data = (uint8_t *)&buf->data[buf->posn*audio.output_sample_size];6.254 + *data++ = (int8_t)(result_left >> 22);6.255 + *data++ = (int8_t)(result_right >>22);6.256 + }6.257 + buf->posn++;6.258 + if( buf->posn == buf->length ) {6.259 + audio_next_write_buffer();6.260 + }6.261 +}6.262 +6.263 +/********************** Internal AICA calls ***************************/6.264 +6.265 +audio_channel_t audio_get_channel( int channel )6.266 +{6.267 + return &audio.channels[channel];6.268 +}6.269 +6.270 +void audio_stop_channel( int channel )6.271 +{6.272 + audio.channels[channel].active = FALSE;6.273 +}6.274 +6.275 +6.276 +void audio_start_channel( int channel )6.277 +{6.278 + audio.channels[channel].posn = 0;6.279 + audio.channels[channel].posn_left = 0;6.280 + audio.channels[channel].adpcm_nibble = 0;6.281 + audio.channels[channel].adpcm_step = 0;6.282 + audio.channels[channel].adpcm_predict = 0;6.283 + audio.channels[channel].active = TRUE;6.284 + DEBUG("Channel %d on", channel );6.285 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00007.2 +++ b/src/aica/audio.h Tue Jan 10 13:56:54 2006 +00007.3 @@ -0,0 +1,113 @@7.4 +/**7.5 + * $Id: audio.h,v 1.1 2006-01-10 13:56:54 nkeynes Exp $7.6 + *7.7 + * Audio engine, ie the part that does the actual work.7.8 + *7.9 + * Copyright (c) 2005 Nathan Keynes.7.10 + *7.11 + * This program is free software; you can redistribute it and/or modify7.12 + * it under the terms of the GNU General Public License as published by7.13 + * the Free Software Foundation; either version 2 of the License, or7.14 + * (at your option) any later version.7.15 + *7.16 + * This program is distributed in the hope that it will be useful,7.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of7.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the7.19 + * GNU General Public License for more details.7.20 + */7.21 +#ifndef dream_audio_H7.22 +#define dream_audio_H 17.23 +7.24 +#include <stdint.h>7.25 +#include <glib/gtypes.h>7.26 +7.27 +#ifdef __cplusplus7.28 +extern "C" {7.29 +#endif7.30 +7.31 +#define AUDIO_FMT_8BIT 07.32 +#define AUDIO_FMT_16BIT 17.33 +#define AUDIO_FMT_ADPCM 27.34 +#define AUDIO_FMT_MONO 07.35 +#define AUDIO_FMT_STEREO 47.36 +#define AUDIO_FMT_SIGNED 07.37 +#define AUDIO_FMT_UNSIGNED 87.38 +7.39 +7.40 +typedef struct audio_channel {7.41 + gboolean active;7.42 + uint32_t posn;7.43 + uint32_t posn_left;7.44 + uint32_t start;7.45 + uint32_t end;7.46 + uint32_t loop_start;7.47 + uint32_t loop_end;7.48 + int loop_count; /* 0 = no loop, -1 = loop forever */7.49 + int vol_left; /* 0..255 */7.50 + int vol_right; /* 0..255 */7.51 + uint32_t sample_rate;7.52 + int sample_format;7.53 + /* Envelope etc stuff */7.54 + /* ADPCM */7.55 + int adpcm_nibble; /* 0 = low nibble, 1 = high nibble */7.56 + int adpcm_step;7.57 + int adpcm_predict;7.58 +} *audio_channel_t;7.59 +7.60 +7.61 +typedef struct audio_buffer {7.62 + uint32_t length; /* Samples */7.63 + uint32_t posn; /* Samples */7.64 + int status;7.65 + char data[0];7.66 +} *audio_buffer_t;7.67 +7.68 +struct audio_driver {7.69 + gboolean (*set_output_format)( uint32_t sample_rate, uint32_t format );7.70 + gboolean (*process_buffer)( audio_buffer_t buffer );7.71 +};7.72 +7.73 +typedef struct audio_driver *audio_driver_t;7.74 +7.75 +extern struct audio_driver null_audio_driver;7.76 +extern struct audio_driver esd_audio_driver;7.77 +7.78 +/**7.79 + * Set the output driver, sample rate and format. Also initializes the7.80 + * output buffers, flushing any current data and reallocating as7.81 + * necessary. Must be called before attempting to generate any audio.7.82 + */7.83 +void audio_set_output( audio_driver_t driver, uint32_t samplerate,7.84 + int format );7.85 +7.86 +/**7.87 + * Mark the current write buffer as full and prepare the next buffer for7.88 + * writing. Returns the next buffer to write to.7.89 + * If all buffers are full, returns NULL.7.90 + */7.91 +audio_buffer_t audio_next_write_buffer();7.92 +7.93 +/**7.94 + * Mark the current read buffer as empty and return the next buffer for7.95 + * reading. If there is no next buffer yet, returns NULL.7.96 + */7.97 +audio_buffer_t audio_next_read_buffer();7.98 +7.99 +/**7.100 + * Mix a single output sample and append it to the output buffers7.101 + */7.102 +void audio_mix_sample( void );7.103 +7.104 +/**7.105 + * Retrieve the channel information for the channel, numbered 0..63.7.106 + */7.107 +audio_channel_t audio_get_channel( int channel );7.108 +7.109 +void audio_start_channel( int channel );7.110 +void audio_stop_channel( int channel );7.111 +7.112 +7.113 +#ifdef __cplusplus7.114 +}7.115 +#endif7.116 +#endif
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00008.2 +++ b/src/drivers/audio_esd.c Tue Jan 10 13:56:54 2006 +00008.3 @@ -0,0 +1,58 @@8.4 +/**8.5 + * $Id: audio_esd.c,v 1.1 2006-01-10 13:56:54 nkeynes Exp $8.6 + *8.7 + * The esd (esound) audio driver8.8 + *8.9 + * Copyright (c) 2005 Nathan Keynes.8.10 + *8.11 + * This program is free software; you can redistribute it and/or modify8.12 + * it under the terms of the GNU General Public License as published by8.13 + * the Free Software Foundation; either version 2 of the License, or8.14 + * (at your option) any later version.8.15 + *8.16 + * This program is distributed in the hope that it will be useful,8.17 + * but WITHOUT ANY WARRANTY; without even the implied warranty of8.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the8.19 + * GNU General Public License for more details.8.20 + */8.21 +#include <esd.h>8.22 +#include "aica/audio.h"8.23 +#include "dream.h"8.24 +8.25 +int esd_handle = -1;8.26 +int esd_sample_size = 1;8.27 +8.28 +gboolean esd_audio_set_format( uint32_t rate, uint32_t format )8.29 +{8.30 + if( esd_handle != -1 ) {8.31 + esd_close(esd_handle);8.32 + }8.33 + esd_format_t esd_format = 0;8.34 + esd_sample_size = 1;8.35 + if( format & AUDIO_FMT_16BIT ) {8.36 + esd_format |= ESD_BITS16;8.37 + esd_sample_size = 1;8.38 + } else esd_format |= ESD_BITS8;8.39 + if( format & AUDIO_FMT_STEREO ) {8.40 + esd_format |= ESD_STEREO;8.41 + esd_sample_size = esd_sample_size << 1 ;8.42 + }8.43 + else esd_format |= ESD_MONO;8.44 +8.45 + esd_handle = esd_play_stream( esd_format, rate, "localhost", "dreamon" );8.46 + return TRUE;8.47 +}8.48 +8.49 +gboolean esd_audio_process_buffer( audio_buffer_t buffer )8.50 +{8.51 + if( esd_handle != -1 ) {8.52 + write( esd_handle, buffer->data, buffer->length * esd_sample_size );8.53 + DEBUG("Wrote buffer" );8.54 + } else {8.55 + ERROR( "ESD not initialized" );8.56 + }8.57 + return TRUE;8.58 +}8.59 +8.60 +struct audio_driver esd_audio_driver = { esd_audio_set_format, esd_audio_process_buffer };8.61 +
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +00009.2 +++ b/src/drivers/audio_null.c Tue Jan 10 13:56:54 2006 +00009.3 @@ -0,0 +1,31 @@9.4 +/**9.5 + * $Id: audio_null.c,v 1.1 2006-01-10 13:56:54 nkeynes Exp $9.6 + *9.7 + * The "null" audio driver, which just discards all input without even9.8 + * looking at it.9.9 + *9.10 + * Copyright (c) 2005 Nathan Keynes.9.11 + *9.12 + * This program is free software; you can redistribute it and/or modify9.13 + * it under the terms of the GNU General Public License as published by9.14 + * the Free Software Foundation; either version 2 of the License, or9.15 + * (at your option) any later version.9.16 + *9.17 + * This program is distributed in the hope that it will be useful,9.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of9.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9.20 + * GNU General Public License for more details.9.21 + */9.22 +#include "aica/audio.h"9.23 +9.24 +gboolean null_audio_set_format( uint32_t rate, uint32_t format )9.25 +{9.26 + return TRUE;9.27 +}9.28 +9.29 +gboolean null_audio_process_buffer( audio_buffer_t buffer )9.30 +{9.31 + return TRUE;9.32 +}9.33 +9.34 +struct audio_driver null_audio_driver = { null_audio_set_format, null_audio_process_buffer };
.