Search
lxdream.org :: lxdream/src/aica/audio.c
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 (15 years ago)
permissions -rw-r--r--
last change Go go gadget audio!
Slow, but it works :)
file annotate diff log raw
nkeynes@66
     1
/**
nkeynes@66
     2
 * $Id: audio.c,v 1.1 2006-01-10 13:56:54 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@66
    28
#define MS_PER_BUFFER 4000
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@66
    74
	audio.output_buffers[i]->length = samples_per_buffer;
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@66
   178
void audio_mix_sample( )
nkeynes@66
   179
{
nkeynes@66
   180
    int i, j;
nkeynes@66
   181
    int32_t result_left = 0, result_right = 0;
nkeynes@66
   182
nkeynes@66
   183
    for( i=0; i < 64; i++ ) {
nkeynes@66
   184
	audio_channel_t channel = &audio.channels[i];
nkeynes@66
   185
	if( channel->active ) {
nkeynes@66
   186
	    int32_t sample;
nkeynes@66
   187
	    switch( channel->sample_format ) {
nkeynes@66
   188
	    case AUDIO_FMT_16BIT:
nkeynes@66
   189
		sample = *(int16_t *)(arm_mem + channel->posn + channel->start);
nkeynes@66
   190
		break;
nkeynes@66
   191
	    case AUDIO_FMT_8BIT:
nkeynes@66
   192
		sample = (*(int8_t *)(arm_mem + channel->posn + channel->start)) << 8;
nkeynes@66
   193
		break;
nkeynes@66
   194
	    case AUDIO_FMT_16BIT|AUDIO_FMT_UNSIGNED:
nkeynes@66
   195
		sample = (int8_t)((*(uint16_t *)(arm_mem + channel->posn + channel->start)) - 0x8000);
nkeynes@66
   196
		break;
nkeynes@66
   197
	    case AUDIO_FMT_8BIT|AUDIO_FMT_UNSIGNED:
nkeynes@66
   198
		sample = (int8_t)((*(uint8_t *)(arm_mem + channel->posn + channel->start)) - 0x80);
nkeynes@66
   199
		break;
nkeynes@66
   200
	    case AUDIO_FMT_ADPCM:
nkeynes@66
   201
		sample = (int16_t)channel->adpcm_predict;
nkeynes@66
   202
	    default:
nkeynes@66
   203
		sample = 0; /* Unsupported */
nkeynes@66
   204
	    }
nkeynes@66
   205
	    result_left += sample * channel->vol_left;
nkeynes@66
   206
	    result_right += sample * channel->vol_right;
nkeynes@66
   207
	    
nkeynes@66
   208
	    channel->posn_left += channel->sample_rate;
nkeynes@66
   209
	    while( channel->posn_left > audio.output_rate ) {
nkeynes@66
   210
		channel->posn_left -= audio.output_rate;
nkeynes@66
   211
		if( channel->sample_format == AUDIO_FMT_ADPCM &&
nkeynes@66
   212
		    channel->adpcm_nibble == 0 ) {
nkeynes@66
   213
		    uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start);
nkeynes@66
   214
		    adpcm_yamaha_decode_nibble( channel, (data >> 4) & 0x0F );
nkeynes@66
   215
		    channel->adpcm_nibble = 1;
nkeynes@66
   216
		    continue;
nkeynes@66
   217
		}
nkeynes@66
   218
nkeynes@66
   219
		channel->posn++;
nkeynes@66
   220
nkeynes@66
   221
		if( channel->loop_count != 0 && 
nkeynes@66
   222
		    channel->posn >= channel->loop_end ) {
nkeynes@66
   223
		    channel->posn = channel->loop_start;
nkeynes@66
   224
		    if( channel->loop_count != -1 )
nkeynes@66
   225
			channel->loop_count --;
nkeynes@66
   226
		} else if( channel->posn >= channel->end ) {
nkeynes@66
   227
		    audio_stop_channel( i );
nkeynes@66
   228
		    break;
nkeynes@66
   229
		}
nkeynes@66
   230
nkeynes@66
   231
		if( channel->sample_format == AUDIO_FMT_ADPCM ) {
nkeynes@66
   232
		    uint8_t data = *(uint8_t *)(arm_mem + channel->posn + channel->start);
nkeynes@66
   233
		    adpcm_yamaha_decode_nibble( channel, data & 0x0F );
nkeynes@66
   234
		    channel->adpcm_nibble = 0;
nkeynes@66
   235
		}
nkeynes@66
   236
	    }
nkeynes@66
   237
	}
nkeynes@66
   238
    }
nkeynes@66
   239
nkeynes@66
   240
    /* Down-render to the final output format */
nkeynes@66
   241
    audio_buffer_t buf = 
nkeynes@66
   242
	audio.output_buffers[audio.write_buffer];
nkeynes@66
   243
    if( audio.output_format & AUDIO_FMT_16BIT ) {
nkeynes@66
   244
	uint16_t *data = (uint16_t *)&buf->data[buf->posn*audio.output_sample_size];
nkeynes@66
   245
	*data++ = (int16_t)(result_left >> 8);
nkeynes@66
   246
	*data++ = (int16_t)(result_right >> 8);
nkeynes@66
   247
    } else {
nkeynes@66
   248
	audio_buffer_t buf = 
nkeynes@66
   249
	    audio.output_buffers[audio.write_buffer];
nkeynes@66
   250
	uint8_t *data = (uint8_t *)&buf->data[buf->posn*audio.output_sample_size];
nkeynes@66
   251
	*data++ = (int8_t)(result_left >> 22);
nkeynes@66
   252
	*data++ = (int8_t)(result_right >>22);
nkeynes@66
   253
    }
nkeynes@66
   254
    buf->posn++;
nkeynes@66
   255
    if( buf->posn == buf->length ) {
nkeynes@66
   256
	audio_next_write_buffer();
nkeynes@66
   257
    }
nkeynes@66
   258
}
nkeynes@66
   259
nkeynes@66
   260
/********************** Internal AICA calls ***************************/
nkeynes@66
   261
nkeynes@66
   262
audio_channel_t audio_get_channel( int channel ) 
nkeynes@66
   263
{
nkeynes@66
   264
    return &audio.channels[channel];
nkeynes@66
   265
}
nkeynes@66
   266
nkeynes@66
   267
void audio_stop_channel( int channel ) 
nkeynes@66
   268
{
nkeynes@66
   269
    audio.channels[channel].active = FALSE;
nkeynes@66
   270
}
nkeynes@66
   271
nkeynes@66
   272
nkeynes@66
   273
void audio_start_channel( int channel )
nkeynes@66
   274
{
nkeynes@66
   275
    audio.channels[channel].posn = 0;
nkeynes@66
   276
    audio.channels[channel].posn_left = 0;
nkeynes@66
   277
    audio.channels[channel].adpcm_nibble = 0;
nkeynes@66
   278
    audio.channels[channel].adpcm_step = 0;
nkeynes@66
   279
    audio.channels[channel].adpcm_predict = 0;
nkeynes@66
   280
    audio.channels[channel].active = TRUE;
nkeynes@66
   281
    DEBUG("Channel %d on", channel );
nkeynes@66
   282
}
.