Commit d9e5aa52 authored by David Reid's avatar David Reid

Remove jar_* from the extras directory.

These are not used by miniaudio and are just a waste of maintenance
costs. Only libraries that are actually used by miniaudio will be
included in the extras directory from now on.
parent 04ce7dd5
// jar_mod.h - v0.01 - public domain C0 - Joshua Reisenauer
//
// HISTORY:
//
// v0.01 2016-03-12 Setup
//
//
// USAGE:
//
// In ONE source file, put:
//
// #define JAR_MOD_IMPLEMENTATION
// #include "jar_mod.h"
//
// Other source files should just include jar_mod.h
//
// SAMPLE CODE:
// jar_mod_context_t modctx;
// short samplebuff[4096];
// bool bufferFull = false;
// int intro_load(void)
// {
// jar_mod_init(&modctx);
// jar_mod_load_file(&modctx, "file.mod");
// return 1;
// }
// int intro_unload(void)
// {
// jar_mod_unload(&modctx);
// return 1;
// }
// int intro_tick(long counter)
// {
// if(!bufferFull)
// {
// jar_mod_fillbuffer(&modctx, samplebuff, 4096, 0);
// bufferFull=true;
// }
// if(IsKeyDown(KEY_ENTER))
// return 1;
// return 0;
// }
//
//
// LISCENSE:
//
// Written by: Jean-Franois DEL NERO (http://hxc2001.com/) <Email : jeanfrancoisdelnero <> free.fr>
// Adapted to jar_mod by: Joshua Adam Reisenauer <kd7tck@gmail.com>
// This program is free software. It comes without any warranty, to the
// extent permitted by applicable law. You can redistribute it and/or
// modify it under the terms of the Do What The Fuck You Want To Public
// License, Version 2, as published by Sam Hocevar. See
// http://sam.zoy.org/wtfpl/COPYING for more details.
///////////////////////////////////////////////////////////////////////////////////
// HxCMOD Core API:
// -------------------------------------------
// int jar_mod_init(jar_mod_context_t * modctx)
//
// - Initialize the jar_mod_context_t buffer. Must be called before doing anything else.
// Return 1 if success. 0 in case of error.
// -------------------------------------------
// mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename)
//
// - "Load" a MOD from file, context must already be initialized.
// Return size of file in bytes.
// -------------------------------------------
// void jar_mod_fillbuffer( jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf )
//
// - Generate and return the next samples chunk to outbuffer.
// nbsample specify the number of stereo 16bits samples you want.
// The output format is by default signed 48000Hz 16-bit Stereo PCM samples, otherwise it is changed with jar_mod_setcfg().
// The output buffer size in bytes must be equal to ( nbsample * 2 * channels ).
// The optional trkbuf parameter can be used to get detailed status of the player. Put NULL/0 is unused.
// -------------------------------------------
// void jar_mod_unload( jar_mod_context_t * modctx )
// - "Unload" / clear the player status.
// -------------------------------------------
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_JAR_MOD_H
#define INCLUDE_JAR_MOD_H
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h> // comment this line out if you have bool defined somewhere else
#include <string.h> // For memset()
#ifdef __cplusplus
extern "C" {
#endif
// Basic type
typedef unsigned char muchar;
typedef unsigned short muint;
typedef short mint;
typedef unsigned long mulong;
#define NUMMAXCHANNELS 32
#define MAXNOTES 12*12
#define DEFAULT_SAMPLE_RATE 48000
//
// MOD file structures
//
#pragma pack(1)
typedef struct {
muchar name[22];
muint length;
muchar finetune;
muchar volume;
muint reppnt;
muint replen;
} sample;
typedef struct {
muchar sampperiod;
muchar period;
muchar sampeffect;
muchar effect;
} note;
typedef struct {
muchar title[20];
sample samples[31];
muchar length; // length of tablepos
muchar protracker;
muchar patterntable[128];
muchar signature[4];
muchar speed;
} module;
#pragma pack()
//
// HxCMod Internal structures
//
typedef struct {
char* sampdata;
muint sampnum;
muint length;
muint reppnt;
muint replen;
mulong samppos;
muint period;
muchar volume;
mulong ticks;
muchar effect;
muchar parameffect;
muint effect_code;
mint decalperiod;
mint portaspeed;
mint portaperiod;
mint vibraperiod;
mint Arpperiods[3];
muchar ArpIndex;
mint oldk;
muchar volumeslide;
muchar vibraparam;
muchar vibrapointeur;
muchar finetune;
muchar cut_param;
muint patternloopcnt;
muint patternloopstartpoint;
} channel;
typedef struct {
module song;
char* sampledata[31];
note* patterndata[128];
mulong playrate;
muint tablepos;
muint patternpos;
muint patterndelay;
muint jump_loop_effect;
muchar bpm;
mulong patternticks;
mulong patterntickse;
mulong patternticksaim;
mulong sampleticksconst;
mulong samplenb;
channel channels[NUMMAXCHANNELS];
muint number_of_channels;
muint fullperiod[MAXNOTES * 8];
muint mod_loaded;
mint last_r_sample;
mint last_l_sample;
mint stereo;
mint stereo_separation;
mint bits;
mint filter;
muchar *modfile; // the raw mod file
mulong modfilesize;
muint loopcount;
} jar_mod_context_t;
//
// Player states structures
//
typedef struct track_state_
{
unsigned char instrument_number;
unsigned short cur_period;
unsigned char cur_volume;
unsigned short cur_effect;
unsigned short cur_parameffect;
}track_state;
typedef struct tracker_state_
{
int number_of_tracks;
int bpm;
int speed;
int cur_pattern;
int cur_pattern_pos;
int cur_pattern_table_pos;
unsigned int buf_index;
track_state tracks[32];
}tracker_state;
typedef struct tracker_state_instrument_
{
char name[22];
int active;
}tracker_state_instrument;
typedef struct jar_mod_tracker_buffer_state_
{
int nb_max_of_state;
int nb_of_state;
int cur_rd_index;
int sample_step;
char name[64];
tracker_state_instrument instruments[31];
tracker_state * track_state_buf;
}jar_mod_tracker_buffer_state;
bool jar_mod_init(jar_mod_context_t * modctx);
bool jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter);
void jar_mod_fillbuffer(jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf);
void jar_mod_unload(jar_mod_context_t * modctx);
mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename);
mulong jar_mod_current_samples(jar_mod_context_t * modctx);
mulong jar_mod_max_samples(jar_mod_context_t * modctx);
void jar_mod_seek_start(jar_mod_context_t * ctx);
#ifdef __cplusplus
}
#endif
//--------------------------------------------------------------------
//-------------------------------------------------------------------------------
#ifdef JAR_MOD_IMPLEMENTATION
// Effects list
#define EFFECT_ARPEGGIO 0x0 // Supported
#define EFFECT_PORTAMENTO_UP 0x1 // Supported
#define EFFECT_PORTAMENTO_DOWN 0x2 // Supported
#define EFFECT_TONE_PORTAMENTO 0x3 // Supported
#define EFFECT_VIBRATO 0x4 // Supported
#define EFFECT_VOLSLIDE_TONEPORTA 0x5 // Supported
#define EFFECT_VOLSLIDE_VIBRATO 0x6 // Supported
#define EFFECT_VOLSLIDE_TREMOLO 0x7 // - TO BE DONE -
#define EFFECT_SET_PANNING 0x8 // - TO BE DONE -
#define EFFECT_SET_OFFSET 0x9 // Supported
#define EFFECT_VOLUME_SLIDE 0xA // Supported
#define EFFECT_JUMP_POSITION 0xB // Supported
#define EFFECT_SET_VOLUME 0xC // Supported
#define EFFECT_PATTERN_BREAK 0xD // Supported
#define EFFECT_EXTENDED 0xE
#define EFFECT_E_FINE_PORTA_UP 0x1 // Supported
#define EFFECT_E_FINE_PORTA_DOWN 0x2 // Supported
#define EFFECT_E_GLISSANDO_CTRL 0x3 // - TO BE DONE -
#define EFFECT_E_VIBRATO_WAVEFORM 0x4 // - TO BE DONE -
#define EFFECT_E_SET_FINETUNE 0x5 // - TO BE DONE -
#define EFFECT_E_PATTERN_LOOP 0x6 // Supported
#define EFFECT_E_TREMOLO_WAVEFORM 0x7 // - TO BE DONE -
#define EFFECT_E_SET_PANNING_2 0x8 // - TO BE DONE -
#define EFFECT_E_RETRIGGER_NOTE 0x9 // - TO BE DONE -
#define EFFECT_E_FINE_VOLSLIDE_UP 0xA // Supported
#define EFFECT_E_FINE_VOLSLIDE_DOWN 0xB // Supported
#define EFFECT_E_NOTE_CUT 0xC // Supported
#define EFFECT_E_NOTE_DELAY 0xD // - TO BE DONE -
#define EFFECT_E_PATTERN_DELAY 0xE // Supported
#define EFFECT_E_INVERT_LOOP 0xF // - TO BE DONE -
#define EFFECT_SET_SPEED 0xF0 // Supported
#define EFFECT_SET_TEMPO 0xF2 // Supported
#define PERIOD_TABLE_LENGTH MAXNOTES
#define FULL_PERIOD_TABLE_LENGTH ( PERIOD_TABLE_LENGTH * 8 )
static const short periodtable[]=
{
27392, 25856, 24384, 23040, 21696, 20480, 19328, 18240, 17216, 16256, 15360, 14496,
13696, 12928, 12192, 11520, 10848, 10240, 9664, 9120, 8606, 8128, 7680, 7248,
6848, 6464, 6096, 5760, 5424, 5120, 4832, 4560, 4304, 4064, 3840, 3624,
3424, 3232, 3048, 2880, 2712, 2560, 2416, 2280, 2152, 2032, 1920, 1812,
1712, 1616, 1524, 1440, 1356, 1280, 1208, 1140, 1076, 1016, 960, 906,
856, 808, 762, 720, 678, 640, 604, 570, 538, 508, 480, 453,
428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226,
214, 202, 190, 180, 170, 160, 151, 143, 135, 127, 120, 113,
107, 101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56,
53, 50, 47, 45, 42, 40, 37, 35, 33, 31, 30, 28,
27, 25, 24, 22, 21, 20, 19, 18, 17, 16, 15, 14,
13, 13, 12, 11, 11, 10, 9, 9, 8, 8, 7, 7
};
static const short sintable[]={
0, 24, 49, 74, 97, 120, 141,161,
180, 197, 212, 224, 235, 244, 250,253,
255, 253, 250, 244, 235, 224, 212,197,
180, 161, 141, 120, 97, 74, 49, 24
};
typedef struct modtype_
{
unsigned char signature[5];
int numberofchannels;
}modtype;
modtype modlist[]=
{
{ "M!K!",4},
{ "M.K.",4},
{ "FLT4",4},
{ "FLT8",8},
{ "4CHN",4},
{ "6CHN",6},
{ "8CHN",8},
{ "10CH",10},
{ "12CH",12},
{ "14CH",14},
{ "16CH",16},
{ "18CH",18},
{ "20CH",20},
{ "22CH",22},
{ "24CH",24},
{ "26CH",26},
{ "28CH",28},
{ "30CH",30},
{ "32CH",32},
{ "",0}
};
///////////////////////////////////////////////////////////////////////////////////
static void memcopy( void * dest, void *source, unsigned long size )
{
unsigned long i;
unsigned char * d,*s;
d=(unsigned char*)dest;
s=(unsigned char*)source;
for(i=0;i<size;i++)
{
d[i]=s[i];
}
}
static void memclear( void * dest, unsigned char value, unsigned long size )
{
unsigned long i;
unsigned char * d;
d=(unsigned char*)dest;
for(i=0;i<size;i++)
{
d[i]=value;
}
}
static int memcompare( unsigned char * buf1, unsigned char * buf2, unsigned int size )
{
unsigned int i;
i = 0;
while(i<size)
{
if(buf1[i] != buf2[i])
{
return 0;
}
i++;
}
return 1;
}
static int getnote( jar_mod_context_t * mod, unsigned short period, int finetune )
{
int i;
for(i = 0; i < FULL_PERIOD_TABLE_LENGTH; i++)
{
if(period >= mod->fullperiod[i])
{
return i;
}
}
return MAXNOTES;
}
static void worknote( note * nptr, channel * cptr, char t, jar_mod_context_t * mod )
{
muint sample, period, effect, operiod;
muint curnote, arpnote;
sample = (nptr->sampperiod & 0xF0) | (nptr->sampeffect >> 4);
period = ((nptr->sampperiod & 0xF) << 8) | nptr->period;
effect = ((nptr->sampeffect & 0xF) << 8) | nptr->effect;
operiod = cptr->period;
if ( period || sample )
{
if( sample && sample < 32 )
{
cptr->sampnum = sample - 1;
}
if( period || sample )
{
cptr->sampdata = (char *) mod->sampledata[cptr->sampnum];
cptr->length = mod->song.samples[cptr->sampnum].length;
cptr->reppnt = mod->song.samples[cptr->sampnum].reppnt;
cptr->replen = mod->song.samples[cptr->sampnum].replen;
cptr->finetune = (mod->song.samples[cptr->sampnum].finetune)&0xF;
if(effect>>8!=4 && effect>>8!=6)
{
cptr->vibraperiod=0;
cptr->vibrapointeur=0;
}
}
if( (sample != 0) && ( (effect>>8) != EFFECT_VOLSLIDE_TONEPORTA ) )
{
cptr->volume = mod->song.samples[cptr->sampnum].volume;
cptr->volumeslide = 0;
}
if( ( (effect>>8) != EFFECT_TONE_PORTAMENTO && (effect>>8)!=EFFECT_VOLSLIDE_TONEPORTA) )
{
if (period!=0)
cptr->samppos = 0;
}
cptr->decalperiod = 0;
if( period )
{
if(cptr->finetune)
{
if( cptr->finetune <= 7 )
{
period = mod->fullperiod[getnote(mod,period,0) + cptr->finetune];
}
else
{
period = mod->fullperiod[getnote(mod,period,0) - (16 - (cptr->finetune)) ];
}
}
cptr->period = period;
}
}
cptr->effect = 0;
cptr->parameffect = 0;
cptr->effect_code = effect;
switch (effect >> 8)
{
case EFFECT_ARPEGGIO:
/*
[0]: Arpeggio
Where [0][x][y] means "play note, note+x semitones, note+y
semitones, then return to original note". The fluctuations are
carried out evenly spaced in one pattern division. They are usually
used to simulate chords, but this doesn't work too well. They are
also used to produce heavy vibrato. A major chord is when x=4, y=7.
A minor chord is when x=3, y=7.
*/
if(effect&0xff)
{
cptr->effect = EFFECT_ARPEGGIO;
cptr->parameffect = effect&0xff;
cptr->ArpIndex = 0;
curnote = getnote(mod,cptr->period,cptr->finetune);
cptr->Arpperiods[0] = cptr->period;
arpnote = curnote + (((cptr->parameffect>>4)&0xF)*8);
if( arpnote >= FULL_PERIOD_TABLE_LENGTH )
arpnote = FULL_PERIOD_TABLE_LENGTH - 1;
cptr->Arpperiods[1] = mod->fullperiod[arpnote];
arpnote = curnote + (((cptr->parameffect)&0xF)*8);
if( arpnote >= FULL_PERIOD_TABLE_LENGTH )
arpnote = FULL_PERIOD_TABLE_LENGTH - 1;
cptr->Arpperiods[2] = mod->fullperiod[arpnote];
}
break;
case EFFECT_PORTAMENTO_UP:
/*
[1]: Slide up
Where [1][x][y] means "smoothly decrease the period of current
sample by x*16+y after each tick in the division". The
ticks/division are set with the 'set speed' effect (see below). If
the period of the note being played is z, then the final period
will be z - (x*16 + y)*(ticks - 1). As the slide rate depends on
the speed, changing the speed will change the slide. You cannot
slide beyond the note B3 (period 113).
*/
cptr->effect = EFFECT_PORTAMENTO_UP;
cptr->parameffect = effect&0xff;
break;
case EFFECT_PORTAMENTO_DOWN:
/*
[2]: Slide down
Where [2][x][y] means "smoothly increase the period of current
sample by x*16+y after each tick in the division". Similar to [1],
but lowers the pitch. You cannot slide beyond the note C1 (period
856).
*/
cptr->effect = EFFECT_PORTAMENTO_DOWN;
cptr->parameffect = effect&0xff;
break;
case EFFECT_TONE_PORTAMENTO:
/*
[3]: Slide to note
Where [3][x][y] means "smoothly change the period of current sample
by x*16+y after each tick in the division, never sliding beyond
current period". The period-length in this channel's division is a
parameter to this effect, and hence is not played. Sliding to a
note is similar to effects [1] and [2], but the slide will not go
beyond the given period, and the direction is implied by that
period. If x and y are both 0, then the old slide will continue.
*/
cptr->effect = EFFECT_TONE_PORTAMENTO;
if( (effect&0xff) != 0 )
{
cptr->portaspeed = (short)(effect&0xff);
}
if(period!=0)
{
cptr->portaperiod = period;
cptr->period = operiod;
}
break;
case EFFECT_VIBRATO:
/*
[4]: Vibrato
Where [4][x][y] means "oscillate the sample pitch using a
particular waveform with amplitude y/16 semitones, such that (x *
ticks)/64 cycles occur in the division". The waveform is set using
effect [14][4]. By placing vibrato effects on consecutive
divisions, the vibrato effect can be maintained. If either x or y
are 0, then the old vibrato values will be used.
*/
cptr->effect = EFFECT_VIBRATO;
if( ( effect & 0x0F ) != 0 ) // Depth continue or change ?
cptr->vibraparam = (cptr->vibraparam & 0xF0) | ( effect & 0x0F );
if( ( effect & 0xF0 ) != 0 ) // Speed continue or change ?
cptr->vibraparam = (cptr->vibraparam & 0x0F) | ( effect & 0xF0 );
break;
case EFFECT_VOLSLIDE_TONEPORTA:
/*
[5]: Continue 'Slide to note', but also do Volume slide
Where [5][x][y] means "either slide the volume up x*(ticks - 1) or
slide the volume down y*(ticks - 1), at the same time as continuing
the last 'Slide to note'". It is illegal for both x and y to be
non-zero. You cannot slide outside the volume range 0..64. The
period-length in this channel's division is a parameter to this
effect, and hence is not played.
*/
if( period != 0 )
{
cptr->portaperiod = period;
cptr->period = operiod;
}
cptr->effect = EFFECT_VOLSLIDE_TONEPORTA;
if( ( effect & 0xFF ) != 0 )
cptr->volumeslide = ( effect & 0xFF );
break;
case EFFECT_VOLSLIDE_VIBRATO:
/*
[6]: Continue 'Vibrato', but also do Volume slide
Where [6][x][y] means "either slide the volume up x*(ticks - 1) or
slide the volume down y*(ticks - 1), at the same time as continuing
the last 'Vibrato'". It is illegal for both x and y to be non-zero.
You cannot slide outside the volume range 0..64.
*/
cptr->effect = EFFECT_VOLSLIDE_VIBRATO;
if( (effect & 0xFF) != 0 )
cptr->volumeslide = (effect & 0xFF);
break;
case EFFECT_SET_OFFSET:
/*
[9]: Set sample offset
Where [9][x][y] means "play the sample from offset x*4096 + y*256".
The offset is measured in words. If no sample is given, yet one is
still playing on this channel, it should be retriggered to the new
offset using the current volume.
*/
cptr->samppos = ((effect>>4) * 4096) + ((effect&0xF)*256);
break;
case EFFECT_VOLUME_SLIDE:
/*
[10]: Volume slide
Where [10][x][y] means "either slide the volume up x*(ticks - 1) or
slide the volume down y*(ticks - 1)". If both x and y are non-zero,
then the y value is ignored (assumed to be 0). You cannot slide
outside the volume range 0..64.
*/
cptr->effect = EFFECT_VOLUME_SLIDE;
cptr->volumeslide = (effect & 0xFF);
break;
case EFFECT_JUMP_POSITION:
/*
[11]: Position Jump
Where [11][x][y] means "stop the pattern after this division, and
continue the song at song-position x*16+y". This shifts the
'pattern-cursor' in the pattern table (see above). Legal values for
x*16+y are from 0 to 127.
*/
mod->tablepos = (effect & 0xFF);
if(mod->tablepos >= mod->song.length)
{
mod->tablepos = 0;
}
mod->patternpos = 0;
mod->jump_loop_effect = 1;
break;
case EFFECT_SET_VOLUME:
/*
[12]: Set volume
Where [12][x][y] means "set current sample's volume to x*16+y".
Legal volumes are 0..64.
*/
cptr->volume = (effect & 0xFF);
break;
case EFFECT_PATTERN_BREAK:
/*
[13]: Pattern Break
Where [13][x][y] means "stop the pattern after this division, and
continue the song at the next pattern at division x*10+y" (the 10
is not a typo). Legal divisions are from 0 to 63 (note Protracker
exception above).
*/
mod->patternpos = ( ((effect>>4)&0xF)*10 + (effect&0xF) ) * mod->number_of_channels;
mod->jump_loop_effect = 1;
mod->tablepos++;
if(mod->tablepos >= mod->song.length)
{
mod->tablepos = 0;
}
break;
case EFFECT_EXTENDED:
switch( (effect>>4) & 0xF )
{
case EFFECT_E_FINE_PORTA_UP:
/*
[14][1]: Fineslide up
Where [14][1][x] means "decrement the period of the current sample
by x". The incrementing takes place at the beginning of the
division, and hence there is no actual sliding. You cannot slide
beyond the note B3 (period 113).
*/
cptr->period -= (effect & 0xF);
if( cptr->period < 113 )
cptr->period = 113;
break;
case EFFECT_E_FINE_PORTA_DOWN:
/*
[14][2]: Fineslide down
Where [14][2][x] means "increment the period of the current sample
by x". Similar to [14][1] but shifts the pitch down. You cannot
slide beyond the note C1 (period 856).
*/
cptr->period += (effect & 0xF);
if( cptr->period > 856 )
cptr->period = 856;
break;
case EFFECT_E_FINE_VOLSLIDE_UP:
/*
[14][10]: Fine volume slide up
Where [14][10][x] means "increment the volume of the current sample
by x". The incrementing takes place at the beginning of the
division, and hence there is no sliding. You cannot slide beyond
volume 64.
*/
cptr->volume += (effect & 0xF);
if( cptr->volume>64 )
cptr->volume = 64;
break;
case EFFECT_E_FINE_VOLSLIDE_DOWN:
/*
[14][11]: Fine volume slide down
Where [14][11][x] means "decrement the volume of the current sample
by x". Similar to [14][10] but lowers volume. You cannot slide
beyond volume 0.
*/
cptr->volume -= (effect & 0xF);
if( cptr->volume > 200 )
cptr->volume = 0;
break;
case EFFECT_E_PATTERN_LOOP:
/*
[14][6]: Loop pattern
Where [14][6][x] means "set the start of a loop to this division if
x is 0, otherwise after this division, jump back to the start of a
loop and play it another x times before continuing". If the start
of the loop was not set, it will default to the start of the
current pattern. Hence 'loop pattern' cannot be performed across
multiple patterns. Note that loops do not support nesting, and you
may generate an infinite loop if you try to nest 'loop pattern's.
*/
if( effect & 0xF )
{
if( cptr->patternloopcnt )
{
cptr->patternloopcnt--;
if( cptr->patternloopcnt )
{
mod->patternpos = cptr->patternloopstartpoint;
mod->jump_loop_effect = 1;
}
else
{
cptr->patternloopstartpoint = mod->patternpos ;
}
}
else
{
cptr->patternloopcnt = (effect & 0xF);
mod->patternpos = cptr->patternloopstartpoint;
mod->jump_loop_effect = 1;
}
}
else // Start point
{
cptr->patternloopstartpoint = mod->patternpos;
}
break;
case EFFECT_E_PATTERN_DELAY:
/*
[14][14]: Delay pattern
Where [14][14][x] means "after this division there will be a delay
equivalent to the time taken to play x divisions after which the
pattern will be resumed". The delay only relates to the
interpreting of new divisions, and all effects and previous notes
continue during delay.
*/
mod->patterndelay = (effect & 0xF);
break;
case EFFECT_E_NOTE_CUT:
/*
[14][12]: Cut sample
Where [14][12][x] means "after the current sample has been played
for x ticks in this division, its volume will be set to 0". This
implies that if x is 0, then you will not hear any of the sample.
If you wish to insert "silence" in a pattern, it is better to use a
"silence"-sample (see above) due to the lack of proper support for
this effect.
*/
cptr->effect = EFFECT_E_NOTE_CUT;
cptr->cut_param = (effect & 0xF);
if(!cptr->cut_param)
cptr->volume = 0;
break;
default:
break;
}
break;
case 0xF:
/*
[15]: Set speed
Where [15][x][y] means "set speed to x*16+y". Though it is nowhere
near that simple. Let z = x*16+y. Depending on what values z takes,
different units of speed are set, there being two: ticks/division
and beats/minute (though this one is only a label and not strictly
true). If z=0, then what should technically happen is that the
module stops, but in practice it is treated as if z=1, because
there is already a method for stopping the module (running out of
patterns). If z<=32, then it means "set ticks/division to z"
otherwise it means "set beats/minute to z" (convention says that
this should read "If z<32.." but there are some composers out there
that defy conventions). Default values are 6 ticks/division, and
125 beats/minute (4 divisions = 1 beat). The beats/minute tag is
only meaningful for 6 ticks/division. To get a more accurate view
of how things work, use the following formula:
24 * beats/minute
divisions/minute = -----------------
ticks/division
Hence divisions/minute range from 24.75 to 6120, eg. to get a value
of 2000 divisions/minute use 3 ticks/division and 250 beats/minute.
If multiple "set speed" effects are performed in a single division,
the ones on higher-numbered channels take precedence over the ones
on lower-numbered channels. This effect has a large number of
different implementations, but the one described here has the
widest usage.
*/
if( (effect&0xFF) < 0x21 )
{
if( effect&0xFF )
{
mod->song.speed = effect&0xFF;
mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm)));
}
}
if( (effect&0xFF) >= 0x21 )
{
/// HZ = 2 * BPM / 5
mod->bpm = effect&0xFF;
mod->patternticksaim = (long)mod->song.speed * ((mod->playrate * 5 ) / (((long)2 * (long)mod->bpm)));
}
break;
default:
// Unsupported effect
break;
}
}
static void workeffect( note * nptr, channel * cptr )
{
switch(cptr->effect)
{
case EFFECT_ARPEGGIO:
if( cptr->parameffect )
{
cptr->decalperiod = cptr->period - cptr->Arpperiods[cptr->ArpIndex];
cptr->ArpIndex++;
if( cptr->ArpIndex>2 )
cptr->ArpIndex = 0;
}
break;
case EFFECT_PORTAMENTO_UP:
if(cptr->period)
{
cptr->period -= cptr->parameffect;
if( cptr->period < 113 || cptr->period > 20000 )
cptr->period = 113;
}
break;
case EFFECT_PORTAMENTO_DOWN:
if(cptr->period)
{
cptr->period += cptr->parameffect;
if( cptr->period > 20000 )
cptr->period = 20000;
}
break;
case EFFECT_VOLSLIDE_TONEPORTA:
case EFFECT_TONE_PORTAMENTO:
if( cptr->period && ( cptr->period != cptr->portaperiod ) && cptr->portaperiod )
{
if( cptr->period > cptr->portaperiod )
{
if( cptr->period - cptr->portaperiod >= cptr->portaspeed )
{
cptr->period -= cptr->portaspeed;
}
else
{
cptr->period = cptr->portaperiod;
}
}
else
{
if( cptr->portaperiod - cptr->period >= cptr->portaspeed )
{
cptr->period += cptr->portaspeed;
}
else
{
cptr->period = cptr->portaperiod;
}
}
if( cptr->period == cptr->portaperiod )
{
// If the slide is over, don't let it to be retriggered.
cptr->portaperiod = 0;
}
}
if( cptr->effect == EFFECT_VOLSLIDE_TONEPORTA )
{
if( cptr->volumeslide > 0x0F )
{
cptr->volume = cptr->volume + (cptr->volumeslide>>4);
if(cptr->volume>63)
cptr->volume = 63;
}
else
{
cptr->volume = cptr->volume - (cptr->volumeslide);
if(cptr->volume>63)
cptr->volume=0;
}
}
break;
case EFFECT_VOLSLIDE_VIBRATO:
case EFFECT_VIBRATO:
cptr->vibraperiod = ( (cptr->vibraparam&0xF) * sintable[cptr->vibrapointeur&0x1F] )>>7;
if( cptr->vibrapointeur > 31 )
cptr->vibraperiod = -cptr->vibraperiod;
cptr->vibrapointeur = (cptr->vibrapointeur+(((cptr->vibraparam>>4))&0xf)) & 0x3F;
if( cptr->effect == EFFECT_VOLSLIDE_VIBRATO )
{
if( cptr->volumeslide > 0xF )
{
cptr->volume = cptr->volume+(cptr->volumeslide>>4);
if( cptr->volume > 64 )
cptr->volume = 64;
}
else
{
cptr->volume = cptr->volume - cptr->volumeslide;
if( cptr->volume > 64 )
cptr->volume = 0;
}
}
break;
case EFFECT_VOLUME_SLIDE:
if( cptr->volumeslide > 0xF )
{
cptr->volume += (cptr->volumeslide>>4);
if( cptr->volume > 64 )
cptr->volume = 64;
}
else
{
cptr->volume -= (cptr->volumeslide&0xf);
if( cptr->volume > 64 )
cptr->volume = 0;
}
break;
case EFFECT_E_NOTE_CUT:
if(cptr->cut_param)
cptr->cut_param--;
if(!cptr->cut_param)
cptr->volume = 0;
break;
default:
break;
}
}
///////////////////////////////////////////////////////////////////////////////////
bool jar_mod_init(jar_mod_context_t * modctx)
{
muint i,j;
if( modctx )
{
memclear(modctx, 0, sizeof(jar_mod_context_t));
modctx->playrate = DEFAULT_SAMPLE_RATE;
modctx->stereo = 1;
modctx->stereo_separation = 1;
modctx->bits = 16;
modctx->filter = 1;
for(i=0; i < PERIOD_TABLE_LENGTH - 1; i++)
{
for(j=0; j < 8; j++)
{
modctx->fullperiod[(i*8) + j] = periodtable[i] - ((( periodtable[i] - periodtable[i+1] ) / 8) * j);
}
}
return 1;
}
return 0;
}
bool jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter)
{
if( modctx )
{
modctx->playrate = samplerate;
if( stereo )
modctx->stereo = 1;
else
modctx->stereo = 0;
if(stereo_separation < 4)
{
modctx->stereo_separation = stereo_separation;
}
if( bits == 8 || bits == 16 )
modctx->bits = bits;
else
modctx->bits = 16;
if( filter )
modctx->filter = 1;
else
modctx->filter = 0;
return 1;
}
return 0;
}
// make certain that mod_data stays in memory while playing
static bool jar_mod_load( jar_mod_context_t * modctx, void * mod_data, int mod_data_size )
{
muint i, max;
unsigned short t;
sample *sptr;
unsigned char * modmemory,* endmodmemory;
modmemory = (unsigned char *)mod_data;
endmodmemory = modmemory + mod_data_size;
if(modmemory)
{
if( modctx )
{
memcopy(&(modctx->song.title),modmemory,1084);
i = 0;
modctx->number_of_channels = 0;
while(modlist[i].numberofchannels)
{
if(memcompare(modctx->song.signature,modlist[i].signature,4))
{
modctx->number_of_channels = modlist[i].numberofchannels;
}
i++;
}
if( !modctx->number_of_channels )
{
// 15 Samples modules support
// Shift the whole datas to make it look likes a standard 4 channels mod.
memcopy(&(modctx->song.signature), "M.K.", 4);
memcopy(&(modctx->song.length), &(modctx->song.samples[15]), 130);
memclear(&(modctx->song.samples[15]), 0, 480);
modmemory += 600;
modctx->number_of_channels = 4;
}
else
{
modmemory += 1084;
}
if( modmemory >= endmodmemory )
return 0; // End passed ? - Probably a bad file !
// Patterns loading
for (i = max = 0; i < 128; i++)
{
while (max <= modctx->song.patterntable[i])
{
modctx->patterndata[max] = (note*)modmemory;
modmemory += (256*modctx->number_of_channels);
max++;
if( modmemory >= endmodmemory )
return 0; // End passed ? - Probably a bad file !
}
}
for (i = 0; i < 31; i++)
modctx->sampledata[i]=0;
// Samples loading
for (i = 0, sptr = modctx->song.samples; i <31; i++, sptr++)
{
t= (sptr->length &0xFF00)>>8 | (sptr->length &0xFF)<<8;
sptr->length = t*2;
t= (sptr->reppnt &0xFF00)>>8 | (sptr->reppnt &0xFF)<<8;
sptr->reppnt = t*2;
t= (sptr->replen &0xFF00)>>8 | (sptr->replen &0xFF)<<8;
sptr->replen = t*2;
if (sptr->length == 0) continue;
modctx->sampledata[i] = (char*)modmemory;
modmemory += sptr->length;
if (sptr->replen + sptr->reppnt > sptr->length)
sptr->replen = sptr->length - sptr->reppnt;
if( modmemory > endmodmemory )
return 0; // End passed ? - Probably a bad file !
}
// States init
modctx->tablepos = 0;
modctx->patternpos = 0;
modctx->song.speed = 6;
modctx->bpm = 125;
modctx->samplenb = 0;
modctx->patternticks = (((long)modctx->song.speed * modctx->playrate * 5)/ (2 * modctx->bpm)) + 1;
modctx->patternticksaim = ((long)modctx->song.speed * modctx->playrate * 5) / (2 * modctx->bpm);
modctx->sampleticksconst = 3546894UL / modctx->playrate; //8448*428/playrate;
for(i=0; i < modctx->number_of_channels; i++)
{
modctx->channels[i].volume = 0;
modctx->channels[i].period = 0;
}
modctx->mod_loaded = 1;
return 1;
}
}
return 0;
}
void jar_mod_fillbuffer( jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf )
{
unsigned long i, j;
unsigned long k;
unsigned char c;
unsigned int state_remaining_steps;
int l,r;
int ll,lr;
int tl,tr;
short finalperiod;
note *nptr;
channel *cptr;
if( modctx && outbuffer )
{
if(modctx->mod_loaded)
{
state_remaining_steps = 0;
if( trkbuf )
{
trkbuf->cur_rd_index = 0;
memcopy(trkbuf->name,modctx->song.title,sizeof(modctx->song.title));
for(i=0;i<31;i++)
{
memcopy(trkbuf->instruments[i].name,modctx->song.samples[i].name,sizeof(trkbuf->instruments[i].name));
}
}
ll = modctx->last_l_sample;
lr = modctx->last_r_sample;
for (i = 0; i < nbsample; i++)
{
//---------------------------------------
if( modctx->patternticks++ > modctx->patternticksaim )
{
if( !modctx->patterndelay )
{
nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]];
nptr = nptr + modctx->patternpos;
cptr = modctx->channels;
modctx->patternticks = 0;
modctx->patterntickse = 0;
for(c=0;c<modctx->number_of_channels;c++)
{
worknote((note*)(nptr+c), (channel*)(cptr+c),(char)(c+1),modctx);
}
if( !modctx->jump_loop_effect )
modctx->patternpos += modctx->number_of_channels;
else
modctx->jump_loop_effect = 0;
if( modctx->patternpos == 64*modctx->number_of_channels )
{
modctx->tablepos++;
modctx->patternpos = 0;
if(modctx->tablepos >= modctx->song.length)
{
modctx->tablepos = 0;
modctx->loopcount++; // count next loop
}
}
}
else
{
modctx->patterndelay--;
modctx->patternticks = 0;
modctx->patterntickse = 0;
}
}
if( modctx->patterntickse++ > (modctx->patternticksaim/modctx->song.speed) )
{
nptr = modctx->patterndata[modctx->song.patterntable[modctx->tablepos]];
nptr = nptr + modctx->patternpos;
cptr = modctx->channels;
for(c=0;c<modctx->number_of_channels;c++)
{
workeffect(nptr+c, cptr+c);
}
modctx->patterntickse = 0;
}
//---------------------------------------
if( trkbuf && !state_remaining_steps )
{
if( trkbuf->nb_of_state < trkbuf->nb_max_of_state )
{
memclear(&trkbuf->track_state_buf[trkbuf->nb_of_state], 0, sizeof(tracker_state));
}
}
l=0;
r=0;
for(j =0, cptr = modctx->channels; j < modctx->number_of_channels ; j++, cptr++)
{
if( cptr->period != 0 )
{
finalperiod = cptr->period - cptr->decalperiod - cptr->vibraperiod;
if( finalperiod )
{
cptr->samppos += ( (modctx->sampleticksconst<<10) / finalperiod );
}
cptr->ticks++;
if( cptr->replen<=2 )
{
if( (cptr->samppos>>10) >= (cptr->length) )
{
cptr->length = 0;
cptr->reppnt = 0;
if( cptr->length )
cptr->samppos = cptr->samppos % (((unsigned long)cptr->length)<<10);
else
cptr->samppos = 0;
}
}
else
{
if( (cptr->samppos>>10) >= (unsigned long)(cptr->replen+cptr->reppnt) )
{
cptr->samppos = ((unsigned long)(cptr->reppnt)<<10) + (cptr->samppos % ((unsigned long)(cptr->replen+cptr->reppnt)<<10));
}
}
k = cptr->samppos >> 10;
if( cptr->sampdata!=0 && ( ((j&3)==1) || ((j&3)==2) ) )
{
r += ( cptr->sampdata[k] * cptr->volume );
}
if( cptr->sampdata!=0 && ( ((j&3)==0) || ((j&3)==3) ) )
{
l += ( cptr->sampdata[k] * cptr->volume );
}
if( trkbuf && !state_remaining_steps )
{
if( trkbuf->nb_of_state < trkbuf->nb_max_of_state )
{
trkbuf->track_state_buf[trkbuf->nb_of_state].number_of_tracks = modctx->number_of_channels;
trkbuf->track_state_buf[trkbuf->nb_of_state].buf_index = i;
trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern = modctx->song.patterntable[modctx->tablepos];
trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_pos = modctx->patternpos / modctx->number_of_channels;
trkbuf->track_state_buf[trkbuf->nb_of_state].cur_pattern_table_pos = modctx->tablepos;
trkbuf->track_state_buf[trkbuf->nb_of_state].bpm = modctx->bpm;
trkbuf->track_state_buf[trkbuf->nb_of_state].speed = modctx->song.speed;
trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_effect = cptr->effect_code;
trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_parameffect = cptr->parameffect;
trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_period = finalperiod;
trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].cur_volume = cptr->volume;
trkbuf->track_state_buf[trkbuf->nb_of_state].tracks[j].instrument_number = (unsigned char)cptr->sampnum;
}
}
}
}
if( trkbuf && !state_remaining_steps )
{
state_remaining_steps = trkbuf->sample_step;
if(trkbuf->nb_of_state < trkbuf->nb_max_of_state)
trkbuf->nb_of_state++;
}
else
{
state_remaining_steps--;
}
tl = (short)l;
tr = (short)r;
if ( modctx->filter )
{
// Filter
l = (l+ll)>>1;
r = (r+lr)>>1;
}
if ( modctx->stereo_separation == 1 )
{
// Left & Right Stereo panning
l = (l+(r>>1));
r = (r+(l>>1));
}
// Level limitation
if( l > 32767 ) l = 32767;
if( l < -32768 ) l = -32768;
if( r > 32767 ) r = 32767;
if( r < -32768 ) r = -32768;
// Store the final sample.
outbuffer[(i*2)] = l;
outbuffer[(i*2)+1] = r;
ll = tl;
lr = tr;
}
modctx->last_l_sample = ll;
modctx->last_r_sample = lr;
modctx->samplenb = modctx->samplenb+nbsample;
}
else
{
for (i = 0; i < nbsample; i++)
{
// Mod not loaded. Return blank buffer.
outbuffer[(i*2)] = 0;
outbuffer[(i*2)+1] = 0;
}
if(trkbuf)
{
trkbuf->nb_of_state = 0;
trkbuf->cur_rd_index = 0;
trkbuf->name[0] = 0;
memclear(trkbuf->track_state_buf, 0, sizeof(tracker_state) * trkbuf->nb_max_of_state);
memclear(trkbuf->instruments, 0, sizeof(trkbuf->instruments));
}
}
}
}
//resets internals for mod context
static bool jar_mod_reset( jar_mod_context_t * modctx)
{
if(modctx)
{
memclear(&modctx->song, 0, sizeof(modctx->song));
memclear(&modctx->sampledata, 0, sizeof(modctx->sampledata));
memclear(&modctx->patterndata, 0, sizeof(modctx->patterndata));
modctx->tablepos = 0;
modctx->patternpos = 0;
modctx->patterndelay = 0;
modctx->jump_loop_effect = 0;
modctx->bpm = 0;
modctx->patternticks = 0;
modctx->patterntickse = 0;
modctx->patternticksaim = 0;
modctx->sampleticksconst = 0;
modctx->samplenb = 0;
memclear(modctx->channels, 0, sizeof(modctx->channels));
modctx->number_of_channels = 0;
modctx->mod_loaded = 0;
modctx->last_r_sample = 0;
modctx->last_l_sample = 0;
return jar_mod_init(modctx);
}
return 0;
}
void jar_mod_unload( jar_mod_context_t * modctx)
{
if(modctx)
{
if(modctx->modfile)
{
free(modctx->modfile);
modctx->modfile = 0;
modctx->modfilesize = 0;
modctx->loopcount = 0;
}
jar_mod_reset(modctx);
}
}
mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename)
{
mulong fsize = 0;
if(modctx->modfile)
{
free(modctx->modfile);
modctx->modfile = 0;
}
FILE *f = NULL;
#if defined(_MSC_VER) && _MSC_VER >= 1500
fopen_s(&f, filename, "rb");
#else
f = fopen(filename, "rb");
#endif
if(f)
{
fseek(f,0,SEEK_END);
fsize = ftell(f);
fseek(f,0,SEEK_SET);
if(fsize && fsize < 32*1024*1024)
{
modctx->modfile = malloc(fsize);
modctx->modfilesize = fsize;
memset(modctx->modfile, 0, fsize);
fread(modctx->modfile, fsize, 1, f);
fclose(f);
if(!jar_mod_load(modctx, (void*)modctx->modfile, fsize)) fsize = 0;
} else fsize = 0;
}
return fsize;
}
mulong jar_mod_current_samples(jar_mod_context_t * modctx)
{
if(modctx)
return modctx->samplenb;
return 0;
}
// Works, however it is very slow, this data should be cached to ensure it is run only once per file
mulong jar_mod_max_samples(jar_mod_context_t * ctx)
{
mint buff[2];
mulong len;
mulong lastcount = ctx->loopcount;
while(ctx->loopcount <= lastcount)
jar_mod_fillbuffer(ctx, buff, 1, 0);
len = ctx->samplenb;
jar_mod_seek_start(ctx);
return len;
}
// move seek_val to sample index, 0 -> jar_mod_max_samples is the range
void jar_mod_seek_start(jar_mod_context_t * ctx)
{
if(ctx && ctx->modfile)
{
muchar* ftmp = ctx->modfile;
mulong stmp = ctx->modfilesize;
muint lcnt = ctx->loopcount;
if(jar_mod_reset(ctx)){
jar_mod_load(ctx, ftmp, stmp);
ctx->modfile = ftmp;
ctx->modfilesize = stmp;
ctx->loopcount = lcnt;
}
}
}
#endif // end of JAR_MOD_IMPLEMENTATION
//-------------------------------------------------------------------------------
#endif //end of header file
\ No newline at end of file
// jar_xm.h - v0.01 - public domain - Joshua Reisenauer, MAR 2016
//
// HISTORY:
//
// v0.01 2016-02-22 Setup
//
//
// USAGE:
//
// In ONE source file, put:
//
// #define JAR_XM_IMPLEMENTATION
// #include "jar_xm.h"
//
// Other source files should just include jar_xm.h
//
// SAMPLE CODE:
//
// jar_xm_context_t *musicptr;
// float musicBuffer[48000 / 60];
// int intro_load(void)
// {
// jar_xm_create_context_from_file(&musicptr, 48000, "Song.XM");
// return 1;
// }
// int intro_unload(void)
// {
// jar_xm_free_context(musicptr);
// return 1;
// }
// int intro_tick(long counter)
// {
// jar_xm_generate_samples(musicptr, musicBuffer, (48000 / 60) / 2);
// if(IsKeyDown(KEY_ENTER))
// return 1;
// return 0;
// }
//
//
// LISCENSE - FOR LIBXM:
//
// Author: Romain "Artefact2" Dalmaso <artefact2@gmail.com>
// Contributor: Dan Spencer <dan@atomicpotato.net>
// Repackaged into jar_xm.h By: Joshua Adam Reisenauer <kd7tck@gmail.com>
// This program is free software. It comes without any warranty, to the
// extent permitted by applicable law. You can redistribute it and/or
// modify it under the terms of the Do What The Fuck You Want To Public
// License, Version 2, as published by Sam Hocevar. See
// http://sam.zoy.org/wtfpl/COPYING for more details.
#ifndef INCLUDE_JAR_XM_H
#define INCLUDE_JAR_XM_H
#define JAR_XM_DEBUG 0
#define JAR_XM_LINEAR_INTERPOLATION 1 // speed increase with decrease in quality
#define JAR_XM_DEFENSIVE 1
#define JAR_XM_RAMPING 1
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <limits.h>
#include <string.h>
#ifndef true
#include <stdbool.h>
#endif
//-------------------------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
struct jar_xm_context_s;
typedef struct jar_xm_context_s jar_xm_context_t;
/** Create a XM context.
*
* @param moddata the contents of the module
* @param rate play rate in Hz, recommended value of 48000
*
* @returns 0 on success
* @returns 1 if module data is not sane
* @returns 2 if memory allocation failed
* @returns 3 unable to open input file
* @returns 4 fseek() failed
* @returns 5 fread() failed
* @returns 6 unkown error
*
* @deprecated This function is unsafe!
* @see jar_xm_create_context_safe()
*/
int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const char* filename);
/** Create a XM context.
*
* @param moddata the contents of the module
* @param rate play rate in Hz, recommended value of 48000
*
* @returns 0 on success
* @returns 1 if module data is not sane
* @returns 2 if memory allocation failed
*
* @deprecated This function is unsafe!
* @see jar_xm_create_context_safe()
*/
int jar_xm_create_context(jar_xm_context_t**, const char* moddata, uint32_t rate);
/** Create a XM context.
*
* @param moddata the contents of the module
* @param moddata_length the length of the contents of the module, in bytes
* @param rate play rate in Hz, recommended value of 48000
*
* @returns 0 on success
* @returns 1 if module data is not sane
* @returns 2 if memory allocation failed
*/
int jar_xm_create_context_safe(jar_xm_context_t**, const char* moddata, size_t moddata_length, uint32_t rate);
/** Free a XM context created by jar_xm_create_context(). */
void jar_xm_free_context(jar_xm_context_t*);
/** Play the module and put the sound samples in an output buffer.
*
* @param output buffer of 2*numsamples elements (A left and right value for each sample)
* @param numsamples number of samples to generate
*/
void jar_xm_generate_samples(jar_xm_context_t*, float* output, size_t numsamples);
/** Play the module, resample from 32 bit to 16 bit, and put the sound samples in an output buffer.
*
* @param output buffer of 2*numsamples elements (A left and right value for each sample)
* @param numsamples number of samples to generate
*/
void jar_xm_generate_samples_16bit(jar_xm_context_t* ctx, short* output, size_t numsamples)
{
float* musicBuffer = malloc((2*numsamples)*sizeof(float));
jar_xm_generate_samples(ctx, musicBuffer, numsamples);
if(output){
size_t x;
for(x=0;x<2*numsamples;x++)
output[x] = (short)(musicBuffer[x] * SHRT_MAX);
}
free(musicBuffer);
}
/** Play the module, resample from 32 bit to 8 bit, and put the sound samples in an output buffer.
*
* @param output buffer of 2*numsamples elements (A left and right value for each sample)
* @param numsamples number of samples to generate
*/
void jar_xm_generate_samples_8bit(jar_xm_context_t* ctx, char* output, size_t numsamples)
{
float* musicBuffer = malloc((2*numsamples)*sizeof(float));
jar_xm_generate_samples(ctx, musicBuffer, numsamples);
if(output){
size_t x;
for(x=0;x<2*numsamples;x++)
output[x] = (char)(musicBuffer[x] * CHAR_MAX);
}
free(musicBuffer);
}
/** Set the maximum number of times a module can loop. After the
* specified number of loops, calls to jar_xm_generate_samples will only
* generate silence. You can control the current number of loops with
* jar_xm_get_loop_count().
*
* @param loopcnt maximum number of loops. Use 0 to loop
* indefinitely. */
void jar_xm_set_max_loop_count(jar_xm_context_t*, uint8_t loopcnt);
/** Get the loop count of the currently playing module. This value is
* 0 when the module is still playing, 1 when the module has looped
* once, etc. */
uint8_t jar_xm_get_loop_count(jar_xm_context_t*);
/** Mute or unmute a channel.
*
* @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...).
*
* @return whether the channel was muted.
*/
bool jar_xm_mute_channel(jar_xm_context_t*, uint16_t, bool);
/** Mute or unmute an instrument.
*
* @note Instrument numbers go from 1 to
* jar_xm_get_number_of_instruments(...).
*
* @return whether the instrument was muted.
*/
bool jar_xm_mute_instrument(jar_xm_context_t*, uint16_t, bool);
/** Get the module name as a NUL-terminated string. */
const char* jar_xm_get_module_name(jar_xm_context_t*);
/** Get the tracker name as a NUL-terminated string. */
const char* jar_xm_get_tracker_name(jar_xm_context_t*);
/** Get the number of channels. */
uint16_t jar_xm_get_number_of_channels(jar_xm_context_t*);
/** Get the module length (in patterns). */
uint16_t jar_xm_get_module_length(jar_xm_context_t*);
/** Get the number of patterns. */
uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t*);
/** Get the number of rows of a pattern.
*
* @note Pattern numbers go from 0 to
* jar_xm_get_number_of_patterns(...)-1.
*/
uint16_t jar_xm_get_number_of_rows(jar_xm_context_t*, uint16_t);
/** Get the number of instruments. */
uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t*);
/** Get the number of samples of an instrument.
*
* @note Instrument numbers go from 1 to
* jar_xm_get_number_of_instruments(...).
*/
uint16_t jar_xm_get_number_of_samples(jar_xm_context_t*, uint16_t);
/** Get the current module speed.
*
* @param bpm will receive the current BPM
* @param tempo will receive the current tempo (ticks per line)
*/
void jar_xm_get_playing_speed(jar_xm_context_t*, uint16_t* bpm, uint16_t* tempo);
/** Get the current position in the module being played.
*
* @param pattern_index if not NULL, will receive the current pattern
* index in the POT (pattern order table)
*
* @param pattern if not NULL, will receive the current pattern number
*
* @param row if not NULL, will receive the current row
*
* @param samples if not NULL, will receive the total number of
* generated samples (divide by sample rate to get seconds of
* generated audio)
*/
void jar_xm_get_position(jar_xm_context_t*, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples);
/** Get the latest time (in number of generated samples) when a
* particular instrument was triggered in any channel.
*
* @note Instrument numbers go from 1 to
* jar_xm_get_number_of_instruments(...).
*/
uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t*, uint16_t);
/** Get the latest time (in number of generated samples) when a
* particular sample was triggered in any channel.
*
* @note Instrument numbers go from 1 to
* jar_xm_get_number_of_instruments(...).
*
* @note Sample numbers go from 0 to
* jar_xm_get_nubmer_of_samples(...,instr)-1.
*/
uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t*, uint16_t instr, uint16_t sample);
/** Get the latest time (in number of generated samples) when any
* instrument was triggered in a given channel.
*
* @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...).
*/
uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t*, uint16_t);
/** Get the number of remaining samples. Divide by 2 to get the number of individual LR data samples.
*
* @note This is the remaining number of samples before the loop starts module again, or halts if on last pass.
* @note This function is very slow and should only be run once, if at all.
*/
uint64_t jar_xm_get_remaining_samples(jar_xm_context_t*);
#ifdef __cplusplus
}
#endif
//-------------------------------------------------------------------------------
#endif//end of INCLUDE_JAR_XM_H
//Function Definitions-----------------------------------------------------------
#ifdef JAR_XM_IMPLEMENTATION
#include <math.h>
#include <string.h>
#if JAR_XM_DEBUG
#include <stdio.h>
#define DEBUG(fmt, ...) do { \
fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \
fflush(stderr); \
} while(0)
#else
#define DEBUG(...)
#endif
#if jar_xm_BIG_ENDIAN
#error "Big endian platforms are not yet supported, sorry"
/* Make sure the compiler stops, even if #error is ignored */
extern int __fail[-1];
#endif
/* ----- XM constants ----- */
#define SAMPLE_NAME_LENGTH 22
#define INSTRUMENT_NAME_LENGTH 22
#define MODULE_NAME_LENGTH 20
#define TRACKER_NAME_LENGTH 20
#define PATTERN_ORDER_TABLE_LENGTH 256
#define NUM_NOTES 96
#define NUM_ENVELOPE_POINTS 12
#define MAX_NUM_ROWS 256
#if JAR_XM_RAMPING
#define jar_xm_SAMPLE_RAMPING_POINTS 0x20
#endif
/* ----- Data types ----- */
enum jar_xm_waveform_type_e {
jar_xm_SINE_WAVEFORM = 0,
jar_xm_RAMP_DOWN_WAVEFORM = 1,
jar_xm_SQUARE_WAVEFORM = 2,
jar_xm_RANDOM_WAVEFORM = 3,
jar_xm_RAMP_UP_WAVEFORM = 4,
};
typedef enum jar_xm_waveform_type_e jar_xm_waveform_type_t;
enum jar_xm_loop_type_e {
jar_xm_NO_LOOP,
jar_xm_FORWARD_LOOP,
jar_xm_PING_PONG_LOOP,
};
typedef enum jar_xm_loop_type_e jar_xm_loop_type_t;
enum jar_xm_frequency_type_e {
jar_xm_LINEAR_FREQUENCIES,
jar_xm_AMIGA_FREQUENCIES,
};
typedef enum jar_xm_frequency_type_e jar_xm_frequency_type_t;
struct jar_xm_envelope_point_s {
uint16_t frame;
uint16_t value;
};
typedef struct jar_xm_envelope_point_s jar_xm_envelope_point_t;
struct jar_xm_envelope_s {
jar_xm_envelope_point_t points[NUM_ENVELOPE_POINTS];
uint8_t num_points;
uint8_t sustain_point;
uint8_t loop_start_point;
uint8_t loop_end_point;
bool enabled;
bool sustain_enabled;
bool loop_enabled;
};
typedef struct jar_xm_envelope_s jar_xm_envelope_t;
struct jar_xm_sample_s {
char name[SAMPLE_NAME_LENGTH + 1];
int8_t bits; /* Either 8 or 16 */
uint32_t length;
uint32_t loop_start;
uint32_t loop_length;
uint32_t loop_end;
float volume;
int8_t finetune;
jar_xm_loop_type_t loop_type;
float panning;
int8_t relative_note;
uint64_t latest_trigger;
float* data;
};
typedef struct jar_xm_sample_s jar_xm_sample_t;
struct jar_xm_instrument_s {
char name[INSTRUMENT_NAME_LENGTH + 1];
uint16_t num_samples;
uint8_t sample_of_notes[NUM_NOTES];
jar_xm_envelope_t volume_envelope;
jar_xm_envelope_t panning_envelope;
jar_xm_waveform_type_t vibrato_type;
uint8_t vibrato_sweep;
uint8_t vibrato_depth;
uint8_t vibrato_rate;
uint16_t volume_fadeout;
uint64_t latest_trigger;
bool muted;
jar_xm_sample_t* samples;
};
typedef struct jar_xm_instrument_s jar_xm_instrument_t;
struct jar_xm_pattern_slot_s {
uint8_t note; /* 1-96, 97 = Key Off note */
uint8_t instrument; /* 1-128 */
uint8_t volume_column;
uint8_t effect_type;
uint8_t effect_param;
};
typedef struct jar_xm_pattern_slot_s jar_xm_pattern_slot_t;
struct jar_xm_pattern_s {
uint16_t num_rows;
jar_xm_pattern_slot_t* slots; /* Array of size num_rows * num_channels */
};
typedef struct jar_xm_pattern_s jar_xm_pattern_t;
struct jar_xm_module_s {
char name[MODULE_NAME_LENGTH + 1];
char trackername[TRACKER_NAME_LENGTH + 1];
uint16_t length;
uint16_t restart_position;
uint16_t num_channels;
uint16_t num_patterns;
uint16_t num_instruments;
jar_xm_frequency_type_t frequency_type;
uint8_t pattern_table[PATTERN_ORDER_TABLE_LENGTH];
jar_xm_pattern_t* patterns;
jar_xm_instrument_t* instruments; /* Instrument 1 has index 0,
* instrument 2 has index 1, etc. */
};
typedef struct jar_xm_module_s jar_xm_module_t;
struct jar_xm_channel_context_s {
float note;
float orig_note; /* The original note before effect modifications, as read in the pattern. */
jar_xm_instrument_t* instrument; /* Could be NULL */
jar_xm_sample_t* sample; /* Could be NULL */
jar_xm_pattern_slot_t* current;
float sample_position;
float period;
float frequency;
float step;
bool ping; /* For ping-pong samples: true is -->, false is <-- */
float volume; /* Ideally between 0 (muted) and 1 (loudest) */
float panning; /* Between 0 (left) and 1 (right); 0.5 is centered */
uint16_t autovibrato_ticks;
bool sustained;
float fadeout_volume;
float volume_envelope_volume;
float panning_envelope_panning;
uint16_t volume_envelope_frame_count;
uint16_t panning_envelope_frame_count;
float autovibrato_note_offset;
bool arp_in_progress;
uint8_t arp_note_offset;
uint8_t volume_slide_param;
uint8_t fine_volume_slide_param;
uint8_t global_volume_slide_param;
uint8_t panning_slide_param;
uint8_t portamento_up_param;
uint8_t portamento_down_param;
uint8_t fine_portamento_up_param;
uint8_t fine_portamento_down_param;
uint8_t extra_fine_portamento_up_param;
uint8_t extra_fine_portamento_down_param;
uint8_t tone_portamento_param;
float tone_portamento_target_period;
uint8_t multi_retrig_param;
uint8_t note_delay_param;
uint8_t pattern_loop_origin; /* Where to restart a E6y loop */
uint8_t pattern_loop_count; /* How many loop passes have been done */
bool vibrato_in_progress;
jar_xm_waveform_type_t vibrato_waveform;
bool vibrato_waveform_retrigger; /* True if a new note retriggers the waveform */
uint8_t vibrato_param;
uint16_t vibrato_ticks; /* Position in the waveform */
float vibrato_note_offset;
jar_xm_waveform_type_t tremolo_waveform;
bool tremolo_waveform_retrigger;
uint8_t tremolo_param;
uint8_t tremolo_ticks;
float tremolo_volume;
uint8_t tremor_param;
bool tremor_on;
uint64_t latest_trigger;
bool muted;
#if JAR_XM_RAMPING
/* These values are updated at the end of each tick, to save
* a couple of float operations on every generated sample. */
float target_panning;
float target_volume;
unsigned long frame_count;
float end_of_previous_sample[jar_xm_SAMPLE_RAMPING_POINTS];
#endif
float actual_panning;
float actual_volume;
};
typedef struct jar_xm_channel_context_s jar_xm_channel_context_t;
struct jar_xm_context_s {
void* allocated_memory;
jar_xm_module_t module;
uint32_t rate;
uint16_t tempo;
uint16_t bpm;
float global_volume;
float amplification;
#if JAR_XM_RAMPING
/* How much is a channel final volume allowed to change per
* sample; this is used to avoid abrubt volume changes which
* manifest as "clicks" in the generated sound. */
float volume_ramp;
float panning_ramp; /* Same for panning. */
#endif
uint8_t current_table_index;
uint8_t current_row;
uint16_t current_tick; /* Can go below 255, with high tempo and a pattern delay */
float remaining_samples_in_tick;
uint64_t generated_samples;
bool position_jump;
bool pattern_break;
uint8_t jump_dest;
uint8_t jump_row;
/* Extra ticks to be played before going to the next row -
* Used for EEy effect */
uint16_t extra_ticks;
uint8_t* row_loop_count; /* Array of size MAX_NUM_ROWS * module_length */
uint8_t loop_count;
uint8_t max_loop_count;
jar_xm_channel_context_t* channels;
};
/* ----- Internal API ----- */
#if JAR_XM_DEFENSIVE
/** Check the module data for errors/inconsistencies.
*
* @returns 0 if everything looks OK. Module should be safe to load.
*/
int jar_xm_check_sanity_preload(const char*, size_t);
/** Check a loaded module for errors/inconsistencies.
*
* @returns 0 if everything looks OK.
*/
int jar_xm_check_sanity_postload(jar_xm_context_t*);
#endif
/** Get the number of bytes needed to store the module data in a
* dynamically allocated blank context.
*
* Things that are dynamically allocated:
* - sample data
* - sample structures in instruments
* - pattern data
* - row loop count arrays
* - pattern structures in module
* - instrument structures in module
* - channel contexts
* - context structure itself
* @returns 0 if everything looks OK.
*/
size_t jar_xm_get_memory_needed_for_context(const char*, size_t);
/** Populate the context from module data.
*
* @returns pointer to the memory pool
*/
char* jar_xm_load_module(jar_xm_context_t*, const char*, size_t, char*);
int jar_xm_create_context(jar_xm_context_t** ctxp, const char* moddata, uint32_t rate) {
return jar_xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate);
}
int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {
#if JAR_XM_DEFENSIVE
int ret;
#endif
size_t bytes_needed;
char* mempool;
jar_xm_context_t* ctx;
#if JAR_XM_DEFENSIVE
if((ret = jar_xm_check_sanity_preload(moddata, moddata_length))) {
DEBUG("jar_xm_check_sanity_preload() returned %i, module is not safe to load", ret);
return 1;
}
#endif
bytes_needed = jar_xm_get_memory_needed_for_context(moddata, moddata_length);
mempool = malloc(bytes_needed);
if(mempool == NULL && bytes_needed > 0) {
/* malloc() failed, trouble ahead */
DEBUG("call to malloc() failed, returned %p", (void*)mempool);
return 2;
}
/* Initialize most of the fields to 0, 0.f, NULL or false depending on type */
memset(mempool, 0, bytes_needed);
ctx = (*ctxp = (jar_xm_context_t*)mempool);
ctx->allocated_memory = mempool; /* Keep original pointer for free() */
mempool += sizeof(jar_xm_context_t);
ctx->rate = rate;
mempool = jar_xm_load_module(ctx, moddata, moddata_length, mempool);
ctx->channels = (jar_xm_channel_context_t*)mempool;
mempool += ctx->module.num_channels * sizeof(jar_xm_channel_context_t);
ctx->global_volume = 1.f;
ctx->amplification = .25f; /* XXX: some bad modules may still clip. Find out something better. */
#if JAR_XM_RAMPING
ctx->volume_ramp = (1.f / 128.f);
ctx->panning_ramp = (1.f / 128.f);
#endif
for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
jar_xm_channel_context_t* ch = ctx->channels + i;
ch->ping = true;
ch->vibrato_waveform = jar_xm_SINE_WAVEFORM;
ch->vibrato_waveform_retrigger = true;
ch->tremolo_waveform = jar_xm_SINE_WAVEFORM;
ch->tremolo_waveform_retrigger = true;
ch->volume = ch->volume_envelope_volume = ch->fadeout_volume = 1.0f;
ch->panning = ch->panning_envelope_panning = .5f;
ch->actual_volume = .0f;
ch->actual_panning = .5f;
}
ctx->row_loop_count = (uint8_t*)mempool;
mempool += MAX_NUM_ROWS * sizeof(uint8_t);
#if JAR_XM_DEFENSIVE
if((ret = jar_xm_check_sanity_postload(ctx))) {
DEBUG("jar_xm_check_sanity_postload() returned %i, module is not safe to play", ret);
jar_xm_free_context(ctx);
return 1;
}
#endif
return 0;
}
void jar_xm_free_context(jar_xm_context_t* context) {
free(context->allocated_memory);
}
void jar_xm_set_max_loop_count(jar_xm_context_t* context, uint8_t loopcnt) {
context->max_loop_count = loopcnt;
}
uint8_t jar_xm_get_loop_count(jar_xm_context_t* context) {
return context->loop_count;
}
bool jar_xm_mute_channel(jar_xm_context_t* ctx, uint16_t channel, bool mute) {
bool old = ctx->channels[channel - 1].muted;
ctx->channels[channel - 1].muted = mute;
return old;
}
bool jar_xm_mute_instrument(jar_xm_context_t* ctx, uint16_t instr, bool mute) {
bool old = ctx->module.instruments[instr - 1].muted;
ctx->module.instruments[instr - 1].muted = mute;
return old;
}
const char* jar_xm_get_module_name(jar_xm_context_t* ctx) {
return ctx->module.name;
}
const char* jar_xm_get_tracker_name(jar_xm_context_t* ctx) {
return ctx->module.trackername;
}
uint16_t jar_xm_get_number_of_channels(jar_xm_context_t* ctx) {
return ctx->module.num_channels;
}
uint16_t jar_xm_get_module_length(jar_xm_context_t* ctx) {
return ctx->module.length;
}
uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t* ctx) {
return ctx->module.num_patterns;
}
uint16_t jar_xm_get_number_of_rows(jar_xm_context_t* ctx, uint16_t pattern) {
return ctx->module.patterns[pattern].num_rows;
}
uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t* ctx) {
return ctx->module.num_instruments;
}
uint16_t jar_xm_get_number_of_samples(jar_xm_context_t* ctx, uint16_t instrument) {
return ctx->module.instruments[instrument - 1].num_samples;
}
void jar_xm_get_playing_speed(jar_xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo) {
if(bpm) *bpm = ctx->bpm;
if(tempo) *tempo = ctx->tempo;
}
void jar_xm_get_position(jar_xm_context_t* ctx, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples) {
if(pattern_index) *pattern_index = ctx->current_table_index;
if(pattern) *pattern = ctx->module.pattern_table[ctx->current_table_index];
if(row) *row = ctx->current_row;
if(samples) *samples = ctx->generated_samples;
}
uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t* ctx, uint16_t instr) {
return ctx->module.instruments[instr - 1].latest_trigger;
}
uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t* ctx, uint16_t instr, uint16_t sample) {
return ctx->module.instruments[instr - 1].samples[sample].latest_trigger;
}
uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t* ctx, uint16_t chn) {
return ctx->channels[chn - 1].latest_trigger;
}
/* .xm files are little-endian. (XXX: Are they really?) */
/* Bounded reader macros.
* If we attempt to read the buffer out-of-bounds, pretend that the buffer is
* infinitely padded with zeroes.
*/
#define READ_U8(offset) (((offset) < moddata_length) ? (*(uint8_t*)(moddata + (offset))) : 0)
#define READ_U16(offset) ((uint16_t)READ_U8(offset) | ((uint16_t)READ_U8((offset) + 1) << 8))
#define READ_U32(offset) ((uint32_t)READ_U16(offset) | ((uint32_t)READ_U16((offset) + 2) << 16))
#define READ_MEMCPY(ptr, offset, length) memcpy_pad(ptr, length, moddata, moddata_length, offset)
static inline void memcpy_pad(void* dst, size_t dst_len, const void* src, size_t src_len, size_t offset) {
uint8_t* dst_c = dst;
const uint8_t* src_c = src;
/* how many bytes can be copied without overrunning `src` */
size_t copy_bytes = (src_len >= offset) ? (src_len - offset) : 0;
copy_bytes = copy_bytes > dst_len ? dst_len : copy_bytes;
memcpy(dst_c, src_c + offset, copy_bytes);
/* padded bytes */
memset(dst_c + copy_bytes, 0, dst_len - copy_bytes);
}
#if JAR_XM_DEFENSIVE
int jar_xm_check_sanity_preload(const char* module, size_t module_length) {
if(module_length < 60) {
return 4;
}
if(memcmp("Extended Module: ", module, 17) != 0) {
return 1;
}
if(module[37] != 0x1A) {
return 2;
}
if(module[59] != 0x01 || module[58] != 0x04) {
/* Not XM 1.04 */
return 3;
}
return 0;
}
int jar_xm_check_sanity_postload(jar_xm_context_t* ctx) {
/* @todo: plenty of stuff to do here… */
/* Check the POT */
for(uint8_t i = 0; i < ctx->module.length; ++i) {
if(ctx->module.pattern_table[i] >= ctx->module.num_patterns) {
if(i+1 == ctx->module.length && ctx->module.length > 1) {
/* Cheap fix */
--ctx->module.length;
DEBUG("trimming invalid POT at pos %X", i);
} else {
DEBUG("module has invalid POT, pos %X references nonexistent pattern %X",
i,
ctx->module.pattern_table[i]);
return 1;
}
}
}
return 0;
}
#endif
size_t jar_xm_get_memory_needed_for_context(const char* moddata, size_t moddata_length) {
size_t memory_needed = 0;
size_t offset = 60; /* Skip the first header */
uint16_t num_channels;
uint16_t num_patterns;
uint16_t num_instruments;
/* Read the module header */
num_channels = READ_U16(offset + 8);
num_channels = READ_U16(offset + 8);
num_patterns = READ_U16(offset + 10);
memory_needed += num_patterns * sizeof(jar_xm_pattern_t);
num_instruments = READ_U16(offset + 12);
memory_needed += num_instruments * sizeof(jar_xm_instrument_t);
memory_needed += MAX_NUM_ROWS * READ_U16(offset + 4) * sizeof(uint8_t); /* Module length */
/* Header size */
offset += READ_U32(offset);
/* Read pattern headers */
for(uint16_t i = 0; i < num_patterns; ++i) {
uint16_t num_rows;
num_rows = READ_U16(offset + 5);
memory_needed += num_rows * num_channels * sizeof(jar_xm_pattern_slot_t);
/* Pattern header length + packed pattern data size */
offset += READ_U32(offset) + READ_U16(offset + 7);
}
/* Read instrument headers */
for(uint16_t i = 0; i < num_instruments; ++i) {
uint16_t num_samples;
uint32_t sample_header_size = 0;
uint32_t sample_size_aggregate = 0;
num_samples = READ_U16(offset + 27);
memory_needed += num_samples * sizeof(jar_xm_sample_t);
if(num_samples > 0) {
sample_header_size = READ_U32(offset + 29);
}
/* Instrument header size */
offset += READ_U32(offset);
for(uint16_t j = 0; j < num_samples; ++j) {
uint32_t sample_size;
uint8_t flags;
sample_size = READ_U32(offset);
flags = READ_U8(offset + 14);
sample_size_aggregate += sample_size;
if(flags & (1 << 4)) {
/* 16 bit sample */
memory_needed += sample_size * (sizeof(float) >> 1);
} else {
/* 8 bit sample */
memory_needed += sample_size * sizeof(float);
}
offset += sample_header_size;
}
offset += sample_size_aggregate;
}
memory_needed += num_channels * sizeof(jar_xm_channel_context_t);
memory_needed += sizeof(jar_xm_context_t);
return memory_needed;
}
char* jar_xm_load_module(jar_xm_context_t* ctx, const char* moddata, size_t moddata_length, char* mempool) {
size_t offset = 0;
jar_xm_module_t* mod = &(ctx->module);
/* Read XM header */
READ_MEMCPY(mod->name, offset + 17, MODULE_NAME_LENGTH);
READ_MEMCPY(mod->trackername, offset + 38, TRACKER_NAME_LENGTH);
offset += 60;
/* Read module header */
uint32_t header_size = READ_U32(offset);
mod->length = READ_U16(offset + 4);
mod->restart_position = READ_U16(offset + 6);
mod->num_channels = READ_U16(offset + 8);
mod->num_patterns = READ_U16(offset + 10);
mod->num_instruments = READ_U16(offset + 12);
mod->patterns = (jar_xm_pattern_t*)mempool;
mempool += mod->num_patterns * sizeof(jar_xm_pattern_t);
mod->instruments = (jar_xm_instrument_t*)mempool;
mempool += mod->num_instruments * sizeof(jar_xm_instrument_t);
uint16_t flags = READ_U32(offset + 14);
mod->frequency_type = (flags & (1 << 0)) ? jar_xm_LINEAR_FREQUENCIES : jar_xm_AMIGA_FREQUENCIES;
ctx->tempo = READ_U16(offset + 16);
ctx->bpm = READ_U16(offset + 18);
READ_MEMCPY(mod->pattern_table, offset + 20, PATTERN_ORDER_TABLE_LENGTH);
offset += header_size;
/* Read patterns */
for(uint16_t i = 0; i < mod->num_patterns; ++i) {
uint16_t packed_patterndata_size = READ_U16(offset + 7);
jar_xm_pattern_t* pat = mod->patterns + i;
pat->num_rows = READ_U16(offset + 5);
pat->slots = (jar_xm_pattern_slot_t*)mempool;
mempool += mod->num_channels * pat->num_rows * sizeof(jar_xm_pattern_slot_t);
/* Pattern header length */
offset += READ_U32(offset);
if(packed_patterndata_size == 0) {
/* No pattern data is present */
memset(pat->slots, 0, sizeof(jar_xm_pattern_slot_t) * pat->num_rows * mod->num_channels);
} else {
/* This isn't your typical for loop */
for(uint16_t j = 0, k = 0; j < packed_patterndata_size; ++k) {
uint8_t note = READ_U8(offset + j);
jar_xm_pattern_slot_t* slot = pat->slots + k;
if(note & (1 << 7)) {
/* MSB is set, this is a compressed packet */
++j;
if(note & (1 << 0)) {
/* Note follows */
slot->note = READ_U8(offset + j);
++j;
} else {
slot->note = 0;
}
if(note & (1 << 1)) {
/* Instrument follows */
slot->instrument = READ_U8(offset + j);
++j;
} else {
slot->instrument = 0;
}
if(note & (1 << 2)) {
/* Volume column follows */
slot->volume_column = READ_U8(offset + j);
++j;
} else {
slot->volume_column = 0;
}
if(note & (1 << 3)) {
/* Effect follows */
slot->effect_type = READ_U8(offset + j);
++j;
} else {
slot->effect_type = 0;
}
if(note & (1 << 4)) {
/* Effect parameter follows */
slot->effect_param = READ_U8(offset + j);
++j;
} else {
slot->effect_param = 0;
}
} else {
/* Uncompressed packet */
slot->note = note;
slot->instrument = READ_U8(offset + j + 1);
slot->volume_column = READ_U8(offset + j + 2);
slot->effect_type = READ_U8(offset + j + 3);
slot->effect_param = READ_U8(offset + j + 4);
j += 5;
}
}
}
offset += packed_patterndata_size;
}
/* Read instruments */
for(uint16_t i = 0; i < ctx->module.num_instruments; ++i) {
uint32_t sample_header_size = 0;
jar_xm_instrument_t* instr = mod->instruments + i;
READ_MEMCPY(instr->name, offset + 4, INSTRUMENT_NAME_LENGTH);
instr->num_samples = READ_U16(offset + 27);
if(instr->num_samples > 0) {
/* Read extra header properties */
sample_header_size = READ_U32(offset + 29);
READ_MEMCPY(instr->sample_of_notes, offset + 33, NUM_NOTES);
instr->volume_envelope.num_points = READ_U8(offset + 225);
instr->panning_envelope.num_points = READ_U8(offset + 226);
for(uint8_t j = 0; j < instr->volume_envelope.num_points; ++j) {
instr->volume_envelope.points[j].frame = READ_U16(offset + 129 + 4 * j);
instr->volume_envelope.points[j].value = READ_U16(offset + 129 + 4 * j + 2);
}
for(uint8_t j = 0; j < instr->panning_envelope.num_points; ++j) {
instr->panning_envelope.points[j].frame = READ_U16(offset + 177 + 4 * j);
instr->panning_envelope.points[j].value = READ_U16(offset + 177 + 4 * j + 2);
}
instr->volume_envelope.sustain_point = READ_U8(offset + 227);
instr->volume_envelope.loop_start_point = READ_U8(offset + 228);
instr->volume_envelope.loop_end_point = READ_U8(offset + 229);
instr->panning_envelope.sustain_point = READ_U8(offset + 230);
instr->panning_envelope.loop_start_point = READ_U8(offset + 231);
instr->panning_envelope.loop_end_point = READ_U8(offset + 232);
uint8_t flags = READ_U8(offset + 233);
instr->volume_envelope.enabled = flags & (1 << 0);
instr->volume_envelope.sustain_enabled = flags & (1 << 1);
instr->volume_envelope.loop_enabled = flags & (1 << 2);
flags = READ_U8(offset + 234);
instr->panning_envelope.enabled = flags & (1 << 0);
instr->panning_envelope.sustain_enabled = flags & (1 << 1);
instr->panning_envelope.loop_enabled = flags & (1 << 2);
instr->vibrato_type = READ_U8(offset + 235);
if(instr->vibrato_type == 2) {
instr->vibrato_type = 1;
} else if(instr->vibrato_type == 1) {
instr->vibrato_type = 2;
}
instr->vibrato_sweep = READ_U8(offset + 236);
instr->vibrato_depth = READ_U8(offset + 237);
instr->vibrato_rate = READ_U8(offset + 238);
instr->volume_fadeout = READ_U16(offset + 239);
instr->samples = (jar_xm_sample_t*)mempool;
mempool += instr->num_samples * sizeof(jar_xm_sample_t);
} else {
instr->samples = NULL;
}
/* Instrument header size */
offset += READ_U32(offset);
for(uint16_t j = 0; j < instr->num_samples; ++j) {
/* Read sample header */
jar_xm_sample_t* sample = instr->samples + j;
sample->length = READ_U32(offset);
sample->loop_start = READ_U32(offset + 4);
sample->loop_length = READ_U32(offset + 8);
sample->loop_end = sample->loop_start + sample->loop_length;
sample->volume = (float)READ_U8(offset + 12) / (float)0x40;
sample->finetune = (int8_t)READ_U8(offset + 13);
uint8_t flags = READ_U8(offset + 14);
if((flags & 3) == 0) {
sample->loop_type = jar_xm_NO_LOOP;
} else if((flags & 3) == 1) {
sample->loop_type = jar_xm_FORWARD_LOOP;
} else {
sample->loop_type = jar_xm_PING_PONG_LOOP;
}
sample->bits = (flags & (1 << 4)) ? 16 : 8;
sample->panning = (float)READ_U8(offset + 15) / (float)0xFF;
sample->relative_note = (int8_t)READ_U8(offset + 16);
READ_MEMCPY(sample->name, 18, SAMPLE_NAME_LENGTH);
sample->data = (float*)mempool;
if(sample->bits == 16) {
/* 16 bit sample */
mempool += sample->length * (sizeof(float) >> 1);
sample->loop_start >>= 1;
sample->loop_length >>= 1;
sample->loop_end >>= 1;
sample->length >>= 1;
} else {
/* 8 bit sample */
mempool += sample->length * sizeof(float);
}
offset += sample_header_size;
}
for(uint16_t j = 0; j < instr->num_samples; ++j) {
/* Read sample data */
jar_xm_sample_t* sample = instr->samples + j;
uint32_t length = sample->length;
if(sample->bits == 16) {
int16_t v = 0;
for(uint32_t k = 0; k < length; ++k) {
v = v + (int16_t)READ_U16(offset + (k << 1));
sample->data[k] = (float)v / (float)(1 << 15);
}
offset += sample->length << 1;
} else {
int8_t v = 0;
for(uint32_t k = 0; k < length; ++k) {
v = v + (int8_t)READ_U8(offset + k);
sample->data[k] = (float)v / (float)(1 << 7);
}
offset += sample->length;
}
}
}
return mempool;
}
//-------------------------------------------------------------------------------
//THE FOLLOWING IS FOR PLAYING
//-------------------------------------------------------------------------------
/* ----- Static functions ----- */
static float jar_xm_waveform(jar_xm_waveform_type_t, uint8_t);
static void jar_xm_autovibrato(jar_xm_context_t*, jar_xm_channel_context_t*);
static void jar_xm_vibrato(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t);
static void jar_xm_tremolo(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t);
static void jar_xm_arpeggio(jar_xm_context_t*, jar_xm_channel_context_t*, uint8_t, uint16_t);
static void jar_xm_tone_portamento(jar_xm_context_t*, jar_xm_channel_context_t*);
static void jar_xm_pitch_slide(jar_xm_context_t*, jar_xm_channel_context_t*, float);
static void jar_xm_panning_slide(jar_xm_channel_context_t*, uint8_t);
static void jar_xm_volume_slide(jar_xm_channel_context_t*, uint8_t);
static float jar_xm_envelope_lerp(jar_xm_envelope_point_t*, jar_xm_envelope_point_t*, uint16_t);
static void jar_xm_envelope_tick(jar_xm_channel_context_t*, jar_xm_envelope_t*, uint16_t*, float*);
static void jar_xm_envelopes(jar_xm_channel_context_t*);
static float jar_xm_linear_period(float);
static float jar_xm_linear_frequency(float);
static float jar_xm_amiga_period(float);
static float jar_xm_amiga_frequency(float);
static float jar_xm_period(jar_xm_context_t*, float);
static float jar_xm_frequency(jar_xm_context_t*, float, float);
static void jar_xm_update_frequency(jar_xm_context_t*, jar_xm_channel_context_t*);
static void jar_xm_handle_note_and_instrument(jar_xm_context_t*, jar_xm_channel_context_t*, jar_xm_pattern_slot_t*);
static void jar_xm_trigger_note(jar_xm_context_t*, jar_xm_channel_context_t*, unsigned int flags);
static void jar_xm_cut_note(jar_xm_channel_context_t*);
static void jar_xm_key_off(jar_xm_channel_context_t*);
static void jar_xm_post_pattern_change(jar_xm_context_t*);
static void jar_xm_row(jar_xm_context_t*);
static void jar_xm_tick(jar_xm_context_t*);
static float jar_xm_next_of_sample(jar_xm_channel_context_t*);
static void jar_xm_sample(jar_xm_context_t*, float*, float*);
/* ----- Other oddities ----- */
#define jar_xm_TRIGGER_KEEP_VOLUME (1 << 0)
#define jar_xm_TRIGGER_KEEP_PERIOD (1 << 1)
#define jar_xm_TRIGGER_KEEP_SAMPLE_POSITION (1 << 2)
static const uint16_t amiga_frequencies[] = {
1712, 1616, 1525, 1440, /* C-2, C#2, D-2, D#2 */
1357, 1281, 1209, 1141, /* E-2, F-2, F#2, G-2 */
1077, 1017, 961, 907, /* G#2, A-2, A#2, B-2 */
856, /* C-3 */
};
static const float multi_retrig_add[] = {
0.f, -1.f, -2.f, -4.f, /* 0, 1, 2, 3 */
-8.f, -16.f, 0.f, 0.f, /* 4, 5, 6, 7 */
0.f, 1.f, 2.f, 4.f, /* 8, 9, A, B */
8.f, 16.f, 0.f, 0.f /* C, D, E, F */
};
static const float multi_retrig_multiply[] = {
1.f, 1.f, 1.f, 1.f, /* 0, 1, 2, 3 */
1.f, 1.f, .6666667f, .5f, /* 4, 5, 6, 7 */
1.f, 1.f, 1.f, 1.f, /* 8, 9, A, B */
1.f, 1.f, 1.5f, 2.f /* C, D, E, F */
};
#define jar_xm_CLAMP_UP1F(vol, limit) do { \
if((vol) > (limit)) (vol) = (limit); \
} while(0)
#define jar_xm_CLAMP_UP(vol) jar_xm_CLAMP_UP1F((vol), 1.f)
#define jar_xm_CLAMP_DOWN1F(vol, limit) do { \
if((vol) < (limit)) (vol) = (limit); \
} while(0)
#define jar_xm_CLAMP_DOWN(vol) jar_xm_CLAMP_DOWN1F((vol), .0f)
#define jar_xm_CLAMP2F(vol, up, down) do { \
if((vol) > (up)) (vol) = (up); \
else if((vol) < (down)) (vol) = (down); \
} while(0)
#define jar_xm_CLAMP(vol) jar_xm_CLAMP2F((vol), 1.f, .0f)
#define jar_xm_SLIDE_TOWARDS(val, goal, incr) do { \
if((val) > (goal)) { \
(val) -= (incr); \
jar_xm_CLAMP_DOWN1F((val), (goal)); \
} else if((val) < (goal)) { \
(val) += (incr); \
jar_xm_CLAMP_UP1F((val), (goal)); \
} \
} while(0)
#define jar_xm_LERP(u, v, t) ((u) + (t) * ((v) - (u)))
#define jar_xm_INVERSE_LERP(u, v, lerp) (((lerp) - (u)) / ((v) - (u)))
#define HAS_TONE_PORTAMENTO(s) ((s)->effect_type == 3 \
|| (s)->effect_type == 5 \
|| ((s)->volume_column >> 4) == 0xF)
#define HAS_ARPEGGIO(s) ((s)->effect_type == 0 \
&& (s)->effect_param != 0)
#define HAS_VIBRATO(s) ((s)->effect_type == 4 \
|| (s)->effect_param == 6 \
|| ((s)->volume_column >> 4) == 0xB)
#define NOTE_IS_VALID(n) ((n) > 0 && (n) < 97)
/* ----- Function definitions ----- */
static float jar_xm_waveform(jar_xm_waveform_type_t waveform, uint8_t step) {
static unsigned int next_rand = 24492;
step %= 0x40;
switch(waveform) {
case jar_xm_SINE_WAVEFORM:
/* Why not use a table? For saving space, and because there's
* very very little actual performance gain. */
return -sinf(2.f * 3.141592f * (float)step / (float)0x40);
case jar_xm_RAMP_DOWN_WAVEFORM:
/* Ramp down: 1.0f when step = 0; -1.0f when step = 0x40 */
return (float)(0x20 - step) / 0x20;
case jar_xm_SQUARE_WAVEFORM:
/* Square with a 50% duty */
return (step >= 0x20) ? 1.f : -1.f;
case jar_xm_RANDOM_WAVEFORM:
/* Use the POSIX.1-2001 example, just to be deterministic
* across different machines */
next_rand = next_rand * 1103515245 + 12345;
return (float)((next_rand >> 16) & 0x7FFF) / (float)0x4000 - 1.f;
case jar_xm_RAMP_UP_WAVEFORM:
/* Ramp up: -1.f when step = 0; 1.f when step = 0x40 */
return (float)(step - 0x20) / 0x20;
default:
break;
}
return .0f;
}
static void jar_xm_autovibrato(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) {
if(ch->instrument == NULL || ch->instrument->vibrato_depth == 0) return;
jar_xm_instrument_t* instr = ch->instrument;
float sweep = 1.f;
if(ch->autovibrato_ticks < instr->vibrato_sweep) {
/* No idea if this is correct, but it sounds close enough… */
sweep = jar_xm_LERP(0.f, 1.f, (float)ch->autovibrato_ticks / (float)instr->vibrato_sweep);
}
unsigned int step = ((ch->autovibrato_ticks++) * instr->vibrato_rate) >> 2;
ch->autovibrato_note_offset = .25f * jar_xm_waveform(instr->vibrato_type, step)
* (float)instr->vibrato_depth / (float)0xF * sweep;
jar_xm_update_frequency(ctx, ch);
}
static void jar_xm_vibrato(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t pos) {
unsigned int step = pos * (param >> 4);
ch->vibrato_note_offset =
2.f
* jar_xm_waveform(ch->vibrato_waveform, step)
* (float)(param & 0x0F) / (float)0xF;
jar_xm_update_frequency(ctx, ch);
}
static void jar_xm_tremolo(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t pos) {
unsigned int step = pos * (param >> 4);
/* Not so sure about this, it sounds correct by ear compared with
* MilkyTracker, but it could come from other bugs */
ch->tremolo_volume = -1.f * jar_xm_waveform(ch->tremolo_waveform, step)
* (float)(param & 0x0F) / (float)0xF;
}
static void jar_xm_arpeggio(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, uint8_t param, uint16_t tick) {
switch(tick % 3) {
case 0:
ch->arp_in_progress = false;
ch->arp_note_offset = 0;
break;
case 2:
ch->arp_in_progress = true;
ch->arp_note_offset = param >> 4;
break;
case 1:
ch->arp_in_progress = true;
ch->arp_note_offset = param & 0x0F;
break;
}
jar_xm_update_frequency(ctx, ch);
}
static void jar_xm_tone_portamento(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) {
/* 3xx called without a note, wait until we get an actual
* target note. */
if(ch->tone_portamento_target_period == 0.f) return;
if(ch->period != ch->tone_portamento_target_period) {
jar_xm_SLIDE_TOWARDS(ch->period,
ch->tone_portamento_target_period,
(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES ?
4.f : 1.f) * ch->tone_portamento_param
);
jar_xm_update_frequency(ctx, ch);
}
}
static void jar_xm_pitch_slide(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, float period_offset) {
/* Don't ask about the 4.f coefficient. I found mention of it
* nowhere. Found by ear™. */
if(ctx->module.frequency_type == jar_xm_LINEAR_FREQUENCIES) {
period_offset *= 4.f;
}
ch->period += period_offset;
jar_xm_CLAMP_DOWN(ch->period);
/* XXX: upper bound of period ? */
jar_xm_update_frequency(ctx, ch);
}
static void jar_xm_panning_slide(jar_xm_channel_context_t* ch, uint8_t rawval) {
float f;
if((rawval & 0xF0) && (rawval & 0x0F)) {
/* Illegal state */
return;
}
if(rawval & 0xF0) {
/* Slide right */
f = (float)(rawval >> 4) / (float)0xFF;
ch->panning += f;
jar_xm_CLAMP_UP(ch->panning);
} else {
/* Slide left */
f = (float)(rawval & 0x0F) / (float)0xFF;
ch->panning -= f;
jar_xm_CLAMP_DOWN(ch->panning);
}
}
static void jar_xm_volume_slide(jar_xm_channel_context_t* ch, uint8_t rawval) {
float f;
if((rawval & 0xF0) && (rawval & 0x0F)) {
/* Illegal state */
return;
}
if(rawval & 0xF0) {
/* Slide up */
f = (float)(rawval >> 4) / (float)0x40;
ch->volume += f;
jar_xm_CLAMP_UP(ch->volume);
} else {
/* Slide down */
f = (float)(rawval & 0x0F) / (float)0x40;
ch->volume -= f;
jar_xm_CLAMP_DOWN(ch->volume);
}
}
static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* a, jar_xm_envelope_point_t* b, uint16_t pos) {
/* Linear interpolation between two envelope points */
if(pos <= a->frame) return a->value;
else if(pos >= b->frame) return b->value;
else {
float p = (float)(pos - a->frame) / (float)(b->frame - a->frame);
return a->value * (1 - p) + b->value * p;
}
}
static void jar_xm_post_pattern_change(jar_xm_context_t* ctx) {
/* Loop if necessary */
if(ctx->current_table_index >= ctx->module.length) {
ctx->current_table_index = ctx->module.restart_position;
}
}
static float jar_xm_linear_period(float note) {
return 7680.f - note * 64.f;
}
static float jar_xm_linear_frequency(float period) {
return 8363.f * powf(2.f, (4608.f - period) / 768.f);
}
static float jar_xm_amiga_period(float note) {
unsigned int intnote = note;
uint8_t a = intnote % 12;
int8_t octave = note / 12.f - 2;
uint16_t p1 = amiga_frequencies[a], p2 = amiga_frequencies[a + 1];
if(octave > 0) {
p1 >>= octave;
p2 >>= octave;
} else if(octave < 0) {
p1 <<= (-octave);
p2 <<= (-octave);
}
return jar_xm_LERP(p1, p2, note - intnote);
}
static float jar_xm_amiga_frequency(float period) {
if(period == .0f) return .0f;
/* This is the PAL value. No reason to choose this one over the
* NTSC value. */
return 7093789.2f / (period * 2.f);
}
static float jar_xm_period(jar_xm_context_t* ctx, float note) {
switch(ctx->module.frequency_type) {
case jar_xm_LINEAR_FREQUENCIES:
return jar_xm_linear_period(note);
case jar_xm_AMIGA_FREQUENCIES:
return jar_xm_amiga_period(note);
}
return .0f;
}
static float jar_xm_frequency(jar_xm_context_t* ctx, float period, float note_offset) {
uint8_t a;
int8_t octave;
float note;
uint16_t p1, p2;
switch(ctx->module.frequency_type) {
case jar_xm_LINEAR_FREQUENCIES:
return jar_xm_linear_frequency(period - 64.f * note_offset);
case jar_xm_AMIGA_FREQUENCIES:
if(note_offset == 0) {
/* A chance to escape from insanity */
return jar_xm_amiga_frequency(period);
}
/* FIXME: this is very crappy at best */
a = octave = 0;
/* Find the octave of the current period */
if(period > amiga_frequencies[0]) {
--octave;
while(period > (amiga_frequencies[0] << (-octave))) --octave;
} else if(period < amiga_frequencies[12]) {
++octave;
while(period < (amiga_frequencies[12] >> octave)) ++octave;
}
/* Find the smallest note closest to the current period */
for(uint8_t i = 0; i < 12; ++i) {
p1 = amiga_frequencies[i], p2 = amiga_frequencies[i + 1];
if(octave > 0) {
p1 >>= octave;
p2 >>= octave;
} else if(octave < 0) {
p1 <<= (-octave);
p2 <<= (-octave);
}
if(p2 <= period && period <= p1) {
a = i;
break;
}
}
if(JAR_XM_DEBUG && (p1 < period || p2 > period)) {
DEBUG("%i <= %f <= %i should hold but doesn't, this is a bug", p2, period, p1);
}
note = 12.f * (octave + 2) + a + jar_xm_INVERSE_LERP(p1, p2, period);
return jar_xm_amiga_frequency(jar_xm_amiga_period(note + note_offset));
}
return .0f;
}
static void jar_xm_update_frequency(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch) {
ch->frequency = jar_xm_frequency(
ctx, ch->period,
(ch->arp_note_offset > 0 ? ch->arp_note_offset : (
ch->vibrato_note_offset + ch->autovibrato_note_offset
))
);
ch->step = ch->frequency / ctx->rate;
}
static void jar_xm_handle_note_and_instrument(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch,
jar_xm_pattern_slot_t* s) {
if(s->instrument > 0) {
if(HAS_TONE_PORTAMENTO(ch->current) && ch->instrument != NULL && ch->sample != NULL) {
/* Tone portamento in effect, unclear stuff happens */
jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_PERIOD | jar_xm_TRIGGER_KEEP_SAMPLE_POSITION);
} else if(s->instrument > ctx->module.num_instruments) {
/* Invalid instrument, Cut current note */
jar_xm_cut_note(ch);
ch->instrument = NULL;
ch->sample = NULL;
} else {
ch->instrument = ctx->module.instruments + (s->instrument - 1);
if(s->note == 0 && ch->sample != NULL) {
/* Ghost instrument, trigger note */
/* Sample position is kept, but envelopes are reset */
jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_SAMPLE_POSITION);
}
}
}
if(NOTE_IS_VALID(s->note)) {
/* Yes, the real note number is s->note -1. Try finding
* THAT in any of the specs! :-) */
jar_xm_instrument_t* instr = ch->instrument;
if(HAS_TONE_PORTAMENTO(ch->current) && instr != NULL && ch->sample != NULL) {
/* Tone portamento in effect */
ch->note = s->note + ch->sample->relative_note + ch->sample->finetune / 128.f - 1.f;
ch->tone_portamento_target_period = jar_xm_period(ctx, ch->note);
} else if(instr == NULL || ch->instrument->num_samples == 0) {
/* Bad instrument */
jar_xm_cut_note(ch);
} else {
if(instr->sample_of_notes[s->note - 1] < instr->num_samples) {
#if JAR_XM_RAMPING
for(unsigned int z = 0; z < jar_xm_SAMPLE_RAMPING_POINTS; ++z) {
ch->end_of_previous_sample[z] = jar_xm_next_of_sample(ch);
}
ch->frame_count = 0;
#endif
ch->sample = instr->samples + instr->sample_of_notes[s->note - 1];
ch->orig_note = ch->note = s->note + ch->sample->relative_note
+ ch->sample->finetune / 128.f - 1.f;
if(s->instrument > 0) {
jar_xm_trigger_note(ctx, ch, 0);
} else {
/* Ghost note: keep old volume */
jar_xm_trigger_note(ctx, ch, jar_xm_TRIGGER_KEEP_VOLUME);
}
} else {
/* Bad sample */
jar_xm_cut_note(ch);
}
}
} else if(s->note == 97) {
/* Key Off */
jar_xm_key_off(ch);
}
switch(s->volume_column >> 4) {
case 0x5:
if(s->volume_column > 0x50) break;
case 0x1:
case 0x2:
case 0x3:
case 0x4:
/* Set volume */
ch->volume = (float)(s->volume_column - 0x10) / (float)0x40;
break;
case 0x8: /* Fine volume slide down */
jar_xm_volume_slide(ch, s->volume_column & 0x0F);
break;
case 0x9: /* Fine volume slide up */
jar_xm_volume_slide(ch, s->volume_column << 4);
break;
case 0xA: /* Set vibrato speed */
ch->vibrato_param = (ch->vibrato_param & 0x0F) | ((s->volume_column & 0x0F) << 4);
break;
case 0xC: /* Set panning */
ch->panning = (float)(
((s->volume_column & 0x0F) << 4) | (s->volume_column & 0x0F)
) / (float)0xFF;
break;
case 0xF: /* Tone portamento */
if(s->volume_column & 0x0F) {
ch->tone_portamento_param = ((s->volume_column & 0x0F) << 4)
| (s->volume_column & 0x0F);
}
break;
default:
break;
}
switch(s->effect_type) {
case 1: /* 1xx: Portamento up */
if(s->effect_param > 0) {
ch->portamento_up_param = s->effect_param;
}
break;
case 2: /* 2xx: Portamento down */
if(s->effect_param > 0) {
ch->portamento_down_param = s->effect_param;
}
break;
case 3: /* 3xx: Tone portamento */
if(s->effect_param > 0) {
ch->tone_portamento_param = s->effect_param;
}
break;
case 4: /* 4xy: Vibrato */
if(s->effect_param & 0x0F) {
/* Set vibrato depth */
ch->vibrato_param = (ch->vibrato_param & 0xF0) | (s->effect_param & 0x0F);
}
if(s->effect_param >> 4) {
/* Set vibrato speed */
ch->vibrato_param = (s->effect_param & 0xF0) | (ch->vibrato_param & 0x0F);
}
break;
case 5: /* 5xy: Tone portamento + Volume slide */
if(s->effect_param > 0) {
ch->volume_slide_param = s->effect_param;
}
break;
case 6: /* 6xy: Vibrato + Volume slide */
if(s->effect_param > 0) {
ch->volume_slide_param = s->effect_param;
}
break;
case 7: /* 7xy: Tremolo */
if(s->effect_param & 0x0F) {
/* Set tremolo depth */
ch->tremolo_param = (ch->tremolo_param & 0xF0) | (s->effect_param & 0x0F);
}
if(s->effect_param >> 4) {
/* Set tremolo speed */
ch->tremolo_param = (s->effect_param & 0xF0) | (ch->tremolo_param & 0x0F);
}
break;
case 8: /* 8xx: Set panning */
ch->panning = (float)s->effect_param / (float)0xFF;
break;
case 9: /* 9xx: Sample offset */
if(ch->sample != NULL && NOTE_IS_VALID(s->note)) {
uint32_t final_offset = s->effect_param << (ch->sample->bits == 16 ? 7 : 8);
if(final_offset >= ch->sample->length) {
/* Pretend the sample dosen't loop and is done playing */
ch->sample_position = -1;
break;
}
ch->sample_position = final_offset;
}
break;
case 0xA: /* Axy: Volume slide */
if(s->effect_param > 0) {
ch->volume_slide_param = s->effect_param;
}
break;
case 0xB: /* Bxx: Position jump */
if(s->effect_param < ctx->module.length) {
ctx->position_jump = true;
ctx->jump_dest = s->effect_param;
}
break;
case 0xC: /* Cxx: Set volume */
ch->volume = (float)((s->effect_param > 0x40)
? 0x40 : s->effect_param) / (float)0x40;
break;
case 0xD: /* Dxx: Pattern break */
/* Jump after playing this line */
ctx->pattern_break = true;
ctx->jump_row = (s->effect_param >> 4) * 10 + (s->effect_param & 0x0F);
break;
case 0xE: /* EXy: Extended command */
switch(s->effect_param >> 4) {
case 1: /* E1y: Fine portamento up */
if(s->effect_param & 0x0F) {
ch->fine_portamento_up_param = s->effect_param & 0x0F;
}
jar_xm_pitch_slide(ctx, ch, -ch->fine_portamento_up_param);
break;
case 2: /* E2y: Fine portamento down */
if(s->effect_param & 0x0F) {
ch->fine_portamento_down_param = s->effect_param & 0x0F;
}
jar_xm_pitch_slide(ctx, ch, ch->fine_portamento_down_param);
break;
case 4: /* E4y: Set vibrato control */
ch->vibrato_waveform = s->effect_param & 3;
ch->vibrato_waveform_retrigger = !((s->effect_param >> 2) & 1);
break;
case 5: /* E5y: Set finetune */
if(NOTE_IS_VALID(ch->current->note) && ch->sample != NULL) {
ch->note = ch->current->note + ch->sample->relative_note +
(float)(((s->effect_param & 0x0F) - 8) << 4) / 128.f - 1.f;
ch->period = jar_xm_period(ctx, ch->note);
jar_xm_update_frequency(ctx, ch);
}
break;
case 6: /* E6y: Pattern loop */
if(s->effect_param & 0x0F) {
if((s->effect_param & 0x0F) == ch->pattern_loop_count) {
/* Loop is over */
ch->pattern_loop_count = 0;
break;
}
/* Jump to the beginning of the loop */
ch->pattern_loop_count++;
ctx->position_jump = true;
ctx->jump_row = ch->pattern_loop_origin;
ctx->jump_dest = ctx->current_table_index;
} else {
/* Set loop start point */
ch->pattern_loop_origin = ctx->current_row;
/* Replicate FT2 E60 bug */
ctx->jump_row = ch->pattern_loop_origin;
}
break;
case 7: /* E7y: Set tremolo control */
ch->tremolo_waveform = s->effect_param & 3;
ch->tremolo_waveform_retrigger = !((s->effect_param >> 2) & 1);
break;
case 0xA: /* EAy: Fine volume slide up */
if(s->effect_param & 0x0F) {
ch->fine_volume_slide_param = s->effect_param & 0x0F;
}
jar_xm_volume_slide(ch, ch->fine_volume_slide_param << 4);
break;
case 0xB: /* EBy: Fine volume slide down */
if(s->effect_param & 0x0F) {
ch->fine_volume_slide_param = s->effect_param & 0x0F;
}
jar_xm_volume_slide(ch, ch->fine_volume_slide_param);
break;
case 0xD: /* EDy: Note delay */
/* XXX: figure this out better. EDx triggers
* the note even when there no note and no
* instrument. But ED0 acts like like a ghost
* note, EDx (x ≠ 0) does not. */
if(s->note == 0 && s->instrument == 0) {
unsigned int flags = jar_xm_TRIGGER_KEEP_VOLUME;
if(ch->current->effect_param & 0x0F) {
ch->note = ch->orig_note;
jar_xm_trigger_note(ctx, ch, flags);
} else {
jar_xm_trigger_note(
ctx, ch,
flags
| jar_xm_TRIGGER_KEEP_PERIOD
| jar_xm_TRIGGER_KEEP_SAMPLE_POSITION
);
}
}
break;
case 0xE: /* EEy: Pattern delay */
ctx->extra_ticks = (ch->current->effect_param & 0x0F) * ctx->tempo;
break;
default:
break;
}
break;
case 0xF: /* Fxx: Set tempo/BPM */
if(s->effect_param > 0) {
if(s->effect_param <= 0x1F) {
ctx->tempo = s->effect_param;
} else {
ctx->bpm = s->effect_param;
}
}
break;
case 16: /* Gxx: Set global volume */
ctx->global_volume = (float)((s->effect_param > 0x40)
? 0x40 : s->effect_param) / (float)0x40;
break;
case 17: /* Hxy: Global volume slide */
if(s->effect_param > 0) {
ch->global_volume_slide_param = s->effect_param;
}
break;
case 21: /* Lxx: Set envelope position */
ch->volume_envelope_frame_count = s->effect_param;
ch->panning_envelope_frame_count = s->effect_param;
break;
case 25: /* Pxy: Panning slide */
if(s->effect_param > 0) {
ch->panning_slide_param = s->effect_param;
}
break;
case 27: /* Rxy: Multi retrig note */
if(s->effect_param > 0) {
if((s->effect_param >> 4) == 0) {
/* Keep previous x value */
ch->multi_retrig_param = (ch->multi_retrig_param & 0xF0) | (s->effect_param & 0x0F);
} else {
ch->multi_retrig_param = s->effect_param;
}
}
break;
case 29: /* Txy: Tremor */
if(s->effect_param > 0) {
/* Tremor x and y params do not appear to be separately
* kept in memory, unlike Rxy */
ch->tremor_param = s->effect_param;
}
break;
case 33: /* Xxy: Extra stuff */
switch(s->effect_param >> 4) {
case 1: /* X1y: Extra fine portamento up */
if(s->effect_param & 0x0F) {
ch->extra_fine_portamento_up_param = s->effect_param & 0x0F;
}
jar_xm_pitch_slide(ctx, ch, -1.0f * ch->extra_fine_portamento_up_param);
break;
case 2: /* X2y: Extra fine portamento down */
if(s->effect_param & 0x0F) {
ch->extra_fine_portamento_down_param = s->effect_param & 0x0F;
}
jar_xm_pitch_slide(ctx, ch, ch->extra_fine_portamento_down_param);
break;
default:
break;
}
break;
default:
break;
}
}
static void jar_xm_trigger_note(jar_xm_context_t* ctx, jar_xm_channel_context_t* ch, unsigned int flags) {
if(!(flags & jar_xm_TRIGGER_KEEP_SAMPLE_POSITION)) {
ch->sample_position = 0.f;
ch->ping = true;
}
if(ch->sample != NULL) {
if(!(flags & jar_xm_TRIGGER_KEEP_VOLUME)) {
ch->volume = ch->sample->volume;
}
ch->panning = ch->sample->panning;
}
ch->sustained = true;
ch->fadeout_volume = ch->volume_envelope_volume = 1.0f;
ch->panning_envelope_panning = .5f;
ch->volume_envelope_frame_count = ch->panning_envelope_frame_count = 0;
ch->vibrato_note_offset = 0.f;
ch->tremolo_volume = 0.f;
ch->tremor_on = false;
ch->autovibrato_ticks = 0;
if(ch->vibrato_waveform_retrigger) {
ch->vibrato_ticks = 0; /* XXX: should the waveform itself also
* be reset to sine? */
}
if(ch->tremolo_waveform_retrigger) {
ch->tremolo_ticks = 0;
}
if(!(flags & jar_xm_TRIGGER_KEEP_PERIOD)) {
ch->period = jar_xm_period(ctx, ch->note);
jar_xm_update_frequency(ctx, ch);
}
ch->latest_trigger = ctx->generated_samples;
if(ch->instrument != NULL) {
ch->instrument->latest_trigger = ctx->generated_samples;
}
if(ch->sample != NULL) {
ch->sample->latest_trigger = ctx->generated_samples;
}
}
static void jar_xm_cut_note(jar_xm_channel_context_t* ch) {
/* NB: this is not the same as Key Off */
ch->volume = .0f;
}
static void jar_xm_key_off(jar_xm_channel_context_t* ch) {
/* Key Off */
ch->sustained = false;
/* If no volume envelope is used, also cut the note */
if(ch->instrument == NULL || !ch->instrument->volume_envelope.enabled) {
jar_xm_cut_note(ch);
}
}
static void jar_xm_row(jar_xm_context_t* ctx) {
if(ctx->position_jump) {
ctx->current_table_index = ctx->jump_dest;
ctx->current_row = ctx->jump_row;
ctx->position_jump = false;
ctx->pattern_break = false;
ctx->jump_row = 0;
jar_xm_post_pattern_change(ctx);
} else if(ctx->pattern_break) {
ctx->current_table_index++;
ctx->current_row = ctx->jump_row;
ctx->pattern_break = false;
ctx->jump_row = 0;
jar_xm_post_pattern_change(ctx);
}
jar_xm_pattern_t* cur = ctx->module.patterns + ctx->module.pattern_table[ctx->current_table_index];
bool in_a_loop = false;
/* Read notes… */
for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
jar_xm_pattern_slot_t* s = cur->slots + ctx->current_row * ctx->module.num_channels + i;
jar_xm_channel_context_t* ch = ctx->channels + i;
ch->current = s;
if(s->effect_type != 0xE || s->effect_param >> 4 != 0xD) {
jar_xm_handle_note_and_instrument(ctx, ch, s);
} else {
ch->note_delay_param = s->effect_param & 0x0F;
}
if(!in_a_loop && ch->pattern_loop_count > 0) {
in_a_loop = true;
}
}
if(!in_a_loop) {
/* No E6y loop is in effect (or we are in the first pass) */
ctx->loop_count = (ctx->row_loop_count[MAX_NUM_ROWS * ctx->current_table_index + ctx->current_row]++);
}
ctx->current_row++; /* Since this is an uint8, this line can
* increment from 255 to 0, in which case it
* is still necessary to go the next
* pattern. */
if(!ctx->position_jump && !ctx->pattern_break &&
(ctx->current_row >= cur->num_rows || ctx->current_row == 0)) {
ctx->current_table_index++;
ctx->current_row = ctx->jump_row; /* This will be 0 most of
* the time, except when E60
* is used */
ctx->jump_row = 0;
jar_xm_post_pattern_change(ctx);
}
}
static void jar_xm_envelope_tick(jar_xm_channel_context_t* ch,
jar_xm_envelope_t* env,
uint16_t* counter,
float* outval) {
if(env->num_points < 2) {
/* Don't really know what to do… */
if(env->num_points == 1) {
/* XXX I am pulling this out of my ass */
*outval = (float)env->points[0].value / (float)0x40;
if(*outval > 1) {
*outval = 1;
}
}
return;
} else {
uint8_t j;
if(env->loop_enabled) {
uint16_t loop_start = env->points[env->loop_start_point].frame;
uint16_t loop_end = env->points[env->loop_end_point].frame;
uint16_t loop_length = loop_end - loop_start;
if(*counter >= loop_end) {
*counter -= loop_length;
}
}
for(j = 0; j < (env->num_points - 2); ++j) {
if(env->points[j].frame <= *counter &&
env->points[j+1].frame >= *counter) {
break;
}
}
*outval = jar_xm_envelope_lerp(env->points + j, env->points + j + 1, *counter) / (float)0x40;
/* Make sure it is safe to increment frame count */
if(!ch->sustained || !env->sustain_enabled ||
*counter != env->points[env->sustain_point].frame) {
(*counter)++;
}
}
}
static void jar_xm_envelopes(jar_xm_channel_context_t* ch) {
if(ch->instrument != NULL) {
if(ch->instrument->volume_envelope.enabled) {
if(!ch->sustained) {
ch->fadeout_volume -= (float)ch->instrument->volume_fadeout / 65536.f;
jar_xm_CLAMP_DOWN(ch->fadeout_volume);
}
jar_xm_envelope_tick(ch,
&(ch->instrument->volume_envelope),
&(ch->volume_envelope_frame_count),
&(ch->volume_envelope_volume));
}
if(ch->instrument->panning_envelope.enabled) {
jar_xm_envelope_tick(ch,
&(ch->instrument->panning_envelope),
&(ch->panning_envelope_frame_count),
&(ch->panning_envelope_panning));
}
}
}
static void jar_xm_tick(jar_xm_context_t* ctx) {
if(ctx->current_tick == 0) {
jar_xm_row(ctx);
}
for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
jar_xm_channel_context_t* ch = ctx->channels + i;
jar_xm_envelopes(ch);
jar_xm_autovibrato(ctx, ch);
if(ch->arp_in_progress && !HAS_ARPEGGIO(ch->current)) {
ch->arp_in_progress = false;
ch->arp_note_offset = 0;
jar_xm_update_frequency(ctx, ch);
}
if(ch->vibrato_in_progress && !HAS_VIBRATO(ch->current)) {
ch->vibrato_in_progress = false;
ch->vibrato_note_offset = 0.f;
jar_xm_update_frequency(ctx, ch);
}
switch(ch->current->volume_column >> 4) {
case 0x6: /* Volume slide down */
if(ctx->current_tick == 0) break;
jar_xm_volume_slide(ch, ch->current->volume_column & 0x0F);
break;
case 0x7: /* Volume slide up */
if(ctx->current_tick == 0) break;
jar_xm_volume_slide(ch, ch->current->volume_column << 4);
break;
case 0xB: /* Vibrato */
if(ctx->current_tick == 0) break;
ch->vibrato_in_progress = false;
jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++);
break;
case 0xD: /* Panning slide left */
if(ctx->current_tick == 0) break;
jar_xm_panning_slide(ch, ch->current->volume_column & 0x0F);
break;
case 0xE: /* Panning slide right */
if(ctx->current_tick == 0) break;
jar_xm_panning_slide(ch, ch->current->volume_column << 4);
break;
case 0xF: /* Tone portamento */
if(ctx->current_tick == 0) break;
jar_xm_tone_portamento(ctx, ch);
break;
default:
break;
}
switch(ch->current->effect_type) {
case 0: /* 0xy: Arpeggio */
if(ch->current->effect_param > 0) {
char arp_offset = ctx->tempo % 3;
switch(arp_offset) {
case 2: /* 0 -> x -> 0 -> y -> x -> … */
if(ctx->current_tick == 1) {
ch->arp_in_progress = true;
ch->arp_note_offset = ch->current->effect_param >> 4;
jar_xm_update_frequency(ctx, ch);
break;
}
/* No break here, this is intended */
case 1: /* 0 -> 0 -> y -> x -> … */
if(ctx->current_tick == 0) {
ch->arp_in_progress = false;
ch->arp_note_offset = 0;
jar_xm_update_frequency(ctx, ch);
break;
}
/* No break here, this is intended */
case 0: /* 0 -> y -> x -> … */
jar_xm_arpeggio(ctx, ch, ch->current->effect_param, ctx->current_tick - arp_offset);
default:
break;
}
}
break;
case 1: /* 1xx: Portamento up */
if(ctx->current_tick == 0) break;
jar_xm_pitch_slide(ctx, ch, -ch->portamento_up_param);
break;
case 2: /* 2xx: Portamento down */
if(ctx->current_tick == 0) break;
jar_xm_pitch_slide(ctx, ch, ch->portamento_down_param);
break;
case 3: /* 3xx: Tone portamento */
if(ctx->current_tick == 0) break;
jar_xm_tone_portamento(ctx, ch);
break;
case 4: /* 4xy: Vibrato */
if(ctx->current_tick == 0) break;
ch->vibrato_in_progress = true;
jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++);
break;
case 5: /* 5xy: Tone portamento + Volume slide */
if(ctx->current_tick == 0) break;
jar_xm_tone_portamento(ctx, ch);
jar_xm_volume_slide(ch, ch->volume_slide_param);
break;
case 6: /* 6xy: Vibrato + Volume slide */
if(ctx->current_tick == 0) break;
ch->vibrato_in_progress = true;
jar_xm_vibrato(ctx, ch, ch->vibrato_param, ch->vibrato_ticks++);
jar_xm_volume_slide(ch, ch->volume_slide_param);
break;
case 7: /* 7xy: Tremolo */
if(ctx->current_tick == 0) break;
jar_xm_tremolo(ctx, ch, ch->tremolo_param, ch->tremolo_ticks++);
break;
case 0xA: /* Axy: Volume slide */
if(ctx->current_tick == 0) break;
jar_xm_volume_slide(ch, ch->volume_slide_param);
break;
case 0xE: /* EXy: Extended command */
switch(ch->current->effect_param >> 4) {
case 0x9: /* E9y: Retrigger note */
if(ctx->current_tick != 0 && ch->current->effect_param & 0x0F) {
if(!(ctx->current_tick % (ch->current->effect_param & 0x0F))) {
jar_xm_trigger_note(ctx, ch, 0);
jar_xm_envelopes(ch);
}
}
break;
case 0xC: /* ECy: Note cut */
if((ch->current->effect_param & 0x0F) == ctx->current_tick) {
jar_xm_cut_note(ch);
}
break;
case 0xD: /* EDy: Note delay */
if(ch->note_delay_param == ctx->current_tick) {
jar_xm_handle_note_and_instrument(ctx, ch, ch->current);
jar_xm_envelopes(ch);
}
break;
default:
break;
}
break;
case 17: /* Hxy: Global volume slide */
if(ctx->current_tick == 0) break;
if((ch->global_volume_slide_param & 0xF0) &&
(ch->global_volume_slide_param & 0x0F)) {
/* Illegal state */
break;
}
if(ch->global_volume_slide_param & 0xF0) {
/* Global slide up */
float f = (float)(ch->global_volume_slide_param >> 4) / (float)0x40;
ctx->global_volume += f;
jar_xm_CLAMP_UP(ctx->global_volume);
} else {
/* Global slide down */
float f = (float)(ch->global_volume_slide_param & 0x0F) / (float)0x40;
ctx->global_volume -= f;
jar_xm_CLAMP_DOWN(ctx->global_volume);
}
break;
case 20: /* Kxx: Key off */
/* Most documentations will tell you the parameter has no
* use. Don't be fooled. */
if(ctx->current_tick == ch->current->effect_param) {
jar_xm_key_off(ch);
}
break;
case 25: /* Pxy: Panning slide */
if(ctx->current_tick == 0) break;
jar_xm_panning_slide(ch, ch->panning_slide_param);
break;
case 27: /* Rxy: Multi retrig note */
if(ctx->current_tick == 0) break;
if(((ch->multi_retrig_param) & 0x0F) == 0) break;
if((ctx->current_tick % (ch->multi_retrig_param & 0x0F)) == 0) {
float v = ch->volume * multi_retrig_multiply[ch->multi_retrig_param >> 4]
+ multi_retrig_add[ch->multi_retrig_param >> 4];
jar_xm_CLAMP(v);
jar_xm_trigger_note(ctx, ch, 0);
ch->volume = v;
}
break;
case 29: /* Txy: Tremor */
if(ctx->current_tick == 0) break;
ch->tremor_on = (
(ctx->current_tick - 1) % ((ch->tremor_param >> 4) + (ch->tremor_param & 0x0F) + 2)
>
(ch->tremor_param >> 4)
);
break;
default:
break;
}
float panning, volume;
panning = ch->panning +
(ch->panning_envelope_panning - .5f) * (.5f - fabsf(ch->panning - .5f)) * 2.0f;
if(ch->tremor_on) {
volume = .0f;
} else {
volume = ch->volume + ch->tremolo_volume;
jar_xm_CLAMP(volume);
volume *= ch->fadeout_volume * ch->volume_envelope_volume;
}
#if JAR_XM_RAMPING
ch->target_panning = panning;
ch->target_volume = volume;
#else
ch->actual_panning = panning;
ch->actual_volume = volume;
#endif
}
ctx->current_tick++;
if(ctx->current_tick >= ctx->tempo + ctx->extra_ticks) {
ctx->current_tick = 0;
ctx->extra_ticks = 0;
}
/* FT2 manual says number of ticks / second = BPM * 0.4 */
ctx->remaining_samples_in_tick += (float)ctx->rate / ((float)ctx->bpm * 0.4f);
}
static float jar_xm_next_of_sample(jar_xm_channel_context_t* ch) {
if(ch->instrument == NULL || ch->sample == NULL || ch->sample_position < 0) {
#if JAR_XM_RAMPING
if(ch->frame_count < jar_xm_SAMPLE_RAMPING_POINTS) {
return jar_xm_LERP(ch->end_of_previous_sample[ch->frame_count], .0f,
(float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS);
}
#endif
return .0f;
}
if(ch->sample->length == 0) {
return .0f;
}
float u, v, t;
uint32_t a, b;
a = (uint32_t)ch->sample_position; /* This cast is fine,
* sample_position will not
* go above integer
* ranges */
if(JAR_XM_LINEAR_INTERPOLATION) {
b = a + 1;
t = ch->sample_position - a; /* Cheaper than fmodf(., 1.f) */
}
u = ch->sample->data[a];
switch(ch->sample->loop_type) {
case jar_xm_NO_LOOP:
if(JAR_XM_LINEAR_INTERPOLATION) {
v = (b < ch->sample->length) ? ch->sample->data[b] : .0f;
}
ch->sample_position += ch->step;
if(ch->sample_position >= ch->sample->length) {
ch->sample_position = -1;
}
break;
case jar_xm_FORWARD_LOOP:
if(JAR_XM_LINEAR_INTERPOLATION) {
v = ch->sample->data[
(b == ch->sample->loop_end) ? ch->sample->loop_start : b
];
}
ch->sample_position += ch->step;
while(ch->sample_position >= ch->sample->loop_end) {
ch->sample_position -= ch->sample->loop_length;
}
break;
case jar_xm_PING_PONG_LOOP:
if(ch->ping) {
ch->sample_position += ch->step;
} else {
ch->sample_position -= ch->step;
}
/* XXX: this may not work for very tight ping-pong loops
* (ie switches direction more than once per sample */
if(ch->ping) {
if(JAR_XM_LINEAR_INTERPOLATION) {
v = (b >= ch->sample->loop_end) ? ch->sample->data[a] : ch->sample->data[b];
}
if(ch->sample_position >= ch->sample->loop_end) {
ch->ping = false;
ch->sample_position = (ch->sample->loop_end << 1) - ch->sample_position;
}
/* sanity checking */
if(ch->sample_position >= ch->sample->length) {
ch->ping = false;
ch->sample_position -= ch->sample->length - 1;
}
} else {
if(JAR_XM_LINEAR_INTERPOLATION) {
v = u;
u = (b == 1 || b - 2 <= ch->sample->loop_start) ? ch->sample->data[a] : ch->sample->data[b - 2];
}
if(ch->sample_position <= ch->sample->loop_start) {
ch->ping = true;
ch->sample_position = (ch->sample->loop_start << 1) - ch->sample_position;
}
/* sanity checking */
if(ch->sample_position <= .0f) {
ch->ping = true;
ch->sample_position = .0f;
}
}
break;
default:
v = .0f;
break;
}
float endval = JAR_XM_LINEAR_INTERPOLATION ? jar_xm_LERP(u, v, t) : u;
#if JAR_XM_RAMPING
if(ch->frame_count < jar_xm_SAMPLE_RAMPING_POINTS) {
/* Smoothly transition between old and new sample. */
return jar_xm_LERP(ch->end_of_previous_sample[ch->frame_count], endval,
(float)ch->frame_count / (float)jar_xm_SAMPLE_RAMPING_POINTS);
}
#endif
return endval;
}
static void jar_xm_sample(jar_xm_context_t* ctx, float* left, float* right) {
if(ctx->remaining_samples_in_tick <= 0) {
jar_xm_tick(ctx);
}
ctx->remaining_samples_in_tick--;
*left = 0.f;
*right = 0.f;
if(ctx->max_loop_count > 0 && ctx->loop_count >= ctx->max_loop_count) {
return;
}
for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
jar_xm_channel_context_t* ch = ctx->channels + i;
if(ch->instrument == NULL || ch->sample == NULL || ch->sample_position < 0) {
continue;
}
const float fval = jar_xm_next_of_sample(ch);
if(!ch->muted && !ch->instrument->muted) {
*left += fval * ch->actual_volume * (1.f - ch->actual_panning);
*right += fval * ch->actual_volume * ch->actual_panning;
}
#if JAR_XM_RAMPING
ch->frame_count++;
jar_xm_SLIDE_TOWARDS(ch->actual_volume, ch->target_volume, ctx->volume_ramp);
jar_xm_SLIDE_TOWARDS(ch->actual_panning, ch->target_panning, ctx->panning_ramp);
#endif
}
const float fgvol = ctx->global_volume * ctx->amplification;
*left *= fgvol;
*right *= fgvol;
#if JAR_XM_DEBUG
if(fabs(*left) > 1 || fabs(*right) > 1) {
DEBUG("clipping frame: %f %f, this is a bad module or a libxm bug", *left, *right);
}
#endif
}
void jar_xm_generate_samples(jar_xm_context_t* ctx, float* output, size_t numsamples) {
if(ctx && output) {
ctx->generated_samples += numsamples;
for(size_t i = 0; i < numsamples; i++) {
jar_xm_sample(ctx, output + (2 * i), output + (2 * i + 1));
}
}
}
uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx)
{
uint64_t total = 0;
uint8_t currentLoopCount = jar_xm_get_loop_count(ctx);
jar_xm_set_max_loop_count(ctx, 0);
while(jar_xm_get_loop_count(ctx) == currentLoopCount)
{
total += ctx->remaining_samples_in_tick;
ctx->remaining_samples_in_tick = 0;
jar_xm_tick(ctx);
}
ctx->loop_count = currentLoopCount;
return total;
}
//--------------------------------------------
//FILE LOADER - TODO - NEEDS TO BE CLEANED UP
//--------------------------------------------
#undef DEBUG
#define DEBUG(...) do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stderr); \
} while(0)
#define DEBUG_ERR(...) do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stderr); \
} while(0)
#define FATAL(...) do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stderr); \
exit(1); \
} while(0)
#define FATAL_ERR(...) do { \
fprintf(stderr, __VA_ARGS__); \
fflush(stderr); \
exit(1); \
} while(0)
int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const char* filename) {
FILE* xmf;
int size;
#if defined(_MSC_VER) && _MSC_VER >= 1500
xmf = NULL;
fopen_s(&xmf, filename, "rb");
#else
xmf = fopen(filename, "rb");
#endif
if(xmf == NULL) {
DEBUG_ERR("Could not open input file");
*ctx = NULL;
return 3;
}
fseek(xmf, 0, SEEK_END);
size = ftell(xmf);
rewind(xmf);
if(size == -1) {
fclose(xmf);
DEBUG_ERR("fseek() failed");
*ctx = NULL;
return 4;
}
char* data = malloc(size + 1);
if(fread(data, 1, size, xmf) < size) {
fclose(xmf);
DEBUG_ERR("fread() failed");
*ctx = NULL;
return 5;
}
fclose(xmf);
switch(jar_xm_create_context_safe(ctx, data, size, rate)) {
case 0:
break;
case 1:
DEBUG("could not create context: module is not sane\n");
*ctx = NULL;
return 1;
break;
case 2:
FATAL("could not create context: malloc failed\n");
return 2;
break;
default:
FATAL("could not create context: unknown error\n");
return 6;
break;
}
return 0;
}
#endif//end of JAR_XM_IMPLEMENTATION
//-------------------------------------------------------------------------------
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment