32X Audio
Posted: Thu Jun 05, 2008 8:48 am
Here everything relative to 32X audio.
Code: Select all
MARS_PWM_CTRL = 5; //0x20004030
MARS_PWM_CYCLE = pwmCycle; //0x20004032
MARS_PWM_LEFT = pwmLeft; // 0x20004034
MARS_PWM_RIGHT = pwmRight; // 0x20004036
Code: Select all
// init the sound hardware
MARS_PWM_MONO = 1;
MARS_PWM_MONO = 1;
MARS_PWM_MONO = 1;
if (MARS_VDP_DISPMODE & MARS_NTSC_FORMAT)
MARS_PWM_CYCLE = (((23011361 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for NTSC clock
else
MARS_PWM_CYCLE = (((22801467 << 1)/SAMPLE_RATE + 1) >> 1) + 1; // for PAL clock
MARS_PWM_CTRL = 0x0185; // TM = 1, RTP, RMD = right, LMD = left
sample = SAMPLE_MIN;
/* ramp up to SAMPLE_CENTER to avoid click in audio (real 32X) */
while (sample < SAMPLE_CENTER)
{
for (ix=0; ix<(SAMPLE_RATE*2)/(SAMPLE_CENTER - SAMPLE_MIN); ix++)
{
while (MARS_PWM_MONO & 0x8000) ; // wait while full
MARS_PWM_MONO = sample;
}
sample++;
}
Code: Select all
#define SAMPLE_RATE 22050
#define SAMPLE_MIN 2
#define SAMPLE_CENTER 517
#define SAMPLE_MAX 1032
I have taken a look at the xmplayer1.1 src, where in the src are you actually using DMA to send the samples?...but these days I use DMA. In fact, in my XM player example, I use interrupt driven DMA so that I don't have to poll the DMA done flag.
I suggest you look at my XM player code: viewtopic.php?t=1253
I still have questions about this code. For example, is this still part of the init code or just an example of playing a single sample?Code: Select all
sample = SAMPLE_MIN; /* ramp up to SAMPLE_CENTER to avoid click in audio (real 32X) */ while (sample < SAMPLE_CENTER) { for (ix=0; ix<(SAMPLE_RATE*2)/(SAMPLE_CENTER - SAMPLE_MIN); ix++) { while (MARS_PWM_MONO & 0x8000) ; // wait while full MARS_PWM_MONO = sample; } sample++; }
Code: Select all
! void MixSamples(void *mixer, int16_t *buffer, int32_t cnt, int32_t scale);
! On entry: r4 = mixer pointer
! r5 = buffer pointer
! r6 = count (number of stereo 16-bit samples)
! r7 = scale (global volume - possibly fading, 0 - 64)
.align 4
.global _MixSamples
_MixSamples:
Code: Select all
MixSamples(mix, buffer, num_samples, cscale);
Code: Select all
typedef struct
{
const int8_t *data; // points to sample data rom, or 0 for channel off
uint32_t position; // current position in the data (fixed point 18.14)
uint32_t increment; // step size (fixed point 18.14) for pitch
uint32_t length; // size of data (fixed point 18.14)
uint32_t loop_length; // size of section to loop (fixed point 18.14) or 0 for no loop
int8_t volume; // 0 to 64
uint8_t pan; // 0 = left, 255 = right
uint8_t pad[2]; // pad to 24 bytes
} mixer_t;
It's part of the init code to try to avoid a big CLICK when you start the game. The 32X starts with the PWM output at 0, the writes of 1 to the MONO channel fills the PWM FIFO with 1, then the loop gradually changes the PWM output from 1 to the center sample value, which is the relative 0 point for silence on the PWM output.I still have questions about this code. For example, is this still part of the init code or just an example of playing a single sample?
What is the purpose of the inner loop exactly? Does setting MARS_PWM_MONO play that sample's value automatically? Every time I play a sound do I have to similarly loop to "ramp up" and keep filling the PWM register until I reach the target sample's amplitude?
Code: Select all
instrument_t *load_xi(char *fname, int smp)
{
instrument_t *in;
FILE *xif;
char temp[256];
int i, j, n, t;
xif = fopen(fname, "rb");
fread(temp, 1, 21, xif);
if (memcmp(temp, "Extended Instrument: ", 21))
{
printf("Not an XI file\n");
fclose(xif);
return (instrument_t *)NULL;
}
// read name
i = -1;
do
{
i++;
temp[i] = fgetc(xif);
} while (temp[i] != 0x1A);
temp[i] = 0;
printf("Instrument name: %s\n", temp);
// skip tracker name
fseek(xif, 20, SEEK_CUR);
// read version
j = fgetc(xif) | (fgetc(xif) << 8);
if (j != 0x0102)
{
printf("Wrong version: 0x%04X\n", j);
fclose(xif);
return (instrument_t *)NULL;
}
in = malloc(sizeof(instrument_t));
if (!in)
{
printf("Ran out of memory\n");
fclose(xif);
return (instrument_t *)NULL;
}
// skip note instrument table
fseek(xif, 96, SEEK_CUR);
// read envelopes
for (i=0; i<12; i++)
{
j = fgetc(xif) | (fgetc(xif) << 8);
in->volume.x[i] = j;
j = fgetc(xif) | (fgetc(xif) << 8);
in->volume.y[i] = j;
}
for (i=0; i<12; i++)
{
j = fgetc(xif) | (fgetc(xif) << 8);
in->pan.x[i] = j;
j = fgetc(xif) | (fgetc(xif) << 8);
in->pan.y[i] = j;
}
// various values
in->volume.points = fgetc(xif);
in->pan.points = fgetc(xif);
in->volume.sustain = fgetc(xif);
in->volume.loop_start = fgetc(xif);
in->volume.loop_end = fgetc(xif);
in->pan.sustain = fgetc(xif);
in->pan.loop_start = fgetc(xif);
in->pan.loop_end = fgetc(xif);
in->volume.type = fgetc(xif);
in->pan.type = fgetc(xif);
in->vibrato.type = fgetc(xif);
in->vibrato.sweep = fgetc(xif);
in->vibrato.depth = fgetc(xif);
in->vibrato.rate = fgetc(xif);
j = fgetc(xif) | (fgetc(xif) << 8);
in->volfade = j;
// skip res and extra
fseek(xif, 22, SEEK_CUR);
// get number of samples in this instrument
n = fgetc(xif) | (fgetc(xif) << 8);
printf("%d samples in this instrument\n", n);
if (smp >= n)
smp = 0; // smp too big - use first sample
// skip to desired sample header
if (smp)
fseek(xif, 40*smp, SEEK_CUR);
// read sample header
j = fgetc(xif) | (fgetc(xif) << 8) | (fgetc(xif) << 16) | (fgetc(xif) << 24);
in->length = j;
printf("Sample length: %d\n", j);
j = fgetc(xif) | (fgetc(xif) << 8) | (fgetc(xif) << 16) | (fgetc(xif) << 24);
in->loop_start = j;
printf("Loop start: %d\n", j);
j = fgetc(xif) | (fgetc(xif) << 8) | (fgetc(xif) << 16) | (fgetc(xif) << 24);
in->loop_end = j;
printf("Loop end: %d\n", j);
in->volume.base = fgetc(xif);
printf("Volume: %d\n", in->volume.base);
in->finetune = fgetc(xif);
printf("Finetune: %d\n", in->finetune);
t = fgetc(xif);
printf("Type: 0x%02X\n", in->type);
in->pan.base = fgetc(xif);
printf("Pan: %d\n", in->volume.base);
in->relative = fgetc(xif);
printf("Relative: %d\n", in->relative);
// skip res
fgetc(xif);
// read sample name
fread(temp, 1, 22, xif);
temp[22] = 0;
printf("Sample name: %s\n", temp);
// skip other sample headers
if (n > 1)
fseek(xif, (n-1-smp)*40, SEEK_CUR);
// skip to desired sample data
if (smp)
fseek(xif, in->length*smp, SEEK_CUR);
// read first sample deltas
if (t & 0x10)
{
// 16-bit sample
printf("Processing 16-bit sample..\n");
in->length /= 2;
in->loop_start /= 2;
in->loop_length /= 2;
in->data = malloc((t & 0x02) ? in->loop_start + in->loop_length*2 : in->length);
if (!in->data)
{
printf("Ran out of memory\n");
free(in);
fclose(xif);
return (instrument_t *)NULL;
}
n = 0;
for (i=0; i<in->length; i++)
{
j = fgetc(xif) | (fgetc(xif) << 8);
n += (int16_t)j; // delta to sample
in->data[i] = n >> 8;
}
}
else
{
// 8-bit sample
printf("Processing 8-bit sample..\n");
in->data = malloc((t & 0x02) ? in->loop_start + in->loop_length*2 : in->length);
if (!in->data)
{
printf("Ran out of memory\n");
free(in);
fclose(xif);
return (instrument_t *)NULL;
}
n = 0;
for (i=0; i<in->length; i++)
{
j = fgetc(xif);
n += (int8_t)j; // delta to sample
in->data[i] = n;
}
}
if (t & 0x02)
{
// handle bidi looping
printf("Unrolling BIDI loop...\n");
for (i=0; i<in->loop_length; i++)
in->data[in->loop_start + in->loop_length + i] = in->data[in->loop_start + in->loop_length - i - 1];
in->loop_length *= 2;
in->length = in->loop_start + in->loop_length;
}
if (!(t & 0x03))
{
// not a looping instrument - clear loop variables
in->loop_start = 0;
in->loop_length = 0;
}
return in;
}
What's this "empty pattern"?xmplayer-v1.1\xmplayer\music>..\to
ols\xmconvert -v -s -e ..\kung-fu\kung-fu-kick.xm
read xm
xm id : Extended Module:
xm name: →OpenMPT 1.21.01.00 ♦☺§
xm trkr: OpenMPT 1.21.01.00 ♦☺§
xm vers: 0104
xm head: 21
xm song: 1
xm rest: 0
xm chnl: 2
xm patt: 1
xm inst: 2
xm flag: 1 (Linear)
xm tmpo: 1
xm BPM : 125
Order: 00
empty pattern... creating empty pattern
Packed 64 unpacked 128
ins samp: 0
ins hdln: 0
ins name:
ins samp: 0
ins hdln: 0
ins name:
Code: Select all
void MixSamples(void *mixer, int16_t *buffer, int32_t cnt, int32_t scale);
I have a sound file on my PC, lets say in wav format. I want to hear something approximately like that sound in my 32X game. What is the best way to do that?Why are you trying to use xm (or even xi) to play a sound effect? Shocked
How do I determine the values forJust make a mixer struct, fill in the values
Code: Select all
const int8_t *data; // points to sample data rom, or 0 for channel off?
Code: Select all
.global sound1
sound1:
.incbin "sound1.raw"
Code: Select all
extern char sound1[];