mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-14 23:52:26 -05:00
Initial check in dumb 0.9.2 - has a few usages of floating point that should
be rewritten to fixed point. seems to compile cleanly for iriver. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6197 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
7e7662bb71
commit
27be5bc728
67 changed files with 18488 additions and 1 deletions
270
apps/codecs/dumb/src/allegro/alplay.c
Normal file
270
apps/codecs/dumb/src/allegro/alplay.c
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* alplay.c - Functions to play a DUH through / / \ \
|
||||
* an Allegro audio stream. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
|
||||
|
||||
|
||||
#define ADP_PLAYING 1
|
||||
|
||||
struct AL_DUH_PLAYER
|
||||
{
|
||||
int flags;
|
||||
long bufsize;
|
||||
int freq;
|
||||
AUDIOSTREAM *stream;
|
||||
DUH_SIGRENDERER *sigrenderer; /* If this is NULL, stream is invalid. */
|
||||
float volume;
|
||||
int silentcount;
|
||||
};
|
||||
|
||||
|
||||
|
||||
AL_DUH_PLAYER *al_start_duh(DUH *duh, int n_channels, long pos, float volume, long bufsize, int freq)
|
||||
{
|
||||
AL_DUH_PLAYER *dp;
|
||||
|
||||
/* This restriction is imposed by Allegro. */
|
||||
ASSERT(n_channels > 0);
|
||||
ASSERT(n_channels <= 2);
|
||||
|
||||
if (!duh)
|
||||
return NULL;
|
||||
|
||||
dp = malloc(sizeof(*dp));
|
||||
if (!dp)
|
||||
return NULL;
|
||||
|
||||
dp->flags = ADP_PLAYING;
|
||||
dp->bufsize = bufsize;
|
||||
dp->freq = freq;
|
||||
|
||||
dp->stream = play_audio_stream(bufsize, 16, n_channels - 1, freq, 255, 128);
|
||||
|
||||
if (!dp->stream) {
|
||||
free(dp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
voice_set_priority(dp->stream->voice, 255);
|
||||
|
||||
dp->sigrenderer = duh_start_sigrenderer(duh, 0, n_channels, pos);
|
||||
|
||||
if (!dp->sigrenderer) {
|
||||
stop_audio_stream(dp->stream);
|
||||
free(dp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dp->volume = volume;
|
||||
dp->silentcount = 0;
|
||||
|
||||
return dp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void al_stop_duh(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
if (dp) {
|
||||
if (dp->sigrenderer) {
|
||||
duh_end_sigrenderer(dp->sigrenderer);
|
||||
stop_audio_stream(dp->stream);
|
||||
}
|
||||
free(dp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void al_pause_duh(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
if (dp && dp->sigrenderer && (dp->flags & ADP_PLAYING)) {
|
||||
voice_stop(dp->stream->voice);
|
||||
dp->flags &= ~ADP_PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void al_resume_duh(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
if (dp && dp->sigrenderer && !(dp->flags & ADP_PLAYING)) {
|
||||
voice_start(dp->stream->voice);
|
||||
dp->flags |= ADP_PLAYING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void al_duh_set_priority(AL_DUH_PLAYER *dp, int priority)
|
||||
{
|
||||
if (dp && dp->sigrenderer)
|
||||
voice_set_priority(dp->stream->voice, priority);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void al_duh_set_volume(AL_DUH_PLAYER *dp, float volume)
|
||||
{
|
||||
if (dp)
|
||||
dp->volume = volume;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int al_poll_duh(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
unsigned short *sptr;
|
||||
long n;
|
||||
long size;
|
||||
int n_channels;
|
||||
|
||||
if (!dp || !dp->sigrenderer)
|
||||
return 1;
|
||||
|
||||
if (!(dp->flags & ADP_PLAYING))
|
||||
return 0;
|
||||
|
||||
sptr = get_audio_stream_buffer(dp->stream);
|
||||
|
||||
if (!sptr)
|
||||
return 0;
|
||||
|
||||
n = duh_render(dp->sigrenderer, 16, 1, dp->volume, 65536.0 / dp->freq, dp->bufsize, sptr);
|
||||
|
||||
if (n == 0) {
|
||||
if (++dp->silentcount >= 2) {
|
||||
duh_end_sigrenderer(dp->sigrenderer);
|
||||
free_audio_stream_buffer(dp->stream);
|
||||
stop_audio_stream(dp->stream);
|
||||
dp->sigrenderer = NULL;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
n_channels = duh_sigrenderer_get_n_channels(dp->sigrenderer);
|
||||
n *= n_channels;
|
||||
size = dp->bufsize * n_channels;
|
||||
for (; n < size; n++)
|
||||
sptr[n] = 0x8000;
|
||||
|
||||
free_audio_stream_buffer(dp->stream);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
long al_duh_get_position(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
return dp ? duh_sigrenderer_get_position(dp->sigrenderer) : -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_sigrenderer(DUH_SIGRENDERER *sigrenderer, float volume, long bufsize, int freq)
|
||||
{
|
||||
AL_DUH_PLAYER *dp;
|
||||
int n_channels;
|
||||
|
||||
if (!sigrenderer)
|
||||
return NULL;
|
||||
|
||||
dp = malloc(sizeof(*dp));
|
||||
if (!dp)
|
||||
return NULL;
|
||||
|
||||
n_channels = duh_sigrenderer_get_n_channels(sigrenderer);
|
||||
|
||||
/* This restriction is imposed by Allegro. */
|
||||
ASSERT(n_channels > 0);
|
||||
ASSERT(n_channels <= 2);
|
||||
|
||||
dp->flags = ADP_PLAYING;
|
||||
dp->bufsize = bufsize;
|
||||
dp->freq = freq;
|
||||
|
||||
dp->stream = play_audio_stream(bufsize, 16, n_channels - 1, freq, 255, 128);
|
||||
|
||||
if (!dp->stream) {
|
||||
free(dp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
voice_set_priority(dp->stream->voice, 255);
|
||||
|
||||
dp->sigrenderer = sigrenderer;
|
||||
|
||||
dp->volume = volume;
|
||||
dp->silentcount = 0;
|
||||
|
||||
return dp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUH_SIGRENDERER *al_duh_get_sigrenderer(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
return dp ? dp->sigrenderer : NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* IMPORTANT: This function will return NULL if the music has ended. */
|
||||
// Should this be changed? User might want to hack the underlying SIGRENDERER
|
||||
// and resurrect it (e.g. change pattern number), before it gets destroyed...
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_sigrenderer(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
if (dp) {
|
||||
DUH_SIGRENDERER *sigrenderer = dp->sigrenderer;
|
||||
if (sigrenderer) stop_audio_stream(dp->stream);
|
||||
free(dp);
|
||||
return sigrenderer;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
AL_DUH_PLAYER *al_duh_encapsulate_renderer(DUH_SIGRENDERER *dr, float volume, long bufsize, int freq)
|
||||
{
|
||||
return al_duh_encapsulate_sigrenderer(dr, volume, bufsize, freq);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
DUH_SIGRENDERER *al_duh_get_renderer(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
return al_duh_get_sigrenderer(dp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* DEPRECATED */
|
||||
DUH_SIGRENDERER *al_duh_decompose_to_renderer(AL_DUH_PLAYER *dp)
|
||||
{
|
||||
return al_duh_decompose_to_sigrenderer(dp);
|
||||
}
|
||||
60
apps/codecs/dumb/src/allegro/datduh.c
Normal file
60
apps/codecs/dumb/src/allegro/datduh.c
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* datduh.c - Integration with Allegro's / / \ \
|
||||
* datafiles. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
#include "internal/aldumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dat_read_duh(PACKFILE *f, long size)
|
||||
{
|
||||
DUMBFILE *df;
|
||||
DUH *duh;
|
||||
|
||||
(void)size;
|
||||
|
||||
df = dumbfile_open_packfile(f);
|
||||
|
||||
if (!df)
|
||||
return NULL;
|
||||
|
||||
duh = read_duh(df);
|
||||
|
||||
dumbfile_close(df);
|
||||
|
||||
return duh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* dumb_register_dat_duh(): tells Allegro about the DUH datafile object. If
|
||||
* you intend to load a datafile containing a DUH object, you must call this
|
||||
* function first. It is recommended you pass DAT_DUH, but you may have a
|
||||
* reason to use a different type (apart from pride, that doesn't count).
|
||||
*/
|
||||
void dumb_register_dat_duh(long type)
|
||||
{
|
||||
register_datafile_object(
|
||||
type,
|
||||
&dat_read_duh,
|
||||
&_dat_unload_duh
|
||||
);
|
||||
}
|
||||
62
apps/codecs/dumb/src/allegro/datit.c
Normal file
62
apps/codecs/dumb/src/allegro/datit.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* datit.c - Integration of IT files with / / \ \
|
||||
* Allegro's datafiles. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
#include "internal/aldumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dat_read_it(PACKFILE *f, long size)
|
||||
{
|
||||
DUMBFILE *df;
|
||||
DUH *duh;
|
||||
|
||||
(void)size;
|
||||
|
||||
df = dumbfile_open_packfile(f);
|
||||
|
||||
if (!df)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_it(df);
|
||||
|
||||
dumbfile_close(df);
|
||||
|
||||
return duh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* dumb_register_dat_it(): tells Allegro about the IT datafile object. If you
|
||||
* intend to load a datafile containing an IT object, you must call this
|
||||
* function first. It is recommended you pass DUMB_DAT_IT, but you may have a
|
||||
* reason to use a different type (perhaps you already have a datafile with
|
||||
* IT files in and they use a different type).
|
||||
*/
|
||||
void dumb_register_dat_it(long type)
|
||||
{
|
||||
register_datafile_object(
|
||||
type,
|
||||
&dat_read_it,
|
||||
&_dat_unload_duh
|
||||
);
|
||||
}
|
||||
|
||||
61
apps/codecs/dumb/src/allegro/datmod.c
Normal file
61
apps/codecs/dumb/src/allegro/datmod.c
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* datmod.c - Integration of MOD files with / / \ \
|
||||
* Allegro's datafiles. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
#include "internal/aldumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dat_read_mod(PACKFILE *f, long size)
|
||||
{
|
||||
DUMBFILE *df;
|
||||
DUH *duh;
|
||||
|
||||
(void)size;
|
||||
|
||||
df = dumbfile_open_packfile(f);
|
||||
|
||||
if (!df)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_mod(df);
|
||||
|
||||
dumbfile_close(df);
|
||||
|
||||
return duh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* dumb_register_dat_mod(): tells Allegro about the MOD datafile object. If
|
||||
* you intend to load a datafile containing a MOD object, you must call this
|
||||
* function first. It is recommended you pass DUMB_DAT_MOD, but you may have
|
||||
* a reason to use a different type (perhaps you already have a datafile with
|
||||
* MOD files in and they use a different type).
|
||||
*/
|
||||
void dumb_register_dat_mod(long type)
|
||||
{
|
||||
register_datafile_object(
|
||||
type,
|
||||
&dat_read_mod,
|
||||
&_dat_unload_duh
|
||||
);
|
||||
}
|
||||
61
apps/codecs/dumb/src/allegro/dats3m.c
Normal file
61
apps/codecs/dumb/src/allegro/dats3m.c
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* dats3m.c - Integration of S3M files with / / \ \
|
||||
* Allegro's datafiles. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
#include "internal/aldumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dat_read_s3m(PACKFILE *f, long size)
|
||||
{
|
||||
DUMBFILE *df;
|
||||
DUH *duh;
|
||||
|
||||
(void)size;
|
||||
|
||||
df = dumbfile_open_packfile(f);
|
||||
|
||||
if (!df)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_s3m(df);
|
||||
|
||||
dumbfile_close(df);
|
||||
|
||||
return duh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* dumb_register_dat_s3m(): tells Allegro about the S3M datafile object. If
|
||||
* you intend to load a datafile containing an S3M object, you must call this
|
||||
* function first. It is recommended you pass DUMB_DAT_S3M, but you may have
|
||||
* a reason to use a different type (perhaps you already have a datafile with
|
||||
* S3M files in and they use a different type).
|
||||
*/
|
||||
void dumb_register_dat_s3m(long type)
|
||||
{
|
||||
register_datafile_object(
|
||||
type,
|
||||
&dat_read_s3m,
|
||||
&_dat_unload_duh
|
||||
);
|
||||
}
|
||||
31
apps/codecs/dumb/src/allegro/datunld.c
Normal file
31
apps/codecs/dumb/src/allegro/datunld.c
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* datunld.c - Unload function for integration / / \ \
|
||||
* with Allegro's datafiles. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
#include "internal/aldumb.h"
|
||||
|
||||
|
||||
|
||||
void _dat_unload_duh(void *duh)
|
||||
{
|
||||
unload_duh(duh);
|
||||
}
|
||||
|
||||
62
apps/codecs/dumb/src/allegro/datxm.c
Normal file
62
apps/codecs/dumb/src/allegro/datxm.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* datxm.c - Integration of XM files with / / \ \
|
||||
* Allegro's datafiles. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
#include "internal/aldumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dat_read_xm(PACKFILE *f, long size)
|
||||
{
|
||||
DUMBFILE *df;
|
||||
DUH *duh;
|
||||
|
||||
(void)size;
|
||||
|
||||
df = dumbfile_open_packfile(f);
|
||||
|
||||
if (!df)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_xm(df);
|
||||
|
||||
dumbfile_close(df);
|
||||
|
||||
return duh;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* dumb_register_dat_xm(): tells Allegro about the XM datafile object. If you
|
||||
* intend to load a datafile containing an XM object, you must call this
|
||||
* function first. It is recommended you pass DUMB_DAT_XM, but you may have a
|
||||
* reason to use a different type (perhaps you already have a datafile with
|
||||
* XM files in and they use a different type).
|
||||
*/
|
||||
void dumb_register_dat_xm(long type)
|
||||
{
|
||||
register_datafile_object(
|
||||
type,
|
||||
&dat_read_xm,
|
||||
&_dat_unload_duh
|
||||
);
|
||||
}
|
||||
|
||||
98
apps/codecs/dumb/src/allegro/packfile.c
Normal file
98
apps/codecs/dumb/src/allegro/packfile.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* packfile.c - Packfile input module. / / \ \
|
||||
* | < / \_
|
||||
* By entheh. | \/ /\ /
|
||||
* \_ / > /
|
||||
* Note that this does not use file compression; | \ / /
|
||||
* for that you must open the file yourself and | ' /
|
||||
* then use dumbfile_open_packfile(). \__/
|
||||
*/
|
||||
|
||||
#include <allegro.h>
|
||||
|
||||
#include "aldumb.h"
|
||||
|
||||
|
||||
|
||||
static void *dumb_packfile_open(const char *filename)
|
||||
{
|
||||
return pack_fopen(filename, F_READ);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int dumb_packfile_skip(void *f, long n)
|
||||
{
|
||||
return pack_fseek(f, n);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int dumb_packfile_getc(void *f)
|
||||
{
|
||||
return pack_getc(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static long dumb_packfile_getnc(char *ptr, long n, void *f)
|
||||
{
|
||||
return pack_fread(ptr, n, f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void dumb_packfile_close(void *f)
|
||||
{
|
||||
pack_fclose(f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMBFILE_SYSTEM packfile_dfs = {
|
||||
&dumb_packfile_open,
|
||||
&dumb_packfile_skip,
|
||||
&dumb_packfile_getc,
|
||||
&dumb_packfile_getnc,
|
||||
&dumb_packfile_close
|
||||
};
|
||||
|
||||
|
||||
|
||||
void dumb_register_packfiles(void)
|
||||
{
|
||||
register_dumbfile_system(&packfile_dfs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DUMBFILE_SYSTEM packfile_dfs_leave_open = {
|
||||
NULL,
|
||||
&dumb_packfile_skip,
|
||||
&dumb_packfile_getc,
|
||||
&dumb_packfile_getnc,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
|
||||
DUMBFILE *dumbfile_open_packfile(PACKFILE *p)
|
||||
{
|
||||
return dumbfile_open_ex(p, &packfile_dfs_leave_open);
|
||||
}
|
||||
|
||||
|
||||
|
||||
DUMBFILE *dumbfile_from_packfile(PACKFILE *p)
|
||||
{
|
||||
return p ? dumbfile_open_ex(p, &packfile_dfs) : NULL;
|
||||
}
|
||||
71
apps/codecs/dumb/src/core/atexit.c
Normal file
71
apps/codecs/dumb/src/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;
|
||||
}
|
||||
}
|
||||
34
apps/codecs/dumb/src/core/duhlen.c
Normal file
34
apps/codecs/dumb/src/core/duhlen.c
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* duhlen.c - Function to 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;
|
||||
}
|
||||
401
apps/codecs/dumb/src/core/dumbfile.c
Normal file
401
apps/codecs/dumb/src/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
apps/codecs/dumb/src/core/loadduh.c
Normal file
42
apps/codecs/dumb/src/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;
|
||||
}
|
||||
92
apps/codecs/dumb/src/core/makeduh.c
Normal file
92
apps/codecs/dumb/src/core/makeduh.c
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* makeduh.c - Function to construct a DUH from / / \ \
|
||||
* its components. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.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_get_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_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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
return duh;
|
||||
}
|
||||
44
apps/codecs/dumb/src/core/rawsig.c
Normal file
44
apps/codecs/dumb/src/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
apps/codecs/dumb/src/core/readduh.c
Normal file
107
apps/codecs/dumb/src/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
apps/codecs/dumb/src/core/register.c
Normal file
104
apps/codecs/dumb/src/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_get_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;
|
||||
}
|
||||
202
apps/codecs/dumb/src/core/rendduh.c
Normal file
202
apps/codecs/dumb/src/core/rendduh.c
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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 = create_sample_buffer(n_channels, size);
|
||||
|
||||
if (!sampptr)
|
||||
return 0;
|
||||
|
||||
dumb_silence(sampptr[0], n_channels * size);
|
||||
|
||||
size = duh_sigrenderer_get_samples(sigrenderer, volume, delta, size, sampptr);
|
||||
|
||||
if (bits == 16) {
|
||||
int signconv = unsign ? 0x8000 : 0x0000;
|
||||
|
||||
if (n_channels == 2) {
|
||||
for (n = 0; n < size; n++) {
|
||||
CONVERT16(sampptr[0][n], n << 1, signconv);
|
||||
}
|
||||
for (n = 0; n < size; n++) {
|
||||
CONVERT16(sampptr[1][n], (n << 1) + 1, signconv);
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < size; n++) {
|
||||
CONVERT16(sampptr[0][n], n, signconv);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
char signconv = unsign ? 0x80 : 0x00;
|
||||
|
||||
if (n_channels == 2) {
|
||||
for (n = 0; n < size; n++) {
|
||||
CONVERT8(sampptr[0][n], n << 1, signconv);
|
||||
}
|
||||
for (n = 0; n < size; n++) {
|
||||
CONVERT8(sampptr[1][n], (n << 1) + 1, signconv);
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < size; 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;
|
||||
}
|
||||
299
apps/codecs/dumb/src/core/rendsig.c
Normal file
299
apps/codecs/dumb/src/core/rendsig.c
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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_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 ((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;
|
||||
/* FIXME
|
||||
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
|
||||
)
|
||||
{
|
||||
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_get_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_get_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_render_signal(
|
||||
DUH_SIGRENDERER *sigrenderer,
|
||||
float volume, float delta,
|
||||
long size, sample_t **samples
|
||||
)
|
||||
{
|
||||
sample_t **s = create_sample_buffer(sigrenderer->n_channels, size);
|
||||
long rendered;
|
||||
long i;
|
||||
int j;
|
||||
if (!s) return 0;
|
||||
rendered = duh_sigrenderer_get_samples(sigrenderer, volume, delta, size, s);
|
||||
for (j = 0; j < sigrenderer->n_channels; j++)
|
||||
for (i = 0; i < rendered; i++)
|
||||
samples[j][i] += s[j][i] >> 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
|
||||
58
apps/codecs/dumb/src/core/unload.c
Normal file
58
apps/codecs/dumb/src/core/unload.c
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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);
|
||||
}
|
||||
|
||||
free(duh);
|
||||
}
|
||||
}
|
||||
270
apps/codecs/dumb/src/helpers/clickrem.c
Normal file
270
apps/codecs/dumb/src/helpers/clickrem.c
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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, 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;
|
||||
|
||||
while (click) {
|
||||
DUMB_CLICK *next = click->next;
|
||||
ASSERT(click->pos <= length);
|
||||
offset = cr->offset;
|
||||
if (offset < 0) {
|
||||
offset = -offset;
|
||||
while (pos < click->pos) {
|
||||
samples[pos++] -= offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
}
|
||||
offset = -offset;
|
||||
} else {
|
||||
while (pos < click->pos) {
|
||||
samples[pos++] += offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
offset = -offset;
|
||||
} else {
|
||||
while (pos < length) {
|
||||
samples[pos++] += offset;
|
||||
offset = (int)((LONG_LONG)(offset << 1) * factor >> 32);
|
||||
}
|
||||
}
|
||||
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; i++)
|
||||
dumb_remove_clicks(cr[i], samples[i], length, 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
apps/codecs/dumb/src/helpers/memfile.c
Normal file
96
apps/codecs/dumb/src/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);
|
||||
}
|
||||
1177
apps/codecs/dumb/src/helpers/resample.c
Normal file
1177
apps/codecs/dumb/src/helpers/resample.c
Normal file
File diff suppressed because it is too large
Load diff
47
apps/codecs/dumb/src/helpers/sampbuf.c
Normal file
47
apps/codecs/dumb/src/helpers/sampbuf.c
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* sampbuf.c - Helper for allocating sample / / \ \
|
||||
* buffers. | < / \_
|
||||
* | \/ /\ /
|
||||
* By entheh. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "dumb.h"
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void destroy_sample_buffer(sample_t **samples)
|
||||
{
|
||||
if (samples) {
|
||||
free(samples[0]);
|
||||
free(samples);
|
||||
}
|
||||
}
|
||||
29
apps/codecs/dumb/src/helpers/silence.c
Normal file
29
apps/codecs/dumb/src/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
apps/codecs/dumb/src/helpers/stdfile.c
Normal file
93
apps/codecs/dumb/src/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;
|
||||
}
|
||||
43
apps/codecs/dumb/src/it/itload.c
Normal file
43
apps/codecs/dumb/src/it/itload.c
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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(): 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(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_it(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
||||
|
||||
175
apps/codecs/dumb/src/it/itmisc.c
Normal file
175
apps/codecs/dumb/src/it/itmisc.c
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int dumb_it_sd_get_n_orders(DUMB_IT_SIGDATA *sd)
|
||||
{
|
||||
return sd ? sd->n_orders : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
63
apps/codecs/dumb/src/it/itorder.c
Normal file
63
apps/codecs/dumb/src/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;
|
||||
}
|
||||
1181
apps/codecs/dumb/src/it/itread.c
Normal file
1181
apps/codecs/dumb/src/it/itread.c
Normal file
File diff suppressed because it is too large
Load diff
3512
apps/codecs/dumb/src/it/itrender.c
Normal file
3512
apps/codecs/dumb/src/it/itrender.c
Normal file
File diff suppressed because it is too large
Load diff
71
apps/codecs/dumb/src/it/itunload.c
Normal file
71
apps/codecs/dumb/src/it/itunload.c
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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->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].left)
|
||||
free(sigdata->sample[n].left);
|
||||
if (sigdata->sample[n].right)
|
||||
free(sigdata->sample[n].right);
|
||||
}
|
||||
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
apps/codecs/dumb/src/it/loadmod.c
Normal file
42
apps/codecs/dumb/src/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(): 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(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_mod(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
||||
42
apps/codecs/dumb/src/it/loads3m.c
Normal file
42
apps/codecs/dumb/src/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(): 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(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_s3m(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
||||
42
apps/codecs/dumb/src/it/loadxm.c
Normal file
42
apps/codecs/dumb/src/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(): 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(const char *filename)
|
||||
{
|
||||
DUH *duh;
|
||||
DUMBFILE *f = dumbfile_open(filename);
|
||||
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
duh = dumb_read_xm(f);
|
||||
|
||||
dumbfile_close(f);
|
||||
|
||||
return duh;
|
||||
}
|
||||
594
apps/codecs/dumb/src/it/readmod.c
Normal file
594
apps/codecs/dumb/src/it/readmod.c
Normal file
|
|
@ -0,0 +1,594 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readmod.c - Code to read a good old-fashioned / / \ \
|
||||
* Amiga module from an open file. | < / \_
|
||||
* | \/ /\ /
|
||||
* By Ben Davis. \_ / > /
|
||||
* | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* This function does not skip the name (22 bytes); it is assumed the caller
|
||||
* has already done that.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
sample->left = malloc(sample->length * sizeof(*sample->left));
|
||||
|
||||
if (!sample->left)
|
||||
return -1;
|
||||
|
||||
/* Sample data are stored in "8-bit two's compliment format" (sic). */
|
||||
for (i = 0; i < sample->length; i++)
|
||||
sample->left[i] = (int)(signed char)dumbfile_getc(f) << 16;
|
||||
|
||||
/* 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;
|
||||
|
||||
/**
|
||||
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_skip(f, 20)) {
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sigdata = malloc(sizeof(*sigdata));
|
||||
if (!sigdata) {
|
||||
dumbfile_close(f);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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->order = NULL;
|
||||
sigdata->instrument = NULL;
|
||||
sigdata->pattern = NULL;
|
||||
sigdata->midi = NULL;
|
||||
sigdata->checkpoint = NULL;
|
||||
|
||||
for (i = 0; i < sigdata->n_samples; i++)
|
||||
sigdata->sample[i].right = sigdata->sample[i].left = NULL;
|
||||
|
||||
for (i = 0; i < sigdata->n_samples; i++) {
|
||||
if (dumbfile_skip(f, 22) ||
|
||||
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(DUMBFILE *f)
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
long length;
|
||||
|
||||
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
|
||||
|
||||
sigdata = it_mod_load_sigdata(f);
|
||||
|
||||
if (!sigdata)
|
||||
return NULL;
|
||||
|
||||
length = _dumb_it_build_checkpoints(sigdata);
|
||||
|
||||
return make_duh(length, 1, &descptr, &sigdata);
|
||||
}
|
||||
668
apps/codecs/dumb/src/it/reads3m.c
Normal file
668
apps/codecs/dumb/src/it/reads3m.c
Normal file
|
|
@ -0,0 +1,668 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/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 */
|
||||
}
|
||||
|
||||
/* Skip DOS Filename */
|
||||
dumbfile_skip(f, 13);
|
||||
|
||||
*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, three internal variables and sample name. */
|
||||
dumbfile_skip(f, 4+2+2+4+28);
|
||||
|
||||
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;
|
||||
|
||||
sample->left = malloc(sample->length * sizeof(*sample->left));
|
||||
if (!sample->left)
|
||||
return -1;
|
||||
|
||||
if (sample->flags & IT_SAMPLE_STEREO) {
|
||||
sample->right = malloc(sample->length * sizeof(*sample->right));
|
||||
if (!sample->right)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (sample->flags & IT_SAMPLE_STEREO) {
|
||||
if (sample->flags & IT_SAMPLE_16BIT) {
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->right[n] = (int)(signed short)dumbfile_igetw(f) << 8;
|
||||
} else {
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->right[n] = (int)(signed char)dumbfile_getc(f) << 16;
|
||||
}
|
||||
} else if (sample->flags & IT_SAMPLE_16BIT)
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->left[n] = (int)(signed short)dumbfile_igetw(f) << 8;
|
||||
else
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->left[n] = (int)(signed char)dumbfile_getc(f) << 16;
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
if (ffi != 1) {
|
||||
/* Convert to signed. */
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->left[n] ^= 0xFF800000;
|
||||
|
||||
if (sample->right)
|
||||
for (n = 0; n < sample->length; n++)
|
||||
sample->right[n] ^= 0xFF800000;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* Skip song name. */
|
||||
if (dumbfile_skip(f, 28)) return NULL;
|
||||
|
||||
if (dumbfile_getc(f) != 0x1A) return NULL;
|
||||
if (dumbfile_getc(f) != 16) return NULL;
|
||||
|
||||
if (dumbfile_skip(f, 2)) return NULL;
|
||||
|
||||
sigdata = malloc(sizeof(*sigdata));
|
||||
|
||||
if (!sigdata)
|
||||
return 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].right = sigdata->sample[n].left = 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;
|
||||
}
|
||||
}
|
||||
|
||||
/** WARNING: is this right? */
|
||||
sigdata->pan_separation = 64;
|
||||
|
||||
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(DUMBFILE *f)
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
long length;
|
||||
|
||||
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
|
||||
|
||||
sigdata = it_s3m_load_sigdata(f);
|
||||
|
||||
if (!sigdata)
|
||||
return NULL;
|
||||
|
||||
length = _dumb_it_build_checkpoints(sigdata);
|
||||
|
||||
return make_duh(length, 1, &descptr, &sigdata);
|
||||
}
|
||||
998
apps/codecs/dumb/src/it/readxm.c
Normal file
998
apps/codecs/dumb/src/it/readxm.c
Normal file
|
|
@ -0,0 +1,998 @@
|
|||
/* _______ ____ __ ___ ___
|
||||
* \ _ \ \ / \ / \ \ / / ' ' '
|
||||
* | | \ \ | | || | \/ | . .
|
||||
* | | | | | | || ||\ /| |
|
||||
* | | | | | | || || \/ | | ' ' '
|
||||
* | | | | | | || || | | . .
|
||||
* | |_/ / \ \__// || | |
|
||||
* /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
|
||||
* / \
|
||||
* / . \
|
||||
* readxm.c - Code to read a Fast Tracker II / / \ \
|
||||
* module from an open file. | < / \_
|
||||
* | \/ /\ /
|
||||
* By Julien Cugniere. Some bits of code taken \_ / > /
|
||||
* from reads3m.c. | \ / /
|
||||
* | ' /
|
||||
* \__/
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "dumb.h"
|
||||
#include "internal/it.h"
|
||||
|
||||
|
||||
|
||||
/** TODO:
|
||||
|
||||
* XM_TREMOLO doesn't sound quite right...
|
||||
* XM_E_SET_FINETUNE todo.
|
||||
* XM_SET_ENVELOPE_POSITION todo.
|
||||
|
||||
* VIBRATO conversion needs to be checked (sample/effect/volume). Plus check
|
||||
that effect memory is correct when using XM_VOLSLIDE_VIBRATO.
|
||||
- sample vibrato (instrument vibrato) is now handled correctly. - entheh
|
||||
|
||||
* XM_E_SET_VIBRATO/TREMOLO_CONTROL: effectvalue&4 -> don't retrig wave when
|
||||
a new instrument is played. In retrigger_note()?. Is it worth implementing?
|
||||
|
||||
* Lossy fadeout approximation. 0..31 converted to 0 --> won't fade at all.
|
||||
|
||||
* Replace DUMB's sawtooth by ramp_down/ramp_up. Update XM loader.
|
||||
|
||||
* A lot of things need to be reset when the end of the song is reached.
|
||||
|
||||
* It seems that IT and XM don't behave the same way when dealing with
|
||||
mixed loops. When IT encounters multiple SBx (x>0) commands on the same
|
||||
row, it decrements the loop count for all, but only execute the loop of
|
||||
the last one (highest channel). FT2 only decrements the loop count of the
|
||||
last one. Not that I know of any modules using so convoluted combinations!
|
||||
|
||||
* Maybe we could remove patterns that don't appear in the order table ? Or
|
||||
provide a function to "optimize" a DUMB_IT_SIGDATA ?
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#define XM_LINEAR_FREQUENCY 1 /* otherwise, use amiga slides */
|
||||
|
||||
#define XM_ENTRY_PACKED 128
|
||||
#define XM_ENTRY_NOTE 1
|
||||
#define XM_ENTRY_INSTRUMENT 2
|
||||
#define XM_ENTRY_VOLUME 4
|
||||
#define XM_ENTRY_EFFECT 8
|
||||
#define XM_ENTRY_EFFECTVALUE 16
|
||||
|
||||
#define XM_NOTE_OFF 97
|
||||
|
||||
#define XM_ENVELOPE_ON 1
|
||||
#define XM_ENVELOPE_SUSTAIN 2
|
||||
#define XM_ENVELOPE_LOOP 4
|
||||
|
||||
#define XM_SAMPLE_NO_LOOP 0
|
||||
#define XM_SAMPLE_FORWARD_LOOP 1
|
||||
#define XM_SAMPLE_PINGPONG_LOOP 2
|
||||
#define XM_SAMPLE_16BIT 16
|
||||
#define XM_SAMPLE_STEREO 32
|
||||
|
||||
#define XM_VIBRATO_SINE 0
|
||||
#define XM_VIBRATO_SQUARE 1
|
||||
#define XM_VIBRATO_RAMP_DOWN 2
|
||||
#define XM_VIBRATO_RAMP_UP 3
|
||||
|
||||
|
||||
|
||||
/* Probably useless :) */
|
||||
static const char xm_convert_vibrato[] = {
|
||||
IT_VIBRATO_SINE,
|
||||
IT_VIBRATO_SQUARE,
|
||||
IT_VIBRATO_SAWTOOTH,
|
||||
IT_VIBRATO_SAWTOOTH
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define XM_MAX_SAMPLES_PER_INSTRUMENT 16
|
||||
|
||||
|
||||
|
||||
/* Extra data that doesn't fit inside IT_INSTRUMENT */
|
||||
typedef struct XM_INSTRUMENT_EXTRA
|
||||
{
|
||||
int n_samples;
|
||||
int vibrato_type;
|
||||
int vibrato_sweep; /* 0-0xFF */
|
||||
int vibrato_depth; /* 0-0x0F */
|
||||
int vibrato_speed; /* 0-0x3F */
|
||||
}
|
||||
XM_INSTRUMENT_EXTRA;
|
||||
|
||||
|
||||
|
||||
/* Frees the original block if it can't resize it or if size is 0, and acts
|
||||
* as malloc if ptr is NULL.
|
||||
*/
|
||||
static void *safe_realloc(void *ptr, size_t size)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return malloc(size);
|
||||
|
||||
if (size == 0) {
|
||||
free(ptr);
|
||||
return NULL;
|
||||
} else {
|
||||
void *new_block = realloc(ptr, size);
|
||||
if (!new_block)
|
||||
free(ptr);
|
||||
return new_block;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* The interpretation of the XM volume column is left to the player. Here, we
|
||||
* just filter bad values.
|
||||
*/
|
||||
// This function is so tiny now, should we inline it?
|
||||
static void it_xm_convert_volume(int volume, IT_ENTRY *entry)
|
||||
{
|
||||
entry->mask |= IT_ENTRY_VOLPAN;
|
||||
entry->volpan = volume;
|
||||
|
||||
switch (volume >> 4) {
|
||||
case 0xA: /* set vibrato speed */
|
||||
case 0xB: /* vibrato */
|
||||
case 0xF: /* tone porta */
|
||||
case 0x6: /* vol slide up */
|
||||
case 0x7: /* vol slide down */
|
||||
case 0x8: /* fine vol slide up */
|
||||
case 0x9: /* fine vol slide down */
|
||||
case 0xC: /* set panning */
|
||||
case 0xD: /* pan slide left */
|
||||
case 0xE: /* pan slide right */
|
||||
case 0x1: /* set volume */
|
||||
case 0x2: /* set volume */
|
||||
case 0x3: /* set volume */
|
||||
case 0x4: /* set volume */
|
||||
break;
|
||||
|
||||
case 0x5:
|
||||
if (volume == 0x50)
|
||||
break; /* set volume */
|
||||
/* else fall through */
|
||||
|
||||
default:
|
||||
entry->mask &= ~IT_ENTRY_VOLPAN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_xm_read_pattern(IT_PATTERN *pattern, DUMBFILE *f, int n_channels, unsigned char *buffer)
|
||||
{
|
||||
int size;
|
||||
int pos;
|
||||
int channel;
|
||||
int row;
|
||||
int effect, effectvalue;
|
||||
IT_ENTRY *entry;
|
||||
|
||||
/* pattern header size */
|
||||
if (dumbfile_igetl(f) != 0x09) {
|
||||
TRACE("XM error: unexpected pattern header size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* pattern data packing type */
|
||||
if (dumbfile_getc(f) != 0) {
|
||||
TRACE("XM error: unexpected pattern packing type\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pattern->n_rows = dumbfile_igetw(f); /* 1..256 */
|
||||
size = dumbfile_igetw(f);
|
||||
pattern->n_entries = 0;
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
if (size > 1280 * n_channels) {
|
||||
TRACE("XM error: pattern data size > %d bytes\n", 1280 * n_channels);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dumbfile_getnc(buffer, size, f) < size)
|
||||
return -1;
|
||||
|
||||
/* compute number of entries */
|
||||
pattern->n_entries = 0;
|
||||
pos = channel = row = 0;
|
||||
while (pos < size) {
|
||||
if (!(buffer[pos] & XM_ENTRY_PACKED) || (buffer[pos] & 31))
|
||||
pattern->n_entries++;
|
||||
|
||||
channel++;
|
||||
if (channel >= n_channels) {
|
||||
channel = 0;
|
||||
row++;
|
||||
pattern->n_entries++;
|
||||
}
|
||||
|
||||
if (buffer[pos] & XM_ENTRY_PACKED) {
|
||||
static const char offset[] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5 };
|
||||
pos += 1 + offset[buffer[pos] & 31];
|
||||
} else {
|
||||
pos += 5;
|
||||
}
|
||||
}
|
||||
|
||||
if (row != pattern->n_rows) {
|
||||
TRACE("XM error: wrong number of rows in pattern data\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pattern->entry = malloc(pattern->n_entries * sizeof(*pattern->entry));
|
||||
if (!pattern->entry)
|
||||
return -1;
|
||||
|
||||
/* read the entries */
|
||||
entry = pattern->entry;
|
||||
pos = channel = row = 0;
|
||||
while (pos < size) {
|
||||
unsigned char mask;
|
||||
|
||||
if (buffer[pos] & XM_ENTRY_PACKED)
|
||||
mask = buffer[pos++] & 31;
|
||||
else
|
||||
mask = 31;
|
||||
|
||||
if (mask) {
|
||||
ASSERT(entry < pattern->entry + pattern->n_entries);
|
||||
|
||||
entry->channel = channel;
|
||||
entry->mask = 0;
|
||||
|
||||
if (mask & XM_ENTRY_NOTE) {
|
||||
int note = buffer[pos++]; /* 1-96 <=> C0-B7 */
|
||||
entry->note = (note == XM_NOTE_OFF) ? (IT_NOTE_OFF) : (note-1);
|
||||
entry->mask |= IT_ENTRY_NOTE;
|
||||
}
|
||||
|
||||
if (mask & XM_ENTRY_INSTRUMENT) {
|
||||
entry->instrument = buffer[pos++]; /* 1-128 */
|
||||
entry->mask |= IT_ENTRY_INSTRUMENT;
|
||||
}
|
||||
|
||||
if (mask & XM_ENTRY_VOLUME)
|
||||
it_xm_convert_volume(buffer[pos++], entry);
|
||||
|
||||
effect = effectvalue = 0;
|
||||
if (mask & XM_ENTRY_EFFECT) effect = buffer[pos++];
|
||||
if (mask & XM_ENTRY_EFFECTVALUE) effectvalue = buffer[pos++];
|
||||
_dumb_it_xm_convert_effect(effect, effectvalue, entry);
|
||||
|
||||
entry++;
|
||||
}
|
||||
|
||||
channel++;
|
||||
if (channel >= n_channels) {
|
||||
channel = 0;
|
||||
row++;
|
||||
IT_SET_END_ROW(entry);
|
||||
entry++;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_xm_make_envelope(IT_ENVELOPE *envelope, const unsigned short *data, int y_offset)
|
||||
{
|
||||
int i, pos;
|
||||
|
||||
if (envelope->n_nodes > 12) {
|
||||
TRACE("XM error: wrong number of envelope nodes (%d)\n", envelope->n_nodes);
|
||||
envelope->n_nodes = 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos = 0;
|
||||
for (i = 0; i < envelope->n_nodes; i++) {
|
||||
envelope->node_t[i] = data[pos++];
|
||||
if (data[pos] > 64) {
|
||||
TRACE("XM error: out-of-range envelope node (node_y[%d]=%d)\n", i, data[pos]);
|
||||
envelope->n_nodes = 0;
|
||||
return -1;
|
||||
}
|
||||
envelope->node_y[i] = (signed char)(data[pos++] + y_offset);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_xm_read_instrument(IT_INSTRUMENT *instrument, XM_INSTRUMENT_EXTRA *extra, DUMBFILE *f)
|
||||
{
|
||||
unsigned long size, bytes_read;
|
||||
unsigned short vol_points[24];
|
||||
unsigned short pan_points[24];
|
||||
int i, type;
|
||||
|
||||
/* Header size. Tends to be more than the actual size of the structure.
|
||||
* So unread bytes must be skipped before reading the first sample
|
||||
* header.
|
||||
*/
|
||||
size = dumbfile_igetl(f);
|
||||
|
||||
//memset(instrument, 0, sizeof(*instrument));
|
||||
dumbfile_skip(f, 22); /* Instrument name */
|
||||
dumbfile_skip(f, 1); /* Instrument type. Should be 0, but seems random. */
|
||||
extra->n_samples = dumbfile_igetw(f);
|
||||
|
||||
if (dumbfile_error(f) || (unsigned int)extra->n_samples > XM_MAX_SAMPLES_PER_INSTRUMENT)
|
||||
return -1;
|
||||
|
||||
bytes_read = 4 + 22 + 1 + 2;
|
||||
|
||||
if (extra->n_samples) {
|
||||
/* sample header size */
|
||||
if (dumbfile_igetl(f) != 0x28) {
|
||||
TRACE("XM error: unexpected sample header size\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* sample map */
|
||||
for (i = 0; i < 96; i++) {
|
||||
instrument->map_sample[i] = dumbfile_getc(f) + 1;
|
||||
instrument->map_note[i] = i;
|
||||
}
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return 1;
|
||||
|
||||
/* volume/panning envelopes */
|
||||
for (i = 0; i < 24; i++)
|
||||
vol_points[i] = dumbfile_igetw(f);
|
||||
for (i = 0; i < 24; i++)
|
||||
pan_points[i] = dumbfile_igetw(f);
|
||||
|
||||
instrument->volume_envelope.n_nodes = dumbfile_getc(f);
|
||||
instrument->pan_envelope.n_nodes = dumbfile_getc(f);
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
instrument->volume_envelope.sus_loop_start = dumbfile_getc(f);
|
||||
instrument->volume_envelope.loop_start = dumbfile_getc(f);
|
||||
instrument->volume_envelope.loop_end = dumbfile_getc(f);
|
||||
|
||||
instrument->pan_envelope.sus_loop_start = dumbfile_getc(f);
|
||||
instrument->pan_envelope.loop_start = dumbfile_getc(f);
|
||||
instrument->pan_envelope.loop_end = dumbfile_getc(f);
|
||||
|
||||
/* The envelope handler for XM files won't use sus_loop_end. */
|
||||
|
||||
type = dumbfile_getc(f);
|
||||
instrument->volume_envelope.flags = 0;
|
||||
if ((type & XM_ENVELOPE_ON) && instrument->volume_envelope.n_nodes)
|
||||
instrument->volume_envelope.flags |= IT_ENVELOPE_ON;
|
||||
if (type & XM_ENVELOPE_LOOP) instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON;
|
||||
#if 1
|
||||
if (type & XM_ENVELOPE_SUSTAIN) instrument->volume_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP;
|
||||
#else // This is now handled in itrender.c
|
||||
/* let's avoid fading out when reaching the last envelope node */
|
||||
if (!(type & XM_ENVELOPE_LOOP)) {
|
||||
instrument->volume_envelope.loop_start = instrument->volume_envelope.n_nodes-1;
|
||||
instrument->volume_envelope.loop_end = instrument->volume_envelope.n_nodes-1;
|
||||
}
|
||||
instrument->volume_envelope.flags |= IT_ENVELOPE_LOOP_ON;
|
||||
#endif
|
||||
|
||||
type = dumbfile_getc(f);
|
||||
instrument->pan_envelope.flags = 0;
|
||||
if ((type & XM_ENVELOPE_ON) && instrument->pan_envelope.n_nodes)
|
||||
instrument->pan_envelope.flags |= IT_ENVELOPE_ON;
|
||||
if (type & XM_ENVELOPE_LOOP) instrument->pan_envelope.flags |= IT_ENVELOPE_LOOP_ON; // should this be here?
|
||||
if (type & XM_ENVELOPE_SUSTAIN) instrument->pan_envelope.flags |= IT_ENVELOPE_SUSTAIN_LOOP;
|
||||
|
||||
if (it_xm_make_envelope(&instrument->volume_envelope, vol_points, 0) != 0) {
|
||||
TRACE("XM error: volume envelope\n");
|
||||
if (instrument->volume_envelope.flags & IT_ENVELOPE_ON) return -1;
|
||||
}
|
||||
|
||||
if (it_xm_make_envelope(&instrument->pan_envelope, pan_points, -32) != 0) {
|
||||
TRACE("XM error: pan envelope\n");
|
||||
if (instrument->pan_envelope.flags & IT_ENVELOPE_ON) return -1;
|
||||
}
|
||||
|
||||
instrument->pitch_envelope.flags = 0;
|
||||
|
||||
extra->vibrato_type = dumbfile_getc(f);
|
||||
extra->vibrato_sweep = dumbfile_getc(f);
|
||||
extra->vibrato_depth = dumbfile_getc(f);
|
||||
extra->vibrato_speed = dumbfile_getc(f);
|
||||
|
||||
if (dumbfile_error(f) || extra->vibrato_type >= 4)
|
||||
return -1;
|
||||
|
||||
/** WARNING: lossy approximation */
|
||||
instrument->fadeout = (dumbfile_igetw(f)*128 + 64)/0xFFF;
|
||||
|
||||
dumbfile_skip(f, 2); /* reserved */
|
||||
|
||||
bytes_read += 4 + 96 + 48 + 48 + 14*1 + 2 + 2;
|
||||
}
|
||||
|
||||
if (dumbfile_skip(f, size - bytes_read))
|
||||
return -1;
|
||||
|
||||
instrument->new_note_action = NNA_NOTE_CUT;
|
||||
instrument->dup_check_type = DCT_OFF;
|
||||
instrument->dup_check_action = DCA_NOTE_CUT;
|
||||
instrument->pp_separation = 0;
|
||||
instrument->pp_centre = 60; /* C-5 */
|
||||
instrument->global_volume = 128;
|
||||
instrument->default_pan = 32;
|
||||
instrument->random_volume = 0;
|
||||
instrument->random_pan = 0;
|
||||
instrument->filter_cutoff = 0;
|
||||
instrument->filter_resonance = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* I (entheh) have two XM files saved by a very naughty program. After a
|
||||
* 16-bit sample, it saved a rogue byte. The length of the sample was indeed
|
||||
* an odd number, incremented to include the rogue byte.
|
||||
*
|
||||
* In this function we are converting sample lengths and loop points so they
|
||||
* are measured in samples. This means we forget about the extra bytes, and
|
||||
* they don't get skipped. So we fail trying to read the next instrument.
|
||||
*
|
||||
* To get around this, this function returns the number of rogue bytes that
|
||||
* won't be accounted for by reading sample->length samples. It returns a
|
||||
* negative number on failure.
|
||||
*/
|
||||
static int it_xm_read_sample_header(IT_SAMPLE *sample, DUMBFILE *f)
|
||||
{
|
||||
int type;
|
||||
int relative_note_number; /* relative to C4 */
|
||||
int finetune;
|
||||
int roguebytes;
|
||||
int roguebytesmask;
|
||||
|
||||
sample->length = dumbfile_igetl(f);
|
||||
sample->loop_start = dumbfile_igetl(f);
|
||||
sample->loop_end = sample->loop_start + dumbfile_igetl(f);
|
||||
sample->global_volume = 64;
|
||||
sample->default_volume = dumbfile_getc(f);
|
||||
finetune = (signed char)dumbfile_getc(f); /* -128..127 <=> -1 semitone .. +127/128 of a semitone */
|
||||
type = dumbfile_getc(f);
|
||||
sample->default_pan = (dumbfile_getc(f)*64)/255 | 128; /* 0-255 */
|
||||
relative_note_number = (signed char)dumbfile_getc(f);
|
||||
|
||||
dumbfile_skip(f, 1); /* reserved */
|
||||
dumbfile_skip(f, 22); /* sample name */
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
sample->C5_speed = (long)(16726.0*pow(DUMB_SEMITONE_BASE, relative_note_number)*pow(DUMB_PITCH_BASE, finetune*2));
|
||||
|
||||
sample->flags = IT_SAMPLE_EXISTS;
|
||||
|
||||
roguebytes = (int)sample->length;
|
||||
roguebytesmask = 3;
|
||||
|
||||
if (type & XM_SAMPLE_16BIT) {
|
||||
sample->flags |= IT_SAMPLE_16BIT;
|
||||
sample->length >>= 1;
|
||||
sample->loop_start >>= 1;
|
||||
sample->loop_end >>= 1;
|
||||
} else
|
||||
roguebytesmask >>= 1;
|
||||
|
||||
if (type & XM_SAMPLE_STEREO) {
|
||||
sample->flags |= IT_SAMPLE_STEREO;
|
||||
sample->length >>= 1;
|
||||
sample->loop_start >>= 1;
|
||||
sample->loop_end >>= 1;
|
||||
} else
|
||||
roguebytesmask >>= 1;
|
||||
|
||||
roguebytes &= roguebytesmask;
|
||||
|
||||
if ((unsigned int)sample->loop_start < (unsigned int)sample->loop_end) {
|
||||
if (type & XM_SAMPLE_FORWARD_LOOP) sample->flags |= IT_SAMPLE_LOOP;
|
||||
if (type & XM_SAMPLE_PINGPONG_LOOP) sample->flags |= IT_SAMPLE_LOOP | IT_SAMPLE_PINGPONG_LOOP;
|
||||
}
|
||||
|
||||
if (sample->length <= 0)
|
||||
sample->flags &= ~IT_SAMPLE_EXISTS;
|
||||
else 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;
|
||||
|
||||
return roguebytes;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int it_xm_read_sample_data(IT_SAMPLE *sample, unsigned char roguebytes, DUMBFILE *f)
|
||||
{
|
||||
int old;
|
||||
long i;
|
||||
long truncated_size;
|
||||
|
||||
if (!(sample->flags & IT_SAMPLE_EXISTS))
|
||||
return dumbfile_skip(f, roguebytes);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
sample->left = malloc(sample->length * sizeof(*sample->left));
|
||||
if (!sample->left)
|
||||
return -1;
|
||||
|
||||
if (sample->flags & IT_SAMPLE_STEREO) {
|
||||
sample->right = malloc(sample->length * sizeof(*sample->right));
|
||||
if (!sample->right)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* sample data is stored as signed delta values */
|
||||
old = 0;
|
||||
if (sample->flags & IT_SAMPLE_16BIT) {
|
||||
for (i = 0; i < sample->length; i++) {
|
||||
old = sample->left[i] = (int)(signed short)(old + dumbfile_igetw(f));
|
||||
sample->left[i] <<= 8;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < sample->length; i++) {
|
||||
old = sample->left[i] = (int)(signed char)(old + dumbfile_getc(f));
|
||||
sample->left[i] <<= 16;
|
||||
}
|
||||
}
|
||||
|
||||
/* skip truncated data */
|
||||
dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size));
|
||||
|
||||
if (sample->flags & IT_SAMPLE_STEREO) {
|
||||
old = 0;
|
||||
if (sample->flags & IT_SAMPLE_16BIT) {
|
||||
for (i = 0; i < sample->length; i++) {
|
||||
old = sample->right[i] = (int)(signed short)(old + dumbfile_igetw(f));
|
||||
sample->right[i] <<= 8;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < sample->length; i++) {
|
||||
old = sample->right[i] = (int)(signed char)(old + dumbfile_getc(f));
|
||||
sample->right[i] <<= 16;
|
||||
}
|
||||
}
|
||||
/* skip truncated data */
|
||||
dumbfile_skip(f, (sample->flags & IT_SAMPLE_16BIT) ? (2*truncated_size) : (truncated_size));
|
||||
}
|
||||
|
||||
dumbfile_skip(f, roguebytes);
|
||||
|
||||
if (dumbfile_error(f))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* "Real programmers don't document. If it was hard to write,
|
||||
* it should be hard to understand."
|
||||
*
|
||||
* (Never trust the documentation provided with a tracker.
|
||||
* Real files are the only truth...)
|
||||
*/
|
||||
static DUMB_IT_SIGDATA *it_xm_load_sigdata(DUMBFILE *f)
|
||||
{
|
||||
DUMB_IT_SIGDATA *sigdata;
|
||||
char id_text[18];
|
||||
|
||||
int flags;
|
||||
int n_channels;
|
||||
int total_samples;
|
||||
int i, j;
|
||||
|
||||
/* check ID text */
|
||||
if (dumbfile_getnc(id_text, 17, f) < 17)
|
||||
return NULL;
|
||||
id_text[17] = 0;
|
||||
if (strcmp(id_text, "Extended Module: ") != 0) {
|
||||
TRACE("XM error: Not an Extended Module\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* song name */
|
||||
if (dumbfile_skip(f, 20))
|
||||
return NULL;
|
||||
|
||||
if (dumbfile_getc(f) != 0x1A) {
|
||||
TRACE("XM error: 0x1A not found\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* tracker name */
|
||||
if (dumbfile_skip(f, 20))
|
||||
return NULL;
|
||||
|
||||
/* version number */
|
||||
if (dumbfile_igetw(f) != 0x0104) {
|
||||
TRACE("XM error: wrong format version\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
------------------
|
||||
--- Header ---
|
||||
------------------
|
||||
*/
|
||||
|
||||
/* header size */
|
||||
if (dumbfile_igetl(f) != 0x0114) {
|
||||
TRACE("XM error: unexpected header size\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sigdata = malloc(sizeof(*sigdata));
|
||||
if (!sigdata)
|
||||
return NULL;
|
||||
|
||||
sigdata->order = NULL;
|
||||
sigdata->instrument = NULL;
|
||||
sigdata->sample = NULL;
|
||||
sigdata->pattern = NULL;
|
||||
sigdata->midi = NULL;
|
||||
sigdata->checkpoint = NULL;
|
||||
|
||||
sigdata->n_samples = 0;
|
||||
sigdata->n_orders = dumbfile_igetw(f);
|
||||
sigdata->restart_position = dumbfile_igetw(f);
|
||||
n_channels = dumbfile_igetw(f); /* max 32 but we'll be lenient */
|
||||
sigdata->n_patterns = dumbfile_igetw(f);
|
||||
sigdata->n_instruments = dumbfile_igetw(f); /* max 128 */
|
||||
flags = dumbfile_igetw(f);
|
||||
sigdata->speed = dumbfile_igetw(f);
|
||||
sigdata->tempo = dumbfile_igetw(f);
|
||||
|
||||
/* sanity checks */
|
||||
if (dumbfile_error(f) || sigdata->n_orders <= 0 || sigdata->n_orders > 256 || sigdata->n_patterns > 256 || sigdata->n_instruments > 128 || n_channels > DUMB_IT_N_CHANNELS) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//if (sigdata->restart_position >= sigdata->n_orders)
|
||||
//sigdata->restart_position = 0;
|
||||
|
||||
/* order table */
|
||||
sigdata->order = malloc(sigdata->n_orders*sizeof(*sigdata->order));
|
||||
if (!sigdata->order) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
dumbfile_getnc(sigdata->order, sigdata->n_orders, f);
|
||||
dumbfile_skip(f, 256 - sigdata->n_orders);
|
||||
|
||||
if (dumbfile_error(f)) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
--------------------
|
||||
--- Patterns ---
|
||||
--------------------
|
||||
*/
|
||||
|
||||
sigdata->pattern = malloc(sigdata->n_patterns * sizeof(*sigdata->pattern));
|
||||
if (!sigdata->pattern) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < sigdata->n_patterns; i++)
|
||||
sigdata->pattern[i].entry = NULL;
|
||||
|
||||
{
|
||||
unsigned char *buffer = malloc(1280 * n_channels); /* 256 rows * 5 bytes */
|
||||
if (!buffer) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < sigdata->n_patterns; i++) {
|
||||
if (it_xm_read_pattern(&sigdata->pattern[i], f, n_channels, buffer) != 0) {
|
||||
free(buffer);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
/*
|
||||
-----------------------------------
|
||||
--- Instruments and Samples ---
|
||||
-----------------------------------
|
||||
*/
|
||||
|
||||
sigdata->instrument = malloc(sigdata->n_instruments * sizeof(*sigdata->instrument));
|
||||
if (!sigdata->instrument) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* With XM, samples are not global, they're part of an instrument. In a
|
||||
* file, each instrument is stored with its samples. Because of this, I
|
||||
* don't know how to find how many samples are present in the file. Thus
|
||||
* I have to do n_instruments reallocation on sigdata->sample.
|
||||
* Looking at FT2, it doesn't seem possible to have more than 16 samples
|
||||
* per instrument (even though n_samples is stored as 2 bytes). So maybe
|
||||
* we could allocate a 128*16 array of samples, and shrink it back to the
|
||||
* correct size when we know it?
|
||||
* Alternatively, I could allocate samples by blocks of N (still O(n)),
|
||||
* or double the number of allocated samples when I need more (O(log n)).
|
||||
*/
|
||||
total_samples = 0;
|
||||
sigdata->sample = NULL;
|
||||
|
||||
for (i = 0; i < sigdata->n_instruments; i++) {
|
||||
XM_INSTRUMENT_EXTRA extra;
|
||||
|
||||
if (it_xm_read_instrument(&sigdata->instrument[i], &extra, f) < 0) {
|
||||
TRACE("XM error: instrument %d\n", i+1);
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (extra.n_samples) {
|
||||
unsigned char roguebytes[XM_MAX_SAMPLES_PER_INSTRUMENT];
|
||||
|
||||
/* adjust instrument sample map (make indices absolute) */
|
||||
for (j = 0; j < 96; j++)
|
||||
sigdata->instrument[i].map_sample[j] += total_samples;
|
||||
|
||||
sigdata->sample = safe_realloc(sigdata->sample, sizeof(*sigdata->sample)*(total_samples+extra.n_samples));
|
||||
if (!sigdata->sample) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
for (j = total_samples; j < total_samples+extra.n_samples; j++)
|
||||
sigdata->sample[j].right = sigdata->sample[j].left = NULL;
|
||||
|
||||
/* read instrument's samples */
|
||||
for (j = 0; j < extra.n_samples; j++) {
|
||||
IT_SAMPLE *sample = &sigdata->sample[total_samples+j];
|
||||
int b = it_xm_read_sample_header(sample, f);
|
||||
if (b < 0) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
roguebytes[j] = b;
|
||||
// Any reason why these can't be set inside it_xm_read_sample_header()?
|
||||
sample->vibrato_speed = extra.vibrato_speed;
|
||||
sample->vibrato_depth = extra.vibrato_depth;
|
||||
sample->vibrato_rate = extra.vibrato_sweep;
|
||||
/* Rate and sweep don't match, but the difference is
|
||||
* accounted for in itrender.c.
|
||||
*/
|
||||
sample->vibrato_waveform = xm_convert_vibrato[extra.vibrato_type];
|
||||
}
|
||||
for (j = 0; j < extra.n_samples; j++) {
|
||||
if (it_xm_read_sample_data(&sigdata->sample[total_samples+j], roguebytes[j], f) != 0) {
|
||||
_dumb_it_unload_sigdata(sigdata);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
total_samples += extra.n_samples;
|
||||
}
|
||||
}
|
||||
|
||||
sigdata->n_samples = total_samples;
|
||||
|
||||
sigdata->flags = IT_WAS_AN_XM | IT_OLD_EFFECTS | IT_COMPATIBLE_GXX | IT_STEREO | IT_USE_INSTRUMENTS;
|
||||
// Are we OK with IT_COMPATIBLE_GXX off?
|
||||
//
|
||||
// When specifying note + instr + tone portamento, and an old note is still playing (even after note off):
|
||||
// - If Compatible Gxx is on, the new note will be triggered only if the instrument _changes_.
|
||||
// - If Compatible Gxx is off, the new note will always be triggered, provided the instrument is specified.
|
||||
// - FT2 seems to do the latter (unconfirmed).
|
||||
|
||||
// Err, wait. XM playback has its own code. The change made to the IT
|
||||
// playbackc code didn't affect XM playback. Forget this then. There's
|
||||
// still a bug in XM playback though, and it'll need some investigation...
|
||||
// tomorrow...
|
||||
|
||||
// UPDATE: IT_COMPATIBLE_GXX is required to be on, so that tone porta has
|
||||
// separate memory from portamento.
|
||||
|
||||
if (flags & XM_LINEAR_FREQUENCY)
|
||||
sigdata->flags |= IT_LINEAR_SLIDES;
|
||||
|
||||
sigdata->global_volume = 128;
|
||||
sigdata->mixing_volume = 48;
|
||||
sigdata->pan_separation = 128;
|
||||
|
||||
memset(sigdata->channel_volume, 64, DUMB_IT_N_CHANNELS);
|
||||
memset(sigdata->channel_pan, 32, DUMB_IT_N_CHANNELS);
|
||||
|
||||
_dumb_it_fix_invalid_orders(sigdata);
|
||||
|
||||
return sigdata;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if 0 // no fucking way, dude!
|
||||
|
||||
/* The length returned is the time required to play from the beginning of the
|
||||
* file to the last row of the last order (which is when the player will
|
||||
* loop). Depending on the song, the sound might stop sooner.
|
||||
* Due to fixed point roundoffs, I think this is only reliable to the second.
|
||||
* Full precision could be achieved by using a double during the computation,
|
||||
* or maybe a LONG_LONG.
|
||||
*/
|
||||
long it_compute_length(const DUMB_IT_SIGDATA *sigdata)
|
||||
{
|
||||
IT_PATTERN *pattern;
|
||||
int tempo, speed;
|
||||
int loop_start[IT_N_CHANNELS];
|
||||
char loop_count[IT_N_CHANNELS];
|
||||
int order, entry;
|
||||
int row_first_entry = 0;
|
||||
int jump, jump_dest;
|
||||
int delay, fine_delay;
|
||||
int i;
|
||||
long t;
|
||||
|
||||
if (!sigdata)
|
||||
return 0;
|
||||
|
||||
tempo = sigdata->tempo;
|
||||
speed = sigdata->speed;
|
||||
order = entry = 0;
|
||||
jump = jump_dest = 0;
|
||||
t = 0;
|
||||
|
||||
/* for each PATTERN */
|
||||
for (order = 0; order < sigdata->n_orders; order++) {
|
||||
|
||||
if (sigdata->order[order] == IT_ORDER_END) break;
|
||||
if (sigdata->order[order] == IT_ORDER_SKIP) continue;
|
||||
|
||||
for (i = 0; i < IT_N_CHANNELS; i++)
|
||||
loop_count[i] = -1;
|
||||
|
||||
pattern = &sigdata->pattern[ sigdata->order[order] ];
|
||||
entry = 0;
|
||||
if (jump == IT_BREAK_TO_ROW) {
|
||||
int row = 0;
|
||||
while (row < jump_dest)
|
||||
if (pattern->entry[entry++].channel >= IT_N_CHANNELS)
|
||||
row++;
|
||||
}
|
||||
|
||||
/* for each ROW */
|
||||
while (entry < pattern->n_entries) {
|
||||
row_first_entry = entry;
|
||||
delay = fine_delay = 0;
|
||||
jump = 0;
|
||||
|
||||
/* for each note NOTE */
|
||||
while (entry < pattern->n_entries && pattern->entry[entry].channel < IT_N_CHANNELS) {
|
||||
int value = pattern->entry[entry].effectvalue;
|
||||
int channel = pattern->entry[entry].channel;
|
||||
|
||||
switch (pattern->entry[entry].effect) {
|
||||
|
||||
case IT_SET_SPEED: speed = value; break;
|
||||
|
||||
case IT_JUMP_TO_ORDER:
|
||||
if (value <= order) /* infinite loop */
|
||||
return 0;
|
||||
jump = IT_JUMP_TO_ORDER;
|
||||
jump_dest = value;
|
||||
break;
|
||||
|
||||
case IT_BREAK_TO_ROW:
|
||||
jump = IT_BREAK_TO_ROW;
|
||||
jump_dest = value;
|
||||
break;
|
||||
|
||||
case IT_S:
|
||||
switch (HIGH(value)) {
|
||||
case IT_S_PATTERN_DELAY: delay = LOW(value); break;
|
||||
case IT_S_FINE_PATTERN_DELAY: fine_delay = LOW(value); break;
|
||||
case IT_S_PATTERN_LOOP:
|
||||
if (LOW(value) == 0) {
|
||||
loop_start[channel] = row_first_entry;
|
||||
} else {
|
||||
if (loop_count[channel] == -1)
|
||||
loop_count[channel] = LOW(value);
|
||||
|
||||
if (loop_count[channel]) {
|
||||
jump = IT_S_PATTERN_LOOP;
|
||||
jump_dest = loop_start[channel];
|
||||
}
|
||||
loop_count[channel]--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case IT_SET_SONG_TEMPO:
|
||||
switch (HIGH(value)) { /* slides happen every non-row frames */
|
||||
case 0: tempo = tempo - LOW(value)*(speed-1); break;
|
||||
case 1: tempo = tempo + LOW(value)*(speed-1); break;
|
||||
default: tempo = value;
|
||||
}
|
||||
tempo = MID(32, tempo, 255);
|
||||
break;
|
||||
}
|
||||
|
||||
entry++;
|
||||
}
|
||||
|
||||
/* end of ROW */
|
||||
entry++;
|
||||
t += TICK_TIME_DIVIDEND * (speed*(1+delay) + fine_delay) / tempo;
|
||||
|
||||
if (jump == IT_JUMP_TO_ORDER) {
|
||||
order = jump_dest - 1;
|
||||
break;
|
||||
} else if (jump == IT_BREAK_TO_ROW)
|
||||
break;
|
||||
else if (jump == IT_S_PATTERN_LOOP)
|
||||
entry = jump_dest - 1;
|
||||
}
|
||||
|
||||
/* end of PATTERN */
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
#endif /* 0 */
|
||||
|
||||
|
||||
|
||||
DUH *dumb_read_xm(DUMBFILE *f)
|
||||
{
|
||||
sigdata_t *sigdata;
|
||||
long length;
|
||||
|
||||
DUH_SIGTYPE_DESC *descptr = &_dumb_sigtype_it;
|
||||
|
||||
sigdata = it_xm_load_sigdata(f);
|
||||
|
||||
if (!sigdata)
|
||||
return NULL;
|
||||
|
||||
length = _dumb_it_build_checkpoints(sigdata);
|
||||
|
||||
return make_duh(length, 1, &descptr, &sigdata);
|
||||
}
|
||||
242
apps/codecs/dumb/src/it/xmeffect.c
Normal file
242
apps/codecs/dumb/src/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(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:
|
||||
/** WARNING: In FT2, the value seems to do something... Oh well,
|
||||
* this is undocumented anyway!
|
||||
*/
|
||||
entry->mask &= ~IT_ENTRY_EFFECT;
|
||||
entry->mask |= IT_ENTRY_NOTE;
|
||||
entry->note = IT_NOTE_OFF;
|
||||
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…
Add table
Add a link
Reference in a new issue