?? adpcm.c
字號:
/********************************************************************************************** * * streaming ADPCM driver * by Aaron Giles * * Library to transcode from an ADPCM source to raw PCM. * Written by Buffoni Mirko in 08/06/97 * References: various sources and documents. * * HJB 08/31/98 * modified to use an automatically selected oversampling factor * for the current audio_sample_rate * * Mish 21/7/99 * Updated to allow multiple OKI chips with different sample rates * **********************************************************************************************/#include <stdio.h>#include <stdlib.h>#include <math.h>#include "loadroms.h"#include "driver.h"#include "adpcm.h"#include "sasound.h"#define MAX_SAMPLE_CHUNK 10000#define FRAC_BITS 14#define FRAC_ONE (1 << FRAC_BITS)#define FRAC_MASK (FRAC_ONE - 1)/* struct describing a single playing ADPCM voice */struct ADPCMVoice{ int stream; /* which stream are we playing on? */ UINT8 playing; /* 1 if we are actively playing */ UINT8 *region_base; /* pointer to the base of the region */ UINT8 *base; /* pointer to the base memory location */ UINT32 sample; /* current sample number */ UINT32 count; /* total samples to play */ UINT32 signal; /* current ADPCM signal */ UINT32 step; /* current ADPCM step */ UINT32 volume; /* output volume */ INT16 last_sample; /* last sample output */ INT16 curr_sample; /* current sample target */ UINT32 source_step; /* step value for frequency conversion */ UINT32 source_pos; /* current fractional position */};/* array of ADPCM voices */static UINT8 num_voices;static struct ADPCMVoice adpcm[MAX_ADPCM];/* step size index shift table */static int index_shift[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };/* lookup table for the precomputed difference */static int diff_lookup[49*16];/* volume lookup table */static UINT32 volume_table[16];/********************************************************************************************** compute_tables -- compute the difference tables***********************************************************************************************/static void compute_tables(void){ /* nibble to bit map */ static int nbl2bit[16][4] = { { 1, 0, 0, 0}, { 1, 0, 0, 1}, { 1, 0, 1, 0}, { 1, 0, 1, 1}, { 1, 1, 0, 0}, { 1, 1, 0, 1}, { 1, 1, 1, 0}, { 1, 1, 1, 1}, {-1, 0, 0, 0}, {-1, 0, 0, 1}, {-1, 0, 1, 0}, {-1, 0, 1, 1}, {-1, 1, 0, 0}, {-1, 1, 0, 1}, {-1, 1, 1, 0}, {-1, 1, 1, 1} }; int step, nib; /* loop over all possible steps */ for (step = 0; step <= 48; step++) { /* compute the step value */ int stepval = floor(16.0 * pow(11.0 / 10.0, (double)step)); /* loop over all nibbles and compute the difference */ for (nib = 0; nib < 16; nib++) { diff_lookup[step*16 + nib] = nbl2bit[nib][0] * (stepval * nbl2bit[nib][1] + stepval/2 * nbl2bit[nib][2] + stepval/4 * nbl2bit[nib][3] + stepval/8); } } /* generate the OKI6295 volume table */ for (step = 0; step < 16; step++) { double out = 256.0; int vol = step; /* 3dB per step */ while (vol-- > 0) out /= 1.412537545; /* = 10 ^ (3/20) = 3dB */ volume_table[step] = (UINT32)out; }}/********************************************************************************************** generate_adpcm -- general ADPCM decoding routine***********************************************************************************************/static void generate_adpcm(struct ADPCMVoice *voice, INT16 *buffer, int samples){ /* if this voice is active */ if (voice->playing) { UINT8 *base = voice->base; int sample = voice->sample; int signal = voice->signal; int count = voice->count; int step = voice->step; int val; /* loop while we still have samples to generate */ while (samples) { /* compute the new amplitude and update the current step */ val = base[sample / 2] >> (((sample & 1) << 2) ^ 4); signal += diff_lookup[step * 16 + (val & 15)]; /* clamp to the maximum */ if (signal > 2047) signal = 2047; else if (signal < -2048) signal = -2048; /* adjust the step size and clamp */ step += index_shift[val & 7]; if (step > 48) step = 48; else if (step < 0) step = 0; /* output to the buffer, scaling by the volume */ *buffer++ = signal * voice->volume / 16; samples--; /* next! */ if (++sample > count) { voice->playing = 0; break; } } /* update the parameters */ voice->sample = sample; voice->signal = signal; voice->step = step; } /* fill the rest with silence */ while (samples--) *buffer++ = 0;}/********************************************************************************************** adpcm_update -- update the sound chip so that it is in sync with CPU execution***********************************************************************************************/static void adpcm_update(int num, INT16 *buffer, int length){ struct ADPCMVoice *voice = &adpcm[num]; INT16 sample_data[MAX_SAMPLE_CHUNK], *curr_data = sample_data; INT16 prev = voice->last_sample, curr = voice->curr_sample; UINT32 final_pos; UINT32 new_samples; /* finish off the current sample */ if (voice->source_pos > 0) { /* interpolate */ while (length > 0 && voice->source_pos < FRAC_ONE) { *buffer++ = (((INT32)prev * (FRAC_ONE - voice->source_pos)) + ((INT32)curr * voice->source_pos)) >> FRAC_BITS; voice->source_pos += voice->source_step; length--; } /* if we're over, continue; otherwise, we're done */ if (voice->source_pos >= FRAC_ONE) voice->source_pos -= FRAC_ONE; else return; } /* compute how many new samples we need */ final_pos = voice->source_pos + length * voice->source_step; new_samples = (final_pos + FRAC_ONE - 1) >> FRAC_BITS; if (new_samples > MAX_SAMPLE_CHUNK) new_samples = MAX_SAMPLE_CHUNK; /* generate them into our buffer */ generate_adpcm(voice, sample_data, new_samples); prev = curr; curr = *curr_data++; /* then sample-rate convert with linear interpolation */ while (length > 0) { /* interpolate */ while (length > 0 && voice->source_pos < FRAC_ONE) { *buffer++ = (((INT32)prev * (FRAC_ONE - voice->source_pos)) + ((INT32)curr * voice->source_pos)) >> FRAC_BITS; voice->source_pos += voice->source_step; length--; } /* if we're over, grab the next samples */ if (voice->source_pos >= FRAC_ONE) { voice->source_pos -= FRAC_ONE; prev = curr; curr = *curr_data++; } } /* remember the last samples */ voice->last_sample = prev; voice->curr_sample = curr;}/********************************************************************************************** ADPCM_sh_start -- start emulation of several ADPCM output streams***********************************************************************************************/void ADPCMSetBuffers(const struct ADPCMinterface *msound,UINT8 *region,int banksize) { // Init 2 chips, or 1 chip with all voices in the same bank. int i; num_voices = (msound->num * MAX_OKIM6295_VOICES); for (i = 0; i < num_voices; i++) { adpcm[i].base = adpcm[i].region_base = region+(i/MAX_OKIM6295_VOICES)*banksize; } }void ADPCMSetBuffersOne(const struct ADPCMinterface *msound,UINT8 *region,int banksize) { // Init 1 chip, 1st bank only (wwfsstars...) int i; num_voices = (msound->num * MAX_OKIM6295_VOICES); for (i = 0; i < num_voices; i++) { adpcm[i].base = adpcm[i].region_base = region+(i/(MAX_OKIM6295_VOICES/2))*banksize; } }static int old_bank;void OKIM6295_bankswitch(int which, int data) { // I know : memcpy is slow... But for now it will do. int bank = data & 0xf; UINT8 *ADPCM = adpcm[which].region_base; if (bank != old_bank) { old_bank = bank; memcpy(&ADPCM[0x30000], &ADPCM[0x40000 + (bank)*0x10000], 0x10000); }} int ADPCM_sh_start(const struct ADPCMinterface *intf){ char stream_name[40]; int i; /* reset the ADPCM system */ num_voices = intf->num; compute_tables(); /* initialize the voices */ memset(adpcm, 0, sizeof(adpcm)); for (i = 0; i < num_voices; i++) { /* generate the name and create the stream */ sprintf(stream_name, "ADPCM #%d", i); adpcm[i].stream = stream_init(stream_name, audio_sample_rate, 16,i, adpcm_update); /* volume setup */ stream_set_volume(adpcm[i].stream,intf->mixing_level[0]); if (adpcm[i].stream == -1) return 1; /* initialize the rest of the structure */ if (intf->region) // If we have the info... adpcm[i].base = adpcm[i].region_base = load_region[intf->region]; adpcm[i].volume = 255; adpcm[i].signal = -2; if (audio_sample_rate) adpcm[i].source_step = (UINT32)((double)intf->frequency * (double)FRAC_ONE / (double)audio_sample_rate); } /* success */ return 0;}/********************************************************************************************** ADPCM_sh_stop -- stop emulation of several ADPCM output streams***********************************************************************************************/void ADPCM_sh_stop(void){}/********************************************************************************************** ADPCM_sh_update -- update ADPCM streams***********************************************************************************************/void ADPCM_sh_update(void){}/********************************************************************************************** ADPCM_play -- play data from a specific offset for a specific length***********************************************************************************************/void ADPCM_play(int num, int offset, int length){ struct ADPCMVoice *voice = &adpcm[num]; /* bail if we're not playing anything */ if (audio_sample_rate == 0) return; /* range check the numbers */ if (num >= num_voices) {#ifdef RAINE_DEBUG fprintf(stderr,"error: ADPCM_trigger() called with channel = %d, but only %d channels allocated\n", num, num_voices);#endif return; } /* update the ADPCM voice */ stream_update(voice->stream, 0); /* set up the voice to play this sample */ voice->playing = 1; voice->base = &voice->region_base[offset]; voice->sample = 0; voice->count = length; /* also reset the ADPCM parameters */ voice->signal = -2; voice->step = 0;}/********************************************************************************************** ADPCM_play -- stop playback on an ADPCM data channel***********************************************************************************************/void ADPCM_stop(int num){ struct ADPCMVoice *voice = &adpcm[num];
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -