Search
lxdream.org :: lxdream/src/aica/audio.c :: diff
lxdream 0.9.1
released Jun 29
Download Now
filename src/aica/audio.c
changeset 66:2ec5b6eb75e5
next73:0bb57e51ac9e
author nkeynes
date Tue Jan 10 13:56:54 2006 +0000 (14 years ago)
permissions -rw-r--r--
last change Go go gadget audio!
Slow, but it works :)
file annotate diff log raw
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/src/aica/audio.c Tue Jan 10 13:56:54 2006 +0000
1.3 @@ -0,0 +1,282 @@
1.4 +/**
1.5 + * $Id: audio.c,v 1.1 2006-01-10 13:56:54 nkeynes Exp $
1.6 + *
1.7 + * Audio mixer core. Combines all the active streams into a single sound
1.8 + * buffer for output.
1.9 + *
1.10 + * Copyright (c) 2005 Nathan Keynes.
1.11 + *
1.12 + * This program is free software; you can redistribute it and/or modify
1.13 + * it under the terms of the GNU General Public License as published by
1.14 + * the Free Software Foundation; either version 2 of the License, or
1.15 + * (at your option) any later version.
1.16 + *
1.17 + * This program is distributed in the hope that it will be useful,
1.18 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.19 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.20 + * GNU General Public License for more details.
1.21 + */
1.22 +
1.23 +#include "aica/aica.h"
1.24 +#include "aica/audio.h"
1.25 +#include "glib/gmem.h"
1.26 +#include "dream.h"
1.27 +#include <assert.h>
1.28 +#include <string.h>
1.29 +
1.30 +#define NUM_BUFFERS 3
1.31 +#define MS_PER_BUFFER 4000
1.32 +
1.33 +#define BUFFER_EMPTY 0
1.34 +#define BUFFER_WRITING 1
1.35 +#define BUFFER_FULL 2
1.36 +
1.37 +struct audio_state {
1.38 + audio_buffer_t output_buffers[NUM_BUFFERS];
1.39 + int write_buffer;
1.40 + int read_buffer;
1.41 + uint32_t output_format;
1.42 + uint32_t output_rate;
1.43 + uint32_t output_sample_size;
1.44 + struct audio_channel channels[64];
1.45 +} audio;
1.46 +
1.47 +audio_driver_t audio_driver = NULL;
1.48 +
1.49 +#define NEXT_BUFFER() ((audio.write_buffer == NUM_BUFFERS-1) ? 0 : audio.write_buffer+1)
1.50 +
1.51 +extern char *arm_mem;
1.52 +
1.53 +/**
1.54 + * Set the output driver, sample rate and format. Also initializes the
1.55 + * output buffers, flushing any current data and reallocating as
1.56 + * necessary.
1.57 + */
1.58 +void audio_set_output( audio_driver_t driver,
1.59 + uint32_t samplerate, int format )
1.60 +{
1.61 + uint32_t bytes_per_sample = 1;
1.62 + uint32_t samples_per_buffer;
1.63 + int i;
1.64 +
1.65 + if( format & AUDIO_FMT_16BIT )
1.66 + bytes_per_sample = 2;
1.67 + if( format & AUDIO_FMT_STEREO )
1.68 + bytes_per_sample <<= 1;
1.69 + if( samplerate == audio.output_rate &&
1.70 + bytes_per_sample == audio.output_sample_size )
1.71 + return;
1.72 + samples_per_buffer = (samplerate * MS_PER_BUFFER / 1000);
1.73 + for( i=0; i<NUM_BUFFERS; i++ ) {
1.74 + if( audio.output_buffers[i] != NULL )
1.75 + free(audio.output_buffers[i]);
1.76 + audio.output_buffers[i] = g_malloc0( sizeof(struct audio_buffer) + samples_per_buffer * bytes_per_sample );
1.77 + audio.output_buffers[i]->length = samples_per_buffer;
1.78 + audio.output_buffers[i]->posn = 0;
1.79 + audio.output_buffers[i]->status = BUFFER_EMPTY;
1.80 + }
1.81 + audio.output_format = format;
1.82 + audio.output_rate = samplerate;
1.83 + audio.output_sample_size = bytes_per_sample;
1.84 + audio.write_buffer = 0;
1.85 + audio.read_buffer = 0;
1.86 +
1.87 + if( driver == NULL )
1.88 + driver = &null_audio_driver;
1.89 + audio_driver = driver;
1.90 + audio_driver->set_output_format( samplerate, format );
1.91 +}
1.92 +
1.93 +/**
1.94 + * Mark the current write buffer as full and prepare the next buffer for
1.95 + * writing. Returns the next buffer to write to.
1.96 + * If all buffers are full, returns NULL.
1.97 + */
1.98 +audio_buffer_t audio_next_write_buffer( )
1.99 +{
1.100 + audio_buffer_t result = NULL;
1.101 + audio_buffer_t current = audio.output_buffers[audio.write_buffer];
1.102 + current->status = BUFFER_FULL;
1.103 + if( audio.read_buffer == audio.write_buffer &&
1.104 + audio_driver->process_buffer( current ) ) {
1.105 + audio_next_read_buffer();
1.106 + }
1.107 + audio.write_buffer = NEXT_BUFFER();
1.108 + result = audio.output_buffers[audio.write_buffer];
1.109 + if( result->status == BUFFER_FULL )
1.110 + return NULL;
1.111 + else {
1.112 + result->status = BUFFER_WRITING;
1.113 + return result;
1.114 + }
1.115 +}
1.116 +
1.117 +/**
1.118 + * Mark the current read buffer as empty and return the next buffer for
1.119 + * reading. If there is no next buffer yet, returns NULL.
1.120 + */
1.121 +audio_buffer_t audio_next_read_buffer( )
1.122 +{
1.123 + audio_buffer_t current = audio.output_buffers[audio.read_buffer];
1.124 + assert( current->status == BUFFER_FULL );
1.125 + current->status = BUFFER_EMPTY;
1.126 + current->posn = 0;
1.127 + audio.read_buffer++;
1.128 + if( audio.read_buffer == NUM_BUFFERS )
1.129 + audio.read_buffer = 0;
1.130 +
1.131 + current = audio.output_buffers[audio.read_buffer];
1.132 + if( current->status == BUFFER_FULL )
1.133 + return current;
1.134 + else return NULL;
1.135 +}
1.136 +
1.137 +/*************************** ADPCM ***********************************/
1.138 +
1.139 +/**
1.140 + * The following section borrows heavily from ffmpeg, which is
1.141 + * copyright (c) 2001-2003 by the fine folks at the ffmpeg project,
1.142 + * distributed under the GPL version 2 or later.
1.143 + */
1.144 +
1.145 +#define CLAMP_TO_SHORT(value) \
1.146 +if (value > 32767) \
1.147 + value = 32767; \
1.148 +else if (value < -32768) \
1.149 + value = -32768; \
1.150 +
1.151 +static const int yamaha_indexscale[] = {
1.152 + 230, 230, 230, 230, 307, 409, 512, 614,
1.153 + 230, 230, 230, 230, 307, 409, 512, 614
1.154 +};
1.155 +
1.156 +static const int yamaha_difflookup[] = {
1.157 + 1, 3, 5, 7, 9, 11, 13, 15,
1.158 + -1, -3, -5, -7, -9, -11, -13, -15
1.159 +};
1.160 +
1.161 +static inline short adpcm_yamaha_decode_nibble( audio_channel_t c,
1.162 + unsigned char nibble )
1.163 +{
1.164 + if( c->adpcm_step == 0 ) {
1.165 + c->adpcm_predict = 0;
1.166 + c->adpcm_step = 127;
1.167 + }
1.168 +
1.169 + c->adpcm_predict += (c->adpcm_step * yamaha_difflookup[nibble]) >> 3;
1.170 + CLAMP_TO_SHORT(c->adpcm_predict);
1.171 + c->adpcm_step = (c->adpcm_step * yamaha_indexscale[nibble]) >> 8;
1.172 + c->adpcm_step = CLAMP(c->adpcm_step, 127, 24567);
1.173 + return c->adpcm_predict;
1.174 +}
1.175 +
1.176 +/*************************** Sample mixer *****************************/
1.177 +
1.178 +/**
1.179 + * Mix a single output sample.
1.180 + */
1.181 +void audio_mix_sample( )
1.182 +{
1.183 + int i, j;
1.184 + int32_t result_left = 0, result_right = 0;
1.185 +
1.186 + for( i=0; i < 64; i++ ) {
1.187 + audio_channel_t channel = &audio.channels[i];
1.188 + if( channel->active ) {
1.189 + int32_t sample;
1.190 + switch( channel->sample_format ) {
1.191 + case AUDIO_FMT_16BIT:
1.192 + sample = *(int16_t *)(arm_mem + channel->posn + channel->start);
1.193 + break;
1.194 + case AUDIO_FMT_8BIT:
1.195 + sample = (*(int8_t *)(arm_mem + channel->posn + channel->start)) << 8;
1.196 + break;
1.197 + case AUDIO_FMT_16BIT|AUDIO_FMT_UNSIGNED:
1.198 + sample = (int8_t)((*(uint16_t *)(arm_mem + channel->posn + channel->start)) - 0x8000);
1.199 + break;
1.200 + case AUDIO_FMT_8BIT|AUDIO_FMT_UNSIGNED:
1.201 + sample = (int8_t)((*(uint8_t *)(arm_mem + channel->posn + channel->start)) - 0x80);
1.202 + break;
1.203 + case AUDIO_FMT_ADPCM:
1.204 + sample = (int16_t)channel->adpcm_predict;
1.205 + default:
1.206 + sample = 0; /* Unsupported */
1.207 + }
1.208 + result_left += sample * channel->vol_left;
1.209 + result_right += sample * channel->vol_right;
1.210 +
1.211 + channel->posn_left += channel->sample_rate;
1.212 + while( channel->posn_left > audio.output_rate ) {
1.213 + channel->posn_left -= audio.output_rate;
1.214 + if( channel->sample_format == AUDIO_FMT_ADPCM &&
1.215 + channel->adpcm_nibble == 0 ) {
1.216 + uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start);
1.217 + adpcm_yamaha_decode_nibble( channel, (data >> 4) & 0x0F );
1.218 + channel->adpcm_nibble = 1;
1.219 + continue;
1.220 + }
1.221 +
1.222 + channel->posn++;
1.223 +
1.224 + if( channel->loop_count != 0 &&
1.225 + channel->posn >= channel->loop_end ) {
1.226 + channel->posn = channel->loop_start;
1.227 + if( channel->loop_count != -1 )
1.228 + channel->loop_count --;
1.229 + } else if( channel->posn >= channel->end ) {
1.230 + audio_stop_channel( i );
1.231 + break;
1.232 + }
1.233 +
1.234 + if( channel->sample_format == AUDIO_FMT_ADPCM ) {
1.235 + uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start);
1.236 + adpcm_yamaha_decode_nibble( channel, data & 0x0F );
1.237 + channel->adpcm_nibble = 0;
1.238 + }
1.239 + }
1.240 + }
1.241 + }
1.242 +
1.243 + /* Down-render to the final output format */
1.244 + audio_buffer_t buf =
1.245 + audio.output_buffers[audio.write_buffer];
1.246 + if( audio.output_format & AUDIO_FMT_16BIT ) {
1.247 + uint16_t *data = (uint16_t *)&buf->data[buf->posn*audio.output_sample_size];
1.248 + *data++ = (int16_t)(result_left >> 8);
1.249 + *data++ = (int16_t)(result_right >> 8);
1.250 + } else {
1.251 + audio_buffer_t buf =
1.252 + audio.output_buffers[audio.write_buffer];
1.253 + uint8_t *data = (uint8_t *)&buf->data[buf->posn*audio.output_sample_size];
1.254 + *data++ = (int8_t)(result_left >> 22);
1.255 + *data++ = (int8_t)(result_right >>22);
1.256 + }
1.257 + buf->posn++;
1.258 + if( buf->posn == buf->length ) {
1.259 + audio_next_write_buffer();
1.260 + }
1.261 +}
1.262 +
1.263 +/********************** Internal AICA calls ***************************/
1.264 +
1.265 +audio_channel_t audio_get_channel( int channel )
1.266 +{
1.267 + return &audio.channels[channel];
1.268 +}
1.269 +
1.270 +void audio_stop_channel( int channel )
1.271 +{
1.272 + audio.channels[channel].active = FALSE;
1.273 +}
1.274 +
1.275 +
1.276 +void audio_start_channel( int channel )
1.277 +{
1.278 + audio.channels[channel].posn = 0;
1.279 + audio.channels[channel].posn_left = 0;
1.280 + audio.channels[channel].adpcm_nibble = 0;
1.281 + audio.channels[channel].adpcm_step = 0;
1.282 + audio.channels[channel].adpcm_predict = 0;
1.283 + audio.channels[channel].active = TRUE;
1.284 + DEBUG("Channel %d on", channel );
1.285 +}
.