Syntrax/Jaytrax: Replace existing reverse engineered implementation with a different one that handles more songs correctly
parent
44b813d2bf
commit
caf4855b4e
|
@ -1,400 +0,0 @@
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "file.h"
|
|
||||||
|
|
||||||
size_t filesize;
|
|
||||||
|
|
||||||
Song* File_loadSong(const char *path)
|
|
||||||
{
|
|
||||||
Song *synSong;
|
|
||||||
FILE *f;
|
|
||||||
uint8_t *buffer;
|
|
||||||
size_t size;
|
|
||||||
|
|
||||||
if (!(f = fopen(path, "rb"))) return NULL;
|
|
||||||
|
|
||||||
fseek(f, 0, SEEK_END);
|
|
||||||
size = ftell(f);
|
|
||||||
fseek(f, 0, SEEK_SET);
|
|
||||||
|
|
||||||
if (!(buffer = (uint8_t *) malloc(size))) {
|
|
||||||
fclose(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fread(buffer, 1, size, f) != size) {
|
|
||||||
free(buffer);
|
|
||||||
fclose(f);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
synSong = File_loadSongMem(buffer, size);
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
|
|
||||||
return synSong;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t get_le16(const void *p)
|
|
||||||
{
|
|
||||||
return (((const uint8_t*)p)[0]) +
|
|
||||||
(((const uint8_t*)p)[1]) * 0x100;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t get_le32(const void *p)
|
|
||||||
{
|
|
||||||
return (((const uint8_t*)p)[0]) +
|
|
||||||
(((const uint8_t*)p)[1]) * 0x100 +
|
|
||||||
(((const uint8_t*)p)[2]) * 0x10000 +
|
|
||||||
(((const uint8_t*)p)[3]) * 0x1000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long File_readHeader(SongHeader *h, const uint8_t *buffer, size_t size)
|
|
||||||
{
|
|
||||||
if (size < 52) return -1;
|
|
||||||
|
|
||||||
h->version = get_le16(buffer);
|
|
||||||
h->UNK00 = get_le16(buffer + 2);
|
|
||||||
h->patNum = get_le32(buffer + 4);
|
|
||||||
h->subsongNum = get_le32(buffer + 8);
|
|
||||||
h->instrNum = get_le32(buffer + 12);
|
|
||||||
h->UNK01 = get_le32(buffer + 16);
|
|
||||||
h->UNK02 = get_le16(buffer + 20);
|
|
||||||
h->UNK03 = get_le16(buffer + 22);
|
|
||||||
h->UNK04 = get_le16(buffer + 24);
|
|
||||||
h->UNK05 = get_le16(buffer + 26);
|
|
||||||
h->UNK06 = get_le16(buffer + 28);
|
|
||||||
h->UNK07 = get_le16(buffer + 30);
|
|
||||||
h->UNK08 = get_le16(buffer + 32);
|
|
||||||
h->UNK09 = get_le16(buffer + 34);
|
|
||||||
h->UNK0A = get_le16(buffer + 36);
|
|
||||||
h->UNK0B = get_le16(buffer + 38);
|
|
||||||
h->UNK0C = get_le16(buffer + 40);
|
|
||||||
h->UNK0D = get_le16(buffer + 42);
|
|
||||||
h->UNK0E = get_le16(buffer + 44);
|
|
||||||
h->UNK0F = get_le16(buffer + 46);
|
|
||||||
h->UNK10 = get_le16(buffer + 48);
|
|
||||||
h->UNK11 = get_le16(buffer + 50);
|
|
||||||
|
|
||||||
return 52;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long File_readSubSong(Subsong *subSong, const uint8_t *buffer, size_t size)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
if (size < 16564) return -1;
|
|
||||||
|
|
||||||
for (i = 0; i < 16; i++)
|
|
||||||
subSong->UNK00[i] = get_le32(buffer + i * 4);
|
|
||||||
|
|
||||||
for (i = 0; i < SE_MAXCHANS; i++)
|
|
||||||
subSong->mutedChans[i] = buffer[64 + i];
|
|
||||||
|
|
||||||
subSong->tempo = get_le32(buffer + 80);
|
|
||||||
subSong->groove = get_le32(buffer + 84);
|
|
||||||
subSong->startPosCoarse = get_le32(buffer + 88);
|
|
||||||
subSong->startPosFine = get_le32(buffer + 92);
|
|
||||||
subSong->endPosCoarse = get_le32(buffer + 96);
|
|
||||||
subSong->endPosFine = get_le32(buffer + 100);
|
|
||||||
subSong->loopPosCoarse = get_le32(buffer + 104);
|
|
||||||
subSong->loopPosFine = get_le32(buffer + 108);
|
|
||||||
subSong->isLooping = get_le16(buffer + 112);
|
|
||||||
|
|
||||||
memcpy(subSong->m_Name, buffer + 114, 32);
|
|
||||||
subSong->m_Name[32] = '\0';
|
|
||||||
|
|
||||||
subSong->channelNumber = get_le16(buffer + 146);
|
|
||||||
subSong->delayTime = get_le16(buffer + 148);
|
|
||||||
|
|
||||||
for (i = 0; i < SE_MAXCHANS; i++)
|
|
||||||
subSong->chanDelayAmt[i] = buffer[150 + i];
|
|
||||||
|
|
||||||
subSong->amplification = get_le16(buffer + 166);
|
|
||||||
|
|
||||||
subSong->UNK01 = get_le16(buffer + 168);
|
|
||||||
subSong->UNK02 = get_le16(buffer + 170);
|
|
||||||
subSong->UNK03 = get_le16(buffer + 172);
|
|
||||||
subSong->UNK04 = get_le16(buffer + 174);
|
|
||||||
subSong->UNK05 = get_le16(buffer + 176);
|
|
||||||
subSong->UNK06 = get_le16(buffer + 178);
|
|
||||||
|
|
||||||
for (i = 0; i < SE_MAXCHANS; i++) {
|
|
||||||
for (j = 0; j < 0x100; j++) {
|
|
||||||
subSong->orders[i][j].patIndex = get_le16(buffer + 180 + i * 1024 + j * 4);
|
|
||||||
subSong->orders[i][j].patLen = get_le16(buffer + 180 + i * 1024 + j * 4 + 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 16564;
|
|
||||||
}
|
|
||||||
|
|
||||||
long File_readRow(Row *r, const uint8_t *buffer, size_t size)
|
|
||||||
{
|
|
||||||
if (size < 5) return -1;
|
|
||||||
|
|
||||||
r->note = buffer[0];
|
|
||||||
r->dest = buffer[1];
|
|
||||||
r->instr = buffer[2];
|
|
||||||
r->spd = buffer[3];
|
|
||||||
r->command = buffer[4];
|
|
||||||
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long File_readInstrumentEffect(InstrumentEffect *effect, const uint8_t *buffer, size_t size)
|
|
||||||
{
|
|
||||||
if (size < 40) return -1;
|
|
||||||
|
|
||||||
effect->destWave = get_le32(buffer);
|
|
||||||
effect->srcWave1 = get_le32(buffer + 4);
|
|
||||||
effect->srcWave2 = get_le32(buffer + 8);
|
|
||||||
effect->oscWave = get_le32(buffer + 12);
|
|
||||||
effect->variable1 = get_le32(buffer + 16);
|
|
||||||
effect->variable2 = get_le32(buffer + 20);
|
|
||||||
effect->fxSpeed = get_le32(buffer + 24);
|
|
||||||
effect->oscSpeed = get_le32(buffer + 28);
|
|
||||||
effect->effectType = get_le32(buffer + 32);
|
|
||||||
effect->oscSelect = buffer[36];
|
|
||||||
effect->resetEffect = buffer[37];
|
|
||||||
effect->UNK00 = get_le16(buffer + 38);
|
|
||||||
|
|
||||||
return 40;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long File_readInstrument(Instrument *instr, const uint8_t *buffer, size_t size)
|
|
||||||
{
|
|
||||||
int i, j;
|
|
||||||
long sizeRead;
|
|
||||||
|
|
||||||
if (size < 8712) return -1;
|
|
||||||
|
|
||||||
instr->version = get_le16(buffer);
|
|
||||||
|
|
||||||
memcpy(instr->name, buffer + 2, 32);
|
|
||||||
instr->name[32] = '\0';
|
|
||||||
|
|
||||||
instr->waveform = get_le16(buffer + 34);
|
|
||||||
instr->wavelength = get_le16(buffer + 36);
|
|
||||||
instr->masterVolume = get_le16(buffer + 38);
|
|
||||||
instr->amWave = get_le16(buffer + 40);
|
|
||||||
instr->amSpeed = get_le16(buffer + 42);
|
|
||||||
instr->amLoopPoint = get_le16(buffer + 44);
|
|
||||||
instr->finetune = get_le16(buffer + 46);
|
|
||||||
instr->fmWave = get_le16(buffer + 48);
|
|
||||||
instr->fmSpeed = get_le16(buffer + 50);
|
|
||||||
instr->fmLoopPoint = get_le16(buffer + 52);
|
|
||||||
instr->fmDelay = get_le16(buffer + 54);
|
|
||||||
instr->arpIndex = get_le16(buffer + 56);
|
|
||||||
|
|
||||||
for (i = 0; i < SE_MAXCHANS; i++)
|
|
||||||
instr->m_ResetWave[i] = buffer[58 + i];
|
|
||||||
|
|
||||||
instr->panWave = get_le16(buffer + 74);
|
|
||||||
instr->panSpeed = get_le16(buffer + 76);
|
|
||||||
instr->panLoopPoint = get_le16(buffer + 78);
|
|
||||||
instr->UNK00 = get_le16(buffer + 80);
|
|
||||||
instr->UNK01 = get_le16(buffer + 82);
|
|
||||||
instr->UNK02 = get_le16(buffer + 84);
|
|
||||||
instr->UNK03 = get_le16(buffer + 86);
|
|
||||||
instr->UNK04 = get_le16(buffer + 88);
|
|
||||||
instr->UNK05 = get_le16(buffer + 90);
|
|
||||||
|
|
||||||
buffer += 92; size -= 92;
|
|
||||||
|
|
||||||
for (i = 0; i < 4; i++) {
|
|
||||||
sizeRead = File_readInstrumentEffect(&instr->effects[i], buffer, size);
|
|
||||||
if (sizeRead < 0) return -1;
|
|
||||||
buffer += sizeRead; size -= sizeRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(instr->smpFullImportPath, buffer, 192);
|
|
||||||
instr->smpFullImportPath[192] = '\0';
|
|
||||||
|
|
||||||
buffer += 192; size -= 192;
|
|
||||||
|
|
||||||
instr->UNK06 = get_le32(buffer);
|
|
||||||
instr->UNK07 = get_le32(buffer + 4);
|
|
||||||
instr->UNK08 = get_le32(buffer + 8);
|
|
||||||
instr->UNK09 = get_le32(buffer + 12);
|
|
||||||
instr->UNK0A = get_le32(buffer + 16);
|
|
||||||
instr->UNK0B = get_le32(buffer + 20);
|
|
||||||
instr->UNK0C = get_le32(buffer + 24);
|
|
||||||
instr->UNK0D = get_le32(buffer + 28);
|
|
||||||
instr->UNK0E = get_le32(buffer + 32);
|
|
||||||
instr->UNK0F = get_le32(buffer + 36);
|
|
||||||
instr->UNK10 = get_le32(buffer + 40);
|
|
||||||
instr->UNK11 = get_le32(buffer + 44);
|
|
||||||
instr->UNK12 = get_le16(buffer + 48);
|
|
||||||
|
|
||||||
buffer += 50; size -= 50;
|
|
||||||
|
|
||||||
instr->shareSmpDataFromInstr = get_le16(buffer);
|
|
||||||
instr->hasLoop = get_le16(buffer + 2);
|
|
||||||
instr->hasBidiLoop = get_le16(buffer + 4);
|
|
||||||
|
|
||||||
buffer += 6; size -= 6;
|
|
||||||
|
|
||||||
instr->smpStartPoint = get_le32(buffer);
|
|
||||||
instr->smpLoopPoint = get_le32(buffer + 4);
|
|
||||||
instr->smpEndPoint = get_le32(buffer + 8);
|
|
||||||
instr->hasSample = get_le32(buffer + 12);
|
|
||||||
instr->smpLength = get_le32(buffer + 16);
|
|
||||||
|
|
||||||
buffer += 20; size -= 20;
|
|
||||||
|
|
||||||
for (i = 0; i < SE_MAXCHANS; i++) {
|
|
||||||
for (j = 0; j < 0x100; j++) {
|
|
||||||
instr->synthBuffers[i][j] = get_le16(buffer + i * 512 + j * 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 8712;
|
|
||||||
}
|
|
||||||
|
|
||||||
Song* File_loadSongMem(const uint8_t *buffer, size_t size)
|
|
||||||
{
|
|
||||||
int i, j/*, k*/;
|
|
||||||
int songVer;
|
|
||||||
/*Subsong *subs;
|
|
||||||
Order *orderCol;
|
|
||||||
Order *order;
|
|
||||||
Row *row;*/
|
|
||||||
Instrument *instr;
|
|
||||||
Song *synSong;
|
|
||||||
long sizeRead;
|
|
||||||
|
|
||||||
synSong = (Song *) calloc(1, sizeof(Song));
|
|
||||||
if (!synSong) return NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
//unused vars
|
|
||||||
int8_t _local5[] = [0, 0, 0, 0, 0, 0];
|
|
||||||
bool _local2 = false;
|
|
||||||
int _local7 = 0;
|
|
||||||
bool _local8 = true;
|
|
||||||
*/
|
|
||||||
|
|
||||||
sizeRead = File_readHeader(&synSong->h, buffer, size);
|
|
||||||
if (sizeRead < 0) goto FAIL;
|
|
||||||
buffer += sizeRead; size -= sizeRead;
|
|
||||||
|
|
||||||
songVer = synSong->h.version;
|
|
||||||
if ((songVer >= 3456) && (songVer <= 3457)){
|
|
||||||
if (synSong->h.subsongNum > 0){
|
|
||||||
synSong->subsongs = (Subsong *) malloc(synSong->h.subsongNum *sizeof(Subsong));
|
|
||||||
if (!synSong->subsongs) goto FAIL;
|
|
||||||
|
|
||||||
for (i = 0; i < synSong->h.subsongNum; i++) {
|
|
||||||
sizeRead = File_readSubSong(synSong->subsongs + i, buffer, size);
|
|
||||||
if (sizeRead < 0) goto FAIL;
|
|
||||||
buffer += sizeRead; size -= sizeRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
synSong->rows = (Row *) malloc(synSong->h.patNum * 64 *sizeof(Row));
|
|
||||||
if (!synSong->rows) goto FAIL;
|
|
||||||
|
|
||||||
for (i = 0, j = synSong->h.patNum * 64; i < j; i++) {
|
|
||||||
sizeRead = File_readRow(synSong->rows + i, buffer, size);
|
|
||||||
if (sizeRead < 0) goto FAIL;
|
|
||||||
buffer += sizeRead; size -= sizeRead;
|
|
||||||
}
|
|
||||||
|
|
||||||
synSong->patNameSizes = (uint32_t *) malloc(synSong->h.patNum * sizeof(uint32_t));
|
|
||||||
if (!synSong->patNameSizes) goto FAIL;
|
|
||||||
synSong->patternNames = calloc(sizeof(char *), synSong->h.patNum);
|
|
||||||
if (!synSong->patternNames) goto FAIL;
|
|
||||||
|
|
||||||
for (i = 0; i < synSong->h.patNum; i++) {
|
|
||||||
if (size < 4) goto FAIL;
|
|
||||||
j = synSong->patNameSizes[i] = get_le32(buffer);
|
|
||||||
buffer += 4; size -= 4;
|
|
||||||
|
|
||||||
if (size < j) goto FAIL;
|
|
||||||
|
|
||||||
synSong->patternNames[i] = malloc(j + sizeof(char));
|
|
||||||
if (!synSong->patternNames[i]) goto FAIL;
|
|
||||||
|
|
||||||
memcpy(synSong->patternNames[i], buffer, j);
|
|
||||||
synSong->patternNames[i][j] = '\0';
|
|
||||||
|
|
||||||
buffer += j; size -= j;
|
|
||||||
}
|
|
||||||
|
|
||||||
synSong->instruments = malloc(synSong->h.instrNum * sizeof(Instrument));
|
|
||||||
if (!synSong->instruments) goto FAIL;
|
|
||||||
synSong->samples = calloc(sizeof(int16_t *), synSong->h.instrNum);
|
|
||||||
if (!synSong->samples) goto FAIL;
|
|
||||||
|
|
||||||
for (i = 0; i < synSong->h.instrNum; i++) {
|
|
||||||
instr = &synSong->instruments[i];
|
|
||||||
sizeRead = File_readInstrument(instr, buffer, size);
|
|
||||||
if (sizeRead < 0) goto FAIL;
|
|
||||||
buffer += sizeRead; size -= sizeRead;
|
|
||||||
|
|
||||||
if (songVer == 3456){
|
|
||||||
instr->shareSmpDataFromInstr = 0;
|
|
||||||
instr->hasLoop = 0;
|
|
||||||
instr->hasBidiLoop = 0;
|
|
||||||
instr->smpStartPoint = 0;
|
|
||||||
instr->smpLoopPoint = 0;
|
|
||||||
instr->smpEndPoint = 0;
|
|
||||||
if (instr->hasSample){
|
|
||||||
instr->smpStartPoint = 0;
|
|
||||||
instr->smpEndPoint = (instr->smpLength / 2);
|
|
||||||
instr->smpLoopPoint = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (instr->hasSample){
|
|
||||||
//instr->smpLength is in bytes, I think
|
|
||||||
if (size < instr->smpLength) goto FAIL;
|
|
||||||
synSong->samples[i] = malloc(instr->smpLength);
|
|
||||||
if (!synSong->samples[i]) goto FAIL;
|
|
||||||
memcpy(synSong->samples[i], buffer, instr->smpLength);
|
|
||||||
buffer += instr->smpLength; size -= instr->smpLength;
|
|
||||||
} else {
|
|
||||||
synSong->samples[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
memcpy(synSong->arpTable, buffer, 0x100);
|
|
||||||
buffer += 0x100; size -= 0x100;
|
|
||||||
} else goto FAIL;
|
|
||||||
} else goto FAIL;
|
|
||||||
|
|
||||||
return synSong;
|
|
||||||
|
|
||||||
FAIL:
|
|
||||||
File_freeSong(synSong);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void File_freeSong(Song *synSong)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (synSong) {
|
|
||||||
if (synSong->subsongs) free(synSong->subsongs);
|
|
||||||
if (synSong->rows) free(synSong->rows);
|
|
||||||
if (synSong->patNameSizes) free(synSong->patNameSizes);
|
|
||||||
if (synSong->patternNames) {
|
|
||||||
for (i = 0; i < synSong->h.patNum; i++) {
|
|
||||||
if (synSong->patternNames[i]) free(synSong->patternNames[i]);
|
|
||||||
}
|
|
||||||
free(synSong->patternNames);
|
|
||||||
}
|
|
||||||
if (synSong->instruments) free(synSong->instruments);
|
|
||||||
if (synSong->samples) {
|
|
||||||
for (i = 0; i < synSong->h.instrNum; i++) {
|
|
||||||
if (synSong->samples[i]) free(synSong->samples[i]);
|
|
||||||
}
|
|
||||||
free(synSong->samples);
|
|
||||||
}
|
|
||||||
free(synSong);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
#ifndef FILE_H
|
|
||||||
#define FILE_H
|
|
||||||
|
|
||||||
#include <Syntrax_c/syntrax.h>
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Song* File_loadSong(const char *path);
|
|
||||||
Song* File_loadSongMem(const uint8_t *buffer, size_t size);
|
|
||||||
|
|
||||||
void File_freeSong(Song *synSong);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
enum loadErr {
|
||||||
|
ERR_OK,
|
||||||
|
ERR_MALLOC,
|
||||||
|
ERR_BADSONG,
|
||||||
|
ERR_FILEIO
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,264 @@
|
||||||
|
#ifndef JAYTRAX_H
|
||||||
|
#define JAYTRAX_H
|
||||||
|
|
||||||
|
#define WANTEDOVERLAP (15) //wanted declick overlap length(in samples). Will be smaller than a song tick.
|
||||||
|
#define MIXBUF_LEN (512) //temporary mixing buffer length
|
||||||
|
#define SAMPSPOOLSIZE (0xFFF) //buffer for unrolling samples
|
||||||
|
|
||||||
|
enum INTERP_LIST {
|
||||||
|
ITP_NONE,
|
||||||
|
ITP_NEAREST,
|
||||||
|
ITP_LINEAR,
|
||||||
|
ITP_QUADRATIC,
|
||||||
|
ITP_CUBIC,
|
||||||
|
//ITP_BLEP,
|
||||||
|
INTERP_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SE_BUFTYPE {
|
||||||
|
BUF_MAINL,
|
||||||
|
BUF_MAINR,
|
||||||
|
BUF_ECHOL,
|
||||||
|
BUF_ECHOR,
|
||||||
|
MIXBUF_NR
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SE_PLAYMODE {
|
||||||
|
SE_PM_SONG = 0,
|
||||||
|
SE_PM_PATTERN
|
||||||
|
};
|
||||||
|
|
||||||
|
//in case of changing any of the below
|
||||||
|
//please change jxs loader to account for changes
|
||||||
|
#define SE_ORDERS_SUBSONG (256)
|
||||||
|
#define SE_ROWS_PAT (64)
|
||||||
|
#define SE_EFF_INST (4)
|
||||||
|
#define SE_WAVES_INST (16)
|
||||||
|
#define SE_SAMPS_WAVE (256)
|
||||||
|
#define SE_ARPS_SONG (16)
|
||||||
|
#define SE_STEPS_ARP (16)
|
||||||
|
#define SE_NAMELEN (32)
|
||||||
|
|
||||||
|
#define SE_NROFCHANS (16) // number of chans replayer can take
|
||||||
|
#define SE_NROFFINETUNESTEPS (16) // number of finetune scales
|
||||||
|
#define SE_NROFEFFECTS (18) // number of available wave effects
|
||||||
|
|
||||||
|
typedef struct JT1Order JT1Order;
|
||||||
|
struct JT1Order {
|
||||||
|
int16_t patnr; // welk pattern spelen...
|
||||||
|
int16_t patlen; // 0/16/32/48
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct JT1Row JT1Row;
|
||||||
|
struct JT1Row {
|
||||||
|
uint8_t srcnote;
|
||||||
|
uint8_t dstnote;
|
||||||
|
uint8_t inst;
|
||||||
|
int8_t param;
|
||||||
|
uint8_t script;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct JT1Subsong JT1Subsong;
|
||||||
|
struct JT1Subsong {
|
||||||
|
uint8_t mute[SE_NROFCHANS]; // which channels are muted? (1=muted)
|
||||||
|
int32_t songspd; // delay tussen de pattern-stepjes
|
||||||
|
int32_t groove; // groove value... 0=nothing, 1 = swing, 2=shuffle
|
||||||
|
int32_t songpos; // waar start song? (welke maat?)
|
||||||
|
int32_t songstep; // welke patternpos offset? (1/64 van maat)
|
||||||
|
int32_t endpos; // waar stopt song? (welke maat?)
|
||||||
|
int32_t endstep; // welke patternpos offset? (1/64 van maat)
|
||||||
|
int32_t looppos; // waar looped song? (welke maat?)
|
||||||
|
int32_t loopstep; // welke patternpos offset? (1/64 van maat)
|
||||||
|
int16_t songloop; // if true, the song loops inbetween looppos and endpos
|
||||||
|
char name[SE_NAMELEN]; // name of subsong
|
||||||
|
int16_t nrofchans; //nr of channels used
|
||||||
|
uint16_t delaytime; // the delaytime (for the echo effect)
|
||||||
|
uint8_t delayamount[SE_NROFCHANS]; // amount per channel for the echo-effect
|
||||||
|
int16_t amplification; //extra amplification factor (20 to 1000)
|
||||||
|
JT1Order orders[SE_NROFCHANS][SE_ORDERS_SUBSONG];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct JT1Effect JT1Effect;
|
||||||
|
struct JT1Effect {
|
||||||
|
int32_t dsteffect;
|
||||||
|
int32_t srceffect1;
|
||||||
|
int32_t srceffect2;
|
||||||
|
int32_t osceffect;
|
||||||
|
int32_t effectvar1;
|
||||||
|
int32_t effectvar2;
|
||||||
|
int32_t effectspd;
|
||||||
|
int32_t oscspd;
|
||||||
|
int32_t effecttype;
|
||||||
|
int8_t oscflg;
|
||||||
|
int8_t reseteffect;
|
||||||
|
};
|
||||||
|
|
||||||
|
// inst is the structure which has the entire instrument definition.
|
||||||
|
typedef struct JT1Inst JT1Inst;
|
||||||
|
struct JT1Inst {
|
||||||
|
int16_t mugiversion;
|
||||||
|
char instname[SE_NAMELEN];
|
||||||
|
int16_t waveform;
|
||||||
|
int16_t wavelength;
|
||||||
|
int16_t mastervol;
|
||||||
|
int16_t amwave;
|
||||||
|
int16_t amspd;
|
||||||
|
int16_t amlooppoint;
|
||||||
|
int16_t finetune;
|
||||||
|
int16_t fmwave;
|
||||||
|
int16_t fmspd;
|
||||||
|
int16_t fmlooppoint;
|
||||||
|
int16_t fmdelay;
|
||||||
|
int16_t arpeggio;
|
||||||
|
int8_t resetwave[SE_WAVES_INST];
|
||||||
|
int16_t panwave;
|
||||||
|
int16_t panspd;
|
||||||
|
int16_t panlooppoint;
|
||||||
|
JT1Effect fx[SE_EFF_INST];
|
||||||
|
char samplename[SE_NAMELEN];
|
||||||
|
//ugly. Move samples into their own spot
|
||||||
|
int16_t sharing; // sample sharing! sharing contains instr nr of shared sanpledata (0=no sharing)
|
||||||
|
int16_t loopflg; //does the sample loop or play one/shot? (0=1shot)
|
||||||
|
int16_t bidirecflg; // does the sample loop birdirectional? (0=no)
|
||||||
|
int32_t startpoint;
|
||||||
|
int32_t looppoint;
|
||||||
|
int32_t endpoint;
|
||||||
|
uint8_t hasSampData; // pointer naar de sample (mag 0 zijn)
|
||||||
|
int32_t samplelength; // length of sample
|
||||||
|
int16_t waves[SE_WAVES_INST * SE_SAMPS_WAVE];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct JT1Song JT1Song;
|
||||||
|
struct JT1Song {
|
||||||
|
int16_t mugiversion;//version of mugician this song was saved with
|
||||||
|
int32_t nrofpats; //aantal patterns beschikbaar
|
||||||
|
int32_t nrofsongs; //aantal beschikbare subsongs
|
||||||
|
int32_t nrofinst; //aantal gebruikte instruments
|
||||||
|
|
||||||
|
JT1Subsong** subsongs;
|
||||||
|
JT1Row* patterns;
|
||||||
|
char** patNames;
|
||||||
|
JT1Inst** instruments;
|
||||||
|
uint8_t** samples;
|
||||||
|
int8_t arpTable[SE_ARPS_SONG * SE_STEPS_ARP];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------internal structs
|
||||||
|
|
||||||
|
// Chanfx is an internal structure which keeps track of the current effect parameters per active channel
|
||||||
|
typedef struct JT1VoiceEffect JT1VoiceEffect;
|
||||||
|
struct JT1VoiceEffect {
|
||||||
|
int fxcnt1;
|
||||||
|
int fxcnt2;
|
||||||
|
int osccnt;
|
||||||
|
double a0;
|
||||||
|
double b1;
|
||||||
|
double b2;
|
||||||
|
double y1;
|
||||||
|
double y2;
|
||||||
|
int Vhp;
|
||||||
|
int Vbp;
|
||||||
|
int Vlp;
|
||||||
|
};
|
||||||
|
|
||||||
|
// chandat is an internal structure which keeps track of the current instruemnts current variables per active channel
|
||||||
|
typedef struct JT1Voice JT1Voice;
|
||||||
|
struct JT1Voice {
|
||||||
|
int32_t songpos;
|
||||||
|
int32_t patpos;
|
||||||
|
int32_t instrument;
|
||||||
|
int32_t volcnt;
|
||||||
|
int32_t pancnt;
|
||||||
|
int32_t arpcnt;
|
||||||
|
int32_t curnote;
|
||||||
|
int32_t curfreq;
|
||||||
|
int32_t curvol;
|
||||||
|
int32_t curpan;
|
||||||
|
int32_t bendadd; // for the pitchbend
|
||||||
|
int32_t destfreq; // ...
|
||||||
|
int32_t bendspd; // ...
|
||||||
|
int32_t bendtonote;
|
||||||
|
int32_t freqcnt;
|
||||||
|
int32_t freqdel;
|
||||||
|
uint8_t* sampledata;
|
||||||
|
int32_t looppoint;
|
||||||
|
int32_t endpoint;
|
||||||
|
uint8_t loopflg;
|
||||||
|
uint8_t bidirecflg;
|
||||||
|
int32_t synthPos;
|
||||||
|
int32_t samplepos;
|
||||||
|
|
||||||
|
//immediate render vars
|
||||||
|
int16_t* wavePtr;
|
||||||
|
int32_t waveLength;
|
||||||
|
int32_t freqOffset;
|
||||||
|
int16_t gainMainL;
|
||||||
|
int16_t gainMainR;
|
||||||
|
int16_t gainEchoL;
|
||||||
|
int16_t gainEchoR;
|
||||||
|
|
||||||
|
JT1VoiceEffect fx[SE_WAVES_INST];
|
||||||
|
int16_t waves[SE_WAVES_INST * SE_SAMPS_WAVE];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct Interpolator Interpolator;
|
||||||
|
struct Interpolator {
|
||||||
|
uint8_t id;
|
||||||
|
int16_t numTaps;
|
||||||
|
int32_t (*fItp) (int16_t* buf, int32_t pos, int32_t sizeMask);
|
||||||
|
char name[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct JT1Player JT1Player;
|
||||||
|
struct JT1Player {
|
||||||
|
JT1Song* song;
|
||||||
|
JT1Subsong* subsong;
|
||||||
|
JT1Voice voices[SE_NROFCHANS];
|
||||||
|
int32_t subsongNr;
|
||||||
|
int16_t timeCnt; // Samplecounter which stores the njumber of samples before the next songparams are calculated (is reinited with timeSpd)
|
||||||
|
int16_t timeSpd; // Sample accurate counter which indicates every how many samples the song should progress 1 tick. Is dependant on rendering frequency and BPM
|
||||||
|
uint8_t playFlg; // 0 if playback is stopped, 1 if song is being played
|
||||||
|
uint8_t pauseFlg; // 0 if playback is not paused, 1 if playback is paused
|
||||||
|
int32_t playSpeed; // Actual delay in between notes
|
||||||
|
int32_t patternDelay; // Current delay in between notes (resets with playSpeed)
|
||||||
|
int32_t playPosition; // Current position in song (coarse)
|
||||||
|
int32_t playStep; // Current position in song (fine)
|
||||||
|
int32_t masterVolume; // Mastervolume of the replayer (256=max - 0=min)
|
||||||
|
int16_t leftDelayBuffer[65536]; // buffer to simulate an echo on the left stereo channel
|
||||||
|
int16_t rightDelayBuffer[65536]; // buffer to simulate an echo on the right stereo channel
|
||||||
|
int16_t overlapBuffer[WANTEDOVERLAP*2]; // Buffer which stores overlap between waveforms to avoid clicks
|
||||||
|
int16_t overlapCnt; // Used to store how much overlap we have already rendered
|
||||||
|
uint16_t delayCnt; // Internal counter used for delay
|
||||||
|
int32_t tempBuf[MIXBUF_LEN * MIXBUF_NR];
|
||||||
|
Interpolator* itp;
|
||||||
|
|
||||||
|
int32_t playMode; // in which mode is the replayer? Song or patternmode?
|
||||||
|
int32_t currentPattern; // Which pattern are we currently playing (In pattern play mode)
|
||||||
|
int32_t patternLength; // Current length of a pattern (in pattern play mode)
|
||||||
|
int32_t patternOffset; // Current play offset in the pattern (used for display)
|
||||||
|
|
||||||
|
int32_t loopCnt; // If song is meant to loop, the number of times the song has looped
|
||||||
|
};
|
||||||
|
|
||||||
|
//---------------------API
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int jaytrax_loadSong(JT1Player* SELF, JT1Song* sng);
|
||||||
|
void jaytrax_changeSubsong(JT1Player* SELF, int subsongnr);
|
||||||
|
void jaytrax_stopSong(JT1Player* SELF);
|
||||||
|
void jaytrax_pauseSong(JT1Player* SELF);
|
||||||
|
void jaytrax_continueSong(JT1Player* SELF);
|
||||||
|
void jaytrax_setInterpolation(JT1Player* SELF, uint8_t id);
|
||||||
|
JT1Player* jaytrax_init(void);
|
||||||
|
void jaytrax_free(JT1Player* SELF);
|
||||||
|
void jaytrax_renderChunk(JT1Player* SELF, int16_t* renderbuf, int32_t nrofsamples, int32_t frequency);
|
||||||
|
int32_t jaytrax_getLength(JT1Player* SELF, int subsongnr, int loopCnt, int frequency);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,359 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "jaytrax.h"
|
||||||
|
#include "jxs.h"
|
||||||
|
#include "ioutil.h"
|
||||||
|
|
||||||
|
struct memdata
|
||||||
|
{
|
||||||
|
const uint8_t* data;
|
||||||
|
size_t remain;
|
||||||
|
int error;
|
||||||
|
};
|
||||||
|
|
||||||
|
void memread(void* buf, size_t size, size_t count, void* _data)
|
||||||
|
{
|
||||||
|
struct memdata* data = (struct memdata*)_data;
|
||||||
|
size_t unread = 0;
|
||||||
|
size *= count;
|
||||||
|
if (size > data->remain)
|
||||||
|
{
|
||||||
|
unread = size - data->remain;
|
||||||
|
size = data->remain;
|
||||||
|
data->error = 1;
|
||||||
|
}
|
||||||
|
memcpy(buf, data->data, size);
|
||||||
|
data->data += size;
|
||||||
|
data->remain -= size;
|
||||||
|
if (unread)
|
||||||
|
memset(((uint8_t*)buf) + size, 0, unread);
|
||||||
|
}
|
||||||
|
|
||||||
|
int memerror(void* _data)
|
||||||
|
{
|
||||||
|
struct memdata* data = (struct memdata*)_data;
|
||||||
|
return data->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fileread(void* buf, size_t size, size_t count, void* _file)
|
||||||
|
{
|
||||||
|
FILE* file = (FILE*)_file;
|
||||||
|
fread(buf, size, count, file);
|
||||||
|
}
|
||||||
|
|
||||||
|
int fileerror(void* _file)
|
||||||
|
{
|
||||||
|
FILE* file = (FILE*)_file;
|
||||||
|
return ferror(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef void (*thereader)(void*, size_t, size_t, void*);
|
||||||
|
typedef int (*theerror)(void*);
|
||||||
|
|
||||||
|
//---------------------JXS3457
|
||||||
|
|
||||||
|
static int struct_readHeader(JT1Song* dest, thereader reader, theerror iferror, void* fin) {
|
||||||
|
|
||||||
|
f_JT1Header t;
|
||||||
|
|
||||||
|
reader(&t, sizeof(f_JT1Header), 1, fin);
|
||||||
|
dest->mugiversion = t.mugiversion;
|
||||||
|
dest->nrofpats = t.nrofpats;
|
||||||
|
dest->nrofsongs = t.nrofsongs;
|
||||||
|
dest->nrofinst = t.nrofinst;
|
||||||
|
return iferror(fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int struct_readSubsong(JT1Subsong* dest, size_t len, thereader reader, theerror iferror, void* fin) {
|
||||||
|
uint32_t i, j, k;
|
||||||
|
f_JT1Subsong t;
|
||||||
|
|
||||||
|
for (i=0; i < len; i++) {
|
||||||
|
reader(&t, sizeof(f_JT1Subsong), 1, fin);
|
||||||
|
for (j=0; j < J3457_CHANS_SUBSONG; j++) dest[i].mute[j] = t.mute[j];
|
||||||
|
dest[i].songspd = t.songspd;
|
||||||
|
dest[i].groove = t.groove;
|
||||||
|
dest[i].songpos = t.songpos;
|
||||||
|
dest[i].songstep = t.songstep;
|
||||||
|
dest[i].endpos = t.endpos;
|
||||||
|
dest[i].endstep = t.endstep;
|
||||||
|
dest[i].looppos = t.looppos;
|
||||||
|
dest[i].loopstep = t.loopstep;
|
||||||
|
dest[i].songloop = t.songloop;
|
||||||
|
memcpy(&dest[i].name, &t.name, 32);
|
||||||
|
dest[i].nrofchans = t.nrofchans;
|
||||||
|
dest[i].delaytime = t.delaytime;
|
||||||
|
for (j=0; j < J3457_CHANS_SUBSONG; j++) {
|
||||||
|
dest[i].delayamount[j] = t.delayamount[j];
|
||||||
|
}
|
||||||
|
dest[i].amplification = t.amplification;
|
||||||
|
for (j=0; j < J3457_CHANS_SUBSONG; j++) {
|
||||||
|
for (k=0; k < J3457_ORDERS_SUBSONG; k++) {
|
||||||
|
dest[i].orders[j][k].patnr = t.orders[j][k].patnr;
|
||||||
|
dest[i].orders[j][k].patlen = t.orders[j][k].patlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iferror(fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int struct_readPat(JT1Row* dest, size_t len, thereader reader, theerror iferror, void* fin) {
|
||||||
|
uint32_t i, j;
|
||||||
|
f_JT1Row t[J3457_ROWS_PAT];
|
||||||
|
|
||||||
|
for (i=0; i < len; i++) {
|
||||||
|
reader(&t, sizeof(f_JT1Row)*J3457_ROWS_PAT, 1, fin);
|
||||||
|
for (j=0; j < J3457_ROWS_PAT; j++) {
|
||||||
|
uint32_t off = i*J3457_ROWS_PAT + j;
|
||||||
|
dest[off].srcnote = t[j].srcnote;
|
||||||
|
dest[off].dstnote = t[j].dstnote;
|
||||||
|
dest[off].inst = t[j].inst;
|
||||||
|
dest[off].param = t[j].param;
|
||||||
|
dest[off].script = t[j].script;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return iferror(fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int struct_readInst(JT1Inst* dest, size_t len, thereader reader, theerror iferror, void* fin) {
|
||||||
|
uint32_t i, j;
|
||||||
|
f_JT1Inst t;
|
||||||
|
for (i=0; i < len; i++) {
|
||||||
|
reader(&t, sizeof(f_JT1Inst), 1, fin);
|
||||||
|
dest[i].mugiversion = t.mugiversion;
|
||||||
|
memcpy(&dest[i].instname, &t.instname, 32);
|
||||||
|
dest[i].waveform = t.waveform;
|
||||||
|
dest[i].wavelength = t.wavelength;
|
||||||
|
dest[i].mastervol = t.mastervol;
|
||||||
|
dest[i].amwave = t.amwave;
|
||||||
|
dest[i].amspd = t.amspd;
|
||||||
|
dest[i].amlooppoint = t.amlooppoint;
|
||||||
|
dest[i].finetune = t.finetune;
|
||||||
|
dest[i].fmwave = t.fmwave;
|
||||||
|
dest[i].fmspd = t.fmspd;
|
||||||
|
dest[i].fmlooppoint = t.fmlooppoint;
|
||||||
|
dest[i].fmdelay = t.fmdelay;
|
||||||
|
dest[i].arpeggio = t.arpeggio;
|
||||||
|
for (j=0; j < J3457_WAVES_INST; j++) {
|
||||||
|
dest[i].resetwave[j] = t.resetwave[j];
|
||||||
|
}
|
||||||
|
dest[i].panwave = t.panwave;
|
||||||
|
dest[i].panspd = t.panspd;
|
||||||
|
dest[i].panlooppoint = t.panlooppoint;
|
||||||
|
for (j=0; j < J3457_EFF_INST; j++) {
|
||||||
|
dest[i].fx[j].dsteffect = t.fx[j].dsteffect;
|
||||||
|
dest[i].fx[j].srceffect1 = t.fx[j].srceffect1;
|
||||||
|
dest[i].fx[j].srceffect2 = t.fx[j].srceffect2;
|
||||||
|
dest[i].fx[j].osceffect = t.fx[j].osceffect;
|
||||||
|
dest[i].fx[j].effectvar1 = t.fx[j].effectvar1;
|
||||||
|
dest[i].fx[j].effectvar2 = t.fx[j].effectvar2;
|
||||||
|
dest[i].fx[j].effectspd = t.fx[j].effectspd;
|
||||||
|
dest[i].fx[j].oscspd = t.fx[j].oscspd;
|
||||||
|
dest[i].fx[j].effecttype = t.fx[j].effecttype;
|
||||||
|
dest[i].fx[j].oscflg = t.fx[j].oscflg;
|
||||||
|
dest[i].fx[j].reseteffect = t.fx[j].reseteffect;
|
||||||
|
}
|
||||||
|
memcpy(&dest[i].samplename, &t.samplename, 192);
|
||||||
|
//exFnameFromPath(&dest[i].samplename, &t.samplename, SE_NAMELEN);
|
||||||
|
dest[i].sharing = t.sharing;
|
||||||
|
dest[i].loopflg = t.loopflg;
|
||||||
|
dest[i].bidirecflg = t.bidirecflg;
|
||||||
|
dest[i].startpoint = t.startpoint;
|
||||||
|
dest[i].looppoint = t.looppoint;
|
||||||
|
dest[i].endpoint = t.endpoint;
|
||||||
|
dest[i].hasSampData = t.hasSampData ? 1 : 0; //this was a sampdata pointer in original jaytrax
|
||||||
|
dest[i].samplelength = t.samplelength;
|
||||||
|
//memcpy(&dest[i].waves, &t.waves, J3457_WAVES_INST * J3457_SAMPS_WAVE * sizeof(int16_t));
|
||||||
|
reader(&dest->waves, 2, J3457_WAVES_INST * J3457_SAMPS_WAVE, fin);
|
||||||
|
}
|
||||||
|
return iferror(fin);
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------JXS3458
|
||||||
|
|
||||||
|
/* Soon! */
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
static int jxsfile_readSongCb(thereader reader, theerror iferror, void* fin, JT1Song** sngOut) {
|
||||||
|
#define FAIL(x) {error=(x); goto _ERR;}
|
||||||
|
JT1Song* song;
|
||||||
|
int i;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
//song
|
||||||
|
if((song = (JT1Song*)calloc(1, sizeof(JT1Song)))) {
|
||||||
|
int version;
|
||||||
|
|
||||||
|
if (struct_readHeader(song, reader, iferror, fin)) FAIL(ERR_BADSONG);
|
||||||
|
//version magic
|
||||||
|
version = song->mugiversion;
|
||||||
|
if (version >= 3456 && version <= 3457) {
|
||||||
|
int nrSubsongs = song->nrofsongs;
|
||||||
|
int nrPats = song->nrofpats;
|
||||||
|
int nrRows = J3457_ROWS_PAT * nrPats;
|
||||||
|
int nrInst = song->nrofinst;
|
||||||
|
|
||||||
|
//subsongs
|
||||||
|
if ((song->subsongs = (JT1Subsong**)calloc(nrSubsongs, sizeof(JT1Subsong*)))) {
|
||||||
|
for (i=0; i < nrSubsongs; i++) {
|
||||||
|
if ((song->subsongs[i] = (JT1Subsong*)calloc(1, sizeof(JT1Subsong)))) {
|
||||||
|
if (struct_readSubsong(song->subsongs[i], 1, reader, iferror, fin)) FAIL(ERR_BADSONG);
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
}
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
|
||||||
|
//patterns
|
||||||
|
if ((song->patterns = (JT1Row*)calloc(nrRows, sizeof(JT1Row)))) {
|
||||||
|
if (struct_readPat(song->patterns, nrPats, reader, iferror, fin)) FAIL(ERR_BADSONG);
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
|
||||||
|
//pattern names. Length includes \0
|
||||||
|
if ((song->patNames = (char**)calloc(nrPats, sizeof(char*)))) {
|
||||||
|
for (i=0; i < nrPats; i++) {
|
||||||
|
int32_t nameLen = 0;
|
||||||
|
|
||||||
|
reader(&nameLen, 4, 1, fin);
|
||||||
|
if ((song->patNames[i] = (char*)calloc(nameLen, sizeof(char)))) {
|
||||||
|
reader(song->patNames[i], nameLen, 1, fin);
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iferror(fin)) FAIL(ERR_BADSONG);
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
|
||||||
|
//instruments
|
||||||
|
if ((song->instruments = (JT1Inst**)calloc(nrInst, sizeof(JT1Inst*)))) {
|
||||||
|
if (!(song->samples = (uint8_t**)calloc(nrInst, sizeof(uint8_t*)))) FAIL(ERR_MALLOC);
|
||||||
|
for (i=0; i < nrInst; i++) {
|
||||||
|
if ((song->instruments[i] = (JT1Inst*)calloc(1, sizeof(JT1Inst)))) {
|
||||||
|
JT1Inst* inst = song->instruments[i];
|
||||||
|
if (struct_readInst(inst, 1, reader, iferror, fin)) FAIL(ERR_BADSONG);
|
||||||
|
|
||||||
|
//patch old instrument to new
|
||||||
|
if (version == 3456) {
|
||||||
|
inst->sharing = 0;
|
||||||
|
inst->loopflg = 0;
|
||||||
|
inst->bidirecflg = 0;
|
||||||
|
inst->startpoint = 0;
|
||||||
|
inst->looppoint = 0;
|
||||||
|
inst->endpoint = 0;
|
||||||
|
//silly place to put a pointer
|
||||||
|
if (inst->hasSampData) {
|
||||||
|
inst->startpoint=0;
|
||||||
|
inst->endpoint=(inst->samplelength/2);
|
||||||
|
inst->looppoint=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//sample data
|
||||||
|
if (inst->hasSampData) {
|
||||||
|
//inst->samplelength is in bytes, not samples
|
||||||
|
if(!(song->samples[i] = (uint8_t*)calloc(inst->samplelength, sizeof(uint8_t)))) FAIL(ERR_MALLOC);
|
||||||
|
reader(song->samples[i], 1, inst->samplelength, fin);
|
||||||
|
if (iferror(fin)) FAIL(ERR_BADSONG);
|
||||||
|
} else {
|
||||||
|
song->samples[i] = NULL;
|
||||||
|
}
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
}
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
|
||||||
|
//arpeggio table
|
||||||
|
reader(&song->arpTable, J3457_STEPS_ARP, J3457_ARPS_SONG, fin);
|
||||||
|
|
||||||
|
if (iferror(fin)) FAIL(ERR_BADSONG);
|
||||||
|
} else if (version == 3458) {
|
||||||
|
//Soon enough!
|
||||||
|
FAIL(ERR_BADSONG);
|
||||||
|
} else FAIL(ERR_BADSONG);
|
||||||
|
} else FAIL(ERR_MALLOC);
|
||||||
|
|
||||||
|
*sngOut = song;
|
||||||
|
return ERR_OK;
|
||||||
|
#undef FAIL
|
||||||
|
_ERR:
|
||||||
|
jxsfile_freeSong(song);
|
||||||
|
*sngOut = NULL;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int jxsfile_readSong(const char* path, JT1Song** sngOut) {
|
||||||
|
char buf[BUFSIZ];
|
||||||
|
FILE *fin;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!(fin = fopen(path, "rb"))) return ERR_FILEIO;
|
||||||
|
setbuf(fin, buf);
|
||||||
|
|
||||||
|
error = jxsfile_readSongCb(fileread, fileerror, fin, sngOut);
|
||||||
|
|
||||||
|
fclose(fin);
|
||||||
|
return error;
|
||||||
|
|
||||||
|
_ERR:
|
||||||
|
if (fin) fclose(fin);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int jxsfile_readSongMem(const uint8_t* data, size_t size, JT1Song** sngOut) {
|
||||||
|
struct memdata fin;
|
||||||
|
fin.data = data;
|
||||||
|
fin.remain = size;
|
||||||
|
fin.error = 0;
|
||||||
|
|
||||||
|
return jxsfile_readSongCb(memread, memerror, &fin, sngOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jxsfile_freeSong(JT1Song* song) {
|
||||||
|
if (song) {
|
||||||
|
int i;
|
||||||
|
int nrSubsongs = song->nrofsongs;
|
||||||
|
int nrPats = song->nrofpats;
|
||||||
|
int nrInst = song->nrofinst;
|
||||||
|
|
||||||
|
if (song->subsongs) {
|
||||||
|
|
||||||
|
for (i=0; i < nrSubsongs; i++) {
|
||||||
|
if (song->subsongs[i])
|
||||||
|
free(song->subsongs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(song->subsongs);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song->patterns) {
|
||||||
|
free(song->patterns);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song->patNames) {
|
||||||
|
for (i=0; i < nrPats; i++) {
|
||||||
|
if (song->patNames[i])
|
||||||
|
free(song->patNames[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(song->patNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song->instruments && song->samples) {
|
||||||
|
for (i=0; i < nrInst; i++) {
|
||||||
|
if (song->instruments[i])
|
||||||
|
free(song->instruments[i]);
|
||||||
|
if (song->samples[i])
|
||||||
|
free(song->samples[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song->samples) {
|
||||||
|
free(song->samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (song->instruments) {
|
||||||
|
free(song->instruments);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(song);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
#ifndef JXS_H
|
||||||
|
#define JXS_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "jaytrax.h"
|
||||||
|
|
||||||
|
#define J3457_CHANS_SUBSONG (16)
|
||||||
|
#define J3457_ORDERS_SUBSONG (256)
|
||||||
|
#define J3457_ROWS_PAT (64)
|
||||||
|
#define J3457_EFF_INST (4)
|
||||||
|
#define J3457_WAVES_INST (16)
|
||||||
|
#define J3457_SAMPS_WAVE (256)
|
||||||
|
#define J3457_ARPS_SONG (16)
|
||||||
|
#define J3457_STEPS_ARP (16)
|
||||||
|
|
||||||
|
typedef struct f_JT1Order f_JT1Order;
|
||||||
|
struct f_JT1Order {
|
||||||
|
int16_t patnr; // welk pattern spelen...
|
||||||
|
int16_t patlen; // 0/16/32/48
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
typedef struct f_JT1Row f_JT1Row;
|
||||||
|
struct f_JT1Row {
|
||||||
|
uint8_t srcnote;
|
||||||
|
uint8_t dstnote;
|
||||||
|
uint8_t inst;
|
||||||
|
int8_t param;
|
||||||
|
uint8_t script;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
typedef struct f_JT1Header f_JT1Header;
|
||||||
|
struct f_JT1Header {
|
||||||
|
int16_t mugiversion;//version of mugician this song was saved with
|
||||||
|
int16_t PAD00;
|
||||||
|
int32_t nrofpats; //aantal patterns beschikbaar
|
||||||
|
int32_t nrofsongs; //aantal beschikbare subsongs
|
||||||
|
int32_t nrofinst; //aantal gebruikte instruments
|
||||||
|
int32_t PAD01;
|
||||||
|
int16_t PAD02;
|
||||||
|
int16_t PAD03;
|
||||||
|
int16_t PAD04;
|
||||||
|
int16_t PAD05;
|
||||||
|
int16_t PAD06;
|
||||||
|
int16_t PAD07;
|
||||||
|
int16_t PAD08;
|
||||||
|
int16_t PAD09;
|
||||||
|
int16_t PAD0A;
|
||||||
|
int16_t PAD0B;
|
||||||
|
int16_t PAD0C;
|
||||||
|
int16_t PAD0D;
|
||||||
|
int16_t PAD0E;
|
||||||
|
int16_t PAD0F;
|
||||||
|
int16_t PAD10;
|
||||||
|
int16_t PAD11;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
typedef struct f_JT1Subsong f_JT1Subsong;
|
||||||
|
struct f_JT1Subsong {
|
||||||
|
int32_t PAD00[J3457_CHANS_SUBSONG];
|
||||||
|
uint8_t mute[J3457_CHANS_SUBSONG]; // which channels are muted? (1=muted)
|
||||||
|
int32_t songspd; // delay tussen de pattern-stepjes
|
||||||
|
int32_t groove; // groove value... 0=nothing, 1 = swing, 2=shuffle
|
||||||
|
int32_t songpos; // waar start song? (welke maat?)
|
||||||
|
int32_t songstep; // welke patternpos offset? (1/64 van maat)
|
||||||
|
int32_t endpos; // waar stopt song? (welke maat?)
|
||||||
|
int32_t endstep; // welke patternpos offset? (1/64 van maat)
|
||||||
|
int32_t looppos; // waar looped song? (welke maat?)
|
||||||
|
int32_t loopstep; // welke patternpos offset? (1/64 van maat)
|
||||||
|
int16_t songloop; // if true, the song loops inbetween looppos and endpos
|
||||||
|
char name[32]; // name of subsong
|
||||||
|
int16_t nrofchans; //nr of channels used
|
||||||
|
uint16_t delaytime; // the delaytime (for the echo effect)
|
||||||
|
uint8_t delayamount[J3457_CHANS_SUBSONG]; // amount per channel for the echo-effect
|
||||||
|
int16_t amplification; //extra amplification factor (20 to 1000)
|
||||||
|
int16_t PAD01;
|
||||||
|
int16_t PAD02;
|
||||||
|
int16_t PAD03;
|
||||||
|
int16_t PAD04;
|
||||||
|
int16_t PAD05;
|
||||||
|
int16_t PAD06;
|
||||||
|
f_JT1Order orders[J3457_CHANS_SUBSONG][J3457_ORDERS_SUBSONG];
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
typedef struct f_JT1Effect f_JT1Effect;
|
||||||
|
struct f_JT1Effect {
|
||||||
|
int32_t dsteffect;
|
||||||
|
int32_t srceffect1;
|
||||||
|
int32_t srceffect2;
|
||||||
|
int32_t osceffect;
|
||||||
|
int32_t effectvar1;
|
||||||
|
int32_t effectvar2;
|
||||||
|
int32_t effectspd;
|
||||||
|
int32_t oscspd;
|
||||||
|
int32_t effecttype;
|
||||||
|
int8_t oscflg;
|
||||||
|
int8_t reseteffect;
|
||||||
|
int16_t PAD00;
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
// inst is the structure which has the entire instrument definition.
|
||||||
|
typedef struct f_JT1Inst f_JT1Inst;
|
||||||
|
struct f_JT1Inst {
|
||||||
|
int16_t mugiversion;
|
||||||
|
char instname[32];
|
||||||
|
int16_t waveform;
|
||||||
|
int16_t wavelength;
|
||||||
|
int16_t mastervol;
|
||||||
|
int16_t amwave;
|
||||||
|
int16_t amspd;
|
||||||
|
int16_t amlooppoint;
|
||||||
|
int16_t finetune;
|
||||||
|
int16_t fmwave;
|
||||||
|
int16_t fmspd;
|
||||||
|
int16_t fmlooppoint;
|
||||||
|
int16_t fmdelay;
|
||||||
|
int16_t arpeggio;
|
||||||
|
int8_t resetwave[J3457_WAVES_INST];
|
||||||
|
int16_t panwave;
|
||||||
|
int16_t panspd;
|
||||||
|
int16_t panlooppoint;
|
||||||
|
int16_t PAD00;
|
||||||
|
int16_t PAD01;
|
||||||
|
int16_t PAD02;
|
||||||
|
int16_t PAD03;
|
||||||
|
int16_t PAD04;
|
||||||
|
int16_t PAD05;
|
||||||
|
f_JT1Effect fx[J3457_EFF_INST];
|
||||||
|
char samplename[192]; // path naar de gebruikte sample (was _MAX_PATH lang... is nu getruncate naar 192)(in de toekomst nog kleiner?)
|
||||||
|
int32_t PAD06;
|
||||||
|
int32_t PAD07;
|
||||||
|
int32_t PAD08;
|
||||||
|
int32_t PAD09;
|
||||||
|
int32_t PAD0A;
|
||||||
|
int32_t PAD0B;
|
||||||
|
int32_t PAD0C;
|
||||||
|
int32_t PAD0D;
|
||||||
|
int32_t PAD0E;
|
||||||
|
int32_t PAD0F;
|
||||||
|
int32_t PAD10;
|
||||||
|
int32_t PAD11;
|
||||||
|
int16_t PAD12;
|
||||||
|
int16_t sharing; // sample sharing! sharing contains instr nr of shared sanpledata (0=no sharing)
|
||||||
|
int16_t loopflg; //does the sample loop or play one/shot? (0=1shot)
|
||||||
|
int16_t bidirecflg; // does the sample loop birdirectional? (0=no)
|
||||||
|
int32_t startpoint;
|
||||||
|
int32_t looppoint;
|
||||||
|
int32_t endpoint;
|
||||||
|
int32_t hasSampData; // pointer naar de sample (mag 0 zijn)
|
||||||
|
int32_t samplelength; // length of sample
|
||||||
|
//int16_t waves[J3457_WAVES_INST * J3457_SAMPS_WAVE];
|
||||||
|
} __attribute__((__packed__));
|
||||||
|
|
||||||
|
//---------------------JXS3458
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int jxsfile_readSong(const char* path, JT1Song** sngOut);
|
||||||
|
int jxsfile_readSongMem(const uint8_t* data, size_t size, JT1Song** sngOut);
|
||||||
|
void jxsfile_freeSong(JT1Song* song);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
|
@ -0,0 +1,212 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include "mixcore.h"
|
||||||
|
#include "jaytrax.h"
|
||||||
|
|
||||||
|
// ITP_[number of taps]_[input sample width]_[fractional precision]_[readable name]
|
||||||
|
#define ITP_T02_S16_I08_LINEAR(P, F) (P[0]+(((P[1]-P[0])*F) >> 8))
|
||||||
|
#define ITP_T03_S16_I15_QUADRA(P, F) (((((((((P[0] + P[2]) >> 1) - P[1]) * F) >> 16) + P[1]) - ((P[2] + P[0] + (P[0] << 1)) >> 2)) * F) >> 14) + P[0]
|
||||||
|
#define ITP_T04_SXX_F01_CUBIC(P, F) (P[1] + 0.5 * F*(P[2] - P[0] + F*(2.0 * P[0] - 5.0 * P[1] + 4.0 * P[2] - P[3] + F * (3.0 * (P[1] - P[2]) + P[3] - P[0]))))
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------interpolators
|
||||||
|
#define GET_PT(x) buf[((pos + ((x)<<8)) & sizeMask)>>8]
|
||||||
|
|
||||||
|
static int32_t itpNone(int16_t* buf, int32_t pos, int32_t sizeMask) {
|
||||||
|
return 0;(void)buf;(void)pos;(void)sizeMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t itpNearest(int16_t* buf, int32_t pos, int32_t sizeMask) {
|
||||||
|
return GET_PT(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t itpLinear(int16_t* buf, int32_t pos, int32_t sizeMask) {
|
||||||
|
int32_t p[2];
|
||||||
|
int32_t frac = pos & 0xFF;
|
||||||
|
|
||||||
|
p[0] = GET_PT(0);
|
||||||
|
p[1] = GET_PT(1);
|
||||||
|
|
||||||
|
return ITP_T02_S16_I08_LINEAR(p, frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t itpQuad(int16_t* buf, int32_t pos, int32_t sizeMask) {
|
||||||
|
int32_t p[3];
|
||||||
|
int32_t frac = (pos & 0xFF)<<7;
|
||||||
|
|
||||||
|
p[0] = GET_PT(0);
|
||||||
|
p[1] = GET_PT(1);
|
||||||
|
p[2] = GET_PT(2);
|
||||||
|
|
||||||
|
return ITP_T03_S16_I15_QUADRA(p, frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int32_t itpCubic(int16_t* buf, int32_t pos, int32_t sizeMask) {
|
||||||
|
int32_t p[4];
|
||||||
|
float frac = (float)(pos & 0xFF)/256;
|
||||||
|
|
||||||
|
p[0] = GET_PT(0);
|
||||||
|
p[1] = GET_PT(1);
|
||||||
|
p[2] = GET_PT(2);
|
||||||
|
p[3] = GET_PT(3);
|
||||||
|
|
||||||
|
return ITP_T04_SXX_F01_CUBIC(p, frac);
|
||||||
|
}
|
||||||
|
|
||||||
|
Interpolator interps[INTERP_COUNT] = {
|
||||||
|
{ITP_NONE, 0, &itpNone, "None"},
|
||||||
|
{ITP_NEAREST, 1, &itpNearest, "Nearest"},
|
||||||
|
{ITP_LINEAR, 2, &itpLinear, "Linear"},
|
||||||
|
{ITP_QUADRATIC, 3, &itpQuad, "Quadratic"},
|
||||||
|
{ITP_CUBIC, 4, &itpCubic, "Cubic"},
|
||||||
|
//{ITP_BLEP, -1, &mixSynthNearest, &mixSampNearest, "BLEP"} //BLEP needs variable amount of taps
|
||||||
|
};
|
||||||
|
|
||||||
|
static void smpCpyFrw(int16_t* destination, const int16_t* source, int32_t num) {
|
||||||
|
memcpy(destination, source, num * sizeof(int16_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void smpCpyRev(int16_t* destination, const int16_t* source, int32_t num) {
|
||||||
|
for (int i=0; i < num; i++) {
|
||||||
|
destination[i] = source[num-1 - i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//---------------------API
|
||||||
|
|
||||||
|
uint8_t jaymix_setInterp(Interpolator** out, uint8_t id) {
|
||||||
|
for (int8_t i=0; i<INTERP_COUNT; i++) {
|
||||||
|
if (interps[i].id == id) {
|
||||||
|
*out = &interps[i];
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void jaymix_mixCore(JT1Player* SELF, int32_t numSamples) {
|
||||||
|
int32_t tempBuf[MIXBUF_LEN];
|
||||||
|
int32_t ic, is, doneSmp;
|
||||||
|
int32_t* outBuf = &SELF->tempBuf[0];
|
||||||
|
int32_t chanNr = SELF->subsong->nrofchans;
|
||||||
|
|
||||||
|
|
||||||
|
assert(numSamples <= MIXBUF_LEN);
|
||||||
|
memset(&outBuf[0], 0, numSamples * MIXBUF_NR * sizeof(int32_t));
|
||||||
|
|
||||||
|
|
||||||
|
for (ic=0; ic < chanNr; ic++) {
|
||||||
|
JT1Voice* vc = &SELF->voices[ic];
|
||||||
|
int32_t (*fItp) (int16_t* buf, int pos, int sizeMask);
|
||||||
|
int32_t loopLen = vc->endpoint - vc->looppoint;
|
||||||
|
|
||||||
|
doneSmp = 0;
|
||||||
|
if (vc->sampledata) {
|
||||||
|
|
||||||
|
if (!vc->wavePtr) continue;
|
||||||
|
if (vc->samplepos < 0) continue;
|
||||||
|
fItp = SELF->itp->fItp;
|
||||||
|
|
||||||
|
while (doneSmp < numSamples) {
|
||||||
|
int16_t tapArr[32] = {0};
|
||||||
|
int32_t tapPos = vc->samplepos>>8;
|
||||||
|
uint8_t tapDir = 0;
|
||||||
|
|
||||||
|
//slow, but better than nothing
|
||||||
|
//also, not centered
|
||||||
|
for (int i=0; i < SELF->itp->numTaps; i++) {
|
||||||
|
tapArr[i] = vc->wavePtr[tapPos];
|
||||||
|
|
||||||
|
if (tapDir) { //backwards
|
||||||
|
tapPos--;
|
||||||
|
|
||||||
|
if (tapPos < (vc->looppoint>>8)) {
|
||||||
|
tapPos += 2;
|
||||||
|
tapDir = 0;
|
||||||
|
}
|
||||||
|
} else { //forwards
|
||||||
|
tapPos++;
|
||||||
|
|
||||||
|
if (tapPos >= (vc->endpoint>>8)) {
|
||||||
|
if (vc->loopflg) { //has loop
|
||||||
|
if(vc->bidirecflg) { //bidi
|
||||||
|
tapPos -= 2;
|
||||||
|
tapDir = 1;
|
||||||
|
} else { //straight
|
||||||
|
tapPos -= (loopLen>>8);
|
||||||
|
}
|
||||||
|
} else { //oneshot
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tempBuf[doneSmp++] = fItp(tapArr, vc->samplepos&0xFF, 0xFFFFFFFF); //vc->wavePtr[vc->samplepos>>8];
|
||||||
|
vc->samplepos += vc->freqOffset;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (vc->freqOffset < 0) { //backwards
|
||||||
|
if (vc->samplepos < vc->looppoint) {
|
||||||
|
vc->freqOffset *= -1;
|
||||||
|
vc->samplepos += vc->freqOffset;
|
||||||
|
}
|
||||||
|
} else { //forwards
|
||||||
|
if (vc->samplepos >= vc->endpoint) {
|
||||||
|
if (vc->loopflg) { //has loop
|
||||||
|
if(vc->bidirecflg) { //bidi
|
||||||
|
vc->freqOffset *= -1;
|
||||||
|
vc->samplepos += vc->freqOffset;
|
||||||
|
} else { //straight
|
||||||
|
vc->samplepos -= loopLen;
|
||||||
|
}
|
||||||
|
} else { //oneshot
|
||||||
|
vc->samplepos = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else { //synth render
|
||||||
|
int32_t nos;
|
||||||
|
|
||||||
|
if (!vc->wavePtr) {
|
||||||
|
//original replayer plays through an empty wave
|
||||||
|
vc->synthPos += vc->freqOffset * numSamples;
|
||||||
|
vc->synthPos &= vc->waveLength;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
fItp = SELF->itp->fItp;
|
||||||
|
|
||||||
|
//loop unroll optimization
|
||||||
|
nos = numSamples;
|
||||||
|
if (nos&1) {
|
||||||
|
tempBuf[doneSmp++] = fItp(vc->wavePtr, vc->synthPos, vc->waveLength);
|
||||||
|
vc->synthPos += vc->freqOffset;
|
||||||
|
vc->synthPos &= vc->waveLength;
|
||||||
|
nos--;
|
||||||
|
}
|
||||||
|
for(is=0; is < nos; is+=2) {
|
||||||
|
tempBuf[doneSmp++] = fItp(vc->wavePtr, vc->synthPos, vc->waveLength);
|
||||||
|
vc->synthPos += vc->freqOffset;
|
||||||
|
vc->synthPos &= vc->waveLength;
|
||||||
|
tempBuf[doneSmp++] = fItp(vc->wavePtr, vc->synthPos, vc->waveLength);
|
||||||
|
vc->synthPos += vc->freqOffset;
|
||||||
|
vc->synthPos &= vc->waveLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(is=0; is < doneSmp; is++) {
|
||||||
|
int32_t samp, off;
|
||||||
|
|
||||||
|
samp = tempBuf[is];
|
||||||
|
off = is * MIXBUF_NR;
|
||||||
|
outBuf[off + BUF_MAINL] += (samp * vc->gainMainL)>>8;
|
||||||
|
outBuf[off + BUF_MAINR] += (samp * vc->gainMainR)>>8;
|
||||||
|
outBuf[off + BUF_ECHOL] += (samp * vc->gainEchoL)>>8;
|
||||||
|
outBuf[off + BUF_ECHOR] += (samp * vc->gainEchoR)>>8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef MIXCORE_H
|
||||||
|
#define MIXCORE_H
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "jaytrax.h"
|
||||||
|
|
||||||
|
#define CLAMP(x, low, high) (x = ((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
|
||||||
|
//#define CLAMP16(x) if ((int16_t)(x) != x) x = 0x7FFF ^ (x >> 31) //fast 32 -> 16 bit clamp
|
||||||
|
#define CLAMP16(x) (int16_t)(x) != x ? 0x7FFF ^ (x >> 31) : x //fast 32 -> 16 bit clamp
|
||||||
|
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||||
|
#define CHKPOS(VC) ((VC->samplepos >= 0) && ((VC->curdirecflg && ((VC->samplepos & 0xFFFFFF00) >= VC->looppoint)) || ((VC->samplepos & 0xFFFFFF00) < VC->endpoint)))
|
||||||
|
|
||||||
|
void jaymix_mixCore(JT1Player* SELF, int32_t numSamples);
|
||||||
|
uint8_t jaymix_setInterp(Interpolator** out, uint8_t id);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,367 +0,0 @@
|
||||||
#include "resampler.h"
|
|
||||||
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
/* Copyright (C) 2004-2008 Shay Green.
|
|
||||||
Copyright (C) 2021 Christopher Snowhill. This module is free software; you
|
|
||||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
|
||||||
General Public License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version. This
|
|
||||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
||||||
details. You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this module; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
||||||
|
|
||||||
#undef PI
|
|
||||||
#define PI 3.1415926535897932384626433832795029
|
|
||||||
|
|
||||||
enum { imp_scale = 0x7FFF };
|
|
||||||
typedef int16_t imp_t;
|
|
||||||
typedef int32_t imp_off_t; /* for max_res of 512 and impulse width of 32, end offsets must be 32 bits */
|
|
||||||
|
|
||||||
#if RESAMPLER_BITS == 16
|
|
||||||
typedef int32_t intermediate_t;
|
|
||||||
#elif RESAMPLER_BITS == 32
|
|
||||||
typedef int64_t intermediate_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale,
|
|
||||||
int count, imp_t* out )
|
|
||||||
{
|
|
||||||
double angle;
|
|
||||||
|
|
||||||
double const maxh = 256;
|
|
||||||
double const step = PI / maxh * spacing;
|
|
||||||
double const to_w = maxh * 2 / width;
|
|
||||||
double const pow_a_n = pow( rolloff, maxh );
|
|
||||||
scale /= maxh * 2;
|
|
||||||
angle = (count / 2 - 1 + offset) * -step;
|
|
||||||
|
|
||||||
while ( count-- )
|
|
||||||
{
|
|
||||||
double w;
|
|
||||||
*out++ = 0;
|
|
||||||
w = angle * to_w;
|
|
||||||
if ( fabs( w ) < PI )
|
|
||||||
{
|
|
||||||
double rolloff_cos_a = rolloff * cos( angle );
|
|
||||||
double num = 1 - rolloff_cos_a -
|
|
||||||
pow_a_n * cos( maxh * angle ) +
|
|
||||||
pow_a_n * rolloff * cos( (maxh - 1) * angle );
|
|
||||||
double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
|
|
||||||
double sinc = scale * num / den - scale;
|
|
||||||
|
|
||||||
out [-1] = (imp_t) (cos( w ) * sinc + sinc);
|
|
||||||
}
|
|
||||||
angle += step;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum { width = 32 };
|
|
||||||
enum { max_res = 512 };
|
|
||||||
enum { min_width = (width < 4 ? 4 : width) };
|
|
||||||
enum { adj_width = min_width / 4 * 4 + 2 };
|
|
||||||
enum { write_offset = adj_width };
|
|
||||||
|
|
||||||
enum { buffer_size = 128 };
|
|
||||||
|
|
||||||
typedef struct _resampler
|
|
||||||
{
|
|
||||||
int width_;
|
|
||||||
int rate_;
|
|
||||||
int inptr;
|
|
||||||
int infilled;
|
|
||||||
int outptr;
|
|
||||||
int outfilled;
|
|
||||||
|
|
||||||
int latency;
|
|
||||||
|
|
||||||
imp_t const* imp;
|
|
||||||
imp_t impulses [max_res * (adj_width + 2 * (sizeof(imp_off_t) / sizeof(imp_t)))];
|
|
||||||
sample_t buffer_in[buffer_size * 2];
|
|
||||||
sample_t buffer_out[buffer_size];
|
|
||||||
} resampler;
|
|
||||||
|
|
||||||
void * resampler_create()
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *) malloc(sizeof(resampler));
|
|
||||||
if (r) resampler_clear(r);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * resampler_dup(const void *_r)
|
|
||||||
{
|
|
||||||
void *_t = (resampler *) malloc(sizeof(resampler));
|
|
||||||
if (_t) resampler_dup_inplace(_t, _r);
|
|
||||||
return _t;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_dup_inplace(void *_t, const void *_r)
|
|
||||||
{
|
|
||||||
const resampler *r = (const resampler *)_r;
|
|
||||||
resampler *t = (resampler *)_t;
|
|
||||||
if (r && t)
|
|
||||||
{
|
|
||||||
memcpy(t, r, sizeof(resampler));
|
|
||||||
t->imp = t->impulses + (r->imp - r->impulses);
|
|
||||||
}
|
|
||||||
else if (t)
|
|
||||||
{
|
|
||||||
resampler_clear(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_destroy(void *r)
|
|
||||||
{
|
|
||||||
free(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_clear(void *_r)
|
|
||||||
{
|
|
||||||
resampler * r = (resampler *)_r;
|
|
||||||
r->width_ = adj_width;
|
|
||||||
r->inptr = 0;
|
|
||||||
r->infilled = 0;
|
|
||||||
r->outptr = 0;
|
|
||||||
r->outfilled = 0;
|
|
||||||
r->latency = 0;
|
|
||||||
r->imp = r->impulses;
|
|
||||||
|
|
||||||
resampler_set_rate(r, 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_set_rate( void *_r, double new_factor )
|
|
||||||
{
|
|
||||||
int step; //const
|
|
||||||
double filter; //const
|
|
||||||
double fraction, pos;
|
|
||||||
int n;
|
|
||||||
|
|
||||||
resampler *rs = (resampler *)_r;
|
|
||||||
imp_t* out;
|
|
||||||
|
|
||||||
double const rolloff = 0.999;
|
|
||||||
double const gain = 1.0;
|
|
||||||
|
|
||||||
/* determine number of sub-phases that yield lowest error */
|
|
||||||
double ratio_ = 0.0;
|
|
||||||
int res = -1;
|
|
||||||
{
|
|
||||||
double least_error = 2;
|
|
||||||
double pos = 0;
|
|
||||||
int r;
|
|
||||||
for ( r = 1; r <= max_res; r++ )
|
|
||||||
{
|
|
||||||
double nearest, error;
|
|
||||||
pos += new_factor;
|
|
||||||
nearest = floor( pos + 0.5 );
|
|
||||||
error = fabs( pos - nearest );
|
|
||||||
if ( error < least_error )
|
|
||||||
{
|
|
||||||
res = r;
|
|
||||||
ratio_ = nearest / res;
|
|
||||||
least_error = error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rs->rate_ = ratio_;
|
|
||||||
|
|
||||||
/* how much of input is used for each output sample */
|
|
||||||
step = (int) floor( ratio_ );
|
|
||||||
fraction = fmod( ratio_, 1.0 );
|
|
||||||
|
|
||||||
filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
|
|
||||||
pos = 0.0;
|
|
||||||
/*int input_per_cycle = 0;*/
|
|
||||||
out = rs->impulses;
|
|
||||||
|
|
||||||
for ( n = res; --n >= 0; )
|
|
||||||
{
|
|
||||||
int cur_step;
|
|
||||||
|
|
||||||
gen_sinc( rolloff, (int) (rs->width_ * filter + 1) & ~1, pos, filter,
|
|
||||||
(double)(imp_scale * gain * filter), (int) rs->width_, out );
|
|
||||||
out += rs->width_;
|
|
||||||
|
|
||||||
cur_step = step;
|
|
||||||
pos += fraction;
|
|
||||||
if ( pos >= 0.9999999 )
|
|
||||||
{
|
|
||||||
pos -= 1.0;
|
|
||||||
cur_step += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
((imp_off_t*)out)[0] = (cur_step - rs->width_ + 2) * sizeof (sample_t);
|
|
||||||
((imp_off_t*)out)[1] = 2 * sizeof (imp_t) + 2 * sizeof (imp_off_t);
|
|
||||||
out += 2 * (sizeof(imp_off_t) / sizeof(imp_t));
|
|
||||||
/*input_per_cycle += cur_step;*/
|
|
||||||
}
|
|
||||||
/* last offset moves back to beginning of impulses*/
|
|
||||||
((imp_off_t*)out) [-1] -= (char*) out - (char*) rs->impulses;
|
|
||||||
|
|
||||||
rs->imp = rs->impulses;
|
|
||||||
}
|
|
||||||
|
|
||||||
int resampler_get_free(void *_r)
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *)_r;
|
|
||||||
return buffer_size - r->infilled;
|
|
||||||
}
|
|
||||||
|
|
||||||
int resampler_get_min_fill(void *_r)
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *)_r;
|
|
||||||
const int min_needed = write_offset + 1;
|
|
||||||
const int latency = r->latency ? 0 : adj_width;
|
|
||||||
int min_free = min_needed - r->infilled - latency;
|
|
||||||
return min_free < 0 ? 0 : min_free;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_write_sample(void *_r, sample_t s)
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *)_r;
|
|
||||||
|
|
||||||
if (!r->latency)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for ( i = 0; i < adj_width / 2; ++i)
|
|
||||||
{
|
|
||||||
r->buffer_in[r->inptr] = 0;
|
|
||||||
r->buffer_in[buffer_size + r->inptr] = 0;
|
|
||||||
r->inptr = (r->inptr + 1) % (buffer_size);
|
|
||||||
r->infilled += 1;
|
|
||||||
}
|
|
||||||
r->latency = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r->infilled < buffer_size)
|
|
||||||
{
|
|
||||||
r->buffer_in[r->inptr] = s;
|
|
||||||
r->buffer_in[buffer_size + r->inptr + 0] = s;
|
|
||||||
r->inptr = (r->inptr + 1) % (buffer_size);
|
|
||||||
r->infilled += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__GNUC__)
|
|
||||||
#define restrict __restrict
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const sample_t * resampler_inner_loop( resampler *r, sample_t** out_,
|
|
||||||
sample_t const* out_end, sample_t const in [], int in_size )
|
|
||||||
{
|
|
||||||
in_size -= write_offset;
|
|
||||||
if ( in_size > 0 )
|
|
||||||
{
|
|
||||||
sample_t* restrict out = *out_;
|
|
||||||
sample_t const* const in_end = in + in_size;
|
|
||||||
imp_t const* imp = r->imp;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
int n;
|
|
||||||
/* accumulate in extended precision*/
|
|
||||||
int pt = imp [0];
|
|
||||||
intermediate_t s = (intermediate_t)pt * (intermediate_t)(in [0]);
|
|
||||||
if ( out >= out_end )
|
|
||||||
break;
|
|
||||||
for ( n = (adj_width - 2) / 2; n; --n )
|
|
||||||
{
|
|
||||||
pt = imp [1];
|
|
||||||
s += (intermediate_t)pt * (intermediate_t)(in [1]);
|
|
||||||
|
|
||||||
/* pre-increment more efficient on some RISC processors*/
|
|
||||||
imp += 2;
|
|
||||||
pt = imp [0];
|
|
||||||
s += (intermediate_t)pt * (intermediate_t)(in [2]);
|
|
||||||
in += 2;
|
|
||||||
}
|
|
||||||
pt = imp [1];
|
|
||||||
s += (intermediate_t)pt * (intermediate_t)(in [1]);
|
|
||||||
|
|
||||||
/* these two "samples" after the end of the impulse give the
|
|
||||||
* proper offsets to the next input sample and next impulse */
|
|
||||||
in = (sample_t const*) ((char const*) in + ((imp_off_t*)(&imp [2]))[0]); /* some negative value */
|
|
||||||
imp = (imp_t const*) ((char const*) imp + ((imp_off_t*)(&imp [2]))[1]); /* small positive or large negative */
|
|
||||||
|
|
||||||
out [0] = (sample_t) (s >> 15);
|
|
||||||
out += 1;
|
|
||||||
}
|
|
||||||
while ( in < in_end );
|
|
||||||
|
|
||||||
r->imp = imp;
|
|
||||||
*out_ = out;
|
|
||||||
}
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef restrict
|
|
||||||
|
|
||||||
static int resampler_wrapper( resampler *r, sample_t out [], int* out_size,
|
|
||||||
sample_t const in [], int in_size )
|
|
||||||
{
|
|
||||||
sample_t* out_ = out;
|
|
||||||
long result = resampler_inner_loop( r, &out_, out + *out_size, in, in_size ) - in;
|
|
||||||
|
|
||||||
*out_size = (int)(out_ - out);
|
|
||||||
return (int) result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void resampler_fill( resampler *r )
|
|
||||||
{
|
|
||||||
while (!r->outfilled && r->infilled)
|
|
||||||
{
|
|
||||||
int inread;
|
|
||||||
|
|
||||||
int writepos = ( r->outptr + r->outfilled ) % (buffer_size);
|
|
||||||
int writesize = (buffer_size) - writepos;
|
|
||||||
if ( writesize > ( buffer_size - r->outfilled ) )
|
|
||||||
writesize = buffer_size - r->outfilled;
|
|
||||||
inread = resampler_wrapper(r, &r->buffer_out[writepos], &writesize, &r->buffer_in[buffer_size + r->inptr - r->infilled], r->infilled);
|
|
||||||
r->infilled -= inread;
|
|
||||||
r->outfilled += writesize;
|
|
||||||
if (!inread)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int resampler_get_avail(void *_r)
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *)_r;
|
|
||||||
if (r->outfilled < 1 && r->infilled >= r->width_)
|
|
||||||
resampler_fill( r );
|
|
||||||
return r->outfilled;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void resampler_read_sample_internal( resampler *r, sample_t *s, int advance )
|
|
||||||
{
|
|
||||||
if (r->outfilled < 1)
|
|
||||||
resampler_fill( r );
|
|
||||||
if (r->outfilled < 1)
|
|
||||||
{
|
|
||||||
*s = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
*s = r->buffer_out[r->outptr];
|
|
||||||
if (advance)
|
|
||||||
{
|
|
||||||
r->outptr = (r->outptr + 1) % (buffer_size);
|
|
||||||
r->outfilled -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_read_sample( void *_r, sample_t *s )
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *)_r;
|
|
||||||
resampler_read_sample_internal(r, s, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resampler_peek_sample( void *_r, sample_t *s )
|
|
||||||
{
|
|
||||||
resampler *r = (resampler *)_r;
|
|
||||||
resampler_read_sample_internal(r, s, 0);
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
#ifndef _RESAMPLER_H_
|
|
||||||
#define _RESAMPLER_H_
|
|
||||||
|
|
||||||
/* Copyright (C) 2004-2008 Shay Green.
|
|
||||||
Copyright (C) 2021 Christopher Snowhill. This module is free software; you
|
|
||||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
|
||||||
General Public License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version. This
|
|
||||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
||||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
|
||||||
details. You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this module; if not, write to the Free Software Foundation,
|
|
||||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|
||||||
|
|
||||||
#define RESAMPLER_BITS 32
|
|
||||||
#define RESAMPLER_DECORATE syntrax
|
|
||||||
|
|
||||||
#ifdef RESAMPLER_DECORATE
|
|
||||||
#undef PASTE
|
|
||||||
#undef EVALUATE
|
|
||||||
#define PASTE(a,b) a ## b
|
|
||||||
#define EVALUATE(a,b) PASTE(a,b)
|
|
||||||
#define resampler_create EVALUATE(RESAMPLER_DECORATE,_resampler_create)
|
|
||||||
#define resampler_dup EVALUATE(RESAMPLER_DECORATE,_resampler_dup)
|
|
||||||
#define resampler_dup_inplace EVALUATE(RESAMPLER_DECORATE,_resampler_dup_inplace)
|
|
||||||
#define resampler_destroy EVALUATE(RESAMPLER_DECORATE,_resampler_destroy)
|
|
||||||
#define resampler_clear EVALUATE(RESAMPLER_DECORATE,_resampler_clear)
|
|
||||||
#define resampler_set_rate EVALUATE(RESAMPLER_DECORATE,_resampler_set_rate)
|
|
||||||
#define resampler_get_free EVALUATE(RESAMPLER_DECORATE,_resampler_get_free)
|
|
||||||
#define resampler_get_min_fill EVALUATE(RESAMPLER_DECORATE,_resampler_get_min_fill)
|
|
||||||
#define resampler_write_sample EVALUATE(RESAMPLER_DECORATE,_resampler_write_sample)
|
|
||||||
#define resampler_get_avail EVALUATE(RESAMPLER_DECORATE,_resampler_get_avail)
|
|
||||||
#define resampler_read_sample EVALUATE(RESAMPLER_DECORATE,_resampler_read_sample)
|
|
||||||
#define resampler_peek_sample EVALUATE(RESAMPLER_DECORATE,_resampler_peek_sample)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#if RESAMPLER_BITS == 16
|
|
||||||
typedef int16_t sample_t;
|
|
||||||
#elif RESAMPLER_BITS == 32
|
|
||||||
typedef int32_t sample_t;
|
|
||||||
#else
|
|
||||||
#error Choose a bit depth!
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void * resampler_create(void);
|
|
||||||
void * resampler_dup(const void *);
|
|
||||||
void resampler_dup_inplace(void *, const void *);
|
|
||||||
void resampler_destroy(void *);
|
|
||||||
|
|
||||||
void resampler_clear(void *);
|
|
||||||
|
|
||||||
void resampler_set_rate( void *, double new_factor );
|
|
||||||
|
|
||||||
int resampler_get_free(void *);
|
|
||||||
int resampler_get_min_fill(void *);
|
|
||||||
|
|
||||||
void resampler_write_sample(void *, sample_t s);
|
|
||||||
|
|
||||||
int resampler_get_avail(void *);
|
|
||||||
|
|
||||||
void resampler_read_sample( void *, sample_t *s );
|
|
||||||
void resampler_peek_sample( void *, sample_t *s );
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,363 +0,0 @@
|
||||||
#ifndef SYNTRAX_H
|
|
||||||
#define SYNTRAX_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
//----------------------------typedefs-------------------------
|
|
||||||
#ifndef NULL
|
|
||||||
#define NULL 0
|
|
||||||
#endif
|
|
||||||
typedef unsigned uint;
|
|
||||||
|
|
||||||
//----------------------------defines--------------------------
|
|
||||||
#define SE_NROFEFFECTS 18
|
|
||||||
#define SE_MAXCHANS 16
|
|
||||||
#define SE_NROFFINETUNESTEPS 16
|
|
||||||
|
|
||||||
#define SE_OVERLAP 100
|
|
||||||
|
|
||||||
#define BUFFERLENGTH 4096
|
|
||||||
#define CHANNELS 2
|
|
||||||
#define BITRATE 16
|
|
||||||
#define RESAMPLE_POINTS 4
|
|
||||||
|
|
||||||
//some constants for controlling stuff
|
|
||||||
//I've no idea what the abreviations stand for
|
|
||||||
#define SE_PM_SONG 0
|
|
||||||
#define SE_PM_PATTERN 1
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------structures------------------------
|
|
||||||
//player structs
|
|
||||||
//don't pack these
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int wavelength;
|
|
||||||
int gainDelay;
|
|
||||||
int delta;
|
|
||||||
int freq;
|
|
||||||
int isSample;
|
|
||||||
int isPlayingBackward;
|
|
||||||
int smpLoopEnd;
|
|
||||||
int PPMXBYLQ; //unused
|
|
||||||
int gain;
|
|
||||||
const int16_t *waveBuff;
|
|
||||||
int hasBidiLoop;
|
|
||||||
int synthPos;
|
|
||||||
int gainRight;
|
|
||||||
int smpLoopStart;
|
|
||||||
int bkpSynthPos;
|
|
||||||
int sampPos;
|
|
||||||
int gainLeft;
|
|
||||||
int hasLoop;
|
|
||||||
int smpLength;
|
|
||||||
int gainDelayRight;
|
|
||||||
int gainDelayLeft;
|
|
||||||
int hasLooped;
|
|
||||||
void * resampler[2];
|
|
||||||
int last_delta;
|
|
||||||
} Voice;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int VMBNMTNBQU;
|
|
||||||
int TIPUANVVR;
|
|
||||||
double MDTMBBIQHRQ;
|
|
||||||
double YLKJB;
|
|
||||||
double DQVLFV;
|
|
||||||
int MFATTMREMVP;
|
|
||||||
double ILHG;
|
|
||||||
double RKF;
|
|
||||||
int SPYK;
|
|
||||||
int QOMCBTRPXF;
|
|
||||||
int ABJGHAUY;
|
|
||||||
} VoiceEffect;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int JOEEPJCI;
|
|
||||||
int BNWIGU;
|
|
||||||
int isPlayingBackward;
|
|
||||||
int ELPHLDR;
|
|
||||||
int panning;
|
|
||||||
int TVORFCC;
|
|
||||||
int EYRXAB; //rather unused
|
|
||||||
int UHYDBDDI;
|
|
||||||
int XESAWSO;
|
|
||||||
int hasBidiLoop;
|
|
||||||
int fmDelay;
|
|
||||||
int volume;
|
|
||||||
int ACKCWV;
|
|
||||||
int sampPos;
|
|
||||||
int insNum;
|
|
||||||
uint8_t EQMIWERPIF;
|
|
||||||
int freq;
|
|
||||||
int HFRLJCG;
|
|
||||||
int VNVJPDIWAJQ;
|
|
||||||
int hasLoop;
|
|
||||||
int LJHG;
|
|
||||||
const int16_t *sampleBuffer;
|
|
||||||
//SQUASH effect overflows into next buffer
|
|
||||||
//SE_MAXCHANS * 0x100 + 1 must be allocated
|
|
||||||
int16_t synthBuffers[SE_MAXCHANS][0x100];
|
|
||||||
int16_t OVERFLOW_DUMMY;
|
|
||||||
int smpLoopStart;
|
|
||||||
int smpLoopEnd;
|
|
||||||
int hasLooped;
|
|
||||||
VoiceEffect effects[4];
|
|
||||||
} TuneChannel;
|
|
||||||
|
|
||||||
|
|
||||||
//data structs
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t destWave;
|
|
||||||
uint32_t srcWave1;
|
|
||||||
uint32_t srcWave2;
|
|
||||||
uint32_t oscWave;
|
|
||||||
uint32_t variable1;
|
|
||||||
uint32_t variable2;
|
|
||||||
uint32_t fxSpeed;
|
|
||||||
uint32_t oscSpeed;
|
|
||||||
uint32_t effectType;
|
|
||||||
int8_t oscSelect;
|
|
||||||
int8_t resetEffect;
|
|
||||||
int16_t UNK00;
|
|
||||||
} InstrumentEffect;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
int16_t version;
|
|
||||||
char name[33];
|
|
||||||
int16_t waveform;
|
|
||||||
int16_t wavelength;
|
|
||||||
int16_t masterVolume;
|
|
||||||
int16_t amWave;
|
|
||||||
int16_t amSpeed;
|
|
||||||
int16_t amLoopPoint;
|
|
||||||
int16_t finetune;
|
|
||||||
int16_t fmWave;
|
|
||||||
int16_t fmSpeed;
|
|
||||||
int16_t fmLoopPoint;
|
|
||||||
int16_t fmDelay;
|
|
||||||
int16_t arpIndex;
|
|
||||||
uint8_t m_ResetWave[SE_MAXCHANS];
|
|
||||||
int16_t panWave;
|
|
||||||
int16_t panSpeed;
|
|
||||||
int16_t panLoopPoint;
|
|
||||||
int16_t UNK00;
|
|
||||||
int16_t UNK01;
|
|
||||||
int16_t UNK02;
|
|
||||||
int16_t UNK03;
|
|
||||||
int16_t UNK04;
|
|
||||||
int16_t UNK05;
|
|
||||||
InstrumentEffect effects[4];
|
|
||||||
//why do we even need to store a full path?
|
|
||||||
//only filename appears to be used.
|
|
||||||
char smpFullImportPath[193];
|
|
||||||
uint32_t UNK06;
|
|
||||||
uint32_t UNK07;
|
|
||||||
uint32_t UNK08;
|
|
||||||
uint32_t UNK09;
|
|
||||||
uint32_t UNK0A;
|
|
||||||
uint32_t UNK0B;
|
|
||||||
uint32_t UNK0C;
|
|
||||||
uint32_t UNK0D;
|
|
||||||
uint32_t UNK0E;
|
|
||||||
uint32_t UNK0F;
|
|
||||||
uint32_t UNK10;
|
|
||||||
uint32_t UNK11;
|
|
||||||
int16_t UNK12;
|
|
||||||
int16_t shareSmpDataFromInstr; //0 is off
|
|
||||||
int16_t hasLoop;
|
|
||||||
int16_t hasBidiLoop;
|
|
||||||
uint32_t smpStartPoint;
|
|
||||||
uint32_t smpLoopPoint;
|
|
||||||
uint32_t smpEndPoint;
|
|
||||||
uint32_t hasSample;
|
|
||||||
uint32_t smpLength;
|
|
||||||
int16_t synthBuffers[SE_MAXCHANS][0x100];
|
|
||||||
} Instrument;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint8_t note;
|
|
||||||
uint8_t dest;
|
|
||||||
uint8_t instr;
|
|
||||||
int8_t spd;
|
|
||||||
uint8_t command;
|
|
||||||
} Row;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint16_t patIndex; //0 means empty
|
|
||||||
uint16_t patLen;
|
|
||||||
} Order;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint32_t UNK00[16];
|
|
||||||
//UNK00 is used for something. No idea what.
|
|
||||||
//There is a sequence to the data in it.
|
|
||||||
//zeroing it out with hex editor doesn't seem to break stuff with Jaytrax
|
|
||||||
//it could as well be uninitialized memory
|
|
||||||
uint8_t mutedChans[SE_MAXCHANS];
|
|
||||||
uint32_t tempo;
|
|
||||||
uint32_t groove;
|
|
||||||
uint32_t startPosCoarse;
|
|
||||||
uint32_t startPosFine;
|
|
||||||
uint32_t endPosCoarse;
|
|
||||||
uint32_t endPosFine;
|
|
||||||
uint32_t loopPosCoarse;
|
|
||||||
uint32_t loopPosFine;
|
|
||||||
int16_t isLooping;
|
|
||||||
char m_Name[33];
|
|
||||||
int16_t channelNumber;
|
|
||||||
uint16_t delayTime;
|
|
||||||
uint8_t chanDelayAmt[SE_MAXCHANS];
|
|
||||||
int16_t amplification;
|
|
||||||
int16_t UNK01;
|
|
||||||
int16_t UNK02;
|
|
||||||
int16_t UNK03;
|
|
||||||
int16_t UNK04;
|
|
||||||
int16_t UNK05;
|
|
||||||
int16_t UNK06;
|
|
||||||
//if my eyes don't deceive me, this actually happens
|
|
||||||
//waste of space
|
|
||||||
Order orders[SE_MAXCHANS][0x100];
|
|
||||||
} Subsong;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
uint16_t version;
|
|
||||||
uint16_t UNK00;
|
|
||||||
uint32_t patNum;
|
|
||||||
uint32_t subsongNum;
|
|
||||||
uint32_t instrNum;
|
|
||||||
uint32_t UNK01;
|
|
||||||
int16_t UNK02;
|
|
||||||
int16_t UNK03;
|
|
||||||
int16_t UNK04;
|
|
||||||
int16_t UNK05;
|
|
||||||
int16_t UNK06;
|
|
||||||
int16_t UNK07;
|
|
||||||
int16_t UNK08;
|
|
||||||
int16_t UNK09;
|
|
||||||
int16_t UNK0A;
|
|
||||||
int16_t UNK0B;
|
|
||||||
int16_t UNK0C;
|
|
||||||
int16_t UNK0D;
|
|
||||||
int16_t UNK0E;
|
|
||||||
int16_t UNK0F;
|
|
||||||
int16_t UNK10;
|
|
||||||
int16_t UNK11;
|
|
||||||
} SongHeader;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
SongHeader h;
|
|
||||||
int8_t arpTable[0x100];
|
|
||||||
|
|
||||||
Row *rows;
|
|
||||||
//we don't know what maximum pat name length should be
|
|
||||||
//in fact this is probably a buffer overflow target in Syntrax(app crashed on too long name, from UI);
|
|
||||||
uint32_t *patNameSizes;
|
|
||||||
char **patternNames;
|
|
||||||
Instrument *instruments;
|
|
||||||
Subsong *subsongs;
|
|
||||||
int16_t **samples;
|
|
||||||
} Song;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct Player
|
|
||||||
{
|
|
||||||
int16_t *silentBuffer;
|
|
||||||
uint8_t m_LastNotes[SE_MAXCHANS];
|
|
||||||
|
|
||||||
uint32_t *freqTable;
|
|
||||||
int16_t *dynamorphTable;
|
|
||||||
|
|
||||||
const Song *synSong;
|
|
||||||
TuneChannel *tuneChannels;
|
|
||||||
Voice *voices;
|
|
||||||
|
|
||||||
int SAMPLEFREQUENCY;
|
|
||||||
|
|
||||||
int samplesPerBeat;
|
|
||||||
int otherSamplesPerBeat;
|
|
||||||
int someCounter;
|
|
||||||
int channelNumber;
|
|
||||||
int sePmSong;
|
|
||||||
int16_t *overlapBuff;
|
|
||||||
int16_t *delayBufferR;
|
|
||||||
int16_t *delayBufferL;
|
|
||||||
|
|
||||||
int bkpDelayPos;
|
|
||||||
int delayPos;
|
|
||||||
int gainPos;
|
|
||||||
int overlapPos;
|
|
||||||
|
|
||||||
int ISWLKT;
|
|
||||||
int WDTECTE;
|
|
||||||
int PQV;
|
|
||||||
int AMVM;
|
|
||||||
int DONGS;
|
|
||||||
uint8_t posCoarse;
|
|
||||||
int AMYGPFQCHSW;
|
|
||||||
int posFine;
|
|
||||||
int8_t mutedChans[SE_MAXCHANS];
|
|
||||||
|
|
||||||
int selectedSubsong;
|
|
||||||
Subsong curSubsong;
|
|
||||||
|
|
||||||
//local pointers to song structures
|
|
||||||
const Row *rows;
|
|
||||||
const char ** patternNames;
|
|
||||||
Instrument *instruments;
|
|
||||||
const Subsong *subsongs;
|
|
||||||
const int8_t *arpTable;
|
|
||||||
const int16_t **samples;
|
|
||||||
|
|
||||||
uint bufflen;
|
|
||||||
|
|
||||||
uint loopCount;
|
|
||||||
} Player;
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------prototypes------------------------
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Player * playerCreate(int sampleFrequency);
|
|
||||||
void playerDestroy(Player *);
|
|
||||||
|
|
||||||
int loadSong(Player *, const Song *);
|
|
||||||
void initSubsong(Player *, int num);
|
|
||||||
|
|
||||||
void mixChunk(Player *, int16_t *outBuff, uint playbackBufferSize);
|
|
||||||
|
|
||||||
void playInstrument(Player *, int chanNum, int instrNum, int note); //could be handy dandy
|
|
||||||
|
|
||||||
bool playerGetSongEnded(Player *);
|
|
||||||
uint playerGetLoopCount(Player *);
|
|
||||||
|
|
||||||
typedef struct _stmi
|
|
||||||
{
|
|
||||||
unsigned char coarse;
|
|
||||||
unsigned char fine;
|
|
||||||
unsigned char channelsPlaying;
|
|
||||||
const char *subsongName;
|
|
||||||
int selectedSubs;
|
|
||||||
int totalSubs;
|
|
||||||
} syntrax_info;
|
|
||||||
|
|
||||||
void playerGetInfo(Player *, syntrax_info *);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
|
@ -7,23 +7,25 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
83EEAB071C965247002761C5 /* file.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EEAB011C965247002761C5 /* file.c */; };
|
8398BF3D2769D28A0048F38A /* jaytrax.c in Sources */ = {isa = PBXBuildFile; fileRef = 8398BF3C2769D26D0048F38A /* jaytrax.c */; };
|
||||||
83EEAB081C965247002761C5 /* file.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EEAB021C965247002761C5 /* file.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
8398BF3E2769D28D0048F38A /* jxs.c in Sources */ = {isa = PBXBuildFile; fileRef = 8398BF372769D26D0048F38A /* jxs.c */; };
|
||||||
83EEAB091C965247002761C5 /* resampler.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EEAB031C965247002761C5 /* resampler.c */; };
|
8398BF3F2769D2950048F38A /* mixcore.c in Sources */ = {isa = PBXBuildFile; fileRef = 8398BF392769D26D0048F38A /* mixcore.c */; };
|
||||||
83EEAB0A1C965247002761C5 /* resampler.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EEAB041C965247002761C5 /* resampler.h */; };
|
8398BF402769D2EE0048F38A /* mixcore.h in Headers */ = {isa = PBXBuildFile; fileRef = 8398BF362769D26D0048F38A /* mixcore.h */; settings = {ATTRIBUTES = (Private, ); }; };
|
||||||
83EEAB0B1C965247002761C5 /* syntrax.c in Sources */ = {isa = PBXBuildFile; fileRef = 83EEAB051C965247002761C5 /* syntrax.c */; };
|
8398BF412769D2F50048F38A /* ioutil.h in Headers */ = {isa = PBXBuildFile; fileRef = 8398BF3A2769D26D0048F38A /* ioutil.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
83EEAB0C1C965247002761C5 /* syntrax.h in Headers */ = {isa = PBXBuildFile; fileRef = 83EEAB061C965247002761C5 /* syntrax.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
8398BF422769D2F80048F38A /* jaytrax.h in Headers */ = {isa = PBXBuildFile; fileRef = 8398BF382769D26D0048F38A /* jaytrax.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
8398BF432769D2FD0048F38A /* jxs.h in Headers */ = {isa = PBXBuildFile; fileRef = 8398BF3B2769D26D0048F38A /* jxs.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
832FF31B1C96511E0076D662 /* Syntrax_c.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Syntrax_c.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
832FF31B1C96511E0076D662 /* Syntrax_c.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Syntrax_c.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
832FF3201C96511E0076D662 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
832FF3201C96511E0076D662 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
83EEAB011C965247002761C5 /* file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = file.c; sourceTree = "<group>"; };
|
8398BF362769D26D0048F38A /* mixcore.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mixcore.h; sourceTree = "<group>"; };
|
||||||
83EEAB021C965247002761C5 /* file.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file.h; sourceTree = "<group>"; };
|
8398BF372769D26D0048F38A /* jxs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = jxs.c; sourceTree = "<group>"; };
|
||||||
83EEAB031C965247002761C5 /* resampler.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = resampler.c; sourceTree = "<group>"; };
|
8398BF382769D26D0048F38A /* jaytrax.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = jaytrax.h; sourceTree = "<group>"; };
|
||||||
83EEAB041C965247002761C5 /* resampler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = resampler.h; sourceTree = "<group>"; };
|
8398BF392769D26D0048F38A /* mixcore.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mixcore.c; sourceTree = "<group>"; };
|
||||||
83EEAB051C965247002761C5 /* syntrax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = syntrax.c; sourceTree = "<group>"; };
|
8398BF3A2769D26D0048F38A /* ioutil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ioutil.h; sourceTree = "<group>"; };
|
||||||
83EEAB061C965247002761C5 /* syntrax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = syntrax.h; sourceTree = "<group>"; };
|
8398BF3B2769D26D0048F38A /* jxs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = jxs.h; sourceTree = "<group>"; };
|
||||||
|
8398BF3C2769D26D0048F38A /* jaytrax.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = jaytrax.c; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -56,12 +58,13 @@
|
||||||
832FF31D1C96511E0076D662 /* Syntrax-c */ = {
|
832FF31D1C96511E0076D662 /* Syntrax-c */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
83EEAB011C965247002761C5 /* file.c */,
|
8398BF3A2769D26D0048F38A /* ioutil.h */,
|
||||||
83EEAB021C965247002761C5 /* file.h */,
|
8398BF3C2769D26D0048F38A /* jaytrax.c */,
|
||||||
83EEAB031C965247002761C5 /* resampler.c */,
|
8398BF382769D26D0048F38A /* jaytrax.h */,
|
||||||
83EEAB041C965247002761C5 /* resampler.h */,
|
8398BF372769D26D0048F38A /* jxs.c */,
|
||||||
83EEAB051C965247002761C5 /* syntrax.c */,
|
8398BF3B2769D26D0048F38A /* jxs.h */,
|
||||||
83EEAB061C965247002761C5 /* syntrax.h */,
|
8398BF392769D26D0048F38A /* mixcore.c */,
|
||||||
|
8398BF362769D26D0048F38A /* mixcore.h */,
|
||||||
832FF3201C96511E0076D662 /* Info.plist */,
|
832FF3201C96511E0076D662 /* Info.plist */,
|
||||||
);
|
);
|
||||||
path = "Syntrax-c";
|
path = "Syntrax-c";
|
||||||
|
@ -74,9 +77,10 @@
|
||||||
isa = PBXHeadersBuildPhase;
|
isa = PBXHeadersBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
83EEAB0C1C965247002761C5 /* syntrax.h in Headers */,
|
8398BF412769D2F50048F38A /* ioutil.h in Headers */,
|
||||||
83EEAB081C965247002761C5 /* file.h in Headers */,
|
8398BF422769D2F80048F38A /* jaytrax.h in Headers */,
|
||||||
83EEAB0A1C965247002761C5 /* resampler.h in Headers */,
|
8398BF432769D2FD0048F38A /* jxs.h in Headers */,
|
||||||
|
8398BF402769D2EE0048F38A /* mixcore.h in Headers */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -149,9 +153,9 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
83EEAB071C965247002761C5 /* file.c in Sources */,
|
8398BF3D2769D28A0048F38A /* jaytrax.c in Sources */,
|
||||||
83EEAB0B1C965247002761C5 /* syntrax.c in Sources */,
|
8398BF3E2769D28D0048F38A /* jxs.c in Sources */,
|
||||||
83EEAB091C965247002761C5 /* resampler.c in Sources */,
|
8398BF3F2769D2950048F38A /* mixcore.c in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
// Copyright 2016 __NoWork, Inc__. All rights reserved.
|
// Copyright 2016 __NoWork, Inc__. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
#import <Syntrax_c/syntrax.h>
|
#import <Syntrax_c/jxs.h>
|
||||||
#import <Syntrax_c/file.h>
|
#import <Syntrax_c/jaytrax.h>
|
||||||
|
|
||||||
#import "jxsContainer.h"
|
#import "jxsContainer.h"
|
||||||
#import "jxsDecoder.h"
|
#import "jxsDecoder.h"
|
||||||
|
@ -54,8 +54,8 @@
|
||||||
void * data = malloc(size);
|
void * data = malloc(size);
|
||||||
[source read:data amount:size];
|
[source read:data amount:size];
|
||||||
|
|
||||||
Song * synSong = File_loadSongMem(data, size);
|
JT1Song * synSong;
|
||||||
if (!synSong)
|
if (jxsfile_readSongMem(data, size, &synSong))
|
||||||
{
|
{
|
||||||
ALog(@"Open failed for file: %@", [url absoluteString]);
|
ALog(@"Open failed for file: %@", [url absoluteString]);
|
||||||
free(data);
|
free(data);
|
||||||
|
@ -64,33 +64,13 @@
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
Player * synPlayer = playerCreate(44100);
|
int i;
|
||||||
if (!synPlayer)
|
int subsongs = synSong->nrofsongs;
|
||||||
{
|
|
||||||
ALog(@"Failed to create Syntrax-c player for file: %@", [url absoluteString]);
|
|
||||||
File_freeSong(synSong);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loadSong(synPlayer, synSong) < 0)
|
jxsfile_freeSong(synSong);
|
||||||
{
|
|
||||||
ALog(@"Load failed for file: %@", [url absoluteString]);
|
|
||||||
playerDestroy(synPlayer);
|
|
||||||
File_freeSong(synSong);
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSMutableArray *tracks = [NSMutableArray array];
|
NSMutableArray *tracks = [NSMutableArray array];
|
||||||
|
|
||||||
syntrax_info info;
|
|
||||||
|
|
||||||
playerGetInfo(synPlayer, &info);
|
|
||||||
|
|
||||||
playerDestroy(synPlayer);
|
|
||||||
File_freeSong(synSong);
|
|
||||||
|
|
||||||
int i;
|
|
||||||
int subsongs = info.totalSubs;
|
|
||||||
if ( subsongs ) {
|
if ( subsongs ) {
|
||||||
for (i = 0; i < subsongs; i++)
|
for (i = 0; i < subsongs; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,14 +8,14 @@
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
#import <Syntrax_c/file.h>
|
#import <Syntrax_c/jxs.h>
|
||||||
#import <Syntrax_c/syntrax.h>
|
#import <Syntrax_c/jaytrax.h>
|
||||||
|
|
||||||
#import "Plugin.h"
|
#import "Plugin.h"
|
||||||
|
|
||||||
@interface jxsDecoder : NSObject <CogDecoder> {
|
@interface jxsDecoder : NSObject <CogDecoder> {
|
||||||
Song *synSong;
|
JT1Song *synSong;
|
||||||
Player *synPlayer;
|
JT1Player *synPlayer;
|
||||||
int track_num;
|
int track_num;
|
||||||
|
|
||||||
long framesLength;
|
long framesLength;
|
||||||
|
|
|
@ -14,50 +14,6 @@
|
||||||
|
|
||||||
@implementation jxsDecoder
|
@implementation jxsDecoder
|
||||||
|
|
||||||
BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long * loop_length, unsigned int subsong )
|
|
||||||
{
|
|
||||||
Player * synPlayer = playerCreate(44100);
|
|
||||||
|
|
||||||
if (loadSong(synPlayer, synSong) < 0)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
initSubsong(synPlayer, subsong);
|
|
||||||
|
|
||||||
unsigned long length_total = 0;
|
|
||||||
unsigned long length_saved;
|
|
||||||
|
|
||||||
const long length_safety = 44100 * 60 * 30;
|
|
||||||
|
|
||||||
while ( !playerGetSongEnded(synPlayer) && playerGetLoopCount(synPlayer) < 1 && length_total < length_safety )
|
|
||||||
{
|
|
||||||
mixChunk(synPlayer, NULL, 512);
|
|
||||||
length_total += 512;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !playerGetSongEnded(synPlayer) && playerGetLoopCount(synPlayer) < 1 )
|
|
||||||
{
|
|
||||||
*loop_length = 0;
|
|
||||||
*intro_length = 44100 * 60 * 3;
|
|
||||||
playerDestroy(synPlayer);
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
length_saved = length_total;
|
|
||||||
|
|
||||||
while ( !playerGetSongEnded(synPlayer) && playerGetLoopCount(synPlayer) < 2 )
|
|
||||||
{
|
|
||||||
mixChunk(synPlayer, NULL, 512);
|
|
||||||
length_total += 512;
|
|
||||||
}
|
|
||||||
|
|
||||||
playerDestroy(synPlayer);
|
|
||||||
|
|
||||||
*loop_length = length_total - length_saved;
|
|
||||||
*intro_length = length_saved - *loop_length;
|
|
||||||
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id)init
|
- (id)init
|
||||||
{
|
{
|
||||||
self = [super init];
|
self = [super init];
|
||||||
|
@ -82,19 +38,22 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
else
|
else
|
||||||
track_num = [[[s url] fragment] intValue];
|
track_num = [[[s url] fragment] intValue];
|
||||||
|
|
||||||
synSong = File_loadSongMem(data, size);
|
if (jxsfile_readSongMem(data, size, &synSong))
|
||||||
if (!synSong)
|
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
unsigned long intro_length, loop_length;
|
synPlayer = jaytrax_init();
|
||||||
|
if (!synPlayer)
|
||||||
if ( !probe_length(synSong, &intro_length, &loop_length, track_num) )
|
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
framesLength = intro_length + loop_length * 2;
|
if (!jaytrax_loadSong(synPlayer, synSong))
|
||||||
totalFrames = framesLength + 44100 * 8;
|
return NO;
|
||||||
|
|
||||||
|
framesLength = jaytrax_getLength(synPlayer, track_num, 2, 44100);
|
||||||
|
totalFrames = framesLength + ((synPlayer->playFlg) ? 44100 * 8 : 0);
|
||||||
|
|
||||||
|
framesRead = 0;
|
||||||
|
|
||||||
[self willChangeValueForKey:@"properties"];
|
[self willChangeValueForKey:@"properties"];
|
||||||
[self didChangeValueForKey:@"properties"];
|
[self didChangeValueForKey:@"properties"];
|
||||||
|
@ -104,14 +63,24 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
|
|
||||||
- (BOOL)decoderInitialize
|
- (BOOL)decoderInitialize
|
||||||
{
|
{
|
||||||
synPlayer = playerCreate(44100);
|
jaytrax_changeSubsong(synPlayer, track_num);
|
||||||
if (!synPlayer)
|
|
||||||
return NO;
|
|
||||||
|
|
||||||
if (loadSong(synPlayer, synSong) < 0)
|
uint8_t interp = ITP_CUBIC;
|
||||||
return NO;
|
NSString * resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"];
|
||||||
|
if ([resampling isEqualToString:@"zoh"])
|
||||||
|
interp = ITP_NEAREST;
|
||||||
|
else if ([resampling isEqualToString:@"blep"])
|
||||||
|
interp = ITP_NEAREST;
|
||||||
|
else if ([resampling isEqualToString:@"linear"])
|
||||||
|
interp = ITP_LINEAR;
|
||||||
|
else if ([resampling isEqualToString:@"blam"])
|
||||||
|
interp = ITP_LINEAR;
|
||||||
|
else if ([resampling isEqualToString:@"cubic"])
|
||||||
|
interp = ITP_CUBIC;
|
||||||
|
else if ([resampling isEqualToString:@"sinc"])
|
||||||
|
interp = ITP_CUBIC;
|
||||||
|
|
||||||
initSubsong(synPlayer, track_num);
|
jaytrax_setInterpolation(synPlayer, interp);
|
||||||
|
|
||||||
framesRead = 0;
|
framesRead = 0;
|
||||||
|
|
||||||
|
@ -120,11 +89,12 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
|
|
||||||
- (void)decoderShutdown
|
- (void)decoderShutdown
|
||||||
{
|
{
|
||||||
if ( synPlayer )
|
if (synPlayer)
|
||||||
{
|
{
|
||||||
playerDestroy(synPlayer);
|
jaytrax_stopSong(synPlayer);
|
||||||
synPlayer = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
framesRead = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *)properties
|
- (NSDictionary *)properties
|
||||||
|
@ -146,18 +116,18 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
{
|
{
|
||||||
BOOL repeat_one = IsRepeatOneSet();
|
BOOL repeat_one = IsRepeatOneSet();
|
||||||
|
|
||||||
if ( synPlayer && playerGetSongEnded(synPlayer) )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if ( !repeat_one && framesRead >= totalFrames )
|
if ( !repeat_one && framesRead >= totalFrames )
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ( !synPlayer )
|
if ( !framesRead )
|
||||||
{
|
{
|
||||||
if ( ![self decoderInitialize] )
|
if ( ![self decoderInitialize] )
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( synPlayer && !synPlayer->playFlg )
|
||||||
|
return 0;
|
||||||
|
|
||||||
int total = 0;
|
int total = 0;
|
||||||
while ( total < frames ) {
|
while ( total < frames ) {
|
||||||
int framesToRender = 512;
|
int framesToRender = 512;
|
||||||
|
@ -168,7 +138,7 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
|
|
||||||
int16_t * sampleBuf = ( int16_t * ) buf + total * 2;
|
int16_t * sampleBuf = ( int16_t * ) buf + total * 2;
|
||||||
|
|
||||||
mixChunk(synPlayer, sampleBuf, framesToRender);
|
jaytrax_renderChunk(synPlayer, sampleBuf, framesToRender, 44100);
|
||||||
|
|
||||||
if ( !repeat_one && framesRead + framesToRender > framesLength ) {
|
if ( !repeat_one && framesRead + framesToRender > framesLength ) {
|
||||||
long fadeStart = ( framesLength > framesRead ) ? framesLength : framesRead;
|
long fadeStart = ( framesLength > framesRead ) ? framesLength : framesRead;
|
||||||
|
@ -191,7 +161,7 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
total += framesToRender;
|
total += framesToRender;
|
||||||
framesRead += framesToRender;
|
framesRead += framesToRender;
|
||||||
|
|
||||||
if ( playerGetSongEnded(synPlayer) )
|
if ( !synPlayer->playFlg )
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,7 +182,7 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
int frames_todo = INT_MAX;
|
int frames_todo = INT_MAX;
|
||||||
if ( frames_todo > frame - framesRead )
|
if ( frames_todo > frame - framesRead )
|
||||||
frames_todo = (int)( frame - framesRead );
|
frames_todo = (int)( frame - framesRead );
|
||||||
mixChunk(synPlayer, NULL, frames_todo);
|
jaytrax_renderChunk(synPlayer, NULL, frames_todo, 44100);
|
||||||
framesRead += frames_todo;
|
framesRead += frames_todo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,9 +195,15 @@ BOOL probe_length( Song * synSong, unsigned long * intro_length, unsigned long *
|
||||||
{
|
{
|
||||||
[self decoderShutdown];
|
[self decoderShutdown];
|
||||||
|
|
||||||
|
if (synPlayer)
|
||||||
|
{
|
||||||
|
jaytrax_free(synPlayer);
|
||||||
|
synPlayer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (synSong)
|
if (synSong)
|
||||||
{
|
{
|
||||||
File_freeSong(synSong);
|
jxsfile_freeSong(synSong);
|
||||||
synSong = NULL;
|
synSong = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#import "jxsMetadataReader.h"
|
#import "jxsMetadataReader.h"
|
||||||
#import "jxsDecoder.h"
|
#import "jxsDecoder.h"
|
||||||
|
|
||||||
#import <Syntrax_c/syntrax.h>
|
#import <Syntrax_c/jxs.h>
|
||||||
#import <Syntrax_c/file.h>
|
#import <Syntrax_c/jaytrax.h>
|
||||||
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
|
||||||
|
@ -49,8 +49,8 @@
|
||||||
void * data = malloc(size);
|
void * data = malloc(size);
|
||||||
[source read:data amount:size];
|
[source read:data amount:size];
|
||||||
|
|
||||||
Song * synSong = File_loadSongMem(data, size);
|
JT1Song* synSong;
|
||||||
if (!synSong)
|
if (jxsfile_readSongMem(data, size, &synSong))
|
||||||
{
|
{
|
||||||
ALog(@"Open failed for file: %@", [url absoluteString]);
|
ALog(@"Open failed for file: %@", [url absoluteString]);
|
||||||
free(data);
|
free(data);
|
||||||
|
@ -59,19 +59,19 @@
|
||||||
|
|
||||||
free(data);
|
free(data);
|
||||||
|
|
||||||
Player * synPlayer = playerCreate(44100);
|
JT1Player * synPlayer = jaytrax_init();
|
||||||
if (!synPlayer)
|
if (!synPlayer)
|
||||||
{
|
{
|
||||||
ALog(@"Failed to create player for file: %@", [url absoluteString]);
|
ALog(@"Failed to create player for file: %@", [url absoluteString]);
|
||||||
File_freeSong(synSong);
|
jxsfile_freeSong(synSong);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loadSong(synPlayer, synSong) < 0)
|
if (!jaytrax_loadSong(synPlayer, synSong))
|
||||||
{
|
{
|
||||||
ALog(@"Load failed for file: %@", [url absoluteString]);
|
ALog(@"Load failed for file: %@", [url absoluteString]);
|
||||||
playerDestroy(synPlayer);
|
jaytrax_free(synPlayer);
|
||||||
File_freeSong(synSong);
|
jxsfile_freeSong(synSong);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,16 +81,13 @@
|
||||||
else
|
else
|
||||||
track_num = [[url fragment] intValue];
|
track_num = [[url fragment] intValue];
|
||||||
|
|
||||||
initSubsong(synPlayer, track_num);
|
jaytrax_changeSubsong(synPlayer, track_num);
|
||||||
|
|
||||||
syntrax_info info;
|
|
||||||
playerGetInfo(synPlayer, &info);
|
|
||||||
|
|
||||||
playerDestroy(synPlayer);
|
|
||||||
File_freeSong(synSong);
|
|
||||||
|
|
||||||
//Some titles are all spaces?!
|
//Some titles are all spaces?!
|
||||||
NSString *title = [[NSString stringWithUTF8String: info.subsongName] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
NSString *title = [[NSString stringWithUTF8String: synPlayer->subsong->name] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
|
||||||
|
jaytrax_free(synPlayer);
|
||||||
|
jxsfile_freeSong(synSong);
|
||||||
|
|
||||||
if (title == nil) {
|
if (title == nil) {
|
||||||
title = @"";
|
title = @"";
|
||||||
|
|
Loading…
Reference in New Issue