mirror of
https://github.com/Fledge68/WiiFlow_Lite.git
synced 2024-12-12 21:12:37 +01:00
652 lines
14 KiB
C
652 lines
14 KiB
C
/*
|
|
* Copyright (c) 2002, 2003, 2004, 2005, 2007 by Christian Nowak <chnowak@web.de>
|
|
* Last update: 20th October, 2007
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "modplay.h"
|
|
#include "s3m.h"
|
|
#include "mod.h"
|
|
#include "xm.h"
|
|
|
|
|
|
const MODFORMAT mod_formats[] = {
|
|
|
|
{ MODFILE_SetS3M,
|
|
MODFILE_IsS3M,
|
|
MODFILE_S3MGetFormatID,
|
|
MODFILE_S3MGetDescription,
|
|
MODFILE_S3MGetAuthor,
|
|
MODFILE_S3MGetVersion,
|
|
MODFILE_S3MGetCopyright
|
|
},
|
|
|
|
{ MODFILE_SetXM,
|
|
MODFILE_IsXM,
|
|
MODFILE_XMGetFormatID,
|
|
MODFILE_XMGetDescription,
|
|
MODFILE_XMGetAuthor,
|
|
MODFILE_XMGetVersion,
|
|
MODFILE_XMGetCopyright
|
|
},
|
|
|
|
{ MODFILE_SetMOD,
|
|
MODFILE_IsMOD,
|
|
MODFILE_MODGetFormatID,
|
|
MODFILE_MODGetDescription,
|
|
MODFILE_MODGetAuthor,
|
|
MODFILE_MODGetVersion,
|
|
MODFILE_MODGetCopyright
|
|
},
|
|
|
|
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void *loadFile(const char *fname, int *filelength);
|
|
*
|
|
* Tries to load a file specified by the fname parameter
|
|
* into a chunk of memory which the function allocates
|
|
* with the malloc() function prior to loading.
|
|
*
|
|
* Returns NULL on error and the address of the file in
|
|
* memory on success.
|
|
*
|
|
* Parameters:
|
|
* fname - The file to be loaded
|
|
* filelength - The length of the file in bytes is
|
|
* written to this location on success.
|
|
**/
|
|
static void *loadFile(const char *fname, int *filelength) {
|
|
|
|
FILE *fhandle;
|
|
void *file;
|
|
|
|
fhandle = fopen(fname, "rb");
|
|
|
|
if (fhandle == NULL)
|
|
return NULL;
|
|
|
|
fseek(fhandle, 0, SEEK_END);
|
|
(*filelength) = ftell(fhandle);
|
|
fseek(fhandle, 0, SEEK_SET);
|
|
|
|
file = malloc(*filelength);
|
|
if (file == NULL) {
|
|
|
|
fclose(fhandle);
|
|
return NULL;
|
|
}
|
|
|
|
fread(file, 1, *filelength, fhandle);
|
|
fclose(fhandle);
|
|
|
|
return file;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* BOOL MODFILE_Is(u8 *modfile, int modlength);
|
|
*
|
|
* Checks whether the raw data in memory is a valid
|
|
* MOD file of any supported format.
|
|
*
|
|
* Returns TRUE if the data is of any supported
|
|
* format, FALSE if not.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* modfile - Pointer to the raw data to be checked
|
|
* modlength - Length of the raw data in bytes
|
|
**/
|
|
BOOL MODFILE_Is(u8 *modfile, int modlength) {
|
|
|
|
int i = 0;
|
|
|
|
while ((mod_formats[i].is != NULL) &&
|
|
(mod_formats[i].set != NULL)) {
|
|
|
|
if (mod_formats[i].is(modfile, modlength))
|
|
return TRUE;
|
|
|
|
i++;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* int MODFILE_Set(u8 *modfile, int modlength, MODFILE *mod);
|
|
*
|
|
* Processes the raw data of a MOD file of any supported format
|
|
* and copies it to a structure. The structure can then be used
|
|
* as a handle of the MOD file. The original raw data isn't
|
|
* needed by the handle.
|
|
* The function works non-destructive if it fails, ie. it doesn't
|
|
* alter any data in the handle.
|
|
*
|
|
* Returns a value <0 on error.
|
|
*
|
|
* Parameters:
|
|
* modfile - A pointer to the raw MOD data
|
|
* modlength - The length of the raw data in bytes
|
|
* mod - A pointer to the MOD handle
|
|
**/
|
|
int MODFILE_Set(u8 *modfile, int modlength, MODFILE *mod) {
|
|
|
|
int i = 0, retval;
|
|
|
|
if ((mod == NULL) || (modfile == NULL) || (modlength <= 0))
|
|
return -1;
|
|
|
|
if (mod->set)
|
|
return -1;
|
|
|
|
while ((mod_formats[i].set != NULL) &&
|
|
(mod_formats[i].is != NULL)) {
|
|
|
|
if (mod_formats[i].is(modfile, modlength)) {
|
|
|
|
if ((retval = mod_formats[i].set(modfile, modlength, mod)) >= 0) {
|
|
|
|
mod->set = TRUE;
|
|
return 0;
|
|
} else {
|
|
|
|
return retval;
|
|
}
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* int MODFILE_Load(const char *fname, MODFILE *mod);
|
|
*
|
|
* Loads a MOD file of any supported format and copies
|
|
* it to a MODFILE structure. The structure can then
|
|
* be used as a handle of the module.
|
|
*
|
|
* Returns <0 on error.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* fname - Name of the file to be loaded
|
|
* mod - Pointer to the MODFILE structure
|
|
**/
|
|
int MODFILE_Load(const char *fname, MODFILE *mod) {
|
|
|
|
u8 *modfile = NULL;
|
|
int modlength = 0;
|
|
int ret;
|
|
|
|
if ((fname == NULL) || (mod == NULL))
|
|
return -1;
|
|
|
|
modfile = loadFile(fname, &modlength);
|
|
if (modfile == NULL)
|
|
return -1;
|
|
|
|
ret = MODFILE_Set(modfile, modlength, mod);
|
|
|
|
free(modfile);
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void MODFILE_Start(MODFILE *mod);
|
|
*
|
|
* Resets all runtime-data in the MODFILE structure,
|
|
* prepares the music for playback and allocates
|
|
* a mixing buffer.
|
|
*
|
|
* Parameters:
|
|
* mod - A pointer too the module handle
|
|
**/
|
|
void MODFILE_Start(MODFILE *mod) {
|
|
|
|
int i;
|
|
|
|
if (mod == NULL)
|
|
return;
|
|
|
|
mod->speed = mod->start_speed;
|
|
MODFILE_SetBPM(mod, mod->start_tempo);
|
|
mod->pattern_line = 0;
|
|
mod->play_position = 0;
|
|
|
|
mod->patterndelay = 0;
|
|
mod->samplescounter = 0;
|
|
mod->speedcounter = 0;
|
|
|
|
mod->patternloop_to = 0;
|
|
mod->patternloop_count = 0;
|
|
|
|
mod->cur_master_volume = mod->master_volume;
|
|
|
|
mod->tempmixbuf = malloc(MODFILE_BPM2SamplesPerTick(mod, 32) * sizeof(s32) * 2);
|
|
|
|
for (i = 0; i < mod->nSamples; i++) {
|
|
|
|
mod->samples[i].middle_c = mod->samples[i].default_middle_c;
|
|
}
|
|
|
|
for (i = 0; i < MODPLAY_MAX_CHANNELS; i++) {
|
|
|
|
int c;
|
|
|
|
mod->channels[i].voiceInfo.playing = FALSE;
|
|
mod->channels[i].voiceInfo.volume = 64;
|
|
mod->channels[i].last_instrument = 0;
|
|
|
|
for (c = 0; c < MODPLAY_NUM_COMMANDS; c++) {
|
|
|
|
mod->channels[i].effects[c].cur_effect = 255;
|
|
mod->channels[i].effects[c].cur_operand = 255;
|
|
mod->channels[i].effects[c].vibrato_wave = 0;
|
|
mod->channels[i].effects[c].tremolo_wave = 0;
|
|
mod->channels[i].effects[c].tremolo_sintabpos = 0;
|
|
mod->channels[i].effects[c].vibrato_sintabpos = 0;
|
|
}
|
|
|
|
mod->channels[i].voiceInfo.panning = mod->channels[i].default_panning;
|
|
}
|
|
mod->playing = TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void MODFILE_Stop(MODFILE *mod);
|
|
*
|
|
* Stops music playback and deallocates the
|
|
* mixing buffer.
|
|
**/
|
|
void MODFILE_Stop(MODFILE *mod) {
|
|
|
|
if (mod->tempmixbuf != NULL) {
|
|
|
|
free(mod->tempmixbuf);
|
|
mod->tempmixbuf = NULL;
|
|
mod->playing = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void MODFILE_Player(MODFILE *mod);
|
|
*
|
|
* Calculates mod->mixingbuflen bytes of music data
|
|
* in the format specified with the MODFILE_SetFormat()
|
|
* format and stores the resulting data in the memory
|
|
* pointed to by mod->mixingbuf.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* mod - A pointer to the MODFILE structure which defines
|
|
* the music to be calculated.
|
|
**/
|
|
void MODFILE_Player(MODFILE *mod) {
|
|
|
|
int len = mod->mixingbuflen;
|
|
int remain, l;
|
|
int mixflags;
|
|
u32 retval = 0;
|
|
u8 *buf8 = (u8*)mod->mixingbuf;
|
|
|
|
if (mod->mixchannels == 2)
|
|
len >>= 1;
|
|
if (mod->bits == 16)
|
|
len >>= 1;
|
|
|
|
mixflags = 0;
|
|
mixflags |= MIXER_USE_S32;
|
|
if (mod->mixchannels == 2)
|
|
mixflags |= MIXER_DEST_STEREO;
|
|
if (mod->bits == 16)
|
|
mixflags |= MIXER_DEST_16BIT;
|
|
if (mod->mixsigned)
|
|
mixflags |= MIXER_DEST_SIGNED;
|
|
remain = len;
|
|
l = 0;
|
|
|
|
do {
|
|
|
|
int tick_remain = mod->samplespertick - mod->samplescounter;
|
|
int res;
|
|
|
|
res = MODFILE_Mix(mod, mixflags, &buf8[mix_destbufsize(mixflags) * l], tick_remain <= remain ? tick_remain : remain);
|
|
|
|
l += res;
|
|
remain -= res;
|
|
|
|
mod->samplescounter += res;
|
|
|
|
if (mod->samplescounter >= mod->samplespertick) {
|
|
|
|
mod->samplescounter -= mod->samplespertick;
|
|
mod->speedcounter++;
|
|
|
|
if (mod->speedcounter >= (mod->speed + mod->patterndelay)) {
|
|
|
|
mod->patterndelay = 0;
|
|
|
|
retval |= MODFILE_Process(mod);
|
|
|
|
mod->speedcounter = 0;
|
|
}
|
|
retval |= MODFILE_EffectHandler(mod);
|
|
}
|
|
} while (remain > 0);
|
|
|
|
mod->notebeats = retval;
|
|
|
|
if (mod->callback != NULL)
|
|
mod->callback(mod);
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void MODFILE_Free(MODFILE *mod);
|
|
*
|
|
* Deallocates all resources occupied by the module
|
|
* in the MODFILE structure after they have been
|
|
* allocated by the MODFILE_Load() or any of the
|
|
* MODFILE_Set*() functions.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* mod - A pointer to the MODFILE structure of which
|
|
* the resource shall be deallocated
|
|
**/
|
|
void MODFILE_Free(MODFILE *mod) {
|
|
|
|
int i;
|
|
|
|
if (mod == NULL)
|
|
return;
|
|
|
|
if (!mod->set)
|
|
return;
|
|
|
|
/* Free patterns */
|
|
if (mod->patterns != NULL) {
|
|
|
|
for (i = 0; i < mod->nPatterns; i++) {
|
|
|
|
if (mod->patterns[i] != NULL) {
|
|
|
|
free(mod->patterns[i]);
|
|
mod->patterns[i] = NULL;
|
|
}
|
|
}
|
|
free(mod->patterns);
|
|
mod->patterns = NULL;
|
|
}
|
|
|
|
/* Free instruments */
|
|
if (mod->instruments != NULL) {
|
|
|
|
for (i = 0; i < mod->nInstruments; i++) {
|
|
|
|
if (mod->instruments[i].envVolume.envPoints != NULL) {
|
|
|
|
free(mod->instruments[i].envVolume.envPoints);
|
|
mod->instruments[i].envVolume.envPoints = NULL;
|
|
}
|
|
|
|
if (mod->instruments[i].envPanning.envPoints != NULL) {
|
|
|
|
free(mod->instruments[i].envPanning.envPoints);
|
|
mod->instruments[i].envPanning.envPoints = NULL;
|
|
}
|
|
}
|
|
free(mod->instruments);
|
|
mod->instruments = NULL;
|
|
}
|
|
|
|
/* Free samples */
|
|
if (mod->samples != NULL ) {
|
|
|
|
for (i = 0; i < mod->nSamples; i++) {
|
|
|
|
if (mod->samples[i].sampleInfo.sampledata != NULL) {
|
|
|
|
free(mod->samples[i].sampleInfo.sampledata);
|
|
mod->samples[i].sampleInfo.sampledata = NULL;
|
|
}
|
|
}
|
|
free(mod->samples);
|
|
mod->samples = NULL;
|
|
}
|
|
|
|
if (mod->patternLengths != NULL) {
|
|
|
|
free(mod->patternLengths);
|
|
mod->patternLengths = NULL;
|
|
}
|
|
|
|
mod->set = FALSE;
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void MODFILE_Init(MODFILE *mod);
|
|
*
|
|
* Initializes a MODFILE structure for usage. Must
|
|
* be called before the structure can be used by
|
|
* any other function.
|
|
*
|
|
* Parameters:
|
|
*
|
|
* mod - A pointer to the MODFILE structure
|
|
**/
|
|
void MODFILE_Init(MODFILE *mod) {
|
|
|
|
if (mod == NULL)
|
|
return;
|
|
|
|
memset(mod, 0, sizeof(MODFILE));
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
* void MODFILE_SetFormat(MODFILE *mod, int freq, int channels, int bits, BOOL mixsigned);
|
|
*
|
|
* Sets the format of the output audio stream. Must
|
|
* be called prior to calling MODFILE_Start() and
|
|
* MODFILE_Player().
|
|
*
|
|
* Parameters:
|
|
*
|
|
* mod - A pointer to the MODFILE structure of
|
|
* which the output format shall be changed
|
|
* freq - Output frequency. Common values are
|
|
* 11025Hz, 22050Hz and 44100Hz
|
|
* channels - 1 for mono and 2 for stereo
|
|
* bits - 8 or 16 are valid values
|
|
* mixsigned - TRUE if the output stream shall consist
|
|
* of signed values
|
|
**/
|
|
void MODFILE_SetFormat(MODFILE *mod, int freq, int channels, int bits, BOOL mixsigned) {
|
|
|
|
mod->playfreq = freq;
|
|
if ((channels == 1) || (channels == 2))
|
|
mod->mixchannels = channels;
|
|
if ((bits == 8) || (bits == 16))
|
|
mod->bits = bits;
|
|
mod->mixsigned = mixsigned;
|
|
|
|
if (mod->playing) {
|
|
|
|
if (mod->tempmixbuf != NULL) {
|
|
|
|
free(mod->tempmixbuf);
|
|
mod->tempmixbuf = malloc(MODFILE_BPM2SamplesPerTick(mod,32) * sizeof(s32) * 2);
|
|
}
|
|
MODFILE_SetBPM(mod, mod->bpm);
|
|
}
|
|
}
|
|
|
|
|
|
int MODFILE_AllocSFXChannels(MODFILE *mod, int nChannels) {
|
|
|
|
int i;
|
|
|
|
if (mod == NULL || nChannels < 0 || nChannels > 32)
|
|
return -1;
|
|
|
|
if (mod->nChannels + nChannels > MODPLAY_MAX_CHANNELS)
|
|
return -1;
|
|
|
|
mod->nSFXChannels = nChannels;
|
|
|
|
for (i = mod->nChannels; i < mod->nChannels + mod->nSFXChannels; i++) {
|
|
|
|
mod->channels[i].voiceInfo.enabled = TRUE;
|
|
printf("%d\n", i);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MOD_Instrument *MODFILE_MakeInstrument(void *rawData, int nBytes, int nBits) {
|
|
|
|
MOD_Instrument *instr;
|
|
MOD_Sample *smpl;
|
|
int shiftVal = nBits == 16 ? 1 : 0;
|
|
int i;
|
|
|
|
if (rawData == NULL || nBytes <= 0 || (nBits != 8 && nBits != 16))
|
|
return NULL;
|
|
|
|
instr = malloc(sizeof(MOD_Instrument));
|
|
if (instr == NULL)
|
|
return NULL;
|
|
memset(instr, 0, sizeof(MOD_Instrument));
|
|
|
|
smpl = malloc(sizeof(MOD_Sample));
|
|
if (smpl == NULL) {
|
|
|
|
free(instr);
|
|
return NULL;
|
|
}
|
|
memset(smpl, 0, sizeof(MOD_Sample));
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
|
|
instr->samples[i] = smpl;
|
|
instr->note[i] = i;
|
|
}
|
|
|
|
instr->name[0] = '\0';
|
|
instr->volumeFade = 4096;
|
|
|
|
instr->envVolume.sustain = 255;
|
|
instr->envVolume.loop_start = 255;
|
|
instr->envVolume.loop_end = 255;
|
|
|
|
instr->envPanning.sustain = 255;
|
|
instr->envPanning.loop_start = 255;
|
|
instr->envPanning.loop_end = 255;
|
|
|
|
smpl->name[0] = '\0';
|
|
smpl->default_volume = 64;
|
|
smpl->middle_c = 8363;
|
|
smpl->default_middle_c = 8363;
|
|
smpl->finetune = 0;
|
|
smpl->relative_note = 0;
|
|
smpl->panning = 32;
|
|
smpl->volume = 64;
|
|
|
|
smpl->sampleInfo.length = nBytes >> shiftVal;
|
|
smpl->sampleInfo.loop_start = 0;
|
|
smpl->sampleInfo.loop_end = (nBytes >> shiftVal) - 1;
|
|
smpl->sampleInfo.looped = FALSE;
|
|
smpl->sampleInfo.pingpong = FALSE;
|
|
smpl->sampleInfo.sampledata = rawData;
|
|
smpl->sampleInfo.bit_16 = nBits == 16;
|
|
smpl->sampleInfo.stereo = FALSE;
|
|
|
|
return instr;
|
|
}
|
|
|
|
void MODFILE_TriggerSFX(MODFILE *mod, MOD_Instrument *instr, int channel, u8 note) {
|
|
|
|
u8 oct, sem;
|
|
u8 dnote;
|
|
int sfxchan;
|
|
|
|
if (mod == NULL || instr == NULL)
|
|
return;
|
|
|
|
if (channel < 0 || channel >= mod->nSFXChannels)
|
|
return;
|
|
|
|
oct = note >> 4;
|
|
sem = note & 0x0f;
|
|
|
|
if (sem >= 12)
|
|
return;
|
|
|
|
sfxchan = mod->nChannels + channel;
|
|
|
|
mod->channels[sfxchan].voiceInfo.playpos = 0;
|
|
mod->channels[sfxchan].voiceInfo.forward = TRUE;
|
|
mod->channels[sfxchan].instrument = instr;
|
|
mod->channels[sfxchan].sample = instr->samples[note];
|
|
mod->channels[sfxchan].voiceInfo.sampleInfo =
|
|
&mod->channels[sfxchan].sample->sampleInfo;
|
|
|
|
dnote = ((note >> 4) * 12) + (note & 0x0f);
|
|
dnote += mod->channels[sfxchan].sample->relative_note;
|
|
dnote = ((dnote / 12) << 4) | (dnote % 12);
|
|
MODFILE_SetNote(mod, sfxchan,
|
|
mod->channels[sfxchan].instrument->note[dnote],
|
|
mod->channels[sfxchan].sample->middle_c,
|
|
mod->channels[sfxchan].sample->finetune);
|
|
mod->channels[sfxchan].cur_note = dnote;
|
|
|
|
mod->channels[sfxchan].voiceInfo.volume = mod->channels[sfxchan].sample->default_volume;
|
|
mod->channels[sfxchan].envVolume.envConfig = &mod->channels[sfxchan].instrument->envVolume;
|
|
EnvTrigger(&mod->channels[sfxchan].envVolume);
|
|
mod->channels[sfxchan].envPanning.envConfig = &mod->channels[sfxchan].instrument->envPanning;
|
|
EnvTrigger(&mod->channels[sfxchan].envPanning);
|
|
mod->channels[sfxchan].volumeFade = 32768;
|
|
mod->channels[sfxchan].volumeFadeDec = 0;
|
|
|
|
mod->channels[sfxchan].voiceInfo.playing = TRUE;
|
|
|
|
mod->channels[sfxchan].voiceInfo.panning = 128;
|
|
}
|