⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 adpcm.c

📁 著名ARC模拟器源码,包括多个平台
💻 C
📖 第 1 页 / 共 2 页
字号:
/********************************************************************************************** * *   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 + -