Adding DUMB onto the repo
This commit is contained in:
parent
24c725584e
commit
202f3448c2
47 changed files with 13052 additions and 0 deletions
71
dumb/core/atexit.c
Normal file
71
dumb/core/atexit.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* atexit.c - Library Clean-up Management. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
typedef struct DUMB_ATEXIT_PROC
|
||||
{
|
||||
struct DUMB_ATEXIT_PROC *next;
|
||||
void (*proc)(void);
|
||||
}
|
||||
DUMB_ATEXIT_PROC;
|
||||
|
||||
|
||||
|
||||
static DUMB_ATEXIT_PROC *dumb_atexit_proc = NULL;
|
||||
|
||||
|
||||
|
||||
int dumb_atexit(void (*proc)(void))
|
||||
{
|
||||
DUMB_ATEXIT_PROC *dap = dumb_atexit_proc;
|
||||
|
||||
while (dap) {
|
||||
if (dap->proc == proc) return 0;
|
||||
dap = dap->next;
|
||||
}
|
||||
|
||||
dap = malloc(sizeof(*dap));
|
||||
|
||||
if (!dap)
|
||||
return -1;
|
||||
|
||||
dap->next = dumb_atexit_proc;
|
||||
dap->proc = proc;
|
||||
dumb_atexit_proc = dap;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_exit(void)
|
||||
{
|
||||
while (dumb_atexit_proc) {
|
||||
DUMB_ATEXIT_PROC *next = dumb_atexit_proc->next;
|
||||
(*dumb_atexit_proc->proc)();
|
||||
free(dumb_atexit_proc);
|
||||
dumb_atexit_proc = next;
|
||||
}
|
||||
}
|
42
dumb/core/duhlen.c
Normal file
42
dumb/core/duhlen.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* duhlen.c - Functions to set and return the / / \ \
|
||||
* length of a DUH. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* Note that the length of a DUH is a constant | ' /
|
||||
* stored in the DUH struct and in the DUH disk \__/
|
||||
* format. It will be calculated on loading for
|
||||
* other formats in which the length is not explicitly stored. Also note that
|
||||
* it does not necessarily correspond to the length of time for which the DUH
|
||||
* will generate samples. Rather it represents a suitable point for a player
|
||||
* such as Winamp to stop, and in any good DUH it will allow for any final
|
||||
* flourish to fade out and be appreciated.
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
long duh_get_length(DUH *duh)
|
||||
{
|
||||
return duh ? duh->length : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void duh_set_length(DUH *duh, long length)
|
||||
{
|
||||
if (duh)
|
||||
duh->length = length;
|
||||
}
|
38
dumb/core/duhtag.c
Normal file
38
dumb/core/duhtag.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* duhtag.c - Function to return the tags stored / / \ \
|
||||
* in a DUH struct (typically author | < / \_
|
||||
* information). | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
const char *duh_get_tag(DUH *duh, const char *key)
|
||||
{
|
||||
int i;
|
||||
ASSERT(key);
|
||||
if (!duh || !duh->tag) return NULL;
|
||||
|
||||
for (i = 0; i < duh->n_tags; i++)
|
||||
if (strcmp(key, duh->tag[i][0]) == 0)
|
||||
return duh->tag[i][1];
|
||||
|
||||
return NULL;
|
||||
}
|
401
dumb/core/dumbfile.c
Normal file
401
dumb/core/dumbfile.c
Normal file
|
@ -0,0 +1,401 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* dumbfile.c - Hookable, strictly sequential / / \ \
|
||||
* file input functions. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
static DUMBFILE_SYSTEM *the_dfs = NULL;
|
||||
|
||||
|
||||
|
||||
void register_dumbfile_system(DUMBFILE_SYSTEM *dfs)
|
||||
{
|
||||
ASSERT(dfs);
|
||||
ASSERT(dfs->open);
|
||||
ASSERT(dfs->getc);
|
||||
ASSERT(dfs->close);
|
||||
the_dfs = dfs;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct DUMBFILE
|
||||
{
|
||||
DUMBFILE_SYSTEM *dfs;
|
||||
void *file;
|
||||
long pos;
|
||||
};
|
||||
|
||||
|
||||
|
||||
DUMBFILE *dumbfile_open(const char *filename)
|
||||
{
|
||||
DUMBFILE *f;
|
||||
|
||||
ASSERT(the_dfs);
|
||||
|
||||
f = malloc(sizeof(*f));
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
f->dfs = the_dfs;
|
||||
|
||||
f->file = (*the_dfs->open)(filename);
|
||||
|
||||
if (!f->file) {
|
||||
free(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->pos = 0;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs)
|
||||
{
|
||||
DUMBFILE *f;
|
||||
|
||||
ASSERT(dfs);
|
||||
ASSERT(dfs->getc);
|
||||
ASSERT(file);
|
||||
|
||||
f = malloc(sizeof(*f));
|
||||
|
||||
if (!f) {
|
||||
if (dfs->close)
|
||||
(*dfs->close)(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
f->dfs = dfs;
|
||||
f->file = file;
|
||||
|
||||
f->pos = 0;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumbfile_pos(DUMBFILE *f)
|
||||
{
|
||||
ASSERT(f);
|
||||
|
||||
return f->pos;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumbfile_skip(DUMBFILE *f, long n)
|
||||
{
|
||||
int rv;
|
||||
|
||||
ASSERT(f);
|
||||
ASSERT(n >= 0);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
f->pos += n;
|
||||
|
||||
if (f->dfs->skip) {
|
||||
rv = (*f->dfs->skip)(f->file, n);
|
||||
if (rv) {
|
||||
f->pos = -1;
|
||||
return rv;
|
||||
}
|
||||
} else {
|
||||
while (n) {
|
||||
rv = (*f->dfs->getc)(f->file);
|
||||
if (rv < 0) {
|
||||
f->pos = -1;
|
||||
return rv;
|
||||
}
|
||||
n--;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumbfile_getc(DUMBFILE *f)
|
||||
{
|
||||
int rv;
|
||||
|
||||
ASSERT(f);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
rv = (*f->dfs->getc)(f->file);
|
||||
|
||||
if (rv < 0) {
|
||||
f->pos = -1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
f->pos++;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumbfile_igetw(DUMBFILE *f)
|
||||
{
|
||||
int l, h;
|
||||
|
||||
ASSERT(f);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
l = (*f->dfs->getc)(f->file);
|
||||
if (l < 0) {
|
||||
f->pos = -1;
|
||||
return l;
|
||||
}
|
||||
|
||||
h = (*f->dfs->getc)(f->file);
|
||||
if (h < 0) {
|
||||
f->pos = -1;
|
||||
return h;
|
||||
}
|
||||
|
||||
f->pos += 2;
|
||||
|
||||
return l | (h << 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumbfile_mgetw(DUMBFILE *f)
|
||||
{
|
||||
int l, h;
|
||||
|
||||
ASSERT(f);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
h = (*f->dfs->getc)(f->file);
|
||||
if (h < 0) {
|
||||
f->pos = -1;
|
||||
return h;
|
||||
}
|
||||
|
||||
l = (*f->dfs->getc)(f->file);
|
||||
if (l < 0) {
|
||||
f->pos = -1;
|
||||
return l;
|
||||
}
|
||||
|
||||
f->pos += 2;
|
||||
|
||||
return l | (h << 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumbfile_igetl(DUMBFILE *f)
|
||||
{
|
||||
unsigned long rv, b;
|
||||
|
||||
ASSERT(f);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
rv = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)rv < 0) {
|
||||
f->pos = -1;
|
||||
return rv;
|
||||
}
|
||||
|
||||
b = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)b < 0) {
|
||||
f->pos = -1;
|
||||
return b;
|
||||
}
|
||||
rv |= b << 8;
|
||||
|
||||
b = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)b < 0) {
|
||||
f->pos = -1;
|
||||
return b;
|
||||
}
|
||||
rv |= b << 16;
|
||||
|
||||
b = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)b < 0) {
|
||||
f->pos = -1;
|
||||
return b;
|
||||
}
|
||||
rv |= b << 24;
|
||||
|
||||
f->pos += 4;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumbfile_mgetl(DUMBFILE *f)
|
||||
{
|
||||
unsigned long rv, b;
|
||||
|
||||
ASSERT(f);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
rv = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)rv < 0) {
|
||||
f->pos = -1;
|
||||
return rv;
|
||||
}
|
||||
rv <<= 24;
|
||||
|
||||
b = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)b < 0) {
|
||||
f->pos = -1;
|
||||
return b;
|
||||
}
|
||||
rv |= b << 16;
|
||||
|
||||
b = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)b < 0) {
|
||||
f->pos = -1;
|
||||
return b;
|
||||
}
|
||||
rv |= b << 8;
|
||||
|
||||
b = (*f->dfs->getc)(f->file);
|
||||
if ((signed long)b < 0) {
|
||||
f->pos = -1;
|
||||
return b;
|
||||
}
|
||||
rv |= b;
|
||||
|
||||
f->pos += 4;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned long dumbfile_cgetul(DUMBFILE *f)
|
||||
{
|
||||
unsigned long rv = 0;
|
||||
int v;
|
||||
|
||||
do {
|
||||
v = dumbfile_getc(f);
|
||||
|
||||
if (v < 0)
|
||||
return v;
|
||||
|
||||
rv <<= 7;
|
||||
rv |= v & 0x7F;
|
||||
} while (v & 0x80);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
signed long dumbfile_cgetsl(DUMBFILE *f)
|
||||
{
|
||||
unsigned long rv = dumbfile_cgetul(f);
|
||||
|
||||
if (f->pos < 0)
|
||||
return rv;
|
||||
|
||||
return (rv >> 1) | (rv << 31);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumbfile_getnc(char *ptr, long n, DUMBFILE *f)
|
||||
{
|
||||
long rv;
|
||||
|
||||
ASSERT(f);
|
||||
ASSERT(n >= 0);
|
||||
|
||||
if (f->pos < 0)
|
||||
return -1;
|
||||
|
||||
if (f->dfs->getnc) {
|
||||
rv = (*f->dfs->getnc)(ptr, n, f->file);
|
||||
if (rv < n) {
|
||||
f->pos = -1;
|
||||
return MAX(rv, 0);
|
||||
}
|
||||
} else {
|
||||
for (rv = 0; rv < n; rv++) {
|
||||
int c = (*f->dfs->getc)(f->file);
|
||||
if (c < 0) {
|
||||
f->pos = -1;
|
||||
return rv;
|
||||
}
|
||||
*ptr++ = c;
|
||||
}
|
||||
}
|
||||
|
||||
f->pos += rv;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumbfile_error(DUMBFILE *f)
|
||||
{
|
||||
ASSERT(f);
|
||||
|
||||
return f->pos < 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumbfile_close(DUMBFILE *f)
|
||||
{
|
||||
int rv;
|
||||
|
||||
ASSERT(f);
|
||||
|
||||
rv = f->pos < 0;
|
||||
|
||||
if (f->dfs->close)
|
||||
(*f->dfs->close)(f->file);
|
||||
|
||||
free(f);
|
||||
|
||||
return rv;
|
||||
}
|
42
dumb/core/loadduh.c
Normal file
42
dumb/core/loadduh.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loadduh.c - Code to read a DUH from a file, / / \ \
|
||||
* opening and closing the file for | < / \_
|
||||
* you. | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
/* load_duh(): loads a .duh file, returning a pointer to a DUH struct.
|
||||
* When you have finished with it, you must pass the pointer to unload_duh()
|
||||
* so that the memory can be freed.
|
||||
*/
|
||||
DUH *load_duh(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = read_duh(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
132
dumb/core/makeduh.c
Normal file
132
dumb/core/makeduh.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* makeduh.c - Function to construct a DUH from / / \ \
|
||||
* its components. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
static DUH_SIGNAL *make_signal(DUH_SIGTYPE_DESC *desc, sigdata_t *sigdata)
|
||||
{
|
||||
DUH_SIGNAL *signal;
|
||||
|
||||
ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer));
|
||||
ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample);
|
||||
|
||||
signal = malloc(sizeof(*signal));
|
||||
|
||||
if (!signal) {
|
||||
if (desc->unload_sigdata)
|
||||
if (sigdata)
|
||||
(*desc->unload_sigdata)(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
signal->desc = desc;
|
||||
signal->sigdata = sigdata;
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUH *make_duh(
|
||||
long length,
|
||||
int n_tags,
|
||||
const char *const tags[][2],
|
||||
int n_signals,
|
||||
DUH_SIGTYPE_DESC *desc[],
|
||||
sigdata_t *sigdata[]
|
||||
)
|
||||
{
|
||||
DUH *duh = malloc(sizeof(*duh));
|
||||
int i;
|
||||
int fail;
|
||||
|
||||
if (duh) {
|
||||
duh->n_signals = n_signals;
|
||||
|
||||
duh->signal = malloc(n_signals * sizeof(*duh->signal));
|
||||
|
||||
if (!duh->signal) {
|
||||
free(duh);
|
||||
duh = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (!duh) {
|
||||
for (i = 0; i < n_signals; i++)
|
||||
if (desc[i]->unload_sigdata)
|
||||
if (sigdata[i])
|
||||
(*desc[i]->unload_sigdata)(sigdata[i]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
duh->n_tags = 0;
|
||||
duh->tag = NULL;
|
||||
|
||||
fail = 0;
|
||||
|
||||
for (i = 0; i < n_signals; i++) {
|
||||
duh->signal[i] = make_signal(desc[i], sigdata[i]);
|
||||
if (!duh->signal[i])
|
||||
fail = 1;
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
unload_duh(duh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
duh->length = length;
|
||||
|
||||
{
|
||||
int mem = n_tags * 2; /* account for NUL terminators here */
|
||||
char *ptr;
|
||||
|
||||
for (i = 0; i < n_tags; i++)
|
||||
mem += strlen(tags[i][0]) + strlen(tags[i][1]);
|
||||
|
||||
if (mem <= 0) return duh;
|
||||
|
||||
duh->tag = malloc(n_tags * sizeof(*duh->tag));
|
||||
if (!duh->tag) return duh;
|
||||
duh->tag[0][0] = malloc(mem);
|
||||
if (!duh->tag[0][0]) {
|
||||
free(duh->tag);
|
||||
duh->tag = NULL;
|
||||
return duh;
|
||||
}
|
||||
duh->n_tags = n_tags;
|
||||
ptr = duh->tag[0][0];
|
||||
for (i = 0; i < n_tags; i++) {
|
||||
duh->tag[i][0] = ptr;
|
||||
strcpy(ptr, tags[i][0]);
|
||||
ptr += strlen(tags[i][0]) + 1;
|
||||
duh->tag[i][1] = ptr;
|
||||
strcpy(ptr, tags[i][1]);
|
||||
ptr += strlen(tags[i][1]) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return duh;
|
||||
}
|
44
dumb/core/rawsig.c
Normal file
44
dumb/core/rawsig.c
Normal file
|
@ -0,0 +1,44 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* rawsig.c - Function to retrieve raw signal / / \ \
|
||||
* data from a DUH provided you know | < / \_
|
||||
* what type of signal it is. | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
/* You have to specify the type of sigdata, proving you know what to do with
|
||||
* the pointer. If you get it wrong, you can expect NULL back.
|
||||
*/
|
||||
sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type)
|
||||
{
|
||||
DUH_SIGNAL *signal;
|
||||
|
||||
if (!duh) return NULL;
|
||||
|
||||
if ((unsigned int)sig >= (unsigned int)duh->n_signals) return NULL;
|
||||
|
||||
signal = duh->signal[sig];
|
||||
|
||||
if (signal && signal->desc->type == type)
|
||||
return signal->sigdata;
|
||||
|
||||
return NULL;
|
||||
}
|
107
dumb/core/readduh.c
Normal file
107
dumb/core/readduh.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readduh.c - Code to read a DUH from an open / / \ \
|
||||
* file. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
static DUH_SIGNAL *read_signal(DUH *duh, DUMBFILE *f)
|
||||
{
|
||||
DUH_SIGNAL *signal;
|
||||
long type;
|
||||
|
||||
signal = malloc(sizeof(*signal));
|
||||
|
||||
if (!signal)
|
||||
return NULL;
|
||||
|
||||
type = dumbfile_mgetl(f);
|
||||
if (dumbfile_error(f)) {
|
||||
free(signal);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
signal->desc = _dumb_get_sigtype_desc(type);
|
||||
if (!signal->desc) {
|
||||
free(signal);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (signal->desc->load_sigdata) {
|
||||
signal->sigdata = (*signal->desc->load_sigdata)(duh, f);
|
||||
if (!signal->sigdata) {
|
||||
free(signal);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
signal->sigdata = NULL;
|
||||
|
||||
return signal;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* read_duh(): reads a DUH from an already open DUMBFILE, and returns its
|
||||
* pointer, or null on error. The file is not closed.
|
||||
*/
|
||||
DUH *read_duh(DUMBFILE *f)
|
||||
{
|
||||
DUH *duh;
|
||||
int i;
|
||||
|
||||
if (dumbfile_mgetl(f) != DUH_SIGNATURE)
|
||||
return NULL;
|
||||
|
||||
duh = malloc(sizeof(*duh));
|
||||
if (!duh)
|
||||
return NULL;
|
||||
|
||||
duh->length = dumbfile_igetl(f);
|
||||
if (dumbfile_error(f) || duh->length <= 0) {
|
||||
free(duh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
duh->n_signals = dumbfile_igetl(f);
|
||||
if (dumbfile_error(f) || duh->n_signals <= 0) {
|
||||
free(duh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
duh->signal = malloc(sizeof(*duh->signal) * duh->n_signals);
|
||||
if (!duh->signal) {
|
||||
free(duh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < duh->n_signals; i++)
|
||||
duh->signal[i] = NULL;
|
||||
|
||||
for (i = 0; i < duh->n_signals; i++) {
|
||||
if (!(duh->signal[i] = read_signal(duh, f))) {
|
||||
unload_duh(duh);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return duh;
|
||||
}
|
104
dumb/core/register.c
Normal file
104
dumb/core/register.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* register.c - Signal type registration. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
static DUH_SIGTYPE_DESC_LINK *sigtype_desc = NULL;
|
||||
static DUH_SIGTYPE_DESC_LINK **sigtype_desc_tail = &sigtype_desc;
|
||||
|
||||
|
||||
|
||||
/* destroy_sigtypes(): frees all memory allocated while registering signal
|
||||
* types. This function is set up to be called by dumb_exit().
|
||||
*/
|
||||
static void destroy_sigtypes(void)
|
||||
{
|
||||
DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc, *next;
|
||||
sigtype_desc = NULL;
|
||||
sigtype_desc_tail = &sigtype_desc;
|
||||
|
||||
while (desc_link) {
|
||||
next = desc_link->next;
|
||||
free(desc_link);
|
||||
desc_link = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* dumb_register_sigtype(): registers a new signal type with DUMB. The signal
|
||||
* type is identified by a four-character string (e.g. "WAVE"), which you can
|
||||
* encode using the the DUMB_ID() macro (e.g. DUMB_ID('W','A','V','E')). The
|
||||
* signal's behaviour is defined by four functions, whose pointers you pass
|
||||
* here. See the documentation for details.
|
||||
*
|
||||
* If a DUH tries to use a signal that has not been registered using this
|
||||
* function, then the library will fail to load the DUH.
|
||||
*/
|
||||
void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc)
|
||||
{
|
||||
DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc;
|
||||
|
||||
ASSERT((desc->load_sigdata && desc->unload_sigdata) || (!desc->load_sigdata && !desc->unload_sigdata));
|
||||
ASSERT((desc->start_sigrenderer && desc->end_sigrenderer) || (!desc->start_sigrenderer && !desc->end_sigrenderer));
|
||||
ASSERT(desc->sigrenderer_generate_samples && desc->sigrenderer_get_current_sample);
|
||||
|
||||
if (desc_link) {
|
||||
do {
|
||||
if (desc_link->desc->type == desc->type) {
|
||||
desc_link->desc = desc;
|
||||
return;
|
||||
}
|
||||
desc_link = desc_link->next;
|
||||
} while (desc_link);
|
||||
} else
|
||||
dumb_atexit(&destroy_sigtypes);
|
||||
|
||||
desc_link = *sigtype_desc_tail = malloc(sizeof(DUH_SIGTYPE_DESC_LINK));
|
||||
|
||||
if (!desc_link)
|
||||
return;
|
||||
|
||||
desc_link->next = NULL;
|
||||
sigtype_desc_tail = &desc_link->next;
|
||||
|
||||
desc_link->desc = desc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* _dumb_get_sigtype_desc(): searches the registered functions for a signal
|
||||
* type matching the parameter. If such a sigtype is found, it returns a
|
||||
* pointer to a sigtype descriptor containing the necessary functions to
|
||||
* manage the signal. If none is found, it returns NULL.
|
||||
*/
|
||||
DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type)
|
||||
{
|
||||
DUH_SIGTYPE_DESC_LINK *desc_link = sigtype_desc;
|
||||
|
||||
while (desc_link && desc_link->desc->type != type)
|
||||
desc_link = desc_link->next;
|
||||
|
||||
return desc_link ? desc_link->desc : NULL;
|
||||
}
|
184
dumb/core/rendduh.c
Normal file
184
dumb/core/rendduh.c
Normal file
|
@ -0,0 +1,184 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* rendduh.c - Functions for rendering a DUH into / / \ \
|
||||
* an end-user sample format. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
/* On the x86, we can use some tricks to speed stuff up */
|
||||
#if (defined _MSC_VER) || (defined __DJGPP__) || (defined __MINGW__)
|
||||
// Can't we detect Linux and other x86 platforms here? :/
|
||||
|
||||
#define FAST_MID(var, min, max) { \
|
||||
var -= (min); \
|
||||
var &= (~var) >> (sizeof(var) * CHAR_BIT - 1); \
|
||||
var += (min); \
|
||||
var -= (max); \
|
||||
var &= var >> (sizeof(var) * CHAR_BIT - 1); \
|
||||
var += (max); \
|
||||
}
|
||||
|
||||
#define CONVERT8(src, pos, signconv) { \
|
||||
signed int f = (src + 0x8000) >> 16; \
|
||||
FAST_MID(f, -128, 127); \
|
||||
((char*)sptr)[pos] = (char)f ^ signconv; \
|
||||
}
|
||||
|
||||
#define CONVERT16(src, pos, signconv) { \
|
||||
signed int f = (src + 0x80) >> 8; \
|
||||
FAST_MID(f, -32768, 32767); \
|
||||
((short*)sptr)[pos] = (short)(f ^ signconv); \
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define CONVERT8(src, pos, signconv) \
|
||||
{ \
|
||||
signed int f = (src + 0x8000) >> 16; \
|
||||
f = MID(-128, f, 127); \
|
||||
((char *)sptr)[pos] = (char)f ^ signconv; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define CONVERT16(src, pos, signconv) \
|
||||
{ \
|
||||
signed int f = (src + 0x80) >> 8; \
|
||||
f = MID(-32768, f, 32767); \
|
||||
((short *)sptr)[pos] = (short)(f ^ signconv); \
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos)
|
||||
{
|
||||
return duh_start_sigrenderer(duh, 0, n_channels, pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long duh_render(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
int bits, int unsign,
|
||||
float volume, float delta,
|
||||
long size, void *sptr
|
||||
)
|
||||
{
|
||||
long n;
|
||||
|
||||
sample_t **sampptr;
|
||||
|
||||
int n_channels;
|
||||
|
||||
ASSERT(bits == 8 || bits == 16);
|
||||
ASSERT(sptr);
|
||||
|
||||
if (!sigrenderer)
|
||||
return 0;
|
||||
|
||||
n_channels = duh_sigrenderer_get_n_channels(sigrenderer);
|
||||
|
||||
ASSERT(n_channels > 0);
|
||||
/* This restriction will be removed when need be. At the moment, tightly
|
||||
* optimised loops exist for exactly one or two channels.
|
||||
*/
|
||||
ASSERT(n_channels <= 2);
|
||||
|
||||
sampptr = allocate_sample_buffer(n_channels, size);
|
||||
|
||||
if (!sampptr)
|
||||
return 0;
|
||||
|
||||
dumb_silence(sampptr[0], n_channels * size);
|
||||
|
||||
size = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, sampptr);
|
||||
|
||||
if (bits == 16) {
|
||||
int signconv = unsign ? 0x8000 : 0x0000;
|
||||
|
||||
for (n = 0; n < size * n_channels; n++) {
|
||||
CONVERT16(sampptr[0][n], n, signconv);
|
||||
}
|
||||
} else {
|
||||
char signconv = unsign ? 0x80 : 0x00;
|
||||
|
||||
for (n = 0; n < size * n_channels; n++) {
|
||||
CONVERT8(sampptr[0][n], n, signconv);
|
||||
}
|
||||
}
|
||||
|
||||
destroy_sample_buffer(sampptr);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr)
|
||||
{
|
||||
return duh_sigrenderer_get_n_channels(dr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
long duh_renderer_get_position(DUH_SIGRENDERER *dr)
|
||||
{
|
||||
return duh_sigrenderer_get_position(dr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
void duh_end_renderer(DUH_SIGRENDERER *dr)
|
||||
{
|
||||
duh_end_sigrenderer(dr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer)
|
||||
{
|
||||
return sigrenderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr)
|
||||
{
|
||||
return dr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr)
|
||||
{
|
||||
return dr;
|
||||
}
|
344
dumb/core/rendsig.c
Normal file
344
dumb/core/rendsig.c
Normal file
|
@ -0,0 +1,344 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* rendsig.c - Wrappers to render samples from / / \ \
|
||||
* the signals in a DUH. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
struct DUH_SIGRENDERER
|
||||
{
|
||||
DUH_SIGTYPE_DESC *desc;
|
||||
|
||||
sigrenderer_t *sigrenderer;
|
||||
|
||||
int n_channels;
|
||||
|
||||
long pos;
|
||||
int subpos;
|
||||
|
||||
DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback;
|
||||
void *callback_data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos)
|
||||
{
|
||||
DUH_SIGRENDERER *sigrenderer;
|
||||
|
||||
DUH_SIGNAL *signal;
|
||||
DUH_START_SIGRENDERER proc;
|
||||
|
||||
if (!duh)
|
||||
return NULL;
|
||||
|
||||
if ((unsigned int)sig >= (unsigned int)duh->n_signals)
|
||||
return NULL;
|
||||
|
||||
signal = duh->signal[sig];
|
||||
if (!signal)
|
||||
return NULL;
|
||||
|
||||
sigrenderer = malloc(sizeof(*sigrenderer));
|
||||
if (!sigrenderer)
|
||||
return NULL;
|
||||
|
||||
sigrenderer->desc = signal->desc;
|
||||
|
||||
proc = sigrenderer->desc->start_sigrenderer;
|
||||
|
||||
if (proc) {
|
||||
duh->signal[sig] = NULL;
|
||||
sigrenderer->sigrenderer = (*proc)(duh, signal->sigdata, n_channels, pos);
|
||||
duh->signal[sig] = signal;
|
||||
|
||||
if (!sigrenderer->sigrenderer) {
|
||||
free(sigrenderer);
|
||||
return NULL;
|
||||
}
|
||||
} else
|
||||
sigrenderer->sigrenderer = NULL;
|
||||
|
||||
sigrenderer->n_channels = n_channels;
|
||||
|
||||
sigrenderer->pos = pos;
|
||||
sigrenderer->subpos = 0;
|
||||
|
||||
sigrenderer->callback = NULL;
|
||||
|
||||
return sigrenderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
void duh_sigrenderer_set_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_CALLBACK callback, void *data
|
||||
)
|
||||
{
|
||||
(void)sigrenderer;
|
||||
(void)callback;
|
||||
(void)data;
|
||||
fprintf(stderr,
|
||||
"Call to deprecated function duh_sigrenderer_set_callback(). The callback\n"
|
||||
"was not installed. See dumb/docs/deprec.txt for how to fix this.\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void duh_sigrenderer_set_analyser_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data
|
||||
)
|
||||
{
|
||||
(void)sigrenderer;
|
||||
(void)callback;
|
||||
(void)data;
|
||||
fprintf(stderr,
|
||||
"Call to deprecated function duh_sigrenderer_set_analyser_callback(). The\n"
|
||||
"callback was not installed. See dumb/docs/deprec.txt for how to fix this.\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void duh_sigrenderer_set_sample_analyser_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data
|
||||
)
|
||||
{
|
||||
if (sigrenderer) {
|
||||
sigrenderer->callback = callback;
|
||||
sigrenderer->callback_data = data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer)
|
||||
{
|
||||
return sigrenderer ? sigrenderer->n_channels : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer)
|
||||
{
|
||||
return sigrenderer ? sigrenderer->pos : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void duh_sigrenderer_set_sigparam(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
unsigned char id, long value
|
||||
)
|
||||
{
|
||||
DUH_SIGRENDERER_SET_SIGPARAM proc;
|
||||
|
||||
if (!sigrenderer) return;
|
||||
|
||||
proc = sigrenderer->desc->sigrenderer_set_sigparam;
|
||||
if (proc)
|
||||
(*proc)(sigrenderer->sigrenderer, id, value);
|
||||
else
|
||||
TRACE("Parameter #%d = %ld for signal %c%c%c%c, which does not take parameters.\n",
|
||||
(int)id,
|
||||
value,
|
||||
(int)(sigrenderer->desc->type >> 24),
|
||||
(int)(sigrenderer->desc->type >> 16),
|
||||
(int)(sigrenderer->desc->type >> 8),
|
||||
(int)(sigrenderer->desc->type));
|
||||
}
|
||||
|
||||
|
||||
|
||||
long duh_sigrenderer_generate_samples(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
)
|
||||
{
|
||||
long rendered;
|
||||
LONG_LONG t;
|
||||
|
||||
if (!sigrenderer) return 0;
|
||||
|
||||
rendered = (*sigrenderer->desc->sigrenderer_generate_samples)
|
||||
(sigrenderer->sigrenderer, volume, delta, size, samples);
|
||||
|
||||
if (rendered) {
|
||||
if (sigrenderer->callback)
|
||||
(*sigrenderer->callback)(sigrenderer->callback_data,
|
||||
(const sample_t *const *)samples, sigrenderer->n_channels, rendered);
|
||||
|
||||
t = sigrenderer->subpos + (LONG_LONG)(delta * 65536.0 + 0.5) * rendered;
|
||||
|
||||
sigrenderer->pos += (long)(t >> 16);
|
||||
sigrenderer->subpos = (int)t & 65535;
|
||||
}
|
||||
|
||||
return rendered;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
long duh_sigrenderer_get_samples(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
)
|
||||
{
|
||||
sample_t **s;
|
||||
long rendered;
|
||||
long i;
|
||||
int j;
|
||||
if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL);
|
||||
s = allocate_sample_buffer(sigrenderer->n_channels, size);
|
||||
if (!s) return 0;
|
||||
dumb_silence(s[0], sigrenderer->n_channels * size);
|
||||
rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s);
|
||||
for (j = 0; j < sigrenderer->n_channels; j++)
|
||||
for (i = 0; i < rendered; i++)
|
||||
samples[j][i] += s[0][i*sigrenderer->n_channels+j];
|
||||
destroy_sample_buffer(s);
|
||||
return rendered;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
long duh_render_signal(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
)
|
||||
{
|
||||
sample_t **s;
|
||||
long rendered;
|
||||
long i;
|
||||
int j;
|
||||
if (!samples) return duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, NULL);
|
||||
s = allocate_sample_buffer(sigrenderer->n_channels, size);
|
||||
if (!s) return 0;
|
||||
dumb_silence(s[0], sigrenderer->n_channels * size);
|
||||
rendered = duh_sigrenderer_generate_samples(sigrenderer, volume, delta, size, s);
|
||||
for (j = 0; j < sigrenderer->n_channels; j++)
|
||||
for (i = 0; i < rendered; i++)
|
||||
samples[j][i] += s[0][i*sigrenderer->n_channels+j] >> 8;
|
||||
destroy_sample_buffer(s);
|
||||
return rendered;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples)
|
||||
{
|
||||
if (sigrenderer)
|
||||
(*sigrenderer->desc->sigrenderer_get_current_sample)(sigrenderer->sigrenderer, volume, samples);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer)
|
||||
{
|
||||
if (sigrenderer) {
|
||||
if (sigrenderer->desc->end_sigrenderer)
|
||||
if (sigrenderer->sigrenderer)
|
||||
(*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);
|
||||
|
||||
free(sigrenderer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos)
|
||||
{
|
||||
DUH_SIGRENDERER *sigrenderer;
|
||||
|
||||
if (desc->start_sigrenderer && !vsigrenderer) return NULL;
|
||||
|
||||
sigrenderer = malloc(sizeof(*sigrenderer));
|
||||
if (!sigrenderer) {
|
||||
if (desc->end_sigrenderer)
|
||||
if (vsigrenderer)
|
||||
(*desc->end_sigrenderer)(vsigrenderer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sigrenderer->desc = desc;
|
||||
sigrenderer->sigrenderer = vsigrenderer;
|
||||
|
||||
sigrenderer->n_channels = n_channels;
|
||||
|
||||
sigrenderer->pos = pos;
|
||||
sigrenderer->subpos = 0;
|
||||
|
||||
sigrenderer->callback = NULL;
|
||||
|
||||
return sigrenderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
|
||||
{
|
||||
if (sigrenderer && sigrenderer->desc->type == type)
|
||||
return sigrenderer->sigrenderer;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// This function is disabled because we don't know whether we want to destroy
|
||||
// the sigrenderer if the type doesn't match. We don't even know if we need
|
||||
// the function at all. Who would want to keep an IT_SIGRENDERER (for
|
||||
// instance) without keeping the DUH_SIGRENDERER?
|
||||
sigrenderer_t *duh_decompose_to_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type)
|
||||
{
|
||||
if (sigrenderer && sigrenderer->desc->type == type) {
|
||||
|
||||
|
||||
|
||||
if (sigrenderer) {
|
||||
if (sigrenderer->desc->end_sigrenderer)
|
||||
if (sigrenderer->sigrenderer)
|
||||
(*sigrenderer->desc->end_sigrenderer)(sigrenderer->sigrenderer);
|
||||
|
||||
free(sigrenderer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
return sigrenderer->sigrenderer;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
64
dumb/core/unload.c
Normal file
64
dumb/core/unload.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* unload.c - Code to free a DUH from memory. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/dumb.h"
|
||||
|
||||
|
||||
|
||||
static void destroy_signal(DUH_SIGNAL *signal)
|
||||
{
|
||||
if (signal) {
|
||||
if (signal->desc)
|
||||
if (signal->desc->unload_sigdata)
|
||||
if (signal->sigdata)
|
||||
(*signal->desc->unload_sigdata)(signal->sigdata);
|
||||
|
||||
free(signal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* unload_duh(): destroys a DUH struct. You must call this for every DUH
|
||||
* struct created, when you've finished with it.
|
||||
*/
|
||||
void unload_duh(DUH *duh)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (duh) {
|
||||
if (duh->signal) {
|
||||
for (i = 0; i < duh->n_signals; i++)
|
||||
destroy_signal(duh->signal[i]);
|
||||
|
||||
free(duh->signal);
|
||||
}
|
||||
|
||||
if (duh->tag) {
|
||||
if (duh->tag[0][0])
|
||||
free(duh->tag[0][0]);
|
||||
free(duh->tag);
|
||||
}
|
||||
|
||||
free(duh);
|
||||
}
|
||||
}
|
281
dumb/helpers/clickrem.c
Normal file
281
dumb/helpers/clickrem.c
Normal file
|
@ -0,0 +1,281 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* clickrem.c - Click removal helpers. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
typedef struct DUMB_CLICK DUMB_CLICK;
|
||||
|
||||
|
||||
struct DUMB_CLICK_REMOVER
|
||||
{
|
||||
DUMB_CLICK *click;
|
||||
int n_clicks;
|
||||
|
||||
int offset;
|
||||
};
|
||||
|
||||
|
||||
struct DUMB_CLICK
|
||||
{
|
||||
DUMB_CLICK *next;
|
||||
long pos;
|
||||
sample_t step;
|
||||
};
|
||||
|
||||
|
||||
|
||||
DUMB_CLICK_REMOVER *dumb_create_click_remover(void)
|
||||
{
|
||||
DUMB_CLICK_REMOVER *cr = malloc(sizeof(*cr));
|
||||
if (!cr) return NULL;
|
||||
|
||||
cr->click = NULL;
|
||||
cr->n_clicks = 0;
|
||||
|
||||
cr->offset = 0;
|
||||
|
||||
return cr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step)
|
||||
{
|
||||
DUMB_CLICK *click;
|
||||
|
||||
ASSERT(pos >= 0);
|
||||
|
||||
if (!cr || !step) return;
|
||||
|
||||
if (pos == 0) {
|
||||
cr->offset -= step;
|
||||
return;
|
||||
}
|
||||
|
||||
click = malloc(sizeof(*click));
|
||||
if (!click) return;
|
||||
|
||||
click->pos = pos;
|
||||
click->step = step;
|
||||
|
||||
click->next = cr->click;
|
||||
cr->click = click;
|
||||
cr->n_clicks++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMB_CLICK *dumb_click_mergesort(DUMB_CLICK *click, int n_clicks)
|
||||
{
|
||||
int i;
|
||||
DUMB_CLICK *c1, *c2, **cp;
|
||||
|
||||
if (n_clicks <= 1) return click;
|
||||
|
||||
/* Split the list into two */
|
||||
c1 = click;
|
||||
cp = &c1;
|
||||
for (i = 0; i < n_clicks; i += 2) cp = &(*cp)->next;
|
||||
c2 = *cp;
|
||||
*cp = NULL;
|
||||
|
||||
/* Sort the sublists */
|
||||
c1 = dumb_click_mergesort(c1, (n_clicks + 1) >> 1);
|
||||
c2 = dumb_click_mergesort(c2, n_clicks >> 1);
|
||||
|
||||
/* Merge them */
|
||||
cp = &click;
|
||||
while (c1 && c2) {
|
||||
if (c1->pos > c2->pos) {
|
||||
*cp = c2;
|
||||
c2 = c2->next;
|
||||
} else {
|
||||
*cp = c1;
|
||||
c1 = c1->next;
|
||||
}
|
||||
cp = &(*cp)->next;
|
||||
}
|
||||
if (c2)
|
||||
*cp = c2;
|
||||
else
|
||||
*cp = c1;
|
||||
|
||||
return click;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife)
|
||||
{
|
||||
DUMB_CLICK *click;
|
||||
long pos = 0;
|
||||
int offset;
|
||||
int factor;
|
||||
|
||||
if (!cr) return;
|
||||
|
||||
factor = (int)floor(pow(0.5, 1.0/halflife) * (1U << 31));
|
||||
|
||||
click = dumb_click_mergesort(cr->click, cr->n_clicks);
|
||||
cr->click = NULL;
|
||||
cr->n_clicks = 0;
|
||||
|
||||
length *= step;
|
||||
|
||||
while (click) {
|
||||
DUMB_CLICK *next = click->next;
|
||||
int end = click->pos * step;
|
||||
ASSERT(end <= length);
|
||||
offset = cr->offset;
|
||||
if (offset < 0) {
|
||||
offset = -offset;
|
||||
while (pos < end) {
|
||||
samples[pos] -= offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
pos += step;
|
||||
}
|
||||
offset = -offset;
|
||||
} else {
|
||||
while (pos < end) {
|
||||
samples[pos] += offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
pos += step;
|
||||
}
|
||||
}
|
||||
cr->offset = offset - click->step;
|
||||
free(click);
|
||||
click = next;
|
||||
}
|
||||
|
||||
offset = cr->offset;
|
||||
if (offset < 0) {
|
||||
offset = -offset;
|
||||
while (pos < length) {
|
||||
samples[pos] -= offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
pos += step;
|
||||
}
|
||||
offset = -offset;
|
||||
} else {
|
||||
while (pos < length) {
|
||||
samples[pos] += offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
pos += step;
|
||||
}
|
||||
}
|
||||
cr->offset = offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr)
|
||||
{
|
||||
return cr ? cr->offset : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr)
|
||||
{
|
||||
if (cr) {
|
||||
DUMB_CLICK *click = cr->click;
|
||||
while (click) {
|
||||
DUMB_CLICK *next = click->next;
|
||||
free(click);
|
||||
click = next;
|
||||
}
|
||||
free(cr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n)
|
||||
{
|
||||
int i;
|
||||
DUMB_CLICK_REMOVER **cr;
|
||||
if (n <= 0) return NULL;
|
||||
cr = malloc(n * sizeof(*cr));
|
||||
if (!cr) return NULL;
|
||||
for (i = 0; i < n; i++) cr[i] = dumb_create_click_remover();
|
||||
return cr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step)
|
||||
{
|
||||
if (cr) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
dumb_record_click(cr[i], pos, step[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step)
|
||||
{
|
||||
if (cr) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
dumb_record_click(cr[i], pos, -step[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife)
|
||||
{
|
||||
if (cr) {
|
||||
int i;
|
||||
for (i = 0; i < n >> 1; i++) {
|
||||
dumb_remove_clicks(cr[i << 1], samples[i], length, 2, halflife);
|
||||
dumb_remove_clicks(cr[(i << 1) + 1], samples[i] + 1, length, 2, halflife);
|
||||
}
|
||||
if (n & 1)
|
||||
dumb_remove_clicks(cr[i << 1], samples[i], length, 1, halflife);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset)
|
||||
{
|
||||
if (cr) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++)
|
||||
if (cr[i]) offset[i] += cr[i]->offset;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr)
|
||||
{
|
||||
if (cr) {
|
||||
int i;
|
||||
for (i = 0; i < n; i++) dumb_destroy_click_remover(cr[i]);
|
||||
free(cr);
|
||||
}
|
||||
}
|
96
dumb/helpers/memfile.c
Normal file
96
dumb/helpers/memfile.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* memfile.c - Module for reading data from / / \ \
|
||||
* memory using a DUMBFILE. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
typedef struct MEMFILE MEMFILE;
|
||||
|
||||
struct MEMFILE
|
||||
{
|
||||
const char *ptr;
|
||||
long left;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int dumb_memfile_skip(void *f, long n)
|
||||
{
|
||||
MEMFILE *m = f;
|
||||
if (n > m->left) return -1;
|
||||
m->ptr += n;
|
||||
m->left -= n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int dumb_memfile_getc(void *f)
|
||||
{
|
||||
MEMFILE *m = f;
|
||||
if (m->left <= 0) return -1;
|
||||
m->left--;
|
||||
return *(const unsigned char *)m->ptr++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static long dumb_memfile_getnc(char *ptr, long n, void *f)
|
||||
{
|
||||
MEMFILE *m = f;
|
||||
if (n > m->left) n = m->left;
|
||||
memcpy(ptr, m->ptr, n);
|
||||
m->ptr += n;
|
||||
m->left -= n;
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dumb_memfile_close(void *f)
|
||||
{
|
||||
free(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMBFILE_SYSTEM memfile_dfs = {
|
||||
NULL,
|
||||
&dumb_memfile_skip,
|
||||
&dumb_memfile_getc,
|
||||
&dumb_memfile_getnc,
|
||||
&dumb_memfile_close
|
||||
};
|
||||
|
||||
|
||||
|
||||
DUMBFILE *dumbfile_open_memory(const char *data, long size)
|
||||
{
|
||||
MEMFILE *m = malloc(sizeof(*m));
|
||||
if (!m) return NULL;
|
||||
|
||||
m->ptr = data;
|
||||
m->left = size;
|
||||
|
||||
return dumbfile_open_ex(m, &memfile_dfs);
|
||||
}
|
134
dumb/helpers/resamp2.inc
Normal file
134
dumb/helpers/resamp2.inc
Normal file
|
@ -0,0 +1,134 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* resamp2.inc - Resampling helper template. / / \ \
|
||||
* | < / \_
|
||||
* By Bob and entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* In order to find a good trade-off between | \ / /
|
||||
* speed and accuracy in this code, some tests | ' /
|
||||
* were carried out regarding the behaviour of \__/
|
||||
* long long ints with gcc. The following code
|
||||
* was tested:
|
||||
*
|
||||
* int a, b, c;
|
||||
* c = ((long long)a * b) >> 16;
|
||||
*
|
||||
* DJGPP GCC Version 3.0.3 generated the following assembly language code for
|
||||
* the multiplication and scaling, leaving the 32-bit result in EAX.
|
||||
*
|
||||
* movl -8(%ebp), %eax ; read one int into EAX
|
||||
* imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
|
||||
* shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
|
||||
*
|
||||
* Note that a 32*32->64 multiplication is performed, allowing for high
|
||||
* accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
|
||||
* so it is a minor concern when four multiplications are being performed
|
||||
* (the cubic resampler). On the Pentium MMX and earlier, it takes four or
|
||||
* more cycles, so this method is unsuitable for use in the low-quality
|
||||
* resamplers.
|
||||
*
|
||||
* Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
|
||||
* defined in dumb.h. We may investigate later what code MSVC generates, but
|
||||
* if it seems too slow then we suggest you use a good compiler.
|
||||
*
|
||||
* FIXME: these comments are somewhat out of date now.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define SUFFIX3 _1
|
||||
|
||||
/* For convenience, returns nonzero on stop. */
|
||||
static int process_pickup(DUMB_RESAMPLER *resampler)
|
||||
{
|
||||
if (resampler->overshot < 0) {
|
||||
resampler->overshot = 0;
|
||||
dumb_resample(resampler, NULL, 2, MONO_DEST_VOLUME_ZEROS, 1.0f); /* Doesn't matter which SUFFIX3. */
|
||||
COPYSRC(resampler->X, 0, resampler->X, 1);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
SRCTYPE *src = resampler->src;
|
||||
|
||||
if (resampler->dir < 0) {
|
||||
if (resampler->overshot >= 3 && resampler->pos+3 >= resampler->start) COPYSRC(resampler->X, 0, src, resampler->pos+3);
|
||||
if (resampler->overshot >= 2 && resampler->pos+2 >= resampler->start) COPYSRC(resampler->X, 1, src, resampler->pos+2);
|
||||
if (resampler->overshot >= 1 && resampler->pos+1 >= resampler->start) COPYSRC(resampler->X, 2, src, resampler->pos+1);
|
||||
resampler->overshot = resampler->start - resampler->pos - 1;
|
||||
} else {
|
||||
if (resampler->overshot >= 3 && resampler->pos-3 < resampler->end) COPYSRC(resampler->X, 0, src, resampler->pos-3);
|
||||
if (resampler->overshot >= 2 && resampler->pos-2 < resampler->end) COPYSRC(resampler->X, 1, src, resampler->pos-2);
|
||||
if (resampler->overshot >= 1 && resampler->pos-1 < resampler->end) COPYSRC(resampler->X, 2, src, resampler->pos-1);
|
||||
resampler->overshot = resampler->pos - resampler->end;
|
||||
}
|
||||
|
||||
if (resampler->overshot < 0) {
|
||||
resampler->overshot = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!resampler->pickup) {
|
||||
resampler->dir = 0;
|
||||
return 1;
|
||||
}
|
||||
(*resampler->pickup)(resampler, resampler->pickup_data);
|
||||
if (resampler->dir == 0) return 1;
|
||||
ASSERT(resampler->dir == -1 || resampler->dir == 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create mono destination resampler. */
|
||||
/* SUFFIX3 was set above. */
|
||||
#define VOLUME_PARAMETERS MONO_DEST_VOLUME_PARAMETERS
|
||||
#define VOLUME_VARIABLES MONO_DEST_VOLUME_VARIABLES
|
||||
#define SET_VOLUME_VARIABLES SET_MONO_DEST_VOLUME_VARIABLES
|
||||
#define VOLUMES_ARE_ZERO MONO_DEST_VOLUMES_ARE_ZERO
|
||||
#define MIX_ALIAS(op, offset) MONO_DEST_MIX_ALIAS(op, offset)
|
||||
#define MIX_LINEAR(op, o0, o1) MONO_DEST_MIX_LINEAR(op, o0, o1)
|
||||
#define MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3)
|
||||
#define MIX_ZEROS(op) *dst++ op 0
|
||||
#include "resamp3.inc"
|
||||
|
||||
/* Create stereo destination resampler. */
|
||||
#define SUFFIX3 _2
|
||||
#define VOLUME_PARAMETERS float volume_left, float volume_right
|
||||
#define VOLUME_VARIABLES lvol, rvol
|
||||
#define SET_VOLUME_VARIABLES { \
|
||||
lvol = (int)floor(volume_left * 65536.0 + 0.5); \
|
||||
rvol = (int)floor(volume_right * 65536.0 + 0.5); \
|
||||
}
|
||||
#define VOLUMES_ARE_ZERO (lvol == 0 && rvol == 0)
|
||||
#define MIX_ALIAS(op, offset) STEREO_DEST_MIX_ALIAS(op, offset)
|
||||
#define MIX_LINEAR(op, o0, o1) STEREO_DEST_MIX_LINEAR(op, o0, o1)
|
||||
#define MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3)
|
||||
#define MIX_ZEROS(op) { *dst++ op 0; *dst++ op 0; }
|
||||
#include "resamp3.inc"
|
||||
|
||||
|
||||
|
||||
#undef STEREO_DEST_MIX_CUBIC
|
||||
#undef MONO_DEST_MIX_CUBIC
|
||||
#undef STEREO_DEST_MIX_LINEAR
|
||||
#undef MONO_DEST_MIX_LINEAR
|
||||
#undef STEREO_DEST_MIX_ALIAS
|
||||
#undef MONO_DEST_MIX_ALIAS
|
||||
#undef MONO_DEST_VOLUMES_ARE_ZERO
|
||||
#undef SET_MONO_DEST_VOLUME_VARIABLES
|
||||
#undef MONO_DEST_VOLUME_ZEROS
|
||||
#undef MONO_DEST_VOLUME_VARIABLES
|
||||
#undef MONO_DEST_VOLUME_PARAMETERS
|
||||
#undef COPYSRC2
|
||||
#undef COPYSRC
|
||||
#undef DIVIDE_BY_SRC_CHANNELS
|
||||
#undef SRC_CHANNELS
|
||||
#undef SUFFIX2
|
371
dumb/helpers/resamp3.inc
Normal file
371
dumb/helpers/resamp3.inc
Normal file
|
@ -0,0 +1,371 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* resamp3.inc - Resampling helper template. / / \ \
|
||||
* | < / \_
|
||||
* By Bob and entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* In order to find a good trade-off between | \ / /
|
||||
* speed and accuracy in this code, some tests | ' /
|
||||
* were carried out regarding the behaviour of \__/
|
||||
* long long ints with gcc. The following code
|
||||
* was tested:
|
||||
*
|
||||
* int a, b, c;
|
||||
* c = ((long long)a * b) >> 16;
|
||||
*
|
||||
* DJGPP GCC Version 3.0.3 generated the following assembly language code for
|
||||
* the multiplication and scaling, leaving the 32-bit result in EAX.
|
||||
*
|
||||
* movl -8(%ebp), %eax ; read one int into EAX
|
||||
* imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
|
||||
* shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
|
||||
*
|
||||
* Note that a 32*32->64 multiplication is performed, allowing for high
|
||||
* accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
|
||||
* so it is a minor concern when four multiplications are being performed
|
||||
* (the cubic resampler). On the Pentium MMX and earlier, it takes four or
|
||||
* more cycles, so this method is unsuitable for use in the low-quality
|
||||
* resamplers.
|
||||
*
|
||||
* Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
|
||||
* defined in dumb.h. We may investigate later what code MSVC generates, but
|
||||
* if it seems too slow then we suggest you use a good compiler.
|
||||
*
|
||||
* FIXME: these comments are somewhat out of date now.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
long dumb_resample(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, VOLUME_PARAMETERS, float delta)
|
||||
{
|
||||
int dt;
|
||||
int VOLUME_VARIABLES;
|
||||
long done;
|
||||
long todo;
|
||||
int quality;
|
||||
|
||||
if (!resampler || resampler->dir == 0) return 0;
|
||||
ASSERT(resampler->dir == -1 || resampler->dir == 1);
|
||||
|
||||
done = 0;
|
||||
dt = (int)(delta * 65536.0 + 0.5);
|
||||
SET_VOLUME_VARIABLES;
|
||||
|
||||
if (VOLUMES_ARE_ZERO) dst = NULL;
|
||||
|
||||
init_cubic();
|
||||
|
||||
quality = dumb_resampling_quality;
|
||||
if (quality > resampler->max_quality) quality = resampler->max_quality;
|
||||
else if (quality < resampler->min_quality) quality = resampler->min_quality;
|
||||
|
||||
while (done < dst_size) {
|
||||
if (process_pickup(resampler)) return done;
|
||||
|
||||
if ((resampler->dir ^ dt) < 0)
|
||||
dt = -dt;
|
||||
|
||||
if (resampler->dir < 0)
|
||||
todo = (long)((((LONG_LONG)(resampler->pos - resampler->start) << 16) + resampler->subpos - dt) / -dt);
|
||||
else
|
||||
todo = (long)((((LONG_LONG)(resampler->end - resampler->pos) << 16) - resampler->subpos - 1 + dt) / dt);
|
||||
|
||||
if (todo < 0)
|
||||
todo = 0;
|
||||
else if (todo > dst_size - done)
|
||||
todo = dst_size - done;
|
||||
|
||||
done += todo;
|
||||
|
||||
{
|
||||
SRCTYPE *src = resampler->src;
|
||||
long pos = resampler->pos;
|
||||
int subpos = resampler->subpos;
|
||||
long diff = pos;
|
||||
long overshot;
|
||||
if (resampler->dir < 0) {
|
||||
if (!dst) {
|
||||
/* Silence or simulation */
|
||||
LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo;
|
||||
pos += (long)(new_subpos >> 16);
|
||||
subpos = (long)new_subpos & 65535;
|
||||
} else if (quality <= DUMB_RQ_ALIASING) {
|
||||
/* Aliasing, backwards */
|
||||
SRCTYPE xbuf[2*SRC_CHANNELS];
|
||||
SRCTYPE *x = &xbuf[0];
|
||||
SRCTYPE *xstart;
|
||||
COPYSRC(xbuf, 0, resampler->X, 1);
|
||||
COPYSRC(xbuf, 1, resampler->X, 2);
|
||||
while (todo && x < &xbuf[2*SRC_CHANNELS]) {
|
||||
// TODO: check what happens when multiple tempo slides occur per row
|
||||
HEAVYASSERT(pos >= resampler->start);
|
||||
MIX_ALIAS(+=, 0);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x -= (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
todo--;
|
||||
}
|
||||
x = xstart = &src[pos*SRC_CHANNELS];
|
||||
LOOP4(todo,
|
||||
MIX_ALIAS(+=, 2);
|
||||
subpos += dt;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
);
|
||||
pos += DIVIDE_BY_SRC_CHANNELS(x - xstart);
|
||||
} else if (quality <= DUMB_RQ_LINEAR) {
|
||||
/* Linear interpolation, backwards */
|
||||
SRCTYPE xbuf[3*SRC_CHANNELS];
|
||||
SRCTYPE *x = &xbuf[1*SRC_CHANNELS];
|
||||
COPYSRC(xbuf, 0, resampler->X, 1);
|
||||
COPYSRC(xbuf, 1, resampler->X, 2);
|
||||
COPYSRC(xbuf, 2, src, pos);
|
||||
while (todo && x < &xbuf[3*SRC_CHANNELS]) {
|
||||
HEAVYASSERT(pos >= resampler->start);
|
||||
MIX_LINEAR(+=, 0, -1);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x -= (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
todo--;
|
||||
}
|
||||
// TODO: use xstart for others too
|
||||
x = &src[pos*SRC_CHANNELS];
|
||||
LOOP4(todo,
|
||||
HEAVYASSERT(pos >= resampler->start);
|
||||
MIX_LINEAR(+=, 1, 2);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
);
|
||||
} else {
|
||||
/* Cubic interpolation, backwards */
|
||||
SRCTYPE xbuf[6*SRC_CHANNELS];
|
||||
SRCTYPE *x = &xbuf[3*SRC_CHANNELS];
|
||||
COPYSRC(xbuf, 0, resampler->X, 0);
|
||||
COPYSRC(xbuf, 1, resampler->X, 1);
|
||||
COPYSRC(xbuf, 2, resampler->X, 2);
|
||||
COPYSRC(xbuf, 3, src, pos);
|
||||
if (pos-1 >= resampler->start) COPYSRC(xbuf, 4, src, pos-1);
|
||||
if (pos-2 >= resampler->start) COPYSRC(xbuf, 5, src, pos-2);
|
||||
while (todo && x < &xbuf[6*SRC_CHANNELS]) {
|
||||
HEAVYASSERT(pos >= resampler->start);
|
||||
MIX_CUBIC(+=, x, x, 0, -1, -2, -3);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x -= (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
todo--;
|
||||
}
|
||||
x = &src[pos*SRC_CHANNELS];
|
||||
LOOP4(todo,
|
||||
HEAVYASSERT(pos >= resampler->start);
|
||||
MIX_CUBIC(+=, x, x, 0, 1, 2, 3);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
);
|
||||
}
|
||||
diff = diff - pos;
|
||||
overshot = resampler->start - pos - 1;
|
||||
if (diff >= 3) {
|
||||
COPYSRC2(resampler->X, 0, overshot < 3, src, pos+3);
|
||||
COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2);
|
||||
COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1);
|
||||
} else if (diff >= 2) {
|
||||
COPYSRC(resampler->X, 0, resampler->X, 2);
|
||||
COPYSRC2(resampler->X, 1, overshot < 2, src, pos+2);
|
||||
COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1);
|
||||
} else if (diff >= 1) {
|
||||
COPYSRC(resampler->X, 0, resampler->X, 1);
|
||||
COPYSRC(resampler->X, 1, resampler->X, 2);
|
||||
COPYSRC2(resampler->X, 2, overshot < 1, src, pos+1);
|
||||
}
|
||||
} else {
|
||||
if (!dst) {
|
||||
/* Silence or simulation */
|
||||
LONG_LONG new_subpos = subpos + (LONG_LONG)dt * todo;
|
||||
pos += (long)(new_subpos >> 16);
|
||||
subpos = (long)new_subpos & 65535;
|
||||
} else if (dumb_resampling_quality <= DUMB_RQ_ALIASING) {
|
||||
/* Aliasing, forwards */
|
||||
SRCTYPE xbuf[2*SRC_CHANNELS];
|
||||
SRCTYPE *x = &xbuf[0];
|
||||
SRCTYPE *xstart;
|
||||
COPYSRC(xbuf, 0, resampler->X, 1);
|
||||
COPYSRC(xbuf, 1, resampler->X, 2);
|
||||
while (todo && x < &xbuf[2*SRC_CHANNELS]) {
|
||||
HEAVYASSERT(pos < resampler->end);
|
||||
MIX_ALIAS(+=, 0);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
todo--;
|
||||
}
|
||||
x = xstart = &src[pos*SRC_CHANNELS];
|
||||
LOOP4(todo,
|
||||
MIX_ALIAS(+=, -2);
|
||||
subpos += dt;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
);
|
||||
pos += DIVIDE_BY_SRC_CHANNELS(x - xstart);
|
||||
} else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
|
||||
/* Linear interpolation, forwards */
|
||||
SRCTYPE xbuf[3*SRC_CHANNELS];
|
||||
SRCTYPE *x = &xbuf[1*SRC_CHANNELS];
|
||||
COPYSRC(xbuf, 0, resampler->X, 1);
|
||||
COPYSRC(xbuf, 1, resampler->X, 2);
|
||||
COPYSRC(xbuf, 2, src, pos);
|
||||
while (todo && x < &xbuf[3*SRC_CHANNELS]) {
|
||||
HEAVYASSERT(pos < resampler->end);
|
||||
MIX_LINEAR(+=, -1, 0);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
todo--;
|
||||
}
|
||||
x = &src[pos*SRC_CHANNELS];
|
||||
LOOP4(todo,
|
||||
HEAVYASSERT(pos < resampler->end);
|
||||
MIX_LINEAR(+=, -2, -1);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
);
|
||||
} else {
|
||||
/* Cubic interpolation, forwards */
|
||||
SRCTYPE xbuf[6*SRC_CHANNELS];
|
||||
SRCTYPE *x = &xbuf[3*SRC_CHANNELS];
|
||||
COPYSRC(xbuf, 0, resampler->X, 0);
|
||||
COPYSRC(xbuf, 1, resampler->X, 1);
|
||||
COPYSRC(xbuf, 2, resampler->X, 2);
|
||||
COPYSRC(xbuf, 3, src, pos);
|
||||
if (pos+1 < resampler->end) COPYSRC(xbuf, 4, src, pos+1);
|
||||
if (pos+2 < resampler->end) COPYSRC(xbuf, 5, src, pos+2);
|
||||
while (todo && x < &xbuf[6*SRC_CHANNELS]) {
|
||||
HEAVYASSERT(pos < resampler->end);
|
||||
MIX_CUBIC(+=, x, x, -3, -2, -1, 0);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
todo--;
|
||||
}
|
||||
x = &src[pos*SRC_CHANNELS];
|
||||
LOOP4(todo,
|
||||
HEAVYASSERT(pos < resampler->end);
|
||||
MIX_CUBIC(+=, x, x, -3, -2, -1, 0);
|
||||
subpos += dt;
|
||||
pos += subpos >> 16;
|
||||
x += (subpos >> 16) * SRC_CHANNELS;
|
||||
subpos &= 65535;
|
||||
);
|
||||
}
|
||||
diff = pos - diff;
|
||||
overshot = pos - resampler->end;
|
||||
if (diff >= 3) {
|
||||
COPYSRC2(resampler->X, 0, overshot < 3, src, pos-3);
|
||||
COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2);
|
||||
COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1);
|
||||
} else if (diff >= 2) {
|
||||
COPYSRC(resampler->X, 0, resampler->X, 2);
|
||||
COPYSRC2(resampler->X, 1, overshot < 2, src, pos-2);
|
||||
COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1);
|
||||
} else if (diff >= 1) {
|
||||
COPYSRC(resampler->X, 0, resampler->X, 1);
|
||||
COPYSRC(resampler->X, 1, resampler->X, 2);
|
||||
COPYSRC2(resampler->X, 2, overshot < 1, src, pos-1);
|
||||
}
|
||||
}
|
||||
resampler->pos = pos;
|
||||
resampler->subpos = subpos;
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_resample_get_current_sample(DUMB_RESAMPLER *resampler, VOLUME_PARAMETERS, sample_t *dst)
|
||||
{
|
||||
int VOLUME_VARIABLES;
|
||||
SRCTYPE *src;
|
||||
long pos;
|
||||
int subpos;
|
||||
int quality;
|
||||
SRCTYPE *x;
|
||||
|
||||
if (!resampler || resampler->dir == 0) { MIX_ZEROS(=); return; }
|
||||
ASSERT(resampler->dir == -1 || resampler->dir == 1);
|
||||
|
||||
if (process_pickup(resampler)) { MIX_ZEROS(=); return; }
|
||||
|
||||
SET_VOLUME_VARIABLES;
|
||||
|
||||
if (VOLUMES_ARE_ZERO) { MIX_ZEROS(=); return; }
|
||||
|
||||
init_cubic();
|
||||
|
||||
quality = dumb_resampling_quality;
|
||||
if (quality > resampler->max_quality) quality = resampler->max_quality;
|
||||
else if (quality < resampler->min_quality) quality = resampler->min_quality;
|
||||
|
||||
src = resampler->src;
|
||||
pos = resampler->pos;
|
||||
subpos = resampler->subpos;
|
||||
x = resampler->X;
|
||||
|
||||
if (resampler->dir < 0) {
|
||||
HEAVYASSERT(pos >= resampler->start);
|
||||
if (dumb_resampling_quality <= 0) {
|
||||
/* Aliasing, backwards */
|
||||
MIX_ALIAS(=, 1);
|
||||
} else if (quality <= DUMB_RQ_LINEAR) {
|
||||
/* Linear interpolation, backwards */
|
||||
MIX_LINEAR(=, 2, 1);
|
||||
} else {
|
||||
/* Cubic interpolation, backwards */
|
||||
MIX_CUBIC(=, src, x, pos, 2, 1, 0);
|
||||
}
|
||||
} else {
|
||||
HEAVYASSERT(pos < resampler->end);
|
||||
if (dumb_resampling_quality <= 0) {
|
||||
/* Aliasing */
|
||||
MIX_ALIAS(=, 1);
|
||||
} else if (dumb_resampling_quality <= DUMB_RQ_LINEAR) {
|
||||
/* Linear interpolation, forwards */
|
||||
MIX_LINEAR(=, 1, 2);
|
||||
} else {
|
||||
/* Cubic interpolation, forwards */
|
||||
MIX_CUBIC(=, x, src, 0, 1, 2, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#undef MIX_ZEROS
|
||||
#undef MIX_CUBIC
|
||||
#undef MIX_LINEAR
|
||||
#undef MIX_ALIAS
|
||||
#undef VOLUMES_ARE_ZERO
|
||||
#undef SET_VOLUME_VARIABLES
|
||||
#undef VOLUME_VARIABLES
|
||||
#undef VOLUME_PARAMETERS
|
||||
#undef SUFFIX3
|
385
dumb/helpers/resample.c
Normal file
385
dumb/helpers/resample.c
Normal file
|
@ -0,0 +1,385 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* resample.c - Resampling helpers. / / \ \
|
||||
* | < / \_
|
||||
* By Bob and entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* In order to find a good trade-off between | \ / /
|
||||
* speed and accuracy in this code, some tests | ' /
|
||||
* were carried out regarding the behaviour of \__/
|
||||
* long long ints with gcc. The following code
|
||||
* was tested:
|
||||
*
|
||||
* int a, b, c;
|
||||
* c = ((long long)a * b) >> 16;
|
||||
*
|
||||
* DJGPP GCC Version 3.0.3 generated the following assembly language code for
|
||||
* the multiplication and scaling, leaving the 32-bit result in EAX.
|
||||
*
|
||||
* movl -8(%ebp), %eax ; read one int into EAX
|
||||
* imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
|
||||
* shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
|
||||
*
|
||||
* Note that a 32*32->64 multiplication is performed, allowing for high
|
||||
* accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
|
||||
* so it is a minor concern when four multiplications are being performed
|
||||
* (the cubic resampler). On the Pentium MMX and earlier, it takes four or
|
||||
* more cycles, so this method is unsuitable for use in the low-quality
|
||||
* resamplers.
|
||||
*
|
||||
* Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
|
||||
* defined in dumb.h. We may investigate later what code MSVC generates, but
|
||||
* if it seems too slow then we suggest you use a good compiler.
|
||||
*
|
||||
* FIXME: these comments are somewhat out of date now.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
/* Compile with -DHEAVYDEBUG if you want to make sure the pick-up function is
|
||||
* called when it should be. There will be a considerable performance hit,
|
||||
* since at least one condition has to be tested for every sample generated.
|
||||
*/
|
||||
#ifdef HEAVYDEBUG
|
||||
#define HEAVYASSERT(cond) ASSERT(cond)
|
||||
#else
|
||||
#define HEAVYASSERT(cond)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* A global variable for controlling resampling quality wherever a local
|
||||
* specification doesn't override it. The following values are valid:
|
||||
*
|
||||
* 0 - DUMB_RQ_ALIASING - fastest
|
||||
* 1 - DUMB_RQ_LINEAR
|
||||
* 2 - DUMB_RQ_CUBIC - nicest
|
||||
*
|
||||
* Values outside the range 0-2 will behave the same as the nearest
|
||||
* value within the range.
|
||||
*/
|
||||
int dumb_resampling_quality = DUMB_RQ_CUBIC;
|
||||
|
||||
|
||||
|
||||
//#define MULSC(a, b) ((int)((LONG_LONG)(a) * (b) >> 16))
|
||||
//#define MULSC(a, b) ((a) * ((b) >> 2) >> 14)
|
||||
#define MULSC(a, b) ((int)((LONG_LONG)((a) << 4) * ((b) << 12) >> 32))
|
||||
#define MULSC16(a, b) ((int)((LONG_LONG)((a) << 12) * ((b) << 12) >> 32))
|
||||
|
||||
|
||||
|
||||
/* Executes the content 'iterator' times.
|
||||
* Clobbers the 'iterator' variable.
|
||||
* The loop is unrolled by four.
|
||||
*/
|
||||
#define LOOP4(iterator, CONTENT) \
|
||||
{ \
|
||||
if ((iterator) & 2) { \
|
||||
CONTENT; \
|
||||
CONTENT; \
|
||||
} \
|
||||
if ((iterator) & 1) { \
|
||||
CONTENT; \
|
||||
} \
|
||||
(iterator) >>= 2; \
|
||||
while (iterator) { \
|
||||
CONTENT; \
|
||||
CONTENT; \
|
||||
CONTENT; \
|
||||
CONTENT; \
|
||||
(iterator)--; \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define PASTERAW(a, b) a ## b /* This does not expand macros in b ... */
|
||||
#define PASTE(a, b) PASTERAW(a, b) /* ... but b is expanded during this substitution. */
|
||||
|
||||
#define X PASTE(x.x, SRCBITS)
|
||||
|
||||
|
||||
|
||||
/* Cubic resampler: look-up tables
|
||||
*
|
||||
* a = 1.5*x1 - 1.5*x2 + 0.5*x3 - 0.5*x0
|
||||
* b = 2*x2 + x0 - 2.5*x1 - 0.5*x3
|
||||
* c = 0.5*x2 - 0.5*x0
|
||||
* d = x1
|
||||
*
|
||||
* x = a*t*t*t + b*t*t + c*t + d
|
||||
* = (-0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3) * t*t*t +
|
||||
* ( 1*x0 - 2.5*x1 + 2 *x2 - 0.5*x3) * t*t +
|
||||
* (-0.5*x0 + 0.5*x2 ) * t +
|
||||
* ( 1*x1 )
|
||||
* = (-0.5*t*t*t + 1 *t*t - 0.5*t ) * x0 +
|
||||
* ( 1.5*t*t*t - 2.5*t*t + 1) * x1 +
|
||||
* (-1.5*t*t*t + 2 *t*t + 0.5*t ) * x2 +
|
||||
* ( 0.5*t*t*t - 0.5*t*t ) * x3
|
||||
* = A0(t) * x0 + A1(t) * x1 + A2(t) * x2 + A3(t) * x3
|
||||
*
|
||||
* A0, A1, A2 and A3 stay within the range [-1,1].
|
||||
* In the tables, they are scaled with 14 fractional bits.
|
||||
*
|
||||
* Turns out we don't need to store A2 and A3; they are symmetrical to A1 and A0.
|
||||
*
|
||||
* TODO: A0 and A3 stay very small indeed. Consider different scale/resolution?
|
||||
*/
|
||||
|
||||
static short cubicA0[1025], cubicA1[1025];
|
||||
|
||||
static void init_cubic(void)
|
||||
{
|
||||
unsigned int t; /* 3*1024*1024*1024 is within range if it's unsigned */
|
||||
static int done = 0;
|
||||
if (done) return;
|
||||
done = 1;
|
||||
for (t = 0; t < 1025; t++) {
|
||||
/* int casts to pacify warnings about negating unsigned values */
|
||||
cubicA0[t] = -(int)( t*t*t >> 17) + (int)( t*t >> 6) - (int)(t << 3);
|
||||
cubicA1[t] = (int)(3*t*t*t >> 17) - (int)(5*t*t >> 7) + (int)(1 << 14);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create resamplers for 24-in-32-bit source samples. */
|
||||
|
||||
/* #define SUFFIX
|
||||
* MSVC warns if we try to paste a null SUFFIX, so instead we define
|
||||
* special macros for the function names that don't bother doing the
|
||||
* corresponding paste. The more generic definitions are further down.
|
||||
*/
|
||||
#define process_pickup PASTE(process_pickup, SUFFIX2)
|
||||
#define dumb_resample PASTE(PASTE(dumb_resample, SUFFIX2), SUFFIX3)
|
||||
#define dumb_resample_get_current_sample PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX2), SUFFIX3)
|
||||
|
||||
#define SRCTYPE sample_t
|
||||
#define SRCBITS 24
|
||||
#define ALIAS(x, vol) MULSC(x, vol)
|
||||
#define LINEAR(x0, x1) (x0 + MULSC(x1 - x0, subpos))
|
||||
/*
|
||||
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
|
||||
a = (3 * (x1 - x2) + (x3 - x0)) >> 1; \
|
||||
b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) >> 1; \
|
||||
c = (x2 - x0) >> 1; \
|
||||
}
|
||||
#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + d, vol)
|
||||
*/
|
||||
#define CUBIC(x0, x1, x2, x3) ( \
|
||||
MULSC(x0, cubicA0[subpos >> 6] << 2) + \
|
||||
MULSC(x1, cubicA1[subpos >> 6] << 2) + \
|
||||
MULSC(x2, cubicA1[1 + (subpos >> 6 ^ 1023)] << 2) + \
|
||||
MULSC(x3, cubicA0[1 + (subpos >> 6 ^ 1023)] << 2))
|
||||
#define CUBICVOL(x, vol) MULSC(x, vol)
|
||||
#include "resample.inc"
|
||||
|
||||
/* Undefine the simplified macros. */
|
||||
#undef dumb_resample_get_current_sample
|
||||
#undef dumb_resample
|
||||
#undef process_pickup
|
||||
|
||||
|
||||
/* Now define the proper ones that use SUFFIX. */
|
||||
#define dumb_reset_resampler PASTE(dumb_reset_resampler, SUFFIX)
|
||||
#define dumb_start_resampler PASTE(dumb_start_resampler, SUFFIX)
|
||||
#define process_pickup PASTE(PASTE(process_pickup, SUFFIX), SUFFIX2)
|
||||
#define dumb_resample PASTE(PASTE(PASTE(dumb_resample, SUFFIX), SUFFIX2), SUFFIX3)
|
||||
#define dumb_resample_get_current_sample PASTE(PASTE(PASTE(dumb_resample_get_current_sample, SUFFIX), SUFFIX2), SUFFIX3)
|
||||
#define dumb_end_resampler PASTE(dumb_end_resampler, SUFFIX)
|
||||
|
||||
/* Create resamplers for 16-bit source samples. */
|
||||
#define SUFFIX _16
|
||||
#define SRCTYPE short
|
||||
#define SRCBITS 16
|
||||
#define ALIAS(x, vol) (x * vol >> 8)
|
||||
#define LINEAR(x0, x1) ((x0 << 8) + MULSC16(x1 - x0, subpos))
|
||||
/*
|
||||
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
|
||||
a = (3 * (x1 - x2) + (x3 - x0)) << 7; \
|
||||
b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 7; \
|
||||
c = (x2 - x0) << 7; \
|
||||
}
|
||||
#define CUBIC(d) MULSC(MULSC(MULSC(MULSC(a, subpos) + b, subpos) + c, subpos) + (d << 8), vol)
|
||||
*/
|
||||
#define CUBIC(x0, x1, x2, x3) ( \
|
||||
x0 * cubicA0[subpos >> 6] + \
|
||||
x1 * cubicA1[subpos >> 6] + \
|
||||
x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \
|
||||
x3 * cubicA0[1 + (subpos >> 6 ^ 1023)])
|
||||
#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 10) >> 32)
|
||||
#include "resample.inc"
|
||||
|
||||
/* Create resamplers for 8-bit source samples. */
|
||||
#define SUFFIX _8
|
||||
#define SRCTYPE signed char
|
||||
#define SRCBITS 8
|
||||
#define ALIAS(x, vol) (x * vol)
|
||||
#define LINEAR(x0, x1) ((x0 << 16) + (x1 - x0) * subpos)
|
||||
/*
|
||||
#define SET_CUBIC_COEFFICIENTS(x0, x1, x2, x3) { \
|
||||
a = 3 * (x1 - x2) + (x3 - x0); \
|
||||
b = ((x2 << 2) + (x0 << 1) - (5 * x1 + x3)) << 15; \
|
||||
c = (x2 - x0) << 15; \
|
||||
}
|
||||
#define CUBIC(d) MULSC(MULSC(MULSC((a * subpos >> 1) + b, subpos) + c, subpos) + (d << 16), vol)
|
||||
*/
|
||||
#define CUBIC(x0, x1, x2, x3) (( \
|
||||
x0 * cubicA0[subpos >> 6] + \
|
||||
x1 * cubicA1[subpos >> 6] + \
|
||||
x2 * cubicA1[1 + (subpos >> 6 ^ 1023)] + \
|
||||
x3 * cubicA0[1 + (subpos >> 6 ^ 1023)]) << 6)
|
||||
#define CUBICVOL(x, vol) (int)((LONG_LONG)(x) * (vol << 12) >> 32)
|
||||
#include "resample.inc"
|
||||
|
||||
|
||||
#undef dumb_reset_resampler
|
||||
#undef dumb_start_resampler
|
||||
#undef process_pickup
|
||||
#undef dumb_resample
|
||||
#undef dumb_resample_get_current_sample
|
||||
#undef dumb_end_resampler
|
||||
|
||||
|
||||
|
||||
void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end)
|
||||
{
|
||||
if (n == 8)
|
||||
dumb_reset_resampler_8(resampler, src, src_channels, pos, start, end);
|
||||
else if (n == 16)
|
||||
dumb_reset_resampler_16(resampler, src, src_channels, pos, start, end);
|
||||
else
|
||||
dumb_reset_resampler(resampler, src, src_channels, pos, start, end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end)
|
||||
{
|
||||
if (n == 8)
|
||||
return dumb_start_resampler_8(src, src_channels, pos, start, end);
|
||||
else if (n == 16)
|
||||
return dumb_start_resampler_16(src, src_channels, pos, start, end);
|
||||
else
|
||||
return dumb_start_resampler(src, src_channels, pos, start, end);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta)
|
||||
{
|
||||
if (n == 8)
|
||||
return dumb_resample_8_1_1(resampler, dst, dst_size, volume, delta);
|
||||
else if (n == 16)
|
||||
return dumb_resample_16_1_1(resampler, dst, dst_size, volume, delta);
|
||||
else
|
||||
return dumb_resample_1_1(resampler, dst, dst_size, volume, delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta)
|
||||
{
|
||||
if (n == 8)
|
||||
return dumb_resample_8_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
else if (n == 16)
|
||||
return dumb_resample_16_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
else
|
||||
return dumb_resample_1_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta)
|
||||
{
|
||||
if (n == 8)
|
||||
return dumb_resample_8_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
else if (n == 16)
|
||||
return dumb_resample_16_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
else
|
||||
return dumb_resample_2_1(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta)
|
||||
{
|
||||
if (n == 8)
|
||||
return dumb_resample_8_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
else if (n == 16)
|
||||
return dumb_resample_16_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
else
|
||||
return dumb_resample_2_2(resampler, dst, dst_size, volume_left, volume_right, delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, float volume, sample_t *dst)
|
||||
{
|
||||
if (n == 8)
|
||||
dumb_resample_get_current_sample_8_1_1(resampler, volume, dst);
|
||||
else if (n == 16)
|
||||
dumb_resample_get_current_sample_16_1_1(resampler, volume, dst);
|
||||
else
|
||||
dumb_resample_get_current_sample_1_1(resampler, volume, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst)
|
||||
{
|
||||
if (n == 8)
|
||||
dumb_resample_get_current_sample_8_1_2(resampler, volume_left, volume_right, dst);
|
||||
else if (n == 16)
|
||||
dumb_resample_get_current_sample_16_1_2(resampler, volume_left, volume_right, dst);
|
||||
else
|
||||
dumb_resample_get_current_sample_1_2(resampler, volume_left, volume_right, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst)
|
||||
{
|
||||
if (n == 8)
|
||||
dumb_resample_get_current_sample_8_2_1(resampler, volume_left, volume_right, dst);
|
||||
else if (n == 16)
|
||||
dumb_resample_get_current_sample_16_2_1(resampler, volume_left, volume_right, dst);
|
||||
else
|
||||
dumb_resample_get_current_sample_2_1(resampler, volume_left, volume_right, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst)
|
||||
{
|
||||
if (n == 8)
|
||||
dumb_resample_get_current_sample_8_2_2(resampler, volume_left, volume_right, dst);
|
||||
else if (n == 16)
|
||||
dumb_resample_get_current_sample_16_2_2(resampler, volume_left, volume_right, dst);
|
||||
else
|
||||
dumb_resample_get_current_sample_2_2(resampler, volume_left, volume_right, dst);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler)
|
||||
{
|
||||
if (n == 8)
|
||||
dumb_end_resampler_8(resampler);
|
||||
else if (n == 16)
|
||||
dumb_end_resampler_16(resampler);
|
||||
else
|
||||
dumb_end_resampler(resampler);
|
||||
}
|
167
dumb/helpers/resample.inc
Normal file
167
dumb/helpers/resample.inc
Normal file
|
@ -0,0 +1,167 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* resample.inc - Resampling helper template. / / \ \
|
||||
* | < / \_
|
||||
* By Bob and entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* In order to find a good trade-off between | \ / /
|
||||
* speed and accuracy in this code, some tests | ' /
|
||||
* were carried out regarding the behaviour of \__/
|
||||
* long long ints with gcc. The following code
|
||||
* was tested:
|
||||
*
|
||||
* int a, b, c;
|
||||
* c = ((long long)a * b) >> 16;
|
||||
*
|
||||
* DJGPP GCC Version 3.0.3 generated the following assembly language code for
|
||||
* the multiplication and scaling, leaving the 32-bit result in EAX.
|
||||
*
|
||||
* movl -8(%ebp), %eax ; read one int into EAX
|
||||
* imull -4(%ebp) ; multiply by the other; result goes in EDX:EAX
|
||||
* shrdl $16, %edx, %eax ; shift EAX right 16, shifting bits in from EDX
|
||||
*
|
||||
* Note that a 32*32->64 multiplication is performed, allowing for high
|
||||
* accuracy. On the Pentium 2 and above, shrdl takes two cycles (generally),
|
||||
* so it is a minor concern when four multiplications are being performed
|
||||
* (the cubic resampler). On the Pentium MMX and earlier, it takes four or
|
||||
* more cycles, so this method is unsuitable for use in the low-quality
|
||||
* resamplers.
|
||||
*
|
||||
* Since "long long" is a gcc-specific extension, we use LONG_LONG instead,
|
||||
* defined in dumb.h. We may investigate later what code MSVC generates, but
|
||||
* if it seems too slow then we suggest you use a good compiler.
|
||||
*
|
||||
* FIXME: these comments are somewhat out of date now.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
void dumb_reset_resampler(DUMB_RESAMPLER *resampler, SRCTYPE *src, int src_channels, long pos, long start, long end)
|
||||
{
|
||||
int i;
|
||||
resampler->src = src;
|
||||
resampler->pos = pos;
|
||||
resampler->subpos = 0;
|
||||
resampler->start = start;
|
||||
resampler->end = end;
|
||||
resampler->dir = 1;
|
||||
resampler->pickup = NULL;
|
||||
resampler->pickup_data = NULL;
|
||||
resampler->min_quality = 0;
|
||||
resampler->max_quality = DUMB_RQ_N_LEVELS - 1;
|
||||
for (i = 0; i < src_channels*3; i++) resampler->X[i] = 0;
|
||||
resampler->overshot = -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUMB_RESAMPLER *dumb_start_resampler(SRCTYPE *src, int src_channels, long pos, long start, long end)
|
||||
{
|
||||
DUMB_RESAMPLER *resampler = malloc(sizeof(*resampler));
|
||||
if (!resampler) return NULL;
|
||||
dumb_reset_resampler(resampler, src, src_channels, pos, start, end);
|
||||
return resampler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Create mono source resampler. */
|
||||
#define SUFFIX2 _1
|
||||
#define SRC_CHANNELS 1
|
||||
#define DIVIDE_BY_SRC_CHANNELS(x) (x)
|
||||
#define COPYSRC(dstarray, dstindex, srcarray, srcindex) (dstarray)[dstindex] = (srcarray)[srcindex]
|
||||
#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) (dstarray)[dstindex] = condition ? (srcarray)[srcindex] : 0
|
||||
#define MONO_DEST_VOLUME_PARAMETERS float volume
|
||||
#define MONO_DEST_VOLUME_VARIABLES vol
|
||||
#define MONO_DEST_VOLUME_ZEROS 0
|
||||
#define SET_MONO_DEST_VOLUME_VARIABLES vol = (int)floor(volume * 65536.0 + 0.5)
|
||||
#define MONO_DEST_VOLUMES_ARE_ZERO (vol == 0)
|
||||
#define MONO_DEST_MIX_ALIAS(op, offset) *dst++ op ALIAS(x[offset], vol)
|
||||
#define STEREO_DEST_MIX_ALIAS(op, offset) { \
|
||||
int xm = x[offset]; \
|
||||
*dst++ op ALIAS(xm, lvol); \
|
||||
*dst++ op ALIAS(xm, rvol); \
|
||||
}
|
||||
#define MONO_DEST_MIX_LINEAR(op, o0, o1) *dst++ op MULSC(LINEAR(x[o0], x[o1]), vol)
|
||||
#define STEREO_DEST_MIX_LINEAR(op, o0, o1) { \
|
||||
int xm = LINEAR(x[o0], x[o1]); \
|
||||
*dst++ op MULSC(xm, lvol); \
|
||||
*dst++ op MULSC(xm, rvol); \
|
||||
}
|
||||
#define MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) *dst++ op CUBICVOL(CUBIC(x0[o0], x[o1], x[o2], x3[o3]), vol)
|
||||
#define STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) { \
|
||||
int xm = CUBIC(x0[o0], x[o1], x[o2], x3[o3]); \
|
||||
*dst++ op CUBICVOL(xm, lvol); \
|
||||
*dst++ op CUBICVOL(xm, rvol); \
|
||||
}
|
||||
#include "resamp2.inc"
|
||||
|
||||
/* Create stereo source resampler. */
|
||||
#define SUFFIX2 _2
|
||||
#define SRC_CHANNELS 2
|
||||
#define DIVIDE_BY_SRC_CHANNELS(x) ((x) >> 1)
|
||||
#define COPYSRC(dstarray, dstindex, srcarray, srcindex) { \
|
||||
(dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \
|
||||
(dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \
|
||||
}
|
||||
#define COPYSRC2(dstarray, dstindex, condition, srcarray, srcindex) { \
|
||||
if (condition) { \
|
||||
(dstarray)[(dstindex)*2] = (srcarray)[(srcindex)*2]; \
|
||||
(dstarray)[(dstindex)*2+1] = (srcarray)[(srcindex)*2+1]; \
|
||||
} else { \
|
||||
(dstarray)[(dstindex)*2] = 0; \
|
||||
(dstarray)[(dstindex)*2+1] = 0; \
|
||||
} \
|
||||
}
|
||||
#define MONO_DEST_VOLUME_PARAMETERS float volume_left, float volume_right
|
||||
#define MONO_DEST_VOLUME_VARIABLES lvol, rvol
|
||||
#define MONO_DEST_VOLUME_ZEROS 0, 0
|
||||
#define SET_MONO_DEST_VOLUME_VARIABLES { \
|
||||
lvol = (int)floor(volume_left * 65536.0 + 0.5); \
|
||||
rvol = (int)floor(volume_right * 65536.0 + 0.5); \
|
||||
}
|
||||
#define MONO_DEST_VOLUMES_ARE_ZERO (lvol == 0 && rvol == 0)
|
||||
#define MONO_DEST_MIX_ALIAS(op, offset) *dst++ op ALIAS(x[(offset)*2], lvol) + ALIAS(x[(offset)*2+1], rvol)
|
||||
#define STEREO_DEST_MIX_ALIAS(op, offset) { \
|
||||
*dst++ op ALIAS(x[(offset)*2], lvol); \
|
||||
*dst++ op ALIAS(x[(offset)*2+1], rvol); \
|
||||
}
|
||||
#define MONO_DEST_MIX_LINEAR(op, o0, o1) *dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol) + MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol)
|
||||
#define STEREO_DEST_MIX_LINEAR(op, o0, o1) { \
|
||||
*dst++ op MULSC(LINEAR(x[(o0)*2], x[(o1)*2]), lvol); \
|
||||
*dst++ op MULSC(LINEAR(x[(o0)*2+1], x[(o1)*2+1]), rvol); \
|
||||
}
|
||||
#define MONO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) *dst++ op \
|
||||
CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol) + \
|
||||
CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol)
|
||||
#define STEREO_DEST_MIX_CUBIC(op, x0, x3, o0, o1, o2, o3) { \
|
||||
*dst++ op CUBICVOL(CUBIC(x0[(o0)*2], x[(o1)*2], x[(o2)*2], x3[(o3)*2]), lvol); \
|
||||
*dst++ op CUBICVOL(CUBIC(x0[(o0)*2+1], x[(o1)*2+1], x[(o2)*2+1], x3[(o3)*2+1]), rvol); \
|
||||
}
|
||||
#include "resamp2.inc"
|
||||
|
||||
|
||||
|
||||
void dumb_end_resampler(DUMB_RESAMPLER *resampler)
|
||||
{
|
||||
if (resampler)
|
||||
free(resampler);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#undef CUBICVOL
|
||||
#undef CUBIC
|
||||
#undef LINEAR
|
||||
#undef ALIAS
|
||||
#undef SRCBITS
|
||||
#undef SRCTYPE
|
||||
#undef SUFFIX
|
64
dumb/helpers/sampbuf.c
Normal file
64
dumb/helpers/sampbuf.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* sampbuf.c - Helper for allocating sample / / \ \
|
||||
* buffers. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
sample_t **create_sample_buffer(int n_channels, long length)
|
||||
{
|
||||
int i;
|
||||
sample_t **samples = malloc(n_channels * sizeof(*samples));
|
||||
if (!samples) return NULL;
|
||||
samples[0] = malloc(n_channels * length * sizeof(*samples[0]));
|
||||
if (!samples[0]) {
|
||||
free(samples);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 1; i < n_channels; i++) samples[i] = samples[i-1] + length;
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
|
||||
sample_t **allocate_sample_buffer(int n_channels, long length)
|
||||
{
|
||||
int i;
|
||||
sample_t **samples = malloc(((n_channels + 1) >> 1) * sizeof(*samples));
|
||||
if (!samples) return NULL;
|
||||
samples[0] = malloc(n_channels * length * sizeof(*samples[0]));
|
||||
if (!samples[0]) {
|
||||
free(samples);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 1; i < (n_channels + 1) >> 1; i++) samples[i] = samples[i-1] + length*2;
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void destroy_sample_buffer(sample_t **samples)
|
||||
{
|
||||
if (samples) {
|
||||
free(samples[0]);
|
||||
free(samples);
|
||||
}
|
||||
}
|
29
dumb/helpers/silence.c
Normal file
29
dumb/helpers/silence.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* silence.c - Silencing helper. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
void dumb_silence(sample_t *samples, long length)
|
||||
{
|
||||
memset(samples, 0, length * sizeof(*samples));
|
||||
}
|
||||
|
93
dumb/helpers/stdfile.c
Normal file
93
dumb/helpers/stdfile.c
Normal file
|
@ -0,0 +1,93 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* stdfile.c - stdio file input module. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dumb_stdfile_open(const char *filename)
|
||||
{
|
||||
return fopen(filename, "rb");
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int dumb_stdfile_skip(void *f, long n)
|
||||
{
|
||||
return fseek(f, n, SEEK_CUR);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int dumb_stdfile_getc(void *f)
|
||||
{
|
||||
return fgetc(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static long dumb_stdfile_getnc(char *ptr, long n, void *f)
|
||||
{
|
||||
return fread(ptr, 1, n, f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dumb_stdfile_close(void *f)
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMBFILE_SYSTEM stdfile_dfs = {
|
||||
&dumb_stdfile_open,
|
||||
&dumb_stdfile_skip,
|
||||
&dumb_stdfile_getc,
|
||||
&dumb_stdfile_getnc,
|
||||
&dumb_stdfile_close
|
||||
};
|
||||
|
||||
|
||||
|
||||
void dumb_register_stdfiles(void)
|
||||
{
|
||||
register_dumbfile_system(&stdfile_dfs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMBFILE_SYSTEM stdfile_dfs_leave_open = {
|
||||
NULL,
|
||||
&dumb_stdfile_skip,
|
||||
&dumb_stdfile_getc,
|
||||
&dumb_stdfile_getnc,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
DUMBFILE *dumbfile_open_stdfile(FILE *p)
|
||||
{
|
||||
DUMBFILE *d = dumbfile_open_ex(p, &stdfile_dfs_leave_open);
|
||||
|
||||
return d;
|
||||
}
|
98
dumb/include/aldumb.h
Normal file
98
dumb/include/aldumb.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* aldumb.h - The user header file for DUMB with / / \ \
|
||||
* Allegro. | < / \_
|
||||
* | \/ /\ /
|
||||
* Include this file if you wish to use DUMB \_ / > /
|
||||
* with Allegro. It will include dumb.h for you, | \ / /
|
||||
* and provide extra functionality such as audio | ' /
|
||||
* stream and datafile integration. \__/
|
||||
*/
|
||||
|
||||
#ifndef ALDUMB_H
|
||||
#define ALDUMB_H
|
||||
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Packfile Support */
|
||||
|
||||
void dumb_register_packfiles(void);
|
||||
|
||||
DUMBFILE *dumbfile_open_packfile(PACKFILE *p);
|
||||
DUMBFILE *dumbfile_from_packfile(PACKFILE *p);
|
||||
|
||||
|
||||
/* Datafile Registration Functions */
|
||||
|
||||
#define DUMB_DAT_DUH DAT_ID('D','U','H',' ')
|
||||
#define DUMB_DAT_IT DAT_ID('I','T',' ',' ')
|
||||
#define DUMB_DAT_XM DAT_ID('X','M',' ',' ')
|
||||
#define DUMB_DAT_S3M DAT_ID('S','3','M',' ')
|
||||
#define DUMB_DAT_MOD DAT_ID('M','O','D',' ')
|
||||
|
||||
void dumb_register_dat_duh(long type);
|
||||
void dumb_register_dat_it(long type);
|
||||
void dumb_register_dat_xm(long type);
|
||||
void dumb_register_dat_s3m(long type);
|
||||
void dumb_register_dat_mod(long type);
|
||||
void dumb_register_dat_it_quick(long type);
|
||||
void dumb_register_dat_xm_quick(long type);
|
||||
void dumb_register_dat_s3m_quick(long type);
|
||||
void dumb_register_dat_mod_quick(long type);
|
||||
|
||||
|
||||
/* DUH Playing Functions */
|
||||
|
||||
typedef struct AL_DUH_PLAYER AL_DUH_PLAYER;
|
||||
|
||||
AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos, float volume, long bufsize, int freq);
|
||||
void al_stop_duh(AL_DUH_PLAYER *dp);
|
||||
void al_pause_duh(AL_DUH_PLAYER *dp);
|
||||
void al_resume_duh(AL_DUH_PLAYER *dp);
|
||||
void al_duh_set_priority(AL_DUH_PLAYER *dp, int priority);
|
||||
void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume);
|
||||
float al_duh_get_volume(AL_DUH_PLAYER *dp);
|
||||
int al_poll_duh(AL_DUH_PLAYER *dp);
|
||||
long al_duh_get_position(AL_DUH_PLAYER *dp);
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq);
|
||||
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
|
||||
/* IMPORTANT: This function will return NULL if the music has ended. */
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_renderer(DUH_SIGRENDERER *dr, float volume, long bufsize, int freq) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_renderer(AL_DUH_PLAYER *dp) DUMB_DEPRECATED;
|
||||
/* Replace 'renderer' with 'sigrenderer' in each case where you called one of
|
||||
* these functions.
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* ALDUMB_H */
|
687
dumb/include/dumb.h
Normal file
687
dumb/include/dumb.h
Normal file
|
@ -0,0 +1,687 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* dumb.h - The user header file for DUMB. / / \ \
|
||||
* | < / \_
|
||||
* Include this file in any of your files in | \/ /\ /
|
||||
* which you wish to use the DUMB functions \_ / > /
|
||||
* and variables. | \ / /
|
||||
* | ' /
|
||||
* Allegro users, you will probably want aldumb.h. \__/
|
||||
*/
|
||||
|
||||
#ifndef DUMB_H
|
||||
#define DUMB_H
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#define DUMB_MAJOR_VERSION 0
|
||||
#define DUMB_MINOR_VERSION 9
|
||||
#define DUMB_REVISION_VERSION 3
|
||||
|
||||
#define DUMB_VERSION (DUMB_MAJOR_VERSION*10000 + DUMB_MINOR_VERSION*100 + DUMB_REVISION_VERSION)
|
||||
|
||||
#define DUMB_VERSION_STR "0.9.3"
|
||||
|
||||
#define DUMB_NAME "DUMB v"DUMB_VERSION_STR
|
||||
|
||||
#define DUMB_YEAR 2005
|
||||
#define DUMB_MONTH 8
|
||||
#define DUMB_DAY 7
|
||||
|
||||
#define DUMB_YEAR_STR2 "05"
|
||||
#define DUMB_YEAR_STR4 "2005"
|
||||
#define DUMB_MONTH_STR1 "8"
|
||||
#define DUMB_DAY_STR1 "7"
|
||||
|
||||
#if DUMB_MONTH < 10
|
||||
#define DUMB_MONTH_STR2 "0"DUMB_MONTH_STR1
|
||||
#else
|
||||
#define DUMB_MONTH_STR2 DUMB_MONTH_STR1
|
||||
#endif
|
||||
|
||||
#if DUMB_DAY < 10
|
||||
#define DUMB_DAY_STR2 "0"DUMB_DAY_STR1
|
||||
#else
|
||||
#define DUMB_DAY_STR2 DUMB_DAY_STR1
|
||||
#endif
|
||||
|
||||
|
||||
/* WARNING: The month and day were inadvertently swapped in the v0.8 release.
|
||||
* Please do not compare this constant against any date in 2002. In
|
||||
* any case, DUMB_VERSION is probably more useful for this purpose.
|
||||
*/
|
||||
#define DUMB_DATE (DUMB_YEAR*10000 + DUMB_MONTH*100 + DUMB_DAY)
|
||||
|
||||
#define DUMB_DATE_STR DUMB_DAY_STR1"."DUMB_MONTH_STR1"."DUMB_YEAR_STR4
|
||||
|
||||
|
||||
#undef MIN
|
||||
#undef MAX
|
||||
#undef MID
|
||||
|
||||
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
|
||||
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
||||
#define MID(x,y,z) MAX((x), MIN((y), (z)))
|
||||
|
||||
#undef ABS
|
||||
#define ABS(x) (((x) >= 0) ? (x) : (-(x)))
|
||||
|
||||
|
||||
#ifdef DEBUGMODE
|
||||
|
||||
#ifndef ASSERT
|
||||
#include <assert.h>
|
||||
#define ASSERT(n) assert(n)
|
||||
#endif
|
||||
#ifndef TRACE
|
||||
// it would be nice if this did actually trace ...
|
||||
#define TRACE 1 ? (void)0 : (void)printf
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ASSERT
|
||||
#define ASSERT(n)
|
||||
#endif
|
||||
#ifndef TRACE
|
||||
#define TRACE 1 ? (void)0 : (void)printf
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#define DUMB_ID(a,b,c,d) (((unsigned int)(a) << 24) | \
|
||||
((unsigned int)(b) << 16) | \
|
||||
((unsigned int)(c) << 8) | \
|
||||
((unsigned int)(d) ))
|
||||
|
||||
|
||||
|
||||
#ifndef LONG_LONG
|
||||
#if defined __GNUC__ || defined __INTEL_COMPILER || defined __MWERKS__
|
||||
#define LONG_LONG long long
|
||||
#elif defined _MSC_VER || defined __WATCOMC__
|
||||
#define LONG_LONG __int64
|
||||
#elif defined __sgi
|
||||
#define LONG_LONG long long
|
||||
#else
|
||||
#error 64-bit integer type unknown
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __GNUC__ * 100 + __GNUC_MINOR__ >= 301 /* GCC 3.1+ */
|
||||
#ifndef DUMB_DECLARE_DEPRECATED
|
||||
#define DUMB_DECLARE_DEPRECATED
|
||||
#endif
|
||||
#define DUMB_DEPRECATED __attribute__((__deprecated__))
|
||||
#else
|
||||
#define DUMB_DEPRECATED
|
||||
#endif
|
||||
|
||||
|
||||
/* Basic Sample Type. Normal range is -0x800000 to 0x7FFFFF. */
|
||||
|
||||
typedef int sample_t;
|
||||
|
||||
|
||||
/* Library Clean-up Management */
|
||||
|
||||
int dumb_atexit(void (*proc)(void));
|
||||
|
||||
void dumb_exit(void);
|
||||
|
||||
|
||||
/* File Input Functions */
|
||||
|
||||
typedef struct DUMBFILE_SYSTEM
|
||||
{
|
||||
void *(*open)(const char *filename);
|
||||
int (*skip)(void *f, long n);
|
||||
int (*getc)(void *f);
|
||||
long (*getnc)(char *ptr, long n, void *f);
|
||||
void (*close)(void *f);
|
||||
}
|
||||
DUMBFILE_SYSTEM;
|
||||
|
||||
typedef struct DUMBFILE DUMBFILE;
|
||||
|
||||
void register_dumbfile_system(DUMBFILE_SYSTEM *dfs);
|
||||
|
||||
DUMBFILE *dumbfile_open(const char *filename);
|
||||
DUMBFILE *dumbfile_open_ex(void *file, DUMBFILE_SYSTEM *dfs);
|
||||
|
||||
long dumbfile_pos(DUMBFILE *f);
|
||||
int dumbfile_skip(DUMBFILE *f, long n);
|
||||
|
||||
int dumbfile_getc(DUMBFILE *f);
|
||||
|
||||
int dumbfile_igetw(DUMBFILE *f);
|
||||
int dumbfile_mgetw(DUMBFILE *f);
|
||||
|
||||
long dumbfile_igetl(DUMBFILE *f);
|
||||
long dumbfile_mgetl(DUMBFILE *f);
|
||||
|
||||
unsigned long dumbfile_cgetul(DUMBFILE *f);
|
||||
signed long dumbfile_cgetsl(DUMBFILE *f);
|
||||
|
||||
long dumbfile_getnc(char *ptr, long n, DUMBFILE *f);
|
||||
|
||||
int dumbfile_error(DUMBFILE *f);
|
||||
int dumbfile_close(DUMBFILE *f);
|
||||
|
||||
|
||||
/* stdio File Input Module */
|
||||
|
||||
void dumb_register_stdfiles(void);
|
||||
|
||||
DUMBFILE *dumbfile_open_stdfile(FILE *p);
|
||||
|
||||
|
||||
/* Memory File Input Module */
|
||||
|
||||
DUMBFILE *dumbfile_open_memory(const char *data, long size);
|
||||
|
||||
|
||||
/* DUH Management */
|
||||
|
||||
typedef struct DUH DUH;
|
||||
|
||||
#define DUH_SIGNATURE DUMB_ID('D','U','H','!')
|
||||
|
||||
void unload_duh(DUH *duh);
|
||||
|
||||
DUH *load_duh(const char *filename);
|
||||
DUH *read_duh(DUMBFILE *f);
|
||||
|
||||
long duh_get_length(DUH *duh);
|
||||
|
||||
const char *duh_get_tag(DUH *duh, const char *key);
|
||||
|
||||
|
||||
/* Signal Rendering Functions */
|
||||
|
||||
typedef struct DUH_SIGRENDERER DUH_SIGRENDERER;
|
||||
|
||||
DUH_SIGRENDERER *duh_start_sigrenderer(DUH *duh, int sig, int n_channels, long pos);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
typedef void (*DUH_SIGRENDERER_CALLBACK)(void *data, sample_t **samples, int n_channels, long length);
|
||||
/* This is deprecated, but is not marked as such because GCC tends to
|
||||
* complain spuriously when the typedef is used later. See comments below.
|
||||
*/
|
||||
|
||||
void duh_sigrenderer_set_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_CALLBACK callback, void *data
|
||||
) DUMB_DEPRECATED;
|
||||
/* The 'callback' argument's type has changed for const-correctness. See the
|
||||
* DUH_SIGRENDERER_CALLBACK definition just above. Also note that the samples
|
||||
* in the buffer are now 256 times as large; the normal range is -0x800000 to
|
||||
* 0x7FFFFF. The function has been renamed partly because its functionality
|
||||
* has changed slightly and partly so that its name is more meaningful. The
|
||||
* new one is duh_sigrenderer_set_analyser_callback(), and the typedef for
|
||||
* the function pointer has also changed, from DUH_SIGRENDERER_CALLBACK to
|
||||
* DUH_SIGRENDERER_ANALYSER_CALLBACK. (If you wanted to use this callback to
|
||||
* apply a DSP effect, don't worry; there is a better way of doing this. It
|
||||
* is undocumented, so contact me and I shall try to help. Contact details
|
||||
* are in readme.txt.)
|
||||
*/
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length);
|
||||
/* This is deprecated, but is not marked as such because GCC tends to
|
||||
* complain spuriously when the typedef is used later. See comments below.
|
||||
*/
|
||||
|
||||
void duh_sigrenderer_set_analyser_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_ANALYSER_CALLBACK callback, void *data
|
||||
) DUMB_DEPRECATED;
|
||||
/* This is deprecated because the meaning of the 'samples' parameter in the
|
||||
* callback needed to change. For stereo applications, the array used to be
|
||||
* indexed with samples[channel][pos]. It is now indexed with
|
||||
* samples[0][pos*2+channel]. Mono sample data are still indexed with
|
||||
* samples[0][pos]. The array is still 2D because samples will probably only
|
||||
* ever be interleaved in twos. In order to fix your code, adapt it to the
|
||||
* new sample layout and then call
|
||||
* duh_sigrenderer_set_sample_analyser_callback below instead of this
|
||||
* function.
|
||||
*/
|
||||
#endif
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK)(void *data, const sample_t *const *samples, int n_channels, long length);
|
||||
|
||||
void duh_sigrenderer_set_sample_analyser_callback(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
DUH_SIGRENDERER_SAMPLE_ANALYSER_CALLBACK callback, void *data
|
||||
);
|
||||
|
||||
int duh_sigrenderer_get_n_channels(DUH_SIGRENDERER *sigrenderer);
|
||||
long duh_sigrenderer_get_position(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
void duh_sigrenderer_set_sigparam(DUH_SIGRENDERER *sigrenderer, unsigned char id, long value);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
long duh_sigrenderer_get_samples(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
) DUMB_DEPRECATED;
|
||||
/* The sample format has changed, so if you were using this function,
|
||||
* you should switch to duh_sigrenderer_generate_samples() and change
|
||||
* how you interpret the samples array. See the comments for
|
||||
* duh_sigrenderer_set_analyser_callback().
|
||||
*/
|
||||
#endif
|
||||
|
||||
long duh_sigrenderer_generate_samples(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
);
|
||||
|
||||
void duh_sigrenderer_get_current_sample(DUH_SIGRENDERER *sigrenderer, float volume, sample_t *samples);
|
||||
|
||||
void duh_end_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
|
||||
/* DUH Rendering Functions */
|
||||
|
||||
long duh_render(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
int bits, int unsign,
|
||||
float volume, float delta,
|
||||
long size, void *sptr
|
||||
);
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
|
||||
long duh_render_signal(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
) DUMB_DEPRECATED;
|
||||
/* Please use duh_sigrenderer_generate_samples(), and see the
|
||||
* comments for the deprecated duh_sigrenderer_get_samples() too.
|
||||
*/
|
||||
|
||||
typedef DUH_SIGRENDERER DUH_RENDERER DUMB_DEPRECATED;
|
||||
/* Please use DUH_SIGRENDERER instead of DUH_RENDERER. */
|
||||
|
||||
DUH_SIGRENDERER *duh_start_renderer(DUH *duh, int n_channels, long pos) DUMB_DEPRECATED;
|
||||
/* Please use duh_start_sigrenderer() instead. Pass 0 for 'sig'. */
|
||||
|
||||
int duh_renderer_get_n_channels(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
long duh_renderer_get_position(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
/* Please use the duh_sigrenderer_*() equivalents of these two functions. */
|
||||
|
||||
void duh_end_renderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
/* Please use duh_end_sigrenderer() instead. */
|
||||
|
||||
DUH_SIGRENDERER *duh_renderer_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *duh_renderer_get_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
DUH_SIGRENDERER *duh_renderer_decompose_to_sigrenderer(DUH_SIGRENDERER *dr) DUMB_DEPRECATED;
|
||||
/* These functions have become no-ops that just return the parameter.
|
||||
* So, for instance, replace
|
||||
* duh_renderer_encapsulate_sigrenderer(my_sigrenderer)
|
||||
* with
|
||||
* my_sigrenderer
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* Impulse Tracker Support */
|
||||
|
||||
extern int dumb_it_max_to_mix;
|
||||
|
||||
typedef struct DUMB_IT_SIGDATA DUMB_IT_SIGDATA;
|
||||
typedef struct DUMB_IT_SIGRENDERER DUMB_IT_SIGRENDERER;
|
||||
|
||||
DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh);
|
||||
DUH_SIGRENDERER *duh_encapsulate_it_sigrenderer(DUMB_IT_SIGRENDERER *it_sigrenderer, int n_channels, long pos);
|
||||
DUMB_IT_SIGRENDERER *duh_get_it_sigrenderer(DUH_SIGRENDERER *sigrenderer);
|
||||
|
||||
DUH_SIGRENDERER *dumb_it_start_at_order(DUH *duh, int n_channels, int startorder);
|
||||
|
||||
void dumb_it_set_loop_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
|
||||
void dumb_it_set_xm_speed_zero_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data), void *data);
|
||||
void dumb_it_set_midi_callback(DUMB_IT_SIGRENDERER *sigrenderer, int (*callback)(void *data, int channel, unsigned char midi_byte), void *data);
|
||||
|
||||
int dumb_it_callback_terminate(void *data);
|
||||
int dumb_it_callback_midi_block(void *data, int channel, unsigned char midi_byte);
|
||||
|
||||
DUH *dumb_load_it(const char *filename);
|
||||
DUH *dumb_load_xm(const char *filename);
|
||||
DUH *dumb_load_s3m(const char *filename);
|
||||
DUH *dumb_load_mod(const char *filename);
|
||||
|
||||
DUH *dumb_read_it(DUMBFILE *f);
|
||||
DUH *dumb_read_xm(DUMBFILE *f);
|
||||
DUH *dumb_read_s3m(DUMBFILE *f);
|
||||
DUH *dumb_read_mod(DUMBFILE *f);
|
||||
|
||||
DUH *dumb_load_it_quick(const char *filename);
|
||||
DUH *dumb_load_xm_quick(const char *filename);
|
||||
DUH *dumb_load_s3m_quick(const char *filename);
|
||||
DUH *dumb_load_mod_quick(const char *filename);
|
||||
|
||||
DUH *dumb_read_it_quick(DUMBFILE *f);
|
||||
DUH *dumb_read_xm_quick(DUMBFILE *f);
|
||||
DUH *dumb_read_s3m_quick(DUMBFILE *f);
|
||||
DUH *dumb_read_mod_quick(DUMBFILE *f);
|
||||
|
||||
long dumb_it_build_checkpoints(DUMB_IT_SIGDATA *sigdata);
|
||||
void dumb_it_do_initial_runthrough(DUH *duh);
|
||||
|
||||
const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd);
|
||||
|
||||
int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd);
|
||||
int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd);
|
||||
int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd);
|
||||
|
||||
const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i);
|
||||
const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i);
|
||||
const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i);
|
||||
const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i);
|
||||
|
||||
int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv);
|
||||
|
||||
int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv);
|
||||
|
||||
int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed);
|
||||
|
||||
int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd);
|
||||
void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo);
|
||||
|
||||
int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel);
|
||||
void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume);
|
||||
|
||||
int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr);
|
||||
int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr);
|
||||
|
||||
int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr);
|
||||
void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv);
|
||||
|
||||
int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr);
|
||||
void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo);
|
||||
|
||||
int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr);
|
||||
void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed);
|
||||
|
||||
#define DUMB_IT_N_CHANNELS 64
|
||||
#define DUMB_IT_N_NNA_CHANNELS 192
|
||||
#define DUMB_IT_TOTAL_CHANNELS (DUMB_IT_N_CHANNELS + DUMB_IT_N_NNA_CHANNELS)
|
||||
|
||||
/* Channels passed to any of these functions are 0-based */
|
||||
int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel);
|
||||
void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume);
|
||||
|
||||
int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel);
|
||||
void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted);
|
||||
|
||||
typedef struct DUMB_IT_CHANNEL_STATE DUMB_IT_CHANNEL_STATE;
|
||||
|
||||
struct DUMB_IT_CHANNEL_STATE
|
||||
{
|
||||
int channel; /* 0-based; meaningful for NNA channels */
|
||||
int sample; /* 1-based; 0 if nothing playing, then other fields undef */
|
||||
int freq; /* in Hz */
|
||||
float volume; /* 1.0 maximum; affected by ALL factors, inc. mixing vol */
|
||||
unsigned char pan; /* 0-64, 100 for surround */
|
||||
signed char subpan; /* use (pan + subpan/256.0f) or ((pan<<8)+subpan) */
|
||||
unsigned char filter_cutoff; /* 0-127 cutoff=127 AND resonance=0 */
|
||||
unsigned char filter_subcutoff; /* 0-255 -> no filters (subcutoff */
|
||||
unsigned char filter_resonance; /* 0-127 always 0 in this case) */
|
||||
/* subcutoff only changes from zero if filter envelopes are in use. The
|
||||
* calculation (filter_cutoff + filter_subcutoff/256.0f) gives a more
|
||||
* accurate filter cutoff measurement as a float. It would often be more
|
||||
* useful to use a scaled int such as ((cutoff<<8) + subcutoff).
|
||||
*/
|
||||
};
|
||||
|
||||
/* Values of 64 or more will access NNA channels here. */
|
||||
void dumb_it_sr_get_channel_state(DUMB_IT_SIGRENDERER *sr, int channel, DUMB_IT_CHANNEL_STATE *state);
|
||||
|
||||
|
||||
/* Signal Design Helper Values */
|
||||
|
||||
/* Use pow(DUMB_SEMITONE_BASE, n) to get the 'delta' value to transpose up by
|
||||
* n semitones. To transpose down, use negative n.
|
||||
*/
|
||||
#define DUMB_SEMITONE_BASE 1.059463094359295309843105314939748495817
|
||||
|
||||
/* Use pow(DUMB_QUARTERTONE_BASE, n) to get the 'delta' value to transpose up
|
||||
* by n quartertones. To transpose down, use negative n.
|
||||
*/
|
||||
#define DUMB_QUARTERTONE_BASE 1.029302236643492074463779317738953977823
|
||||
|
||||
/* Use pow(DUMB_PITCH_BASE, n) to get the 'delta' value to transpose up by n
|
||||
* units. In this case, 256 units represent one semitone; 3072 units
|
||||
* represent one octave. These units are used by the sequence signal (SEQU).
|
||||
*/
|
||||
#define DUMB_PITCH_BASE 1.000225659305069791926712241547647863626
|
||||
|
||||
|
||||
/* Signal Design Function Types */
|
||||
|
||||
typedef void sigdata_t;
|
||||
typedef void sigrenderer_t;
|
||||
|
||||
typedef sigdata_t *(*DUH_LOAD_SIGDATA)(DUH *duh, DUMBFILE *file);
|
||||
|
||||
typedef sigrenderer_t *(*DUH_START_SIGRENDERER)(
|
||||
DUH *duh,
|
||||
sigdata_t *sigdata,
|
||||
int n_channels,
|
||||
long pos
|
||||
);
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_SET_SIGPARAM)(
|
||||
sigrenderer_t *sigrenderer,
|
||||
unsigned char id, long value
|
||||
);
|
||||
|
||||
typedef long (*DUH_SIGRENDERER_GENERATE_SAMPLES)(
|
||||
sigrenderer_t *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
);
|
||||
|
||||
typedef void (*DUH_SIGRENDERER_GET_CURRENT_SAMPLE)(
|
||||
sigrenderer_t *sigrenderer,
|
||||
float volume,
|
||||
sample_t *samples
|
||||
);
|
||||
|
||||
typedef void (*DUH_END_SIGRENDERER)(sigrenderer_t *sigrenderer);
|
||||
|
||||
typedef void (*DUH_UNLOAD_SIGDATA)(sigdata_t *sigdata);
|
||||
|
||||
|
||||
/* Signal Design Function Registration */
|
||||
|
||||
typedef struct DUH_SIGTYPE_DESC
|
||||
{
|
||||
long type;
|
||||
DUH_LOAD_SIGDATA load_sigdata;
|
||||
DUH_START_SIGRENDERER start_sigrenderer;
|
||||
DUH_SIGRENDERER_SET_SIGPARAM sigrenderer_set_sigparam;
|
||||
DUH_SIGRENDERER_GENERATE_SAMPLES sigrenderer_generate_samples;
|
||||
DUH_SIGRENDERER_GET_CURRENT_SAMPLE sigrenderer_get_current_sample;
|
||||
DUH_END_SIGRENDERER end_sigrenderer;
|
||||
DUH_UNLOAD_SIGDATA unload_sigdata;
|
||||
}
|
||||
DUH_SIGTYPE_DESC;
|
||||
|
||||
void dumb_register_sigtype(DUH_SIGTYPE_DESC *desc);
|
||||
|
||||
|
||||
// Decide where to put these functions; new heading?
|
||||
|
||||
sigdata_t *duh_get_raw_sigdata(DUH *duh, int sig, long type);
|
||||
|
||||
DUH_SIGRENDERER *duh_encapsulate_raw_sigrenderer(sigrenderer_t *vsigrenderer, DUH_SIGTYPE_DESC *desc, int n_channels, long pos);
|
||||
sigrenderer_t *duh_get_raw_sigrenderer(DUH_SIGRENDERER *sigrenderer, long type);
|
||||
|
||||
|
||||
/* Standard Signal Types */
|
||||
|
||||
//void dumb_register_sigtype_sample(void);
|
||||
|
||||
|
||||
/* Sample Buffer Allocation Helpers */
|
||||
|
||||
#ifdef DUMB_DECLARE_DEPRECATED
|
||||
sample_t **create_sample_buffer(int n_channels, long length) DUMB_DEPRECATED;
|
||||
/* DUMB has been changed to interleave stereo samples. Use
|
||||
* allocate_sample_buffer() instead, and see the comments for
|
||||
* duh_sigrenderer_set_analyser_callback().
|
||||
*/
|
||||
#endif
|
||||
sample_t **allocate_sample_buffer(int n_channels, long length);
|
||||
void destroy_sample_buffer(sample_t **samples);
|
||||
|
||||
|
||||
/* Silencing Helper */
|
||||
|
||||
void dumb_silence(sample_t *samples, long length);
|
||||
|
||||
|
||||
/* Click Removal Helpers */
|
||||
|
||||
typedef struct DUMB_CLICK_REMOVER DUMB_CLICK_REMOVER;
|
||||
|
||||
DUMB_CLICK_REMOVER *dumb_create_click_remover(void);
|
||||
void dumb_record_click(DUMB_CLICK_REMOVER *cr, long pos, sample_t step);
|
||||
void dumb_remove_clicks(DUMB_CLICK_REMOVER *cr, sample_t *samples, long length, int step, float halflife);
|
||||
sample_t dumb_click_remover_get_offset(DUMB_CLICK_REMOVER *cr);
|
||||
void dumb_destroy_click_remover(DUMB_CLICK_REMOVER *cr);
|
||||
|
||||
DUMB_CLICK_REMOVER **dumb_create_click_remover_array(int n);
|
||||
void dumb_record_click_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step);
|
||||
void dumb_record_click_negative_array(int n, DUMB_CLICK_REMOVER **cr, long pos, sample_t *step);
|
||||
void dumb_remove_clicks_array(int n, DUMB_CLICK_REMOVER **cr, sample_t **samples, long length, float halflife);
|
||||
void dumb_click_remover_get_offset_array(int n, DUMB_CLICK_REMOVER **cr, sample_t *offset);
|
||||
void dumb_destroy_click_remover_array(int n, DUMB_CLICK_REMOVER **cr);
|
||||
|
||||
|
||||
/* Resampling Helpers */
|
||||
|
||||
#define DUMB_RQ_ALIASING 0
|
||||
#define DUMB_RQ_LINEAR 1
|
||||
#define DUMB_RQ_CUBIC 2
|
||||
#define DUMB_RQ_N_LEVELS 3
|
||||
extern int dumb_resampling_quality;
|
||||
|
||||
typedef struct DUMB_RESAMPLER DUMB_RESAMPLER;
|
||||
|
||||
typedef void (*DUMB_RESAMPLE_PICKUP)(DUMB_RESAMPLER *resampler, void *data);
|
||||
|
||||
struct DUMB_RESAMPLER
|
||||
{
|
||||
void *src;
|
||||
long pos;
|
||||
int subpos;
|
||||
long start, end;
|
||||
int dir;
|
||||
DUMB_RESAMPLE_PICKUP pickup;
|
||||
void *pickup_data;
|
||||
int min_quality;
|
||||
int max_quality;
|
||||
/* Everything below this point is internal: do not use. */
|
||||
union {
|
||||
sample_t x24[3*2];
|
||||
short x16[3*2];
|
||||
signed char x8[3*2];
|
||||
} x;
|
||||
int overshot;
|
||||
};
|
||||
|
||||
void dumb_reset_resampler(DUMB_RESAMPLER *resampler, sample_t *src, int src_channels, long pos, long start, long end);
|
||||
DUMB_RESAMPLER *dumb_start_resampler(sample_t *src, int src_channels, long pos, long start, long end);
|
||||
long dumb_resample_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta);
|
||||
long dumb_resample_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
void dumb_resample_get_current_sample_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_2_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_end_resampler(DUMB_RESAMPLER *resampler);
|
||||
|
||||
void dumb_reset_resampler_16(DUMB_RESAMPLER *resampler, short *src, int src_channels, long pos, long start, long end);
|
||||
DUMB_RESAMPLER *dumb_start_resampler_16(short *src, int src_channels, long pos, long start, long end);
|
||||
long dumb_resample_16_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta);
|
||||
long dumb_resample_16_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_16_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_16_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
void dumb_resample_get_current_sample_16_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_16_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_16_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_16_2_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_end_resampler_16(DUMB_RESAMPLER *resampler);
|
||||
|
||||
void dumb_reset_resampler_8(DUMB_RESAMPLER *resampler, signed char *src, int src_channels, long pos, long start, long end);
|
||||
DUMB_RESAMPLER *dumb_start_resampler_8(signed char *src, int src_channels, long pos, long start, long end);
|
||||
long dumb_resample_8_1_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta);
|
||||
long dumb_resample_8_1_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_8_2_1(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_8_2_2(DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
void dumb_resample_get_current_sample_8_1_1(DUMB_RESAMPLER *resampler, float volume, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_8_1_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_8_2_1(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_8_2_2(DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_end_resampler_8(DUMB_RESAMPLER *resampler);
|
||||
|
||||
void dumb_reset_resampler_n(int n, DUMB_RESAMPLER *resampler, void *src, int src_channels, long pos, long start, long end);
|
||||
DUMB_RESAMPLER *dumb_start_resampler_n(int n, void *src, int src_channels, long pos, long start, long end);
|
||||
long dumb_resample_n_1_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume, float delta);
|
||||
long dumb_resample_n_1_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_n_2_1(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
long dumb_resample_n_2_2(int n, DUMB_RESAMPLER *resampler, sample_t *dst, long dst_size, float volume_left, float volume_right, float delta);
|
||||
void dumb_resample_get_current_sample_n_1_1(int n, DUMB_RESAMPLER *resampler, float volume, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_n_1_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_n_2_1(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_resample_get_current_sample_n_2_2(int n, DUMB_RESAMPLER *resampler, float volume_left, float volume_right, sample_t *dst);
|
||||
void dumb_end_resampler_n(int n, DUMB_RESAMPLER *resampler);
|
||||
|
||||
|
||||
/* DUH Construction */
|
||||
|
||||
DUH *make_duh(
|
||||
long length,
|
||||
int n_tags,
|
||||
const char *const tag[][2],
|
||||
int n_signals,
|
||||
DUH_SIGTYPE_DESC *desc[],
|
||||
sigdata_t *sigdata[]
|
||||
);
|
||||
|
||||
void duh_set_length(DUH *duh, long length);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* DUMB_H */
|
34
dumb/include/internal/aldumb.h
Normal file
34
dumb/include/internal/aldumb.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* internal/aldumb.h - The internal header file / / \ \
|
||||
* for DUMB with Allegro. | < / \_
|
||||
* | \/ /\ /
|
||||
* This header file provides access to the \_ / > /
|
||||
* internal structure of DUMB, and is liable | \ / /
|
||||
* to change, mutate or cease to exist at any | ' /
|
||||
* moment. Include it at your own peril. \__/
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* Seriously. You don't need access to anything in this file. All right, you
|
||||
* probably do actually. But if you use it, you will be relying on a specific
|
||||
* version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please
|
||||
* contact the authors so that we can provide a public API for what you need.
|
||||
*/
|
||||
|
||||
#ifndef INTERNAL_ALDUMB_H
|
||||
#define INTERNAL_ALDUMB_H
|
||||
|
||||
|
||||
void _dat_unload_duh(void *duh);
|
||||
|
||||
|
||||
#endif /* INTERNAL_DUMB_H */
|
61
dumb/include/internal/dumb.h
Normal file
61
dumb/include/internal/dumb.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* internal/dumb.h - DUMB's internal declarations. / / \ \
|
||||
* | < / \_
|
||||
* This header file provides access to the | \/ /\ /
|
||||
* internal structure of DUMB, and is liable \_ / > /
|
||||
* to change, mutate or cease to exist at any | \ / /
|
||||
* moment. Include it at your own peril. | ' /
|
||||
* \__/
|
||||
* ...
|
||||
*
|
||||
* Seriously. You don't need access to anything in this file. All right, you
|
||||
* probably do actually. But if you use it, you will be relying on a specific
|
||||
* version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please
|
||||
* contact the authors so that we can provide a public API for what you need.
|
||||
*/
|
||||
|
||||
#ifndef INTERNAL_DUMB_H
|
||||
#define INTERNAL_DUMB_H
|
||||
|
||||
|
||||
typedef struct DUH_SIGTYPE_DESC_LINK
|
||||
{
|
||||
struct DUH_SIGTYPE_DESC_LINK *next;
|
||||
DUH_SIGTYPE_DESC *desc;
|
||||
}
|
||||
DUH_SIGTYPE_DESC_LINK;
|
||||
|
||||
|
||||
typedef struct DUH_SIGNAL
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
DUH_SIGTYPE_DESC *desc;
|
||||
}
|
||||
DUH_SIGNAL;
|
||||
|
||||
|
||||
struct DUH
|
||||
{
|
||||
long length;
|
||||
|
||||
int n_tags;
|
||||
char *(*tag)[2];
|
||||
|
||||
int n_signals;
|
||||
DUH_SIGNAL **signal;
|
||||
};
|
||||
|
||||
|
||||
DUH_SIGTYPE_DESC *_dumb_get_sigtype_desc(long type);
|
||||
|
||||
|
||||
#endif /* INTERNAL_DUMB_H */
|
733
dumb/include/internal/it.h
Normal file
733
dumb/include/internal/it.h
Normal file
|
@ -0,0 +1,733 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* internal/it.h - Internal stuff for IT playback / / \ \
|
||||
* and MOD/XM/S3M conversion. | < / \_
|
||||
* | \/ /\ /
|
||||
* This header file provides access to the \_ / > /
|
||||
* internal structure of DUMB, and is liable | \ / /
|
||||
* to change, mutate or cease to exist at any | ' /
|
||||
* moment. Include it at your own peril. \__/
|
||||
*
|
||||
* ...
|
||||
*
|
||||
* Seriously. You don't need access to anything in this file. All right, you
|
||||
* probably do actually. But if you use it, you will be relying on a specific
|
||||
* version of DUMB, so please check DUMB_VERSION defined in dumb.h. Please
|
||||
* contact the authors so that we can provide a public API for what you need.
|
||||
*/
|
||||
|
||||
#ifndef INTERNAL_IT_H
|
||||
#define INTERNAL_IT_H
|
||||
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
|
||||
|
||||
/** TO DO: THINK ABOUT THE FOLLOWING:
|
||||
|
||||
sigdata->flags & IT_COMPATIBLE_GXX
|
||||
|
||||
Bit 5: On = Link Effect G's memory with Effect E/F. Also
|
||||
Gxx with an instrument present will cause the
|
||||
envelopes to be retriggered. If you change a
|
||||
sample on a row with Gxx, it'll adjust the
|
||||
frequency of the current note according to:
|
||||
|
||||
NewFrequency = OldFrequency * NewC5 / OldC5;
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/* These #defines are TEMPORARY. They are used to write alternative code to
|
||||
* handle ambiguities in the format specification. The correct code in each
|
||||
* case will be determined most likely by experimentation.
|
||||
*/
|
||||
#define STEREO_SAMPLES_COUNT_AS_TWO
|
||||
#define INVALID_ORDERS_END_SONG
|
||||
#define INVALID_NOTES_CAUSE_NOTE_CUT
|
||||
#define SUSTAIN_LOOP_OVERRIDES_NORMAL_LOOP
|
||||
#define VOLUME_OUT_OF_RANGE_SETS_MAXIMUM
|
||||
|
||||
|
||||
|
||||
#define SIGTYPE_IT DUMB_ID('I', 'T', ' ', ' ')
|
||||
|
||||
#define IT_SIGNATURE DUMB_ID('I', 'M', 'P', 'M')
|
||||
#define IT_INSTRUMENT_SIGNATURE DUMB_ID('I', 'M', 'P', 'I')
|
||||
#define IT_SAMPLE_SIGNATURE DUMB_ID('I', 'M', 'P', 'S')
|
||||
|
||||
|
||||
|
||||
/* 1 minute per 4 rows, each row 6 ticks; this is divided by the tempo to get
|
||||
* the interval between ticks.
|
||||
*/
|
||||
#define TICK_TIME_DIVIDEND ((65536 * 60) / (4 * 6))
|
||||
|
||||
|
||||
|
||||
/* I'm not going to try to explain this, because I didn't derive it very
|
||||
* formally ;)
|
||||
*/
|
||||
/* #define AMIGA_DIVISOR ((float)(4.0 * 14317056.0)) */
|
||||
/* I believe the following one to be more accurate. */
|
||||
#define AMIGA_DIVISOR ((float)(8.0 * 7159090.5))
|
||||
|
||||
|
||||
|
||||
typedef struct IT_MIDI IT_MIDI;
|
||||
typedef struct IT_FILTER_STATE IT_FILTER_STATE;
|
||||
typedef struct IT_ENVELOPE IT_ENVELOPE;
|
||||
typedef struct IT_INSTRUMENT IT_INSTRUMENT;
|
||||
typedef struct IT_SAMPLE IT_SAMPLE;
|
||||
typedef struct IT_ENTRY IT_ENTRY;
|
||||
typedef struct IT_PATTERN IT_PATTERN;
|
||||
typedef struct IT_PLAYING_ENVELOPE IT_PLAYING_ENVELOPE;
|
||||
typedef struct IT_PLAYING IT_PLAYING;
|
||||
typedef struct IT_CHANNEL IT_CHANNEL;
|
||||
typedef struct IT_CHECKPOINT IT_CHECKPOINT;
|
||||
typedef struct IT_CALLBACKS IT_CALLBACKS;
|
||||
|
||||
|
||||
|
||||
struct IT_MIDI
|
||||
{
|
||||
unsigned char SFmacro[16][16]; // read these from 0x120
|
||||
unsigned char SFmacrolen[16];
|
||||
unsigned short SFmacroz[16]; /* Bitfield; bit 0 set = z in first position */
|
||||
unsigned char Zmacro[128][16]; // read these from 0x320
|
||||
unsigned char Zmacrolen[128];
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_FILTER_STATE
|
||||
{
|
||||
sample_t currsample, prevsample;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_ENVELOPE_ON 1
|
||||
#define IT_ENVELOPE_LOOP_ON 2
|
||||
#define IT_ENVELOPE_SUSTAIN_LOOP 4
|
||||
#define IT_ENVELOPE_PITCH_IS_FILTER 128
|
||||
|
||||
struct IT_ENVELOPE
|
||||
{
|
||||
unsigned char flags;
|
||||
unsigned char n_nodes;
|
||||
unsigned char loop_start;
|
||||
unsigned char loop_end;
|
||||
unsigned char sus_loop_start;
|
||||
unsigned char sus_loop_end;
|
||||
signed char node_y[25];
|
||||
unsigned short node_t[25];
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define NNA_NOTE_CUT 0
|
||||
#define NNA_NOTE_CONTINUE 1
|
||||
#define NNA_NOTE_OFF 2
|
||||
#define NNA_NOTE_FADE 3
|
||||
|
||||
#define DCT_OFF 0
|
||||
#define DCT_NOTE 1
|
||||
#define DCT_SAMPLE 2
|
||||
#define DCT_INSTRUMENT 3
|
||||
|
||||
#define DCA_NOTE_CUT 0
|
||||
#define DCA_NOTE_OFF 1
|
||||
#define DCA_NOTE_FADE 2
|
||||
|
||||
struct IT_INSTRUMENT
|
||||
{
|
||||
unsigned char name[27];
|
||||
unsigned char filename[14];
|
||||
|
||||
int fadeout;
|
||||
|
||||
IT_ENVELOPE volume_envelope;
|
||||
IT_ENVELOPE pan_envelope;
|
||||
IT_ENVELOPE pitch_envelope;
|
||||
|
||||
unsigned char new_note_action;
|
||||
unsigned char dup_check_type;
|
||||
unsigned char dup_check_action;
|
||||
unsigned char pp_separation;
|
||||
unsigned char pp_centre;
|
||||
unsigned char global_volume;
|
||||
unsigned char default_pan;
|
||||
unsigned char random_volume;
|
||||
unsigned char random_pan;
|
||||
|
||||
unsigned char filter_cutoff;
|
||||
unsigned char filter_resonance;
|
||||
|
||||
unsigned char map_note[120];
|
||||
unsigned short map_sample[120];
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_SAMPLE_EXISTS 1
|
||||
#define IT_SAMPLE_16BIT 2
|
||||
#define IT_SAMPLE_STEREO 4
|
||||
#define IT_SAMPLE_LOOP 16
|
||||
#define IT_SAMPLE_SUS_LOOP 32
|
||||
#define IT_SAMPLE_PINGPONG_LOOP 64
|
||||
#define IT_SAMPLE_PINGPONG_SUS_LOOP 128
|
||||
|
||||
#define IT_VIBRATO_SINE 0
|
||||
#define IT_VIBRATO_SAWTOOTH 1 /* Ramp down */
|
||||
#define IT_VIBRATO_SQUARE 2
|
||||
#define IT_VIBRATO_RANDOM 3
|
||||
|
||||
struct IT_SAMPLE
|
||||
{
|
||||
unsigned char name[29];
|
||||
unsigned char filename[14];
|
||||
unsigned char flags;
|
||||
unsigned char global_volume;
|
||||
unsigned char default_volume;
|
||||
unsigned char default_pan;
|
||||
/* default_pan:
|
||||
* 0-255 for XM
|
||||
* ignored for MOD
|
||||
* otherwise, 0-64, and add 128 to enable
|
||||
*/
|
||||
|
||||
long length;
|
||||
long loop_start;
|
||||
long loop_end;
|
||||
long C5_speed;
|
||||
long sus_loop_start;
|
||||
long sus_loop_end;
|
||||
|
||||
unsigned char vibrato_speed;
|
||||
unsigned char vibrato_depth;
|
||||
unsigned char vibrato_rate;
|
||||
unsigned char vibrato_waveform;
|
||||
|
||||
void *data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_ENTRY_NOTE 1
|
||||
#define IT_ENTRY_INSTRUMENT 2
|
||||
#define IT_ENTRY_VOLPAN 4
|
||||
#define IT_ENTRY_EFFECT 8
|
||||
|
||||
#define IT_SET_END_ROW(entry) ((entry)->channel = 255)
|
||||
#define IT_IS_END_ROW(entry) ((entry)->channel >= DUMB_IT_N_CHANNELS)
|
||||
|
||||
#define IT_NOTE_OFF 255
|
||||
#define IT_NOTE_CUT 254
|
||||
|
||||
#define IT_ENVELOPE_SHIFT 8
|
||||
|
||||
#define IT_SURROUND 100
|
||||
#define IT_IS_SURROUND(pan) ((pan) > 64)
|
||||
#define IT_IS_SURROUND_SHIFTED(pan) ((pan) > 64 << IT_ENVELOPE_SHIFT)
|
||||
|
||||
#define IT_SET_SPEED 1
|
||||
#define IT_JUMP_TO_ORDER 2
|
||||
#define IT_BREAK_TO_ROW 3
|
||||
#define IT_VOLUME_SLIDE 4
|
||||
#define IT_PORTAMENTO_DOWN 5
|
||||
#define IT_PORTAMENTO_UP 6
|
||||
#define IT_TONE_PORTAMENTO 7
|
||||
#define IT_VIBRATO 8
|
||||
#define IT_TREMOR 9
|
||||
#define IT_ARPEGGIO 10
|
||||
#define IT_VOLSLIDE_VIBRATO 11
|
||||
#define IT_VOLSLIDE_TONEPORTA 12
|
||||
#define IT_SET_CHANNEL_VOLUME 13
|
||||
#define IT_CHANNEL_VOLUME_SLIDE 14
|
||||
#define IT_SET_SAMPLE_OFFSET 15
|
||||
#define IT_PANNING_SLIDE 16
|
||||
#define IT_RETRIGGER_NOTE 17
|
||||
#define IT_TREMOLO 18
|
||||
#define IT_S 19
|
||||
#define IT_SET_SONG_TEMPO 20
|
||||
#define IT_FINE_VIBRATO 21
|
||||
#define IT_SET_GLOBAL_VOLUME 22
|
||||
#define IT_GLOBAL_VOLUME_SLIDE 23
|
||||
#define IT_SET_PANNING 24
|
||||
#define IT_PANBRELLO 25
|
||||
#define IT_MIDI_MACRO 26 //see MIDI.TXT
|
||||
|
||||
/* Some effects needed for XM compatibility */
|
||||
#define IT_XM_PORTAMENTO_DOWN 27
|
||||
#define IT_XM_PORTAMENTO_UP 28
|
||||
#define IT_XM_FINE_VOLSLIDE_DOWN 29
|
||||
#define IT_XM_FINE_VOLSLIDE_UP 30
|
||||
#define IT_XM_RETRIGGER_NOTE 31
|
||||
#define IT_XM_KEY_OFF 32
|
||||
#define IT_XM_SET_ENVELOPE_POSITION 33
|
||||
|
||||
#define IT_N_EFFECTS 34
|
||||
|
||||
/* These represent the top nibble of the command value. */
|
||||
#define IT_S_SET_FILTER 0 /* Greyed out in IT... */
|
||||
#define IT_S_SET_GLISSANDO_CONTROL 1 /* Greyed out in IT... */
|
||||
#define IT_S_FINETUNE 2 /* Greyed out in IT... */
|
||||
#define IT_S_SET_VIBRATO_WAVEFORM 3
|
||||
#define IT_S_SET_TREMOLO_WAVEFORM 4
|
||||
#define IT_S_SET_PANBRELLO_WAVEFORM 5
|
||||
#define IT_S_FINE_PATTERN_DELAY 6
|
||||
#define IT_S7 7
|
||||
#define IT_S_SET_PAN 8
|
||||
#define IT_S_SET_SURROUND_SOUND 9
|
||||
#define IT_S_SET_HIGH_OFFSET 10
|
||||
#define IT_S_PATTERN_LOOP 11
|
||||
#define IT_S_DELAYED_NOTE_CUT 12
|
||||
#define IT_S_NOTE_DELAY 13
|
||||
#define IT_S_PATTERN_DELAY 14
|
||||
#define IT_S_SET_MIDI_MACRO 15
|
||||
|
||||
/*
|
||||
S0x Set filter
|
||||
S1x Set glissando control
|
||||
S2x Set finetune
|
||||
|
||||
|
||||
S3x Set vibrato waveform to type x
|
||||
S4x Set tremelo waveform to type x
|
||||
S5x Set panbrello waveform to type x
|
||||
Waveforms for commands S3x, S4x and S5x:
|
||||
0: Sine wave
|
||||
1: Ramp down
|
||||
2: Square wave
|
||||
3: Random wave
|
||||
S6x Pattern delay for x ticks
|
||||
S70 Past note cut
|
||||
S71 Past note off
|
||||
S72 Past note fade
|
||||
S73 Set NNA to note cut
|
||||
S74 Set NNA to continue
|
||||
S75 Set NNA to note off
|
||||
S76 Set NNA to note fade
|
||||
S77 Turn off volume envelope
|
||||
S78 Turn on volume envelope
|
||||
S79 Turn off panning envelope
|
||||
S7A Turn on panning envelope
|
||||
S7B Turn off pitch envelope
|
||||
S7C Turn on pitch envelope
|
||||
S8x Set panning position
|
||||
S91 Set surround sound
|
||||
SAy Set high value of sample offset yxx00h
|
||||
SB0 Set loopback point
|
||||
SBx Loop x times to loopback point
|
||||
SCx Note cut after x ticks
|
||||
SDx Note delay for x ticks
|
||||
SEx Pattern delay for x rows
|
||||
SFx Set parameterised MIDI Macro
|
||||
*/
|
||||
|
||||
struct IT_ENTRY
|
||||
{
|
||||
unsigned char channel; /* End of row if channel >= DUMB_IT_N_CHANNELS */
|
||||
unsigned char mask;
|
||||
unsigned char note;
|
||||
unsigned char instrument;
|
||||
unsigned char volpan;
|
||||
unsigned char effect;
|
||||
unsigned char effectvalue;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_PATTERN
|
||||
{
|
||||
int n_rows;
|
||||
int n_entries;
|
||||
IT_ENTRY *entry;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_STEREO 1
|
||||
#define IT_USE_INSTRUMENTS 4
|
||||
#define IT_LINEAR_SLIDES 8 /* If not set, use Amiga slides */
|
||||
#define IT_OLD_EFFECTS 16
|
||||
#define IT_COMPATIBLE_GXX 32
|
||||
|
||||
/* Make sure IT_WAS_AN_XM and IT_WAS_A_MOD aren't set accidentally */
|
||||
#define IT_REAL_FLAGS 63
|
||||
|
||||
#define IT_WAS_AN_XM 64 /* Set for both XMs and MODs */
|
||||
#define IT_WAS_A_MOD 128
|
||||
|
||||
#define IT_ORDER_END 255
|
||||
#define IT_ORDER_SKIP 254
|
||||
|
||||
struct DUMB_IT_SIGDATA
|
||||
{
|
||||
unsigned char name[29];
|
||||
|
||||
unsigned char *song_message;
|
||||
|
||||
int n_orders;
|
||||
int n_instruments;
|
||||
int n_samples;
|
||||
int n_patterns;
|
||||
|
||||
int flags;
|
||||
|
||||
int global_volume;
|
||||
int mixing_volume;
|
||||
int speed;
|
||||
int tempo;
|
||||
int pan_separation;
|
||||
|
||||
unsigned char channel_pan[DUMB_IT_N_CHANNELS];
|
||||
unsigned char channel_volume[DUMB_IT_N_CHANNELS];
|
||||
|
||||
unsigned char *order;
|
||||
unsigned char restart_position; /* for XM compatiblity */
|
||||
|
||||
IT_INSTRUMENT *instrument;
|
||||
IT_SAMPLE *sample;
|
||||
IT_PATTERN *pattern;
|
||||
|
||||
IT_MIDI *midi;
|
||||
|
||||
IT_CHECKPOINT *checkpoint;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_PLAYING_ENVELOPE
|
||||
{
|
||||
int next_node;
|
||||
int tick;
|
||||
int value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_PLAYING_BACKGROUND 1
|
||||
#define IT_PLAYING_SUSTAINOFF 2
|
||||
#define IT_PLAYING_FADING 4
|
||||
#define IT_PLAYING_DEAD 8
|
||||
|
||||
struct IT_PLAYING
|
||||
{
|
||||
int flags;
|
||||
|
||||
IT_CHANNEL *channel;
|
||||
IT_SAMPLE *sample;
|
||||
IT_INSTRUMENT *instrument;
|
||||
IT_INSTRUMENT *env_instrument;
|
||||
|
||||
unsigned short sampnum;
|
||||
unsigned char instnum;
|
||||
|
||||
unsigned char channel_volume;
|
||||
|
||||
unsigned char volume;
|
||||
unsigned short pan;
|
||||
|
||||
unsigned char note;
|
||||
|
||||
unsigned char filter_cutoff;
|
||||
unsigned char filter_resonance;
|
||||
|
||||
unsigned short true_filter_cutoff; /* These incorporate the filter envelope, and will not */
|
||||
unsigned char true_filter_resonance; /* be changed if they would be set to 127<<8 and 0. */
|
||||
|
||||
unsigned char vibrato_speed;
|
||||
unsigned char vibrato_depth;
|
||||
unsigned char vibrato_n; /* May be specified twice: volpan & effect. */
|
||||
unsigned char vibrato_time;
|
||||
|
||||
unsigned char tremolo_speed;
|
||||
unsigned char tremolo_depth;
|
||||
unsigned char tremolo_time;
|
||||
|
||||
unsigned char sample_vibrato_time;
|
||||
int sample_vibrato_depth; /* Starts at rate?0:depth, increases by rate */
|
||||
|
||||
int slide;
|
||||
float delta;
|
||||
|
||||
IT_PLAYING_ENVELOPE volume_envelope;
|
||||
IT_PLAYING_ENVELOPE pan_envelope;
|
||||
IT_PLAYING_ENVELOPE pitch_envelope;
|
||||
|
||||
int fadeoutcount;
|
||||
|
||||
IT_FILTER_STATE filter_state[2]; /* Left and right */
|
||||
|
||||
DUMB_RESAMPLER resampler;
|
||||
|
||||
/* time_lost is used to emulate Impulse Tracker's sample looping
|
||||
* characteristics. When time_lost is added to pos, the result represents
|
||||
* the position in the theoretical version of the sample where all loops
|
||||
* have been expanded. If this is stored, the resampling helpers will
|
||||
* safely convert it for use with new loop boundaries. The situation is
|
||||
* slightly more complicated if dir == -1 when the change takes place; we
|
||||
* must reflect pos off the loop end point and set dir to 1 before
|
||||
* proceeding.
|
||||
*/
|
||||
long time_lost;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IT_CHANNEL_MUTED 1
|
||||
|
||||
struct IT_CHANNEL
|
||||
{
|
||||
int flags;
|
||||
|
||||
unsigned char volume;
|
||||
signed char volslide;
|
||||
signed char xm_volslide;
|
||||
signed char panslide;
|
||||
|
||||
/* xm_volslide is used for volume slides done in the volume column in an
|
||||
* XM file, since it seems the volume column slide is applied first,
|
||||
* followed by clamping, followed by the effects column slide. IT does
|
||||
* not exhibit this behaviour, so xm_volslide is maintained at zero.
|
||||
*/
|
||||
|
||||
unsigned char pan;
|
||||
unsigned short truepan;
|
||||
|
||||
unsigned char channelvolume;
|
||||
signed char channelvolslide;
|
||||
|
||||
unsigned char instrument;
|
||||
unsigned char note;
|
||||
|
||||
unsigned char SFmacro;
|
||||
|
||||
unsigned char filter_cutoff;
|
||||
unsigned char filter_resonance;
|
||||
|
||||
unsigned char key_off_count;
|
||||
unsigned char note_cut_count;
|
||||
unsigned char note_delay_count;
|
||||
IT_ENTRY *note_delay_entry;
|
||||
|
||||
int arpeggio;
|
||||
unsigned char retrig;
|
||||
unsigned char xm_retrig;
|
||||
int retrig_tick;
|
||||
|
||||
unsigned char tremor;
|
||||
unsigned char tremor_time; /* Bit 6 set if note on; bit 7 set if tremor active. */
|
||||
|
||||
int portamento;
|
||||
int toneporta;
|
||||
unsigned char destnote;
|
||||
|
||||
/** WARNING - for neatness, should one or both of these be in the IT_PLAYING struct? */
|
||||
unsigned short sample;
|
||||
unsigned char truenote;
|
||||
|
||||
unsigned char midi_state;
|
||||
|
||||
signed char lastvolslide;
|
||||
unsigned char lastDKL;
|
||||
unsigned char lastEF; /* Doubles as last portamento up for XM files */
|
||||
unsigned char lastG;
|
||||
unsigned char lastHspeed;
|
||||
unsigned char lastHdepth;
|
||||
unsigned char lastRspeed;
|
||||
unsigned char lastRdepth;
|
||||
unsigned char lastI;
|
||||
unsigned char lastJ; /* Doubles as last portamento down for XM files */
|
||||
unsigned char lastN;
|
||||
unsigned char lastO;
|
||||
unsigned char high_offset;
|
||||
unsigned char lastP;
|
||||
unsigned char lastQ;
|
||||
unsigned char lastS;
|
||||
unsigned char pat_loop_row;
|
||||
unsigned char pat_loop_count;
|
||||
unsigned char pat_loop_end_row; /* Used to catch infinite pattern loops */
|
||||
unsigned char lastW;
|
||||
|
||||
unsigned char xm_lastE1;
|
||||
unsigned char xm_lastE2;
|
||||
unsigned char xm_lastEA;
|
||||
unsigned char xm_lastEB;
|
||||
unsigned char xm_lastX1;
|
||||
unsigned char xm_lastX2;
|
||||
|
||||
IT_PLAYING *playing;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct DUMB_IT_SIGRENDERER
|
||||
{
|
||||
DUMB_IT_SIGDATA *sigdata;
|
||||
|
||||
int n_channels;
|
||||
|
||||
unsigned char globalvolume;
|
||||
signed char globalvolslide;
|
||||
|
||||
unsigned char tempo;
|
||||
signed char temposlide;
|
||||
|
||||
IT_CHANNEL channel[DUMB_IT_N_CHANNELS];
|
||||
|
||||
IT_PLAYING *playing[DUMB_IT_N_NNA_CHANNELS];
|
||||
|
||||
int tick;
|
||||
int speed;
|
||||
int rowcount;
|
||||
|
||||
int order; /* Set to -1 if the song is terminated by a callback. */
|
||||
int row;
|
||||
int processorder;
|
||||
int processrow;
|
||||
int breakrow;
|
||||
int pat_loop_row;
|
||||
|
||||
int n_rows;
|
||||
|
||||
IT_ENTRY *entry_start;
|
||||
IT_ENTRY *entry;
|
||||
IT_ENTRY *entry_end;
|
||||
|
||||
long time_left; /* Time before the next tick is processed */
|
||||
int sub_time_left;
|
||||
|
||||
DUMB_CLICK_REMOVER **click_remover;
|
||||
|
||||
IT_CALLBACKS *callbacks;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_CHECKPOINT
|
||||
{
|
||||
IT_CHECKPOINT *next;
|
||||
long time;
|
||||
DUMB_IT_SIGRENDERER *sigrenderer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct IT_CALLBACKS
|
||||
{
|
||||
int (*loop)(void *data);
|
||||
void *loop_data;
|
||||
/* Return 1 to prevent looping; the music will terminate abruptly. If you
|
||||
* want to make the music stop but allow samples to fade (beware, as they
|
||||
* might not fade at all!), use dumb_it_sr_set_speed() and set the speed
|
||||
* to 0. Note that xm_speed_zero() will not be called if you set the
|
||||
* speed manually, and also that this will work for IT and S3M files even
|
||||
* though the music can't stop in this way by itself.
|
||||
*/
|
||||
|
||||
int (*xm_speed_zero)(void *data);
|
||||
void *xm_speed_zero_data;
|
||||
/* Return 1 to terminate the mod, without letting samples fade. */
|
||||
|
||||
int (*midi)(void *data, int channel, unsigned char byte);
|
||||
void *midi_data;
|
||||
/* Return 1 to prevent DUMB from subsequently interpreting the MIDI bytes
|
||||
* itself. In other words, return 1 if the Zxx macros in an IT file are
|
||||
* controlling filters and shouldn't be.
|
||||
*/
|
||||
};
|
||||
|
||||
|
||||
|
||||
void _dumb_it_end_sigrenderer(sigrenderer_t *sigrenderer);
|
||||
void _dumb_it_unload_sigdata(sigdata_t *vsigdata);
|
||||
|
||||
extern DUH_SIGTYPE_DESC _dumb_sigtype_it;
|
||||
|
||||
|
||||
|
||||
#define XM_APPREGIO 0
|
||||
#define XM_PORTAMENTO_UP 1
|
||||
#define XM_PORTAMENTO_DOWN 2
|
||||
#define XM_TONE_PORTAMENTO 3
|
||||
#define XM_VIBRATO 4
|
||||
#define XM_VOLSLIDE_TONEPORTA 5
|
||||
#define XM_VOLSLIDE_VIBRATO 6
|
||||
#define XM_TREMOLO 7
|
||||
#define XM_SET_PANNING 8
|
||||
#define XM_SAMPLE_OFFSET 9
|
||||
#define XM_VOLUME_SLIDE 10 /* A */
|
||||
#define XM_POSITION_JUMP 11 /* B */
|
||||
#define XM_SET_CHANNEL_VOLUME 12 /* C */
|
||||
#define XM_PATTERN_BREAK 13 /* D */
|
||||
#define XM_E 14 /* E */
|
||||
#define XM_SET_TEMPO_BPM 15 /* F */
|
||||
#define XM_SET_GLOBAL_VOLUME 16 /* G */
|
||||
#define XM_GLOBAL_VOLUME_SLIDE 17 /* H */
|
||||
#define XM_KEY_OFF 20 /* K (undocumented) */
|
||||
#define XM_SET_ENVELOPE_POSITION 21 /* L */
|
||||
#define XM_PANNING_SLIDE 25 /* P */
|
||||
#define XM_MULTI_RETRIG 27 /* R */
|
||||
#define XM_TREMOR 29 /* T */
|
||||
#define XM_X 33 /* X */
|
||||
#define XM_N_EFFECTS (10+26)
|
||||
|
||||
#define XM_E_SET_FILTER 0x0
|
||||
#define XM_E_FINE_PORTA_UP 0x1
|
||||
#define XM_E_FINE_PORTA_DOWN 0x2
|
||||
#define XM_E_SET_GLISSANDO_CONTROL 0x3
|
||||
#define XM_E_SET_VIBRATO_CONTROL 0x4
|
||||
#define XM_E_SET_FINETUNE 0x5
|
||||
#define XM_E_SET_LOOP 0x6
|
||||
#define XM_E_SET_TREMOLO_CONTROL 0x7
|
||||
#define XM_E_RETRIG_NOTE 0x9
|
||||
#define XM_E_FINE_VOLSLIDE_UP 0xA
|
||||
#define XM_E_FINE_VOLSLIDE_DOWN 0xB
|
||||
#define XM_E_NOTE_CUT 0xC
|
||||
#define XM_E_NOTE_DELAY 0xD
|
||||
#define XM_E_PATTERN_DELAY 0xE
|
||||
|
||||
#define XM_X_EXTRAFINE_PORTA_UP 1
|
||||
#define XM_X_EXTRAFINE_PORTA_DOWN 2
|
||||
|
||||
/* To make my life a bit simpler during conversion, effect E:xy is converted
|
||||
* to effect number EBASE+x:y. The same applies to effect X, and IT's S. That
|
||||
* way, these effects can be manipulated like regular effects.
|
||||
*/
|
||||
#define EBASE (XM_N_EFFECTS)
|
||||
#define XBASE (EBASE+16)
|
||||
#define SBASE (IT_N_EFFECTS)
|
||||
|
||||
#define EFFECT_VALUE(x, y) (((x)<<4)|(y))
|
||||
#define HIGH(v) ((v)>>4)
|
||||
#define LOW(v) ((v)&0x0F)
|
||||
#define SET_HIGH(v, x) v = (((x)<<4)|((v)&0x0F))
|
||||
#define SET_LOW(v, y) v = (((v)&0xF0)|(y))
|
||||
#define BCD_TO_NORMAL(v) (HIGH(v)*10+LOW(v))
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
unsigned char **_dumb_malloc2(int w, int h);
|
||||
void _dumb_free2(unsigned char **line);
|
||||
#endif
|
||||
|
||||
void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry);
|
||||
int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata);
|
||||
|
||||
|
||||
|
||||
#endif /* INTERNAL_IT_H */
|
42
dumb/it/itload.c
Normal file
42
dumb/it/itload.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* itload.c - Code to read an Impulse Tracker / / \ \
|
||||
* file, opening and closing it for | < / \_
|
||||
* you. | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. Don't worry Bob, you're credited | \ / /
|
||||
* in itread.c! | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/* dumb_load_it_quick(): loads an IT file into a DUH struct, returning a
|
||||
* pointer to the DUH struct. When you have finished with it, you must pass
|
||||
* the pointer to unload_duh() so that the memory can be freed.
|
||||
*/
|
||||
DUH *dumb_load_it_quick(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_it_quick(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
29
dumb/it/itload2.c
Normal file
29
dumb/it/itload2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* itload2.c - Function to read an Impulse Tracker / / \ \
|
||||
* file, opening and closing it for | < / \_
|
||||
* you, and do an initial run-through. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Split off from itload.c by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_load_it(const char *filename)
|
||||
{
|
||||
DUH *duh = dumb_load_it_quick(filename);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
247
dumb/it/itmisc.c
Normal file
247
dumb/it/itmisc.c
Normal file
|
@ -0,0 +1,247 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* itmisc.c - Miscellaneous functions relating / / \ \
|
||||
* to module files. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
DUMB_IT_SIGDATA *duh_get_it_sigdata(DUH *duh)
|
||||
{
|
||||
return duh_get_raw_sigdata(duh, 0, SIGTYPE_IT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
const unsigned char *dumb_it_sd_get_song_message(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->song_message : NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->n_orders : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_n_samples(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->n_samples : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_n_instruments(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->n_instruments : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const unsigned char *dumb_it_sd_get_sample_name(DUMB_IT_SIGDATA *sd, int i)
|
||||
{
|
||||
ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples);
|
||||
return sd->sample[i].name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const unsigned char *dumb_it_sd_get_sample_filename(DUMB_IT_SIGDATA *sd, int i)
|
||||
{
|
||||
ASSERT(sd && sd->sample && i >= 0 && i < sd->n_samples);
|
||||
return sd->sample[i].filename;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const unsigned char *dumb_it_sd_get_instrument_name(DUMB_IT_SIGDATA *sd, int i)
|
||||
{
|
||||
ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments);
|
||||
return sd->instrument[i].name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
const unsigned char *dumb_it_sd_get_instrument_filename(DUMB_IT_SIGDATA *sd, int i)
|
||||
{
|
||||
ASSERT(sd && sd->instrument && i >= 0 && i < sd->n_instruments);
|
||||
return sd->instrument[i].filename;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_initial_global_volume(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->global_volume : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sd_set_initial_global_volume(DUMB_IT_SIGDATA *sd, int gv)
|
||||
{
|
||||
if (sd) sd->global_volume = gv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_mixing_volume(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->mixing_volume : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sd_set_mixing_volume(DUMB_IT_SIGDATA *sd, int mv)
|
||||
{
|
||||
if (sd) sd->mixing_volume = mv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_initial_speed(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->speed : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sd_set_initial_speed(DUMB_IT_SIGDATA *sd, int speed)
|
||||
{
|
||||
if (sd) sd->speed = speed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_initial_tempo(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->tempo : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sd_set_initial_tempo(DUMB_IT_SIGDATA *sd, int tempo)
|
||||
{
|
||||
if (sd) sd->tempo = tempo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel)
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS);
|
||||
return sd ? sd->channel_volume[channel] : 0;
|
||||
}
|
||||
|
||||
void dumb_it_sd_set_initial_channel_volume(DUMB_IT_SIGDATA *sd, int channel, int volume)
|
||||
{
|
||||
ASSERT(channel >= 0 && channel < DUMB_IT_N_CHANNELS);
|
||||
if (sd) sd->channel_volume[channel] = volume;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_current_order(DUMB_IT_SIGRENDERER *sr)
|
||||
{
|
||||
return sr ? sr->order : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_current_row(DUMB_IT_SIGRENDERER *sr)
|
||||
{
|
||||
return sr ? sr->row : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_global_volume(DUMB_IT_SIGRENDERER *sr)
|
||||
{
|
||||
return sr ? sr->globalvolume : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sr_set_global_volume(DUMB_IT_SIGRENDERER *sr, int gv)
|
||||
{
|
||||
if (sr) sr->globalvolume = gv;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_tempo(DUMB_IT_SIGRENDERER *sr)
|
||||
{
|
||||
return sr ? sr->tempo : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sr_set_tempo(DUMB_IT_SIGRENDERER *sr, int tempo)
|
||||
{
|
||||
if (sr) sr->tempo = tempo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_speed(DUMB_IT_SIGRENDERER *sr)
|
||||
{
|
||||
return sr ? sr->speed : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sr_set_speed(DUMB_IT_SIGRENDERER *sr, int speed)
|
||||
{
|
||||
if (sr) sr->speed = speed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel)
|
||||
{
|
||||
return sr ? sr->channel[channel].channelvolume : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sr_set_channel_volume(DUMB_IT_SIGRENDERER *sr, int channel, int volume)
|
||||
{
|
||||
if (sr) sr->channel[channel].channelvolume = volume;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumb_it_sr_set_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel, int muted)
|
||||
{
|
||||
if (sr) {
|
||||
if (muted)
|
||||
sr->channel[channel].flags |= IT_CHANNEL_MUTED;
|
||||
else
|
||||
sr->channel[channel].flags &= ~IT_CHANNEL_MUTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sr_get_channel_muted(DUMB_IT_SIGRENDERER *sr, int channel)
|
||||
{
|
||||
return sr ? (sr->channel[channel].flags & IT_CHANNEL_MUTED) != 0 : 0;
|
||||
}
|
63
dumb/it/itorder.c
Normal file
63
dumb/it/itorder.c
Normal file
|
@ -0,0 +1,63 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* itorder.c - Code to fix invalid patterns in / / \ \
|
||||
* the pattern table. | < / \_
|
||||
* | \/ /\ /
|
||||
* By Julien Cugniere. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/* This function ensures that any pattern mentioned in the order table but
|
||||
* not present in the pattern table is treated as an empty 64 rows pattern.
|
||||
* This is done by adding such a dummy pattern at the end of the pattern
|
||||
* table, and redirect invalid orders to it.
|
||||
* Patterns 254 and 255 are left untouched, unless the signal is an XM.
|
||||
*/
|
||||
int _dumb_it_fix_invalid_orders(DUMB_IT_SIGDATA *sigdata)
|
||||
{
|
||||
int i;
|
||||
int found_some = 0;
|
||||
|
||||
int first_invalid = sigdata->n_patterns;
|
||||
int last_invalid = (sigdata->flags & IT_WAS_AN_XM) ? 255 : 253;
|
||||
|
||||
for (i = 0; i < sigdata->n_orders; i++) {
|
||||
if (sigdata->order[i] >= first_invalid && sigdata->order[i] <= last_invalid) {
|
||||
sigdata->order[i] = sigdata->n_patterns;
|
||||
found_some = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (found_some) {
|
||||
IT_PATTERN *new_pattern = realloc(sigdata->pattern, sizeof(*sigdata->pattern) * (sigdata->n_patterns + 1));
|
||||
if (!new_pattern)
|
||||
return -1;
|
||||
|
||||
new_pattern[sigdata->n_patterns].n_rows = 64;
|
||||
new_pattern[sigdata->n_patterns].n_entries = 0;
|
||||
new_pattern[sigdata->n_patterns].entry = NULL;
|
||||
sigdata->pattern = new_pattern;
|
||||
sigdata->n_patterns++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
1202
dumb/it/itread.c
Normal file
1202
dumb/it/itread.c
Normal file
File diff suppressed because it is too large
Load diff
29
dumb/it/itread2.c
Normal file
29
dumb/it/itread2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* itread2.c - Function to read an Impulse Tracker / / \ \
|
||||
* module from an open file and do an | < / \_
|
||||
* initial run-through. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Split off from itread.c by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_it(DUMBFILE *f)
|
||||
{
|
||||
DUH *duh = dumb_read_it_quick(f);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
3739
dumb/it/itrender.c
Normal file
3739
dumb/it/itrender.c
Normal file
File diff suppressed because it is too large
Load diff
72
dumb/it/itunload.c
Normal file
72
dumb/it/itunload.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* itunload.c - Code to free an Impulse Tracker / / \ \
|
||||
* module from memory. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
void _dumb_it_unload_sigdata(sigdata_t *vsigdata)
|
||||
{
|
||||
if (vsigdata) {
|
||||
DUMB_IT_SIGDATA *sigdata = vsigdata;
|
||||
int n;
|
||||
|
||||
if (sigdata->song_message)
|
||||
free(sigdata->song_message);
|
||||
|
||||
if (sigdata->order)
|
||||
free(sigdata->order);
|
||||
|
||||
if (sigdata->instrument)
|
||||
free(sigdata->instrument);
|
||||
|
||||
if (sigdata->sample) {
|
||||
for (n = 0; n < sigdata->n_samples; n++)
|
||||
if (sigdata->sample[n].data)
|
||||
free(sigdata->sample[n].data);
|
||||
|
||||
free(sigdata->sample);
|
||||
}
|
||||
|
||||
if (sigdata->pattern) {
|
||||
for (n = 0; n < sigdata->n_patterns; n++)
|
||||
if (sigdata->pattern[n].entry)
|
||||
free(sigdata->pattern[n].entry);
|
||||
free(sigdata->pattern);
|
||||
}
|
||||
|
||||
if (sigdata->midi)
|
||||
free(sigdata->midi);
|
||||
|
||||
{
|
||||
IT_CHECKPOINT *checkpoint = sigdata->checkpoint;
|
||||
while (checkpoint) {
|
||||
IT_CHECKPOINT *next = checkpoint->next;
|
||||
_dumb_it_end_sigrenderer(checkpoint->sigrenderer);
|
||||
free(checkpoint);
|
||||
checkpoint = next;
|
||||
}
|
||||
}
|
||||
|
||||
free(vsigdata);
|
||||
}
|
||||
}
|
42
dumb/it/loadmod.c
Normal file
42
dumb/it/loadmod.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loadmod.c - Code to read a good old-fashioned / / \ \
|
||||
* Amiga module file, opening and | < / \_
|
||||
* closing it for you. | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/* dumb_load_mod_quick(): loads a MOD file into a DUH struct, returning a
|
||||
* pointer to the DUH struct. When you have finished with it, you must
|
||||
* pass the pointer to unload_duh() so that the memory can be freed.
|
||||
*/
|
||||
DUH *dumb_load_mod_quick(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_mod_quick(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
29
dumb/it/loadmod2.c
Normal file
29
dumb/it/loadmod2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loadmod2.c - Function to read a good old- / / \ \
|
||||
* fashioned Amiga module file, | < / \_
|
||||
* opening and closing it for you, | \/ /\ /
|
||||
* and do an initial run-through. \_ / > /
|
||||
* | \ / /
|
||||
* Split off from loadmod.c by entheh. | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_load_mod(const char *filename)
|
||||
{
|
||||
DUH *duh = dumb_load_mod_quick(filename);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
42
dumb/it/loads3m.c
Normal file
42
dumb/it/loads3m.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loads3m.c - Code to read a ScreamTracker 3 / / \ \
|
||||
* file, opening and closing it for | < / \_
|
||||
* you. | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/* dumb_load_s3m_quick(): loads an S3M file into a DUH struct, returning
|
||||
* a pointer to the DUH struct. When you have finished with it, you must
|
||||
* pass the pointer to unload_duh() so that the memory can be freed.
|
||||
*/
|
||||
DUH *dumb_load_s3m_quick(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_s3m_quick(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
29
dumb/it/loads3m2.c
Normal file
29
dumb/it/loads3m2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loads3m2.c - Function to read a ScreamTracker 3 / / \ \
|
||||
* file, opening and closing it for | < / \_
|
||||
* you, and do an initial run-through. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Split off from loads3m.c by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_load_s3m(const char *filename)
|
||||
{
|
||||
DUH *duh = dumb_load_s3m_quick(filename);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
42
dumb/it/loadxm.c
Normal file
42
dumb/it/loadxm.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loadxm.c - Code to read a Fast Tracker II / / \ \
|
||||
* file, opening and closing it for | < / \_
|
||||
* you. | \/ /\ /
|
||||
* \_ / > /
|
||||
* By entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/* dumb_load_xm_quick(): loads an XM file into a DUH struct, returning a
|
||||
* pointer to the DUH struct. When you have finished with it, you must
|
||||
* pass the pointer to unload_duh() so that the memory can be freed.
|
||||
*/
|
||||
DUH *dumb_load_xm_quick(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_xm_quick(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
29
dumb/it/loadxm2.c
Normal file
29
dumb/it/loadxm2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* loadxm2.c - Function to read a Fast Tracker II / / \ \
|
||||
* file, opening and closing it for | < / \_
|
||||
* you, and do an initial run-through. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Split off from loadxm.c by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_load_xm(const char *filename)
|
||||
{
|
||||
DUH *duh = dumb_load_xm_quick(filename);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
604
dumb/it/readmod.c
Normal file
604
dumb/it/readmod.c
Normal file
|
@ -0,0 +1,604 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readmod.c - Code to read a good old-fashioned / / \ \
|
||||
* Amiga module from an open file. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
static int it_mod_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer)
|
||||
{
|
||||
int pos;
|
||||
int channel;
|
||||
int row;
|
||||
IT_ENTRY *entry;
|
||||
|
||||
pattern->n_rows = 64;
|
||||
|
||||
if (n_channels == 0) {
|
||||
/* Read the first four channels, leaving gaps for the rest. */
|
||||
for (pos = 0; pos < 64*8*4; pos += 8*4)
|
||||
dumbfile_getnc(buffer + pos, 4*4, f);
|
||||
/* Read the other channels into the gaps we left. */
|
||||
for (pos = 4*4; pos < 64*8*4; pos += 8*4)
|
||||
dumbfile_getnc(buffer + pos, 4*4, f);
|
||||
|
||||
n_channels = 8;
|
||||
} else
|
||||
dumbfile_getnc(buffer, 64 * n_channels * 4, f);
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
/* compute number of entries */
|
||||
pattern->n_entries = 64; /* Account for the row end markers */
|
||||
pos = 0;
|
||||
for (row = 0; row < 64; row++) {
|
||||
for (channel = 0; channel < n_channels; channel++) {
|
||||
if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3])
|
||||
pattern->n_entries++;
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
|
||||
pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
|
||||
if (!pattern->entry)
|
||||
return -1;
|
||||
|
||||
entry = pattern->entry;
|
||||
pos = 0;
|
||||
for (row = 0; row < 64; row++) {
|
||||
for (channel = 0; channel < n_channels; channel++) {
|
||||
if (buffer[pos+0] | buffer[pos+1] | buffer[pos+2] | buffer[pos+3]) {
|
||||
unsigned char sample = (buffer[pos+0] & 0xF0) | (buffer[pos+2] >> 4);
|
||||
int period = ((int)(buffer[pos+0] & 0x0F) << 8) | buffer[pos+1];
|
||||
|
||||
entry->channel = channel;
|
||||
entry->mask = 0;
|
||||
|
||||
if (period) {
|
||||
int note;
|
||||
entry->mask |= IT_ENTRY_NOTE;
|
||||
|
||||
/* frequency = (AMIGA_DIVISOR / 8) / (period * 2)
|
||||
* C-1: period = 214 -> frequency = 16726
|
||||
* so, set C5_speed to 16726
|
||||
* and period = 214 should translate to C5 aka 60
|
||||
* halve the period, go up an octive
|
||||
*
|
||||
* period = 214 / pow(DUMB_SEMITONE_BASE, note - 60)
|
||||
* pow(DUMB_SEMITONE_BASE, note - 60) = 214 / period
|
||||
* note - 60 = log(214/period) / log(DUMB_SEMITONE_BASE)
|
||||
*/
|
||||
note = (int)floor(log(214.0/period) / log(DUMB_SEMITONE_BASE) + 60.5);
|
||||
entry->note = MID(0, note, 119);
|
||||
// or should we preserve the period?
|
||||
//entry->note = buffer[pos+0] & 0x0F; /* High nibble */
|
||||
//entry->volpan = buffer[pos+1]; /* Low byte */
|
||||
// and what about finetune?
|
||||
}
|
||||
|
||||
if (sample) {
|
||||
entry->mask |= IT_ENTRY_INSTRUMENT;
|
||||
entry->instrument = sample;
|
||||
}
|
||||
|
||||
_dumb_it_xm_convert_effect(buffer[pos+2] & 0x0F, buffer[pos+3], entry);
|
||||
|
||||
entry++;
|
||||
}
|
||||
pos += 4;
|
||||
}
|
||||
IT_SET_END_ROW(entry);
|
||||
entry++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_mod_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
|
||||
{
|
||||
int finetune;
|
||||
|
||||
/**
|
||||
21 22 Chars Sample 1 name. If the name is not a full
|
||||
22 chars in length, it will be null
|
||||
terminated.
|
||||
|
||||
If
|
||||
the sample name begins with a '#' character (ASCII $23 (35)) then this is
|
||||
assumed not to be an instrument name, and is probably a message.
|
||||
*/
|
||||
dumbfile_getnc(sample->name, 22, f);
|
||||
sample->name[22] = 0;
|
||||
|
||||
sample->filename[0] = 0;
|
||||
|
||||
sample->length = dumbfile_mgetw(f) << 1;
|
||||
finetune = (signed char)(dumbfile_getc(f) << 4) >> 4; /* signed nibble */
|
||||
/** Each finetune step changes the note 1/8th of a semitone. */
|
||||
sample->global_volume = 64;
|
||||
sample->default_volume = dumbfile_getc(f); // Should we be setting global_volume to this instead?
|
||||
sample->loop_start = dumbfile_mgetw(f) << 1;
|
||||
sample->loop_end = sample->loop_start + (dumbfile_mgetw(f) << 1);
|
||||
/**
|
||||
Once this sample has been played completely from beginning
|
||||
to end, if the repeat length (next field) is greater than two bytes it
|
||||
will loop back to this position in the sample and continue playing. Once
|
||||
it has played for the repeat length, it continues to loop back to the
|
||||
repeat start offset. This means the sample continues playing until it is
|
||||
told to stop.
|
||||
*/
|
||||
|
||||
if (sample->length <= 0) {
|
||||
sample->flags = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sample->flags = IT_SAMPLE_EXISTS;
|
||||
|
||||
sample->default_pan = 0;
|
||||
sample->C5_speed = (long)(16726.0*pow(DUMB_PITCH_BASE, finetune*32));
|
||||
// the above line might be wrong
|
||||
|
||||
if (sample->loop_end > sample->length)
|
||||
sample->loop_end = sample->length;
|
||||
|
||||
if (sample->loop_end - sample->loop_start > 2)
|
||||
sample->flags |= IT_SAMPLE_LOOP;
|
||||
|
||||
sample->vibrato_speed = 0;
|
||||
sample->vibrato_depth = 0;
|
||||
sample->vibrato_rate = 0;
|
||||
sample->vibrato_waveform = 0; // do we have to set _all_ these?
|
||||
|
||||
return dumbfile_error(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_mod_read_sample_data(IT_SAMPLE *sample, DUMBFILE *f)
|
||||
{
|
||||
long i;
|
||||
long truncated_size;
|
||||
|
||||
/* let's get rid of the sample data coming after the end of the loop */
|
||||
if ((sample->flags & IT_SAMPLE_LOOP) && sample->loop_end < sample->length) {
|
||||
truncated_size = sample->length - sample->loop_end;
|
||||
sample->length = sample->loop_end;
|
||||
} else {
|
||||
truncated_size = 0;
|
||||
}
|
||||
|
||||
if (sample->length) {
|
||||
sample->data = malloc(sample->length);
|
||||
|
||||
if (!sample->data)
|
||||
return -1;
|
||||
|
||||
/* Sample data are stored in "8-bit two's compliment format" (sic). */
|
||||
for (i = 0; i < sample->length; i++)
|
||||
((signed char *)sample->data)[i] = dumbfile_getc(f);
|
||||
} else
|
||||
sample->flags &= ~IT_SAMPLE_EXISTS;
|
||||
|
||||
/* skip truncated data */
|
||||
dumbfile_skip(f, truncated_size);
|
||||
// Should we be truncating it?
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct BUFFERED_MOD BUFFERED_MOD;
|
||||
|
||||
struct BUFFERED_MOD
|
||||
{
|
||||
unsigned char *buffered;
|
||||
long ptr, len;
|
||||
DUMBFILE *remaining;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int buffer_mod_skip(void *f, long n)
|
||||
{
|
||||
BUFFERED_MOD *bm = f;
|
||||
if (bm->buffered) {
|
||||
bm->ptr += n;
|
||||
if (bm->ptr >= bm->len) {
|
||||
free(bm->buffered);
|
||||
bm->buffered = NULL;
|
||||
return dumbfile_skip(bm->remaining, bm->ptr - bm->len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return dumbfile_skip(bm->remaining, n);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int buffer_mod_getc(void *f)
|
||||
{
|
||||
BUFFERED_MOD *bm = f;
|
||||
if (bm->buffered) {
|
||||
int rv = bm->buffered[bm->ptr++];
|
||||
if (bm->ptr >= bm->len) {
|
||||
free(bm->buffered);
|
||||
bm->buffered = NULL;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
return dumbfile_getc(bm->remaining);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static long buffer_mod_getnc(char *ptr, long n, void *f)
|
||||
{
|
||||
BUFFERED_MOD *bm = f;
|
||||
if (bm->buffered) {
|
||||
int left = bm->len - bm->ptr;
|
||||
if (n >= left) {
|
||||
int rv;
|
||||
memcpy(ptr, bm->buffered + bm->ptr, left);
|
||||
free(bm->buffered);
|
||||
bm->buffered = NULL;
|
||||
rv = dumbfile_getnc(ptr + left, n - left, bm->remaining);
|
||||
return left + MAX(rv, 0);
|
||||
}
|
||||
memcpy(ptr, bm->buffered + bm->ptr, n);
|
||||
bm->ptr += n;
|
||||
return n;
|
||||
}
|
||||
return dumbfile_getnc(ptr, n, bm->remaining);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void buffer_mod_close(void *f)
|
||||
{
|
||||
BUFFERED_MOD *bm = f;
|
||||
if (bm->buffered) free(bm->buffered);
|
||||
/* Do NOT close bm->remaining */
|
||||
free(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUMBFILE_SYSTEM buffer_mod_dfs = {
|
||||
NULL,
|
||||
&buffer_mod_skip,
|
||||
&buffer_mod_getc,
|
||||
&buffer_mod_getnc,
|
||||
&buffer_mod_close
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define MOD_FFT_OFFSET (20 + 31*(22+2+1+1+2+2) + 1 + 1 + 128)
|
||||
|
||||
static DUMBFILE *dumbfile_buffer_mod(DUMBFILE *f, unsigned long *fft)
|
||||
{
|
||||
BUFFERED_MOD *bm = malloc(sizeof(*bm));
|
||||
if (!bm) return NULL;
|
||||
|
||||
bm->buffered = malloc(MOD_FFT_OFFSET + 4);
|
||||
if (!bm->buffered) {
|
||||
free(bm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bm->len = dumbfile_getnc(bm->buffered, MOD_FFT_OFFSET + 4, f);
|
||||
|
||||
if (bm->len > 0) {
|
||||
if (bm->len >= MOD_FFT_OFFSET + 4)
|
||||
*fft = (unsigned long)bm->buffered[MOD_FFT_OFFSET ] << 24
|
||||
| (unsigned long)bm->buffered[MOD_FFT_OFFSET+1] << 16
|
||||
| (unsigned long)bm->buffered[MOD_FFT_OFFSET+2] << 8
|
||||
| (unsigned long)bm->buffered[MOD_FFT_OFFSET+3];
|
||||
else
|
||||
*fft = 0;
|
||||
bm->ptr = 0;
|
||||
} else {
|
||||
free(bm->buffered);
|
||||
bm->buffered = NULL;
|
||||
}
|
||||
|
||||
bm->remaining = f;
|
||||
|
||||
return dumbfile_open_ex(bm, &buffer_mod_dfs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMB_IT_SIGDATA *it_mod_load_sigdata(DUMBFILE *f)
|
||||
{
|
||||
DUMB_IT_SIGDATA *sigdata;
|
||||
int n_channels;
|
||||
int i;
|
||||
unsigned long fft;
|
||||
|
||||
f = dumbfile_buffer_mod(f, &fft);
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
sigdata = malloc(sizeof(*sigdata));
|
||||
if (!sigdata) {
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
1 20 Chars Title of the song. If the title is not a
|
||||
full 20 chars in length, it will be null-
|
||||
terminated.
|
||||
*/
|
||||
if (dumbfile_getnc(sigdata->name, 20, f) < 20) {
|
||||
free(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
sigdata->name[20] = 0;
|
||||
|
||||
sigdata->n_samples = 31;
|
||||
|
||||
switch (fft) {
|
||||
case DUMB_ID('M','.','K','.'):
|
||||
case DUMB_ID('M','!','K','!'):
|
||||
case DUMB_ID('M','&','K','!'):
|
||||
case DUMB_ID('N','.','T','.'):
|
||||
case DUMB_ID('F','L','T','4'):
|
||||
n_channels = 4;
|
||||
break;
|
||||
case DUMB_ID('F','L','T','8'):
|
||||
n_channels = 0;
|
||||
/* 0 indicates a special case; two four-channel patterns must be
|
||||
* combined into one eight-channel pattern. Pattern indexes must
|
||||
* be halved. Why oh why do they obfuscate so?
|
||||
*/
|
||||
for (i = 0; i < 128; i++)
|
||||
sigdata->order[i] >>= 1;
|
||||
break;
|
||||
case DUMB_ID('C','D','8','1'):
|
||||
case DUMB_ID('O','C','T','A'):
|
||||
case DUMB_ID('O','K','T','A'):
|
||||
n_channels = 8;
|
||||
break;
|
||||
case DUMB_ID('1','6','C','N'):
|
||||
n_channels = 16;
|
||||
break;
|
||||
case DUMB_ID('3','2','C','N'):
|
||||
n_channels = 32;
|
||||
break;
|
||||
default:
|
||||
/* If we get an illegal tag, assume 4 channels 15 samples. */
|
||||
if ((fft & 0x0000FFFFL) == DUMB_ID(0,0,'C','H')) {
|
||||
if (fft >= '1' << 24 && fft < '4' << 24) {
|
||||
n_channels = ((fft & 0x00FF0000L) >> 16) - '0';
|
||||
if ((unsigned int)n_channels >= 10) {
|
||||
/* Rightmost character wasn't a digit. */
|
||||
n_channels = 4;
|
||||
sigdata->n_samples = 15;
|
||||
} else {
|
||||
n_channels += (((fft & 0xFF000000L) >> 24) - '0') * 10;
|
||||
/* MODs should really only go up to 32 channels, but we're lenient. */
|
||||
if ((unsigned int)(n_channels - 1) >= DUMB_IT_N_CHANNELS - 1) {
|
||||
/* No channels or too many? Can't be right... */
|
||||
n_channels = 4;
|
||||
sigdata->n_samples = 15;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
n_channels = 4;
|
||||
sigdata->n_samples = 15;
|
||||
}
|
||||
} else if ((fft & 0x00FFFFFFL) == DUMB_ID(0,'C','H','N')) {
|
||||
n_channels = (fft >> 24) - '0';
|
||||
if ((unsigned int)(n_channels - 1) >= 9) {
|
||||
/* Character was '0' or it wasn't a digit */
|
||||
n_channels = 4;
|
||||
sigdata->n_samples = 15;
|
||||
}
|
||||
} else if ((fft & 0xFFFFFF00L) == DUMB_ID('T','D','Z',0)) {
|
||||
n_channels = (fft & 0x000000FFL) - '0';
|
||||
if ((unsigned int)(n_channels - 1) >= 9) {
|
||||
/* We've been very lenient, given that it should have
|
||||
* been 1, 2 or 3, but this MOD has been very naughty and
|
||||
* must be punished.
|
||||
*/
|
||||
n_channels = 4;
|
||||
sigdata->n_samples = 15;
|
||||
}
|
||||
} else {
|
||||
n_channels = 4;
|
||||
sigdata->n_samples = 15;
|
||||
}
|
||||
}
|
||||
|
||||
sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
|
||||
if (!sigdata->sample) {
|
||||
free(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sigdata->song_message = NULL;
|
||||
sigdata->order = NULL;
|
||||
sigdata->instrument = NULL;
|
||||
sigdata->pattern = NULL;
|
||||
sigdata->midi = NULL;
|
||||
sigdata->checkpoint = NULL;
|
||||
|
||||
sigdata->n_instruments = 0;
|
||||
|
||||
for (i = 0; i < sigdata->n_samples; i++)
|
||||
sigdata->sample[i].data = NULL;
|
||||
|
||||
for (i = 0; i < sigdata->n_samples; i++) {
|
||||
if (it_mod_read_sample_header(&sigdata->sample[i], f)) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
sigdata->n_orders = dumbfile_getc(f);
|
||||
sigdata->restart_position = dumbfile_getc(f);
|
||||
// what if this is >= 127? what about with Fast Tracker II?
|
||||
|
||||
if (sigdata->n_orders <= 0 || sigdata->n_orders > 128) { // is this right?
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//if (sigdata->restart_position >= sigdata->n_orders)
|
||||
//sigdata->restart_position = 0;
|
||||
|
||||
sigdata->order = malloc(128); /* We may need to scan the extra ones! */
|
||||
if (!sigdata->order) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
if (dumbfile_getnc(sigdata->order, 128, f) < 128) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* "The old NST format contains only 15 samples (instead of 31). Further
|
||||
* it doesn't contain a file format tag (id). So Pattern data offset is
|
||||
* at 20+15*30+1+1+128."
|
||||
* - Then I shall assume the File Format Tag never exists if there are
|
||||
* only 15 samples. I hope this isn't a faulty assumption...
|
||||
*/
|
||||
if (sigdata->n_samples == 31)
|
||||
dumbfile_skip(f, 4);
|
||||
|
||||
/* Work out how many patterns there are. */
|
||||
sigdata->n_patterns = -1;
|
||||
for (i = 0; i < 128; i++)
|
||||
if (sigdata->n_patterns < sigdata->order[i])
|
||||
sigdata->n_patterns = sigdata->order[i];
|
||||
sigdata->n_patterns++;
|
||||
|
||||
/* May as well try to save a tiny bit of memory. */
|
||||
if (sigdata->n_orders < 128) {
|
||||
unsigned char *order = realloc(sigdata->order, sigdata->n_orders);
|
||||
if (order) sigdata->order = order;
|
||||
}
|
||||
|
||||
sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
|
||||
if (!sigdata->pattern) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < sigdata->n_patterns; i++)
|
||||
sigdata->pattern[i].entry = NULL;
|
||||
|
||||
/* Read in the patterns */
|
||||
{
|
||||
unsigned char *buffer = malloc(256 * n_channels); /* 64 rows * 4 bytes */
|
||||
if (!buffer) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < sigdata->n_patterns; i++) {
|
||||
if (it_mod_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) {
|
||||
free(buffer);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/* And finally, the sample data */
|
||||
for (i = 0; i < sigdata->n_samples; i++) {
|
||||
if (it_mod_read_sample_data(&sigdata->sample[i], f)) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dumbfile_close(f); /* Destroy the BUFFERED_MOD DUMBFILE we were using. */
|
||||
/* The DUMBFILE originally passed to our function is intact. */
|
||||
|
||||
/* Now let's initialise the remaining variables, and we're done! */
|
||||
sigdata->flags = IT_WAS_AN_XM | IT_WAS_A_MOD | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO;
|
||||
|
||||
sigdata->global_volume = 128;
|
||||
sigdata->mixing_volume = 48;
|
||||
/* We want 50 ticks per second; 50/6 row advances per second;
|
||||
* 50*10=500 row advances per minute; 500/4=125 beats per minute.
|
||||
*/
|
||||
sigdata->speed = 6;
|
||||
sigdata->tempo = 125;
|
||||
sigdata->pan_separation = 128;
|
||||
|
||||
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
|
||||
|
||||
for (i = 0; i < DUMB_IT_N_CHANNELS; i += 4) {
|
||||
sigdata->channel_pan[i+0] = 16;
|
||||
sigdata->channel_pan[i+1] = 48;
|
||||
sigdata->channel_pan[i+2] = 48;
|
||||
sigdata->channel_pan[i+3] = 16;
|
||||
}
|
||||
|
||||
_dumb_it_fix_invalid_orders(sigdata);
|
||||
|
||||
return sigdata;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_mod_quick(DUMBFILE *f)
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
|
||||
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
|
||||
|
||||
sigdata = it_mod_load_sigdata(f);
|
||||
|
||||
if (!sigdata)
|
||||
return NULL;
|
||||
|
||||
{
|
||||
const char *tag[1][2];
|
||||
tag[0][0] = "TITLE";
|
||||
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
|
||||
return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
|
||||
}
|
||||
}
|
29
dumb/it/readmod2.c
Normal file
29
dumb/it/readmod2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readmod2.c - Function to read a good old- / / \ \
|
||||
* fashioned Amiga module from an | < / \_
|
||||
* open file and do an initial | \/ /\ /
|
||||
* run-through. \_ / > /
|
||||
* | \ / /
|
||||
* Split off from readmod.c by entheh. | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_mod(DUMBFILE *f)
|
||||
{
|
||||
DUH *duh = dumb_read_mod_quick(f);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
670
dumb/it/reads3m.c
Normal file
670
dumb/it/reads3m.c
Normal file
|
@ -0,0 +1,670 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* reads3m.c - Code to read a ScreamTracker 3 / / \ \
|
||||
* module from an open file. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
// IT_STEREO... :o
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/** WARNING: this is duplicated in itread.c */
|
||||
static int it_seek(DUMBFILE *f, long offset)
|
||||
{
|
||||
long pos = dumbfile_pos(f);
|
||||
|
||||
if (pos > offset)
|
||||
return -1;
|
||||
|
||||
if (pos < offset)
|
||||
if (dumbfile_skip(f, offset - pos))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_s3m_read_sample_header(IT_SAMPLE *sample, long *offset, DUMBFILE *f)
|
||||
{
|
||||
unsigned char type;
|
||||
int flags;
|
||||
|
||||
type = dumbfile_getc(f);
|
||||
|
||||
if (type > 1) {
|
||||
/** WARNING: no adlib support */
|
||||
}
|
||||
|
||||
dumbfile_getnc(sample->filename, 13, f);
|
||||
sample->filename[13] = 0;
|
||||
|
||||
*offset = dumbfile_igetw(f) << 4;
|
||||
|
||||
sample->length = dumbfile_igetl(f);
|
||||
sample->loop_start = dumbfile_igetl(f);
|
||||
sample->loop_end = dumbfile_igetl(f);
|
||||
|
||||
sample->default_volume = dumbfile_getc(f);
|
||||
|
||||
dumbfile_skip(f, 1);
|
||||
|
||||
if (dumbfile_getc(f) != 0)
|
||||
/* Sample is packed apparently (or error reading from file). We don't
|
||||
* know how to read packed samples.
|
||||
*/
|
||||
return -1;
|
||||
|
||||
flags = dumbfile_getc(f);
|
||||
|
||||
sample->C5_speed = dumbfile_igetl(f) << 1;
|
||||
|
||||
/* Skip four unused bytes and three internal variables. */
|
||||
dumbfile_skip(f, 4+2+2+4);
|
||||
|
||||
dumbfile_getnc(sample->name, 28, f);
|
||||
sample->name[28] = 0;
|
||||
|
||||
if (type == 0) {
|
||||
/* Looks like no-existy. Anyway, there's for sure no 'SCRS' ... */
|
||||
sample->flags &= ~IT_SAMPLE_EXISTS;
|
||||
return dumbfile_error(f);
|
||||
}
|
||||
|
||||
if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','S'))
|
||||
return -1;
|
||||
|
||||
sample->global_volume = 64;
|
||||
|
||||
sample->flags = IT_SAMPLE_EXISTS;
|
||||
if (flags & 1) sample->flags |= IT_SAMPLE_LOOP;
|
||||
if (flags & 2) sample->flags |= IT_SAMPLE_STEREO;
|
||||
if (flags & 4) sample->flags |= IT_SAMPLE_16BIT;
|
||||
|
||||
sample->default_pan = 0; // 0 = don't use, or 160 = centre?
|
||||
|
||||
if (sample->length <= 0)
|
||||
sample->flags &= ~IT_SAMPLE_EXISTS;
|
||||
else if (sample->flags & IT_SAMPLE_LOOP) {
|
||||
if ((unsigned int)sample->loop_end > (unsigned int)sample->length)
|
||||
sample->flags &= ~IT_SAMPLE_LOOP;
|
||||
else if ((unsigned int)sample->loop_start >= (unsigned int)sample->loop_end)
|
||||
sample->flags &= ~IT_SAMPLE_LOOP;
|
||||
else
|
||||
/* ScreamTracker seems not to save what comes after the loop end
|
||||
* point, but rather to assume it is a duplicate of what comes at
|
||||
* the loop start point. I am not completely sure of this though.
|
||||
* It is easy to evade; simply truncate the sample.
|
||||
*/
|
||||
sample->length = sample->loop_end;
|
||||
}
|
||||
|
||||
|
||||
//Do we need to set all these?
|
||||
sample->vibrato_speed = 0;
|
||||
sample->vibrato_depth = 0;
|
||||
sample->vibrato_rate = 0;
|
||||
sample->vibrato_waveform = IT_VIBRATO_SINE;
|
||||
|
||||
return dumbfile_error(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_s3m_read_sample_data(IT_SAMPLE *sample, int ffi, DUMBFILE *f)
|
||||
{
|
||||
long n;
|
||||
|
||||
long datasize = sample->length;
|
||||
if (sample->flags & IT_SAMPLE_STEREO) datasize <<= 1;
|
||||
|
||||
sample->data = malloc(datasize * (sample->flags & IT_SAMPLE_16BIT ? 2 : 1));
|
||||
if (!sample->data)
|
||||
return -1;
|
||||
|
||||
if (sample->flags & IT_SAMPLE_STEREO) {
|
||||
if (sample->flags & IT_SAMPLE_16BIT) {
|
||||
for (n = 0; n < datasize; n += 2)
|
||||
((short *)sample->data)[n] = dumbfile_igetw(f);
|
||||
for (n = 1; n < datasize; n += 2)
|
||||
((short *)sample->data)[n] = dumbfile_igetw(f);
|
||||
} else {
|
||||
for (n = 0; n < datasize; n += 2)
|
||||
((signed char *)sample->data)[n] = dumbfile_getc(f);
|
||||
for (n = 1; n < datasize; n += 2)
|
||||
((signed char *)sample->data)[n] = dumbfile_getc(f);
|
||||
}
|
||||
} else if (sample->flags & IT_SAMPLE_16BIT)
|
||||
for (n = 0; n < sample->length; n++)
|
||||
((short *)sample->data)[n] = dumbfile_igetw(f);
|
||||
else
|
||||
for (n = 0; n < sample->length; n++)
|
||||
((signed char *)sample->data)[n] = dumbfile_getc(f);
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
if (ffi != 1) {
|
||||
/* Convert to signed. */
|
||||
if (sample->flags & IT_SAMPLE_16BIT)
|
||||
for (n = 0; n < datasize; n++)
|
||||
((short *)sample->data)[n] ^= 0x8000;
|
||||
else
|
||||
for (n = 0; n < datasize; n++)
|
||||
((signed char *)sample->data)[n] ^= 0x80;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_s3m_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, unsigned char *buffer)
|
||||
{
|
||||
int buflen = 0;
|
||||
int bufpos = 0;
|
||||
|
||||
IT_ENTRY *entry;
|
||||
|
||||
unsigned char channel;
|
||||
|
||||
/* Haha, this is hilarious!
|
||||
*
|
||||
* Well, after some experimentation, it seems that different S3M writers
|
||||
* define the format in different ways. The S3M docs say that the first
|
||||
* two bytes hold the "length of [the] packed pattern", and the packed
|
||||
* pattern data follow. Judging by the contents of ARMANI.S3M, packaged
|
||||
* with ScreamTracker itself, the measure of length _includes_ the two
|
||||
* bytes used to store the length; in other words, we should read
|
||||
* (length - 2) more bytes. However, aryx.s3m, packaged with ModPlug
|
||||
* Tracker, excludes these two bytes, so (length) more bytes must be
|
||||
* read.
|
||||
*
|
||||
* Call me crazy, but I just find it insanely funny that the format was
|
||||
* misunderstood in this way :D
|
||||
*
|
||||
* Now we can't just risk reading two extra bytes, because then we
|
||||
* overshoot, and DUMBFILEs don't support backward seeking (for a good
|
||||
* reason). Luckily, there is a way. We can read the data little by
|
||||
* little, and stop when we have 64 rows in memory. Provided we protect
|
||||
* against buffer overflow, this method should work with all sensibly
|
||||
* written S3M files. If you find one for which it does not work, please
|
||||
* let me know at entheh@users.sf.net so I can look at it.
|
||||
*/
|
||||
|
||||
/* Discard the length. */
|
||||
dumbfile_skip(f, 2);
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
pattern->n_rows = 0;
|
||||
pattern->n_entries = 0;
|
||||
|
||||
/* Read in the pattern data, little by little, and work out how many
|
||||
* entries we need room for. Sorry, but this is just so funny...
|
||||
*/
|
||||
for (;;) {
|
||||
unsigned char b = buffer[buflen++] = dumbfile_getc(f);
|
||||
|
||||
#if 1
|
||||
static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
|
||||
channel = b & 31;
|
||||
b >>= 5;
|
||||
pattern->n_entries++;
|
||||
if (b) {
|
||||
if (buflen + used[b] >= 65536) return -1;
|
||||
dumbfile_getnc(buffer + buflen, used[b], f);
|
||||
buflen += used[b];
|
||||
} else {
|
||||
/* End of row */
|
||||
if (++pattern->n_rows == 64) break;
|
||||
if (buflen >= 65536) return -1;
|
||||
}
|
||||
#else
|
||||
if (b == 0) {
|
||||
/* End of row */
|
||||
pattern->n_entries++;
|
||||
if (++pattern->n_rows == 64) break;
|
||||
if (buflen >= 65536) return -1;
|
||||
} else {
|
||||
static const unsigned char used[8] = {0, 2, 1, 3, 2, 4, 3, 5};
|
||||
channel = b & 31;
|
||||
b >>= 5;
|
||||
if (b) {
|
||||
pattern->n_entries++;
|
||||
if (buflen + used[b] >= 65536) return -1;
|
||||
dumbfile_getnc(buffer + buflen, used[b], f);
|
||||
buflen += used[b];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* We have ensured that buflen < 65536 at this point, so it is safe
|
||||
* to iterate and read at least one more byte without checking.
|
||||
* However, now would be a good time to check for errors reading from
|
||||
* the file.
|
||||
*/
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
}
|
||||
|
||||
pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
|
||||
|
||||
if (!pattern->entry)
|
||||
return -1;
|
||||
|
||||
entry = pattern->entry;
|
||||
|
||||
while (bufpos < buflen) {
|
||||
unsigned char b = buffer[bufpos++];
|
||||
|
||||
#if 1
|
||||
if (!(b & ~31))
|
||||
#else
|
||||
if (b == 0)
|
||||
#endif
|
||||
{
|
||||
/* End of row */
|
||||
IT_SET_END_ROW(entry);
|
||||
entry++;
|
||||
continue;
|
||||
}
|
||||
|
||||
channel = b & 31;
|
||||
|
||||
if (b & 224) {
|
||||
entry->mask = 0;
|
||||
entry->channel = channel;
|
||||
|
||||
if (b & 32) {
|
||||
unsigned char n = buffer[bufpos++];
|
||||
if (n != 255) {
|
||||
if (n == 254)
|
||||
entry->note = IT_NOTE_CUT;
|
||||
else
|
||||
entry->note = (n >> 4) * 12 + (n & 15);
|
||||
entry->mask |= IT_ENTRY_NOTE;
|
||||
}
|
||||
|
||||
entry->instrument = buffer[bufpos++];
|
||||
if (entry->instrument)
|
||||
entry->mask |= IT_ENTRY_INSTRUMENT;
|
||||
}
|
||||
|
||||
if (b & 64) {
|
||||
entry->volpan = buffer[bufpos++];
|
||||
if (entry->volpan != 255)
|
||||
entry->mask |= IT_ENTRY_VOLPAN;
|
||||
}
|
||||
|
||||
if (b & 128) {
|
||||
entry->effect = buffer[bufpos++];
|
||||
entry->effectvalue = buffer[bufpos++];
|
||||
if (entry->effect != 255) {
|
||||
entry->mask |= IT_ENTRY_EFFECT;
|
||||
if (entry->effect == IT_BREAK_TO_ROW)
|
||||
entry->effectvalue -= (entry->effectvalue >> 4) * 6;
|
||||
}
|
||||
/** WARNING: ARGH! CONVERT TEH EFFECTS!@~ */
|
||||
}
|
||||
|
||||
entry++;
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT(entry == pattern->entry + pattern->n_entries);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** WARNING: this is duplicated in itread.c - also bad practice to use the same struct name unless they are unified in a header */
|
||||
/* Currently we assume the sample data are stored after the sample headers in
|
||||
* module files. This assumption may be unjustified; let me know if you have
|
||||
* trouble.
|
||||
*/
|
||||
|
||||
#define IT_COMPONENT_INSTRUMENT 1
|
||||
#define IT_COMPONENT_PATTERN 2
|
||||
#define IT_COMPONENT_SAMPLE 3
|
||||
|
||||
typedef struct IT_COMPONENT
|
||||
{
|
||||
unsigned char type;
|
||||
unsigned char n;
|
||||
long offset;
|
||||
short sampfirst; /* component[sampfirst] = first sample data after this */
|
||||
short sampnext; /* sampnext is used to create linked lists of sample data */
|
||||
}
|
||||
IT_COMPONENT;
|
||||
|
||||
|
||||
|
||||
static int it_component_compare(const void *e1, const void *e2)
|
||||
{
|
||||
return ((const IT_COMPONENT *)e1)->offset -
|
||||
((const IT_COMPONENT *)e2)->offset;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMB_IT_SIGDATA *it_s3m_load_sigdata(DUMBFILE *f)
|
||||
{
|
||||
DUMB_IT_SIGDATA *sigdata;
|
||||
|
||||
int flags, cwtv, ffi;
|
||||
int default_pan_present;
|
||||
|
||||
IT_COMPONENT *component;
|
||||
int n_components = 0;
|
||||
|
||||
int n;
|
||||
|
||||
unsigned char *buffer;
|
||||
|
||||
sigdata = malloc(sizeof(*sigdata));
|
||||
if (!sigdata) return NULL;
|
||||
|
||||
dumbfile_getnc(sigdata->name, 28, f);
|
||||
sigdata->name[28] = 0;
|
||||
|
||||
if (dumbfile_getc(f) != 0x1A || dumbfile_getc(f) != 16) {
|
||||
free(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dumbfile_skip(f, 2);
|
||||
|
||||
sigdata->song_message = NULL;
|
||||
sigdata->order = NULL;
|
||||
sigdata->instrument = NULL;
|
||||
sigdata->sample = NULL;
|
||||
sigdata->pattern = NULL;
|
||||
sigdata->midi = NULL;
|
||||
sigdata->checkpoint = NULL;
|
||||
|
||||
sigdata->n_orders = dumbfile_igetw(f);
|
||||
sigdata->n_instruments = 0;
|
||||
sigdata->n_samples = dumbfile_igetw(f);
|
||||
sigdata->n_patterns = dumbfile_igetw(f);
|
||||
|
||||
if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_samples > 256 || sigdata->n_patterns > 256) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sigdata->order = malloc(sigdata->n_orders);
|
||||
if (!sigdata->order) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sigdata->n_samples) {
|
||||
sigdata->sample = malloc(sigdata->n_samples * sizeof(*sigdata->sample));
|
||||
if (!sigdata->sample) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
for (n = 0; n < sigdata->n_samples; n++)
|
||||
sigdata->sample[n].data = NULL;
|
||||
}
|
||||
|
||||
if (sigdata->n_patterns) {
|
||||
sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
|
||||
if (!sigdata->pattern) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
for (n = 0; n < sigdata->n_patterns; n++)
|
||||
sigdata->pattern[n].entry = NULL;
|
||||
}
|
||||
|
||||
flags = dumbfile_igetw(f);
|
||||
|
||||
cwtv = dumbfile_igetw(f);
|
||||
|
||||
if (cwtv == 0x1300) {
|
||||
/** WARNING: volume slides on every frame */
|
||||
}
|
||||
|
||||
ffi = dumbfile_igetw(f);
|
||||
|
||||
/** WARNING: which ones? */
|
||||
sigdata->flags = IT_STEREO | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX;
|
||||
|
||||
if (dumbfile_mgetl(f) != DUMB_ID('S','C','R','M')) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sigdata->global_volume = dumbfile_getc(f) << 1;
|
||||
sigdata->speed = dumbfile_getc(f);
|
||||
if (sigdata->speed == 0) sigdata->speed = 6; // Should we? What about tempo?
|
||||
sigdata->tempo = dumbfile_getc(f);
|
||||
/*master_volume = */dumbfile_getc(f); // 7 bits; +128 for stereo
|
||||
//what do we do with master_volume? it's not the same as mixing volume...
|
||||
sigdata->mixing_volume = 48;
|
||||
|
||||
/* Skip GUS Ultra Click Removal byte. */
|
||||
dumbfile_getc(f);
|
||||
|
||||
default_pan_present = dumbfile_getc(f);
|
||||
|
||||
dumbfile_skip(f, 8);
|
||||
|
||||
/* Skip Special Custom Data Pointer. */
|
||||
/** WARNING: investigate this? */
|
||||
dumbfile_igetw(f);
|
||||
|
||||
/* Channel settings for 32 channels, 255=unused, +128=disabled */
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
int c = dumbfile_getc(f);
|
||||
if (!(c & (128 | 16))) { /* +128=disabled, +16=Adlib */
|
||||
sigdata->channel_volume[i] = 64;
|
||||
sigdata->channel_pan[i] = c & 8 ? 12 : 3;
|
||||
/** WARNING: ah, but it should be 7 for mono... */
|
||||
} else {
|
||||
/** WARNING: this could be improved if we support channel muting... */
|
||||
sigdata->channel_volume[i] = 0;
|
||||
sigdata->channel_pan[i] = 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Orders, byte each, length = sigdata->n_orders (should be even) */
|
||||
dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
|
||||
sigdata->restart_position = 0;
|
||||
|
||||
component = malloc(768*sizeof(*component));
|
||||
if (!component) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (n = 0; n < sigdata->n_samples; n++) {
|
||||
component[n_components].type = IT_COMPONENT_SAMPLE;
|
||||
component[n_components].n = n;
|
||||
component[n_components].offset = dumbfile_igetw(f) << 4;
|
||||
component[n_components].sampfirst = -1;
|
||||
n_components++;
|
||||
}
|
||||
|
||||
for (n = 0; n < sigdata->n_patterns; n++) {
|
||||
long offset = dumbfile_igetw(f) << 4;
|
||||
if (offset) {
|
||||
component[n_components].type = IT_COMPONENT_PATTERN;
|
||||
component[n_components].n = n;
|
||||
component[n_components].offset = offset;
|
||||
component[n_components].sampfirst = -1;
|
||||
n_components++;
|
||||
} else {
|
||||
/** WARNING: Empty 64-row pattern ... ? (this does happen!) */
|
||||
sigdata->pattern[n].n_rows = 64;
|
||||
sigdata->pattern[n].n_entries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
qsort(component, n_components, sizeof(IT_COMPONENT), &it_component_compare);
|
||||
|
||||
/* I found a really dumb S3M file that claimed to contain default pan
|
||||
* data but didn't contain any. Programs would load it by reading part of
|
||||
* the first instrument header, assuming the data to be default pan
|
||||
* positions, and then rereading the instrument module. We cannot do this
|
||||
* without obfuscating the file input model, so we insert an extra check
|
||||
* here that we won't overrun the start of the first component.
|
||||
*/
|
||||
if (default_pan_present == 252 && component[0].offset >= dumbfile_pos(f) + 32) {
|
||||
/* Channel default pan positions */
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
int c = dumbfile_getc(f);
|
||||
if (c & 32)
|
||||
sigdata->channel_pan[i] = c & 15;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < 32; i++) {
|
||||
sigdata->channel_pan[i] -= (sigdata->channel_pan[i] & 8) >> 3;
|
||||
sigdata->channel_pan[i] = ((int)sigdata->channel_pan[i] << 5) / 7;
|
||||
}
|
||||
}
|
||||
|
||||
sigdata->pan_separation = 128;
|
||||
|
||||
if (dumbfile_error(f)) {
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer = malloc(65536);
|
||||
if (!buffer) {
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (n = 0; n < n_components; n++) {
|
||||
long offset;
|
||||
int m;
|
||||
|
||||
if (it_seek(f, component[n].offset)) {
|
||||
free(buffer);
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
switch (component[n].type) {
|
||||
|
||||
case IT_COMPONENT_PATTERN:
|
||||
if (it_s3m_read_pattern(&sigdata->pattern[component[n].n], f, buffer)) {
|
||||
free(buffer);
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
case IT_COMPONENT_SAMPLE:
|
||||
if (it_s3m_read_sample_header(&sigdata->sample[component[n].n], &offset, f)) {
|
||||
free(buffer);
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sigdata->sample[component[n].n].flags & IT_SAMPLE_EXISTS) {
|
||||
short *sample;
|
||||
|
||||
for (m = n + 1; m < n_components; m++)
|
||||
if (component[m].offset > offset)
|
||||
break;
|
||||
m--;
|
||||
|
||||
sample = &component[m].sampfirst;
|
||||
|
||||
while (*sample >= 0 && component[*sample].offset <= offset)
|
||||
sample = &component[*sample].sampnext;
|
||||
|
||||
component[n].sampnext = *sample;
|
||||
*sample = n;
|
||||
|
||||
component[n].offset = offset;
|
||||
}
|
||||
}
|
||||
|
||||
m = component[n].sampfirst;
|
||||
|
||||
while (m >= 0) {
|
||||
if (it_seek(f, component[m].offset)) {
|
||||
free(buffer);
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (it_s3m_read_sample_data(&sigdata->sample[component[m].n], ffi, f)) {
|
||||
free(buffer);
|
||||
free(component);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m = component[m].sampnext;
|
||||
}
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
free(component);
|
||||
|
||||
_dumb_it_fix_invalid_orders(sigdata);
|
||||
|
||||
return sigdata;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_s3m_quick(DUMBFILE *f)
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
|
||||
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
|
||||
|
||||
sigdata = it_s3m_load_sigdata(f);
|
||||
|
||||
if (!sigdata)
|
||||
return NULL;
|
||||
|
||||
{
|
||||
const char *tag[1][2];
|
||||
tag[0][0] = "TITLE";
|
||||
tag[0][1] = ((DUMB_IT_SIGDATA *)sigdata)->name;
|
||||
return make_duh(-1, 1, (const char *const (*)[2])tag, 1, &descptr, &sigdata);
|
||||
}
|
||||
}
|
29
dumb/it/reads3m2.c
Normal file
29
dumb/it/reads3m2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* reads3m2.c - Function to read a ScreamTracker 3 / / \ \
|
||||
* module from an open file and do an | < / \_
|
||||
* initial run-through. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Split off from reads3m.c by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_s3m(DUMBFILE *f)
|
||||
{
|
||||
DUH *duh = dumb_read_s3m_quick(f);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
1007
dumb/it/readxm.c
Normal file
1007
dumb/it/readxm.c
Normal file
File diff suppressed because it is too large
Load diff
29
dumb/it/readxm2.c
Normal file
29
dumb/it/readxm2.c
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readxm2.c - Function to read a Fast Tracker II / / \ \
|
||||
* module from an open file and do an | < / \_
|
||||
* initial run-through. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Split off from readxm.c by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_xm(DUMBFILE *f)
|
||||
{
|
||||
DUH *duh = dumb_read_xm_quick(f);
|
||||
dumb_it_do_initial_runthrough(duh);
|
||||
return duh;
|
||||
}
|
242
dumb/it/xmeffect.c
Normal file
242
dumb/it/xmeffect.c
Normal file
|
@ -0,0 +1,242 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* xmeffect.c - Code for converting MOD/XM / / \ \
|
||||
* effects to IT effects. | < / \_
|
||||
* | \/ /\ /
|
||||
* By Julien Cugniere. Ripped out of readxm.c \_ / > /
|
||||
* by entheh. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
unsigned char **_dumb_malloc2(int w, int h)
|
||||
{
|
||||
unsigned char **line = malloc(h * sizeof(*line));
|
||||
int i;
|
||||
if (!line) return NULL;
|
||||
|
||||
line[0] = malloc(w * h * sizeof(*line[0]));
|
||||
if (!line[0]) {
|
||||
free(line);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 1; i < h; i++)
|
||||
line[i] = line[i-1] + w;
|
||||
|
||||
memset(line[0], 0, w*h);
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _dumb_free2(unsigned char **line)
|
||||
{
|
||||
if (line) {
|
||||
if (line[0])
|
||||
free(line[0]);
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Effects having a memory. 2 means that the two parts of the effectvalue
|
||||
* should be handled separately.
|
||||
*/
|
||||
static const char xm_has_memory[] = {
|
||||
/* 0 1 2 3 4 5 6 7 8 9 A B C D (E) F G H K L P R T (X) */
|
||||
0, 1, 1, 1, 2, 1, 1, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
|
||||
/* E0 E1 E2 E3 E4 E5 E6 E7 E9 EA EB EC ED EE X1 X2 */
|
||||
0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* Effects marked with 'special' are handled specifically in itrender.c */
|
||||
void _dumb_it_xm_convert_effect(int effect, int value, IT_ENTRY *entry)
|
||||
{
|
||||
const int log = 0;
|
||||
|
||||
if ((!effect && !value) || (effect >= XM_N_EFFECTS))
|
||||
return;
|
||||
|
||||
if (log) printf("%c%02X", (effect<10)?('0'+effect):('A'+effect-10), value);
|
||||
|
||||
/* Linearisation of the effect number... */
|
||||
if (effect == XM_E) {
|
||||
effect = EBASE + HIGH(value);
|
||||
value = LOW(value);
|
||||
} else if (effect == XM_X) {
|
||||
effect = XBASE + HIGH(value);
|
||||
value = LOW(value);
|
||||
}
|
||||
|
||||
if (log) printf(" - %2d %02X", effect, value);
|
||||
|
||||
#if 0 // This should be handled in itrender.c!
|
||||
/* update effect memory */
|
||||
switch (xm_has_memory[effect]) {
|
||||
case 1:
|
||||
if (!value)
|
||||
value = memory[entry->channel][effect];
|
||||
else
|
||||
memory[entry->channel][effect] = value;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (!HIGH(value))
|
||||
SET_HIGH(value, HIGH(memory[entry->channel][effect]));
|
||||
else
|
||||
SET_HIGH(memory[entry->channel][effect], HIGH(value));
|
||||
|
||||
if (!LOW(value))
|
||||
SET_LOW(value, LOW(memory[entry->channel][effect]));
|
||||
else
|
||||
SET_LOW(memory[entry->channel][effect], LOW(value));
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* convert effect */
|
||||
entry->mask |= IT_ENTRY_EFFECT;
|
||||
switch (effect) {
|
||||
|
||||
case XM_APPREGIO: effect = IT_ARPEGGIO; break;
|
||||
case XM_VIBRATO: effect = IT_VIBRATO; break;
|
||||
case XM_TONE_PORTAMENTO: effect = IT_TONE_PORTAMENTO; break; /** TODO: glissando control */
|
||||
case XM_TREMOLO: effect = IT_TREMOLO; break;
|
||||
case XM_SET_PANNING: effect = IT_SET_PANNING; break;
|
||||
case XM_SAMPLE_OFFSET: effect = IT_SET_SAMPLE_OFFSET; break;
|
||||
case XM_POSITION_JUMP: effect = IT_JUMP_TO_ORDER; break;
|
||||
case XM_MULTI_RETRIG: effect = IT_RETRIGGER_NOTE; break;
|
||||
case XM_TREMOR: effect = IT_TREMOR; break;
|
||||
case XM_PORTAMENTO_UP: effect = IT_XM_PORTAMENTO_UP; break;
|
||||
case XM_PORTAMENTO_DOWN: effect = IT_XM_PORTAMENTO_DOWN; break;
|
||||
case XM_SET_CHANNEL_VOLUME: effect = IT_SET_CHANNEL_VOLUME; break; /* special */
|
||||
case XM_VOLSLIDE_TONEPORTA: effect = IT_VOLSLIDE_TONEPORTA; break; /* special */
|
||||
case XM_VOLSLIDE_VIBRATO: effect = IT_VOLSLIDE_VIBRATO; break; /* special */
|
||||
|
||||
case XM_PATTERN_BREAK:
|
||||
effect = IT_BREAK_TO_ROW;
|
||||
value = BCD_TO_NORMAL(value);
|
||||
break;
|
||||
|
||||
case XM_VOLUME_SLIDE: /* special */
|
||||
effect = IT_VOLUME_SLIDE;
|
||||
value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
|
||||
break;
|
||||
|
||||
case XM_PANNING_SLIDE:
|
||||
effect = IT_PANNING_SLIDE;
|
||||
value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
|
||||
//value = HIGH(value) ? EFFECT_VALUE(0, HIGH(value)) : EFFECT_VALUE(LOW(value), 0);
|
||||
break;
|
||||
|
||||
case XM_GLOBAL_VOLUME_SLIDE: /* special */
|
||||
effect = IT_GLOBAL_VOLUME_SLIDE;
|
||||
value = HIGH(value) ? EFFECT_VALUE(HIGH(value), 0) : EFFECT_VALUE(0, LOW(value));
|
||||
break;
|
||||
|
||||
case XM_SET_TEMPO_BPM:
|
||||
effect = (value < 0x20) ? (IT_SET_SPEED) : (IT_SET_SONG_TEMPO);
|
||||
break;
|
||||
|
||||
case XM_SET_GLOBAL_VOLUME:
|
||||
effect = IT_SET_GLOBAL_VOLUME;
|
||||
value *= 2;
|
||||
break;
|
||||
|
||||
case XM_KEY_OFF:
|
||||
effect = IT_XM_KEY_OFF;
|
||||
break;
|
||||
|
||||
case XM_SET_ENVELOPE_POSITION:
|
||||
effect = IT_XM_SET_ENVELOPE_POSITION;
|
||||
break;
|
||||
|
||||
case EBASE+XM_E_SET_FILTER: effect = SBASE+IT_S_SET_FILTER; break;
|
||||
case EBASE+XM_E_SET_GLISSANDO_CONTROL: effect = SBASE+IT_S_SET_GLISSANDO_CONTROL; break; /** TODO */
|
||||
case EBASE+XM_E_SET_FINETUNE: effect = SBASE+IT_S_FINETUNE; break; /** TODO */
|
||||
case EBASE+XM_E_SET_LOOP: effect = SBASE+IT_S_PATTERN_LOOP; break;
|
||||
case EBASE+XM_E_NOTE_CUT: effect = SBASE+IT_S_DELAYED_NOTE_CUT; break;
|
||||
case EBASE+XM_E_NOTE_DELAY: effect = SBASE+IT_S_NOTE_DELAY; break;
|
||||
case EBASE+XM_E_PATTERN_DELAY: effect = SBASE+IT_S_PATTERN_DELAY; break;
|
||||
case EBASE+XM_E_FINE_VOLSLIDE_UP: effect = IT_XM_FINE_VOLSLIDE_UP; break;
|
||||
case EBASE+XM_E_FINE_VOLSLIDE_DOWN: effect = IT_XM_FINE_VOLSLIDE_DOWN; break;
|
||||
|
||||
case EBASE + XM_E_FINE_PORTA_UP:
|
||||
effect = IT_PORTAMENTO_UP;
|
||||
value = EFFECT_VALUE(0xF, value);
|
||||
break;
|
||||
|
||||
case EBASE + XM_E_FINE_PORTA_DOWN:
|
||||
effect = IT_PORTAMENTO_DOWN;
|
||||
value = EFFECT_VALUE(0xF, value);
|
||||
break;
|
||||
|
||||
case EBASE + XM_E_RETRIG_NOTE:
|
||||
effect = IT_XM_RETRIGGER_NOTE;
|
||||
value = EFFECT_VALUE(0, value);
|
||||
break;
|
||||
|
||||
case EBASE + XM_E_SET_VIBRATO_CONTROL:
|
||||
effect = SBASE+IT_S_SET_VIBRATO_WAVEFORM;
|
||||
value &= ~4; /** TODO: value&4 -> don't retrig wave */
|
||||
break;
|
||||
|
||||
case EBASE + XM_E_SET_TREMOLO_CONTROL:
|
||||
effect = SBASE+IT_S_SET_TREMOLO_WAVEFORM;
|
||||
value &= ~4; /** TODO: value&4 -> don't retrig wave */
|
||||
break;
|
||||
|
||||
case XBASE + XM_X_EXTRAFINE_PORTA_UP:
|
||||
effect = IT_PORTAMENTO_UP;
|
||||
value = EFFECT_VALUE(0xE, value);
|
||||
break;
|
||||
|
||||
case XBASE + XM_X_EXTRAFINE_PORTA_DOWN:
|
||||
effect = IT_PORTAMENTO_DOWN;
|
||||
value = EFFECT_VALUE(0xE, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* user effect (often used in demos for synchronisation) */
|
||||
entry->mask &= ~IT_ENTRY_EFFECT;
|
||||
}
|
||||
|
||||
if (log) printf(" - %2d %02X", effect, value);
|
||||
|
||||
/* Inverse linearisation... */
|
||||
if (effect >= SBASE && effect < SBASE+16) {
|
||||
value = EFFECT_VALUE(effect-SBASE, value);
|
||||
effect = IT_S;
|
||||
}
|
||||
|
||||
if (log) printf(" - %c%02X\n", 'A'+effect-1, value);
|
||||
|
||||
entry->effect = effect;
|
||||
entry->effectvalue = value;
|
||||
}
|
Loading…
Reference in a new issue