filename | src/aica/audio.c |
changeset | 73:0bb57e51ac9e |
prev | 66:2ec5b6eb75e5 |
next | 79:6d832137fdff |
author | nkeynes |
date | Thu Jan 12 11:30:19 2006 +0000 (18 years ago) |
permissions | -rw-r--r-- |
last change | Render multiple samples per shot, should be mildly faster Move timer logic down into armcore Minor tweaks |
file | annotate | diff | log | raw |
nkeynes@66 | 1 | /** |
nkeynes@73 | 2 | * $Id: audio.c,v 1.2 2006-01-12 11:30:19 nkeynes Exp $ |
nkeynes@66 | 3 | * |
nkeynes@66 | 4 | * Audio mixer core. Combines all the active streams into a single sound |
nkeynes@66 | 5 | * buffer for output. |
nkeynes@66 | 6 | * |
nkeynes@66 | 7 | * Copyright (c) 2005 Nathan Keynes. |
nkeynes@66 | 8 | * |
nkeynes@66 | 9 | * This program is free software; you can redistribute it and/or modify |
nkeynes@66 | 10 | * it under the terms of the GNU General Public License as published by |
nkeynes@66 | 11 | * the Free Software Foundation; either version 2 of the License, or |
nkeynes@66 | 12 | * (at your option) any later version. |
nkeynes@66 | 13 | * |
nkeynes@66 | 14 | * This program is distributed in the hope that it will be useful, |
nkeynes@66 | 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
nkeynes@66 | 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
nkeynes@66 | 17 | * GNU General Public License for more details. |
nkeynes@66 | 18 | */ |
nkeynes@66 | 19 | |
nkeynes@66 | 20 | #include "aica/aica.h" |
nkeynes@66 | 21 | #include "aica/audio.h" |
nkeynes@66 | 22 | #include "glib/gmem.h" |
nkeynes@66 | 23 | #include "dream.h" |
nkeynes@66 | 24 | #include <assert.h> |
nkeynes@66 | 25 | #include <string.h> |
nkeynes@66 | 26 | |
nkeynes@66 | 27 | #define NUM_BUFFERS 3 |
nkeynes@73 | 28 | #define MS_PER_BUFFER 500 |
nkeynes@66 | 29 | |
nkeynes@66 | 30 | #define BUFFER_EMPTY 0 |
nkeynes@66 | 31 | #define BUFFER_WRITING 1 |
nkeynes@66 | 32 | #define BUFFER_FULL 2 |
nkeynes@66 | 33 | |
nkeynes@66 | 34 | struct audio_state { |
nkeynes@66 | 35 | audio_buffer_t output_buffers[NUM_BUFFERS]; |
nkeynes@66 | 36 | int write_buffer; |
nkeynes@66 | 37 | int read_buffer; |
nkeynes@66 | 38 | uint32_t output_format; |
nkeynes@66 | 39 | uint32_t output_rate; |
nkeynes@66 | 40 | uint32_t output_sample_size; |
nkeynes@66 | 41 | struct audio_channel channels[64]; |
nkeynes@66 | 42 | } audio; |
nkeynes@66 | 43 | |
nkeynes@66 | 44 | audio_driver_t audio_driver = NULL; |
nkeynes@66 | 45 | |
nkeynes@66 | 46 | #define NEXT_BUFFER() ((audio.write_buffer == NUM_BUFFERS-1) ? 0 : audio.write_buffer+1) |
nkeynes@66 | 47 | |
nkeynes@66 | 48 | extern char *arm_mem; |
nkeynes@66 | 49 | |
nkeynes@66 | 50 | /** |
nkeynes@66 | 51 | * Set the output driver, sample rate and format. Also initializes the |
nkeynes@66 | 52 | * output buffers, flushing any current data and reallocating as |
nkeynes@66 | 53 | * necessary. |
nkeynes@66 | 54 | */ |
nkeynes@66 | 55 | void audio_set_output( audio_driver_t driver, |
nkeynes@66 | 56 | uint32_t samplerate, int format ) |
nkeynes@66 | 57 | { |
nkeynes@66 | 58 | uint32_t bytes_per_sample = 1; |
nkeynes@66 | 59 | uint32_t samples_per_buffer; |
nkeynes@66 | 60 | int i; |
nkeynes@66 | 61 | |
nkeynes@66 | 62 | if( format & AUDIO_FMT_16BIT ) |
nkeynes@66 | 63 | bytes_per_sample = 2; |
nkeynes@66 | 64 | if( format & AUDIO_FMT_STEREO ) |
nkeynes@66 | 65 | bytes_per_sample <<= 1; |
nkeynes@66 | 66 | if( samplerate == audio.output_rate && |
nkeynes@66 | 67 | bytes_per_sample == audio.output_sample_size ) |
nkeynes@66 | 68 | return; |
nkeynes@66 | 69 | samples_per_buffer = (samplerate * MS_PER_BUFFER / 1000); |
nkeynes@66 | 70 | for( i=0; i<NUM_BUFFERS; i++ ) { |
nkeynes@66 | 71 | if( audio.output_buffers[i] != NULL ) |
nkeynes@66 | 72 | free(audio.output_buffers[i]); |
nkeynes@66 | 73 | audio.output_buffers[i] = g_malloc0( sizeof(struct audio_buffer) + samples_per_buffer * bytes_per_sample ); |
nkeynes@73 | 74 | audio.output_buffers[i]->length = samples_per_buffer * bytes_per_sample; |
nkeynes@66 | 75 | audio.output_buffers[i]->posn = 0; |
nkeynes@66 | 76 | audio.output_buffers[i]->status = BUFFER_EMPTY; |
nkeynes@66 | 77 | } |
nkeynes@66 | 78 | audio.output_format = format; |
nkeynes@66 | 79 | audio.output_rate = samplerate; |
nkeynes@66 | 80 | audio.output_sample_size = bytes_per_sample; |
nkeynes@66 | 81 | audio.write_buffer = 0; |
nkeynes@66 | 82 | audio.read_buffer = 0; |
nkeynes@66 | 83 | |
nkeynes@66 | 84 | if( driver == NULL ) |
nkeynes@66 | 85 | driver = &null_audio_driver; |
nkeynes@66 | 86 | audio_driver = driver; |
nkeynes@66 | 87 | audio_driver->set_output_format( samplerate, format ); |
nkeynes@66 | 88 | } |
nkeynes@66 | 89 | |
nkeynes@66 | 90 | /** |
nkeynes@66 | 91 | * Mark the current write buffer as full and prepare the next buffer for |
nkeynes@66 | 92 | * writing. Returns the next buffer to write to. |
nkeynes@66 | 93 | * If all buffers are full, returns NULL. |
nkeynes@66 | 94 | */ |
nkeynes@66 | 95 | audio_buffer_t audio_next_write_buffer( ) |
nkeynes@66 | 96 | { |
nkeynes@66 | 97 | audio_buffer_t result = NULL; |
nkeynes@66 | 98 | audio_buffer_t current = audio.output_buffers[audio.write_buffer]; |
nkeynes@66 | 99 | current->status = BUFFER_FULL; |
nkeynes@66 | 100 | if( audio.read_buffer == audio.write_buffer && |
nkeynes@66 | 101 | audio_driver->process_buffer( current ) ) { |
nkeynes@66 | 102 | audio_next_read_buffer(); |
nkeynes@66 | 103 | } |
nkeynes@66 | 104 | audio.write_buffer = NEXT_BUFFER(); |
nkeynes@66 | 105 | result = audio.output_buffers[audio.write_buffer]; |
nkeynes@66 | 106 | if( result->status == BUFFER_FULL ) |
nkeynes@66 | 107 | return NULL; |
nkeynes@66 | 108 | else { |
nkeynes@66 | 109 | result->status = BUFFER_WRITING; |
nkeynes@66 | 110 | return result; |
nkeynes@66 | 111 | } |
nkeynes@66 | 112 | } |
nkeynes@66 | 113 | |
nkeynes@66 | 114 | /** |
nkeynes@66 | 115 | * Mark the current read buffer as empty and return the next buffer for |
nkeynes@66 | 116 | * reading. If there is no next buffer yet, returns NULL. |
nkeynes@66 | 117 | */ |
nkeynes@66 | 118 | audio_buffer_t audio_next_read_buffer( ) |
nkeynes@66 | 119 | { |
nkeynes@66 | 120 | audio_buffer_t current = audio.output_buffers[audio.read_buffer]; |
nkeynes@66 | 121 | assert( current->status == BUFFER_FULL ); |
nkeynes@66 | 122 | current->status = BUFFER_EMPTY; |
nkeynes@66 | 123 | current->posn = 0; |
nkeynes@66 | 124 | audio.read_buffer++; |
nkeynes@66 | 125 | if( audio.read_buffer == NUM_BUFFERS ) |
nkeynes@66 | 126 | audio.read_buffer = 0; |
nkeynes@66 | 127 | |
nkeynes@66 | 128 | current = audio.output_buffers[audio.read_buffer]; |
nkeynes@66 | 129 | if( current->status == BUFFER_FULL ) |
nkeynes@66 | 130 | return current; |
nkeynes@66 | 131 | else return NULL; |
nkeynes@66 | 132 | } |
nkeynes@66 | 133 | |
nkeynes@66 | 134 | /*************************** ADPCM ***********************************/ |
nkeynes@66 | 135 | |
nkeynes@66 | 136 | /** |
nkeynes@66 | 137 | * The following section borrows heavily from ffmpeg, which is |
nkeynes@66 | 138 | * copyright (c) 2001-2003 by the fine folks at the ffmpeg project, |
nkeynes@66 | 139 | * distributed under the GPL version 2 or later. |
nkeynes@66 | 140 | */ |
nkeynes@66 | 141 | |
nkeynes@66 | 142 | #define CLAMP_TO_SHORT(value) \ |
nkeynes@66 | 143 | if (value > 32767) \ |
nkeynes@66 | 144 | value = 32767; \ |
nkeynes@66 | 145 | else if (value < -32768) \ |
nkeynes@66 | 146 | value = -32768; \ |
nkeynes@66 | 147 | |
nkeynes@66 | 148 | static const int yamaha_indexscale[] = { |
nkeynes@66 | 149 | 230, 230, 230, 230, 307, 409, 512, 614, |
nkeynes@66 | 150 | 230, 230, 230, 230, 307, 409, 512, 614 |
nkeynes@66 | 151 | }; |
nkeynes@66 | 152 | |
nkeynes@66 | 153 | static const int yamaha_difflookup[] = { |
nkeynes@66 | 154 | 1, 3, 5, 7, 9, 11, 13, 15, |
nkeynes@66 | 155 | -1, -3, -5, -7, -9, -11, -13, -15 |
nkeynes@66 | 156 | }; |
nkeynes@66 | 157 | |
nkeynes@66 | 158 | static inline short adpcm_yamaha_decode_nibble( audio_channel_t c, |
nkeynes@66 | 159 | unsigned char nibble ) |
nkeynes@66 | 160 | { |
nkeynes@66 | 161 | if( c->adpcm_step == 0 ) { |
nkeynes@66 | 162 | c->adpcm_predict = 0; |
nkeynes@66 | 163 | c->adpcm_step = 127; |
nkeynes@66 | 164 | } |
nkeynes@66 | 165 | |
nkeynes@66 | 166 | c->adpcm_predict += (c->adpcm_step * yamaha_difflookup[nibble]) >> 3; |
nkeynes@66 | 167 | CLAMP_TO_SHORT(c->adpcm_predict); |
nkeynes@66 | 168 | c->adpcm_step = (c->adpcm_step * yamaha_indexscale[nibble]) >> 8; |
nkeynes@66 | 169 | c->adpcm_step = CLAMP(c->adpcm_step, 127, 24567); |
nkeynes@66 | 170 | return c->adpcm_predict; |
nkeynes@66 | 171 | } |
nkeynes@66 | 172 | |
nkeynes@66 | 173 | /*************************** Sample mixer *****************************/ |
nkeynes@66 | 174 | |
nkeynes@66 | 175 | /** |
nkeynes@66 | 176 | * Mix a single output sample. |
nkeynes@66 | 177 | */ |
nkeynes@73 | 178 | void audio_mix_samples( int num_samples ) |
nkeynes@66 | 179 | { |
nkeynes@66 | 180 | int i, j; |
nkeynes@73 | 181 | int32_t result_buf[num_samples][2]; |
nkeynes@73 | 182 | |
nkeynes@73 | 183 | memset( &result_buf, 0, sizeof(result_buf) ); |
nkeynes@66 | 184 | |
nkeynes@66 | 185 | for( i=0; i < 64; i++ ) { |
nkeynes@66 | 186 | audio_channel_t channel = &audio.channels[i]; |
nkeynes@66 | 187 | if( channel->active ) { |
nkeynes@66 | 188 | int32_t sample; |
nkeynes@66 | 189 | switch( channel->sample_format ) { |
nkeynes@66 | 190 | case AUDIO_FMT_16BIT: |
nkeynes@73 | 191 | for( j=0; j<num_samples; j++ ) { |
nkeynes@73 | 192 | sample = *(int16_t *)(arm_mem + channel->posn + channel->start); |
nkeynes@73 | 193 | result_buf[j][0] += sample * channel->vol_left; |
nkeynes@73 | 194 | result_buf[j][1] += sample * channel->vol_right; |
nkeynes@73 | 195 | |
nkeynes@73 | 196 | channel->posn_left += channel->sample_rate; |
nkeynes@73 | 197 | while( channel->posn_left > audio.output_rate ) { |
nkeynes@73 | 198 | channel->posn_left -= audio.output_rate; |
nkeynes@73 | 199 | channel->posn++; |
nkeynes@73 | 200 | |
nkeynes@73 | 201 | if( channel->posn == channel->end ) { |
nkeynes@73 | 202 | if( channel->loop ) |
nkeynes@73 | 203 | channel->posn = channel->loop_start; |
nkeynes@73 | 204 | else { |
nkeynes@73 | 205 | audio_stop_channel(i); |
nkeynes@73 | 206 | j = num_samples; |
nkeynes@73 | 207 | break; |
nkeynes@73 | 208 | } |
nkeynes@73 | 209 | } |
nkeynes@73 | 210 | } |
nkeynes@73 | 211 | } |
nkeynes@66 | 212 | break; |
nkeynes@66 | 213 | case AUDIO_FMT_8BIT: |
nkeynes@73 | 214 | for( j=0; j<num_samples; j++ ) { |
nkeynes@73 | 215 | sample = (*(int8_t *)(arm_mem + channel->posn + channel->start)) << 8; |
nkeynes@73 | 216 | result_buf[j][0] += sample * channel->vol_left; |
nkeynes@73 | 217 | result_buf[j][1] += sample * channel->vol_right; |
nkeynes@73 | 218 | |
nkeynes@73 | 219 | channel->posn_left += channel->sample_rate; |
nkeynes@73 | 220 | while( channel->posn_left > audio.output_rate ) { |
nkeynes@73 | 221 | channel->posn_left -= audio.output_rate; |
nkeynes@73 | 222 | channel->posn++; |
nkeynes@73 | 223 | |
nkeynes@73 | 224 | if( channel->posn == channel->end ) { |
nkeynes@73 | 225 | if( channel->loop ) |
nkeynes@73 | 226 | channel->posn = channel->loop_start; |
nkeynes@73 | 227 | else { |
nkeynes@73 | 228 | audio_stop_channel(i); |
nkeynes@73 | 229 | j = num_samples; |
nkeynes@73 | 230 | break; |
nkeynes@73 | 231 | } |
nkeynes@73 | 232 | } |
nkeynes@73 | 233 | } |
nkeynes@73 | 234 | } |
nkeynes@66 | 235 | break; |
nkeynes@66 | 236 | case AUDIO_FMT_ADPCM: |
nkeynes@73 | 237 | for( j=0; j<num_samples; j++ ) { |
nkeynes@73 | 238 | sample = (int16_t)channel->adpcm_predict; |
nkeynes@73 | 239 | result_buf[j][0] += sample * channel->vol_left; |
nkeynes@73 | 240 | result_buf[j][1] += sample * channel->vol_right; |
nkeynes@73 | 241 | channel->posn_left += channel->sample_rate; |
nkeynes@73 | 242 | while( channel->posn_left > audio.output_rate ) { |
nkeynes@73 | 243 | channel->posn_left -= audio.output_rate; |
nkeynes@73 | 244 | if( channel->adpcm_nibble == 0 ) { |
nkeynes@73 | 245 | uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start); |
nkeynes@73 | 246 | adpcm_yamaha_decode_nibble( channel, (data >> 4) & 0x0F ); |
nkeynes@73 | 247 | channel->adpcm_nibble = 1; |
nkeynes@73 | 248 | } else { |
nkeynes@73 | 249 | channel->posn++; |
nkeynes@73 | 250 | if( channel->posn == channel->end ) { |
nkeynes@73 | 251 | if( channel->loop ) |
nkeynes@73 | 252 | channel->posn = channel->loop_start; |
nkeynes@73 | 253 | else |
nkeynes@73 | 254 | audio_stop_channel(i); |
nkeynes@73 | 255 | break; |
nkeynes@73 | 256 | } |
nkeynes@73 | 257 | uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start); |
nkeynes@73 | 258 | adpcm_yamaha_decode_nibble( channel, data & 0x0F ); |
nkeynes@73 | 259 | channel->adpcm_nibble = 0; |
nkeynes@73 | 260 | } |
nkeynes@73 | 261 | } |
nkeynes@73 | 262 | } |
nkeynes@73 | 263 | break; |
nkeynes@66 | 264 | default: |
nkeynes@73 | 265 | break; |
nkeynes@66 | 266 | } |
nkeynes@66 | 267 | } |
nkeynes@66 | 268 | } |
nkeynes@73 | 269 | |
nkeynes@66 | 270 | /* Down-render to the final output format */ |
nkeynes@73 | 271 | |
nkeynes@66 | 272 | if( audio.output_format & AUDIO_FMT_16BIT ) { |
nkeynes@73 | 273 | audio_buffer_t buf = audio.output_buffers[audio.write_buffer]; |
nkeynes@73 | 274 | uint16_t *data = (uint16_t *)&buf->data[buf->posn]; |
nkeynes@73 | 275 | for( j=0; j < num_samples; j++ ) { |
nkeynes@73 | 276 | *data++ = (int16_t)(result_buf[j][0] >> 8); |
nkeynes@73 | 277 | *data++ = (int16_t)(result_buf[j][1] >> 8); |
nkeynes@73 | 278 | buf->posn += 4; |
nkeynes@73 | 279 | if( buf->posn == buf->length ) { |
nkeynes@73 | 280 | audio_next_write_buffer(); |
nkeynes@73 | 281 | buf = audio.output_buffers[audio.write_buffer]; |
nkeynes@73 | 282 | data = (uint16_t *)&buf->data[0]; |
nkeynes@73 | 283 | } |
nkeynes@73 | 284 | } |
nkeynes@66 | 285 | } else { |
nkeynes@73 | 286 | audio_buffer_t buf = audio.output_buffers[audio.write_buffer]; |
nkeynes@73 | 287 | uint8_t *data = (uint8_t *)&buf->data[buf->posn]; |
nkeynes@73 | 288 | for( j=0; j < num_samples; j++ ) { |
nkeynes@73 | 289 | *data++ = (uint8_t)(result_buf[j][0] >> 16); |
nkeynes@73 | 290 | *data++ = (uint8_t)(result_buf[j][1] >> 16); |
nkeynes@73 | 291 | buf->posn += 2; |
nkeynes@73 | 292 | if( buf->posn == buf->length ) { |
nkeynes@73 | 293 | audio_next_write_buffer(); |
nkeynes@73 | 294 | buf = audio.output_buffers[audio.write_buffer]; |
nkeynes@73 | 295 | data = (uint8_t *)&buf->data[0]; |
nkeynes@73 | 296 | } |
nkeynes@73 | 297 | } |
nkeynes@66 | 298 | } |
nkeynes@66 | 299 | } |
nkeynes@66 | 300 | |
nkeynes@66 | 301 | /********************** Internal AICA calls ***************************/ |
nkeynes@66 | 302 | |
nkeynes@66 | 303 | audio_channel_t audio_get_channel( int channel ) |
nkeynes@66 | 304 | { |
nkeynes@66 | 305 | return &audio.channels[channel]; |
nkeynes@66 | 306 | } |
nkeynes@66 | 307 | |
nkeynes@66 | 308 | void audio_stop_channel( int channel ) |
nkeynes@66 | 309 | { |
nkeynes@66 | 310 | audio.channels[channel].active = FALSE; |
nkeynes@66 | 311 | } |
nkeynes@66 | 312 | |
nkeynes@66 | 313 | |
nkeynes@66 | 314 | void audio_start_channel( int channel ) |
nkeynes@66 | 315 | { |
nkeynes@66 | 316 | audio.channels[channel].posn = 0; |
nkeynes@66 | 317 | audio.channels[channel].posn_left = 0; |
nkeynes@66 | 318 | audio.channels[channel].adpcm_nibble = 0; |
nkeynes@66 | 319 | audio.channels[channel].adpcm_step = 0; |
nkeynes@66 | 320 | audio.channels[channel].adpcm_predict = 0; |
nkeynes@66 | 321 | audio.channels[channel].active = TRUE; |
nkeynes@66 | 322 | DEBUG("Channel %d on", channel ); |
nkeynes@66 | 323 | } |
.