townengine/third-party/libxm/src/context.c
2024-07-08 18:01:08 +03:00

257 lines
7.4 KiB
C

/* Author: Romain "Artefact2" Dalmaso <artefact2@gmail.com> */
/* This program is free software. It comes without any warranty, to the
* extent permitted by applicable law. You can redistribute it and/or
* modify it under the terms of the Do What The Fuck You Want To Public
* License, Version 2, as published by Sam Hocevar. See
* http://sam.zoy.org/wtfpl/COPYING for more details. */
#include "xm_internal.h"
#define OFFSET(ptr) do { \
(ptr) = (void*)((intptr_t)(ptr) + (intptr_t)(*ctxp)); \
} while(0)
#define CHECK_CHANNEL(ctx, c) do { \
if(XM_DEBUG && ((c) == 0 || (c) > (ctx)->module.num_channels)) \
DEBUG("invalid channel %d", (c)); \
} while(0)
#define CHECK_INSTRUMENT(ctx, i) do { \
if(XM_DEBUG && ((i) == 0 || (i) > (ctx)->module.num_instruments)) \
DEBUG("invalid instrument %d", (i)); \
} while(0)
#define CHECK_SAMPLE(ctx, i, s) do { \
CHECK_INSTRUMENT((ctx), (i)); \
if(XM_DEBUG && ((s) > (ctx)->module.instruments[(i)].num_samples)) \
DEBUG("invalid sample %d for instrument %d", (s), (i)); \
} while(0)
int xm_create_context(xm_context_t** ctxp, const char* moddata, uint32_t rate) {
return xm_create_context_safe(ctxp, moddata, SIZE_MAX, rate);
}
int xm_create_context_safe(xm_context_t** ctxp, const char* moddata, size_t moddata_length, uint32_t rate) {
size_t bytes_needed;
char* mempool;
xm_context_t* ctx;
if(XM_DEFENSIVE) {
int ret;
if((ret = xm_check_sanity_preload(moddata, moddata_length))) {
DEBUG("xm_check_sanity_preload() returned %i, module is not safe to load", ret);
return 1;
}
}
bytes_needed = xm_get_memory_needed_for_context(moddata, moddata_length);
mempool = malloc(bytes_needed);
if(mempool == NULL && bytes_needed > 0) {
/* malloc() failed, trouble ahead */
DEBUG("call to malloc() failed, returned %p", (void*)mempool);
return 2;
}
/* Initialize most of the fields to 0, 0.f, NULL or false depending on type */
memset(mempool, 0, bytes_needed);
ctx = (*ctxp = (xm_context_t*)mempool);
ctx->ctx_size = bytes_needed; /* Keep original requested size for xmconvert */
mempool += sizeof(xm_context_t);
ctx->rate = rate;
mempool = xm_load_module(ctx, moddata, moddata_length, mempool);
ctx->channels = (xm_channel_context_t*)mempool;
mempool += ctx->module.num_channels * sizeof(xm_channel_context_t);
ctx->global_volume = 1.f;
ctx->amplification = .25f; /* XXX: some bad modules may still clip. Find out something better. */
#if XM_RAMPING
ctx->volume_ramp = (1.f / 128.f);
#endif
for(uint8_t i = 0; i < ctx->module.num_channels; ++i) {
xm_channel_context_t* ch = ctx->channels + i;
ch->ping = true;
ch->vibrato_waveform = XM_SINE_WAVEFORM;
ch->vibrato_waveform_retrigger = true;
ch->tremolo_waveform = XM_SINE_WAVEFORM;
ch->tremolo_waveform_retrigger = true;
ch->volume = ch->volume_envelope_volume = ch->fadeout_volume = 1.0f;
ch->panning = ch->panning_envelope_panning = .5f;
ch->actual_volume[0] = .0f;
ch->actual_volume[1] = .0f;
}
ctx->row_loop_count = (uint8_t*)mempool;
mempool += ctx->module.length * MAX_NUM_ROWS * sizeof(uint8_t);
if(XM_DEFENSIVE) {
int ret;
if((ret = xm_check_sanity_postload(ctx))) {
DEBUG("xm_check_sanity_postload() returned %i, module is not safe to play", ret);
xm_free_context(ctx);
return 1;
}
}
return 0;
}
void xm_free_context(xm_context_t* context) {
free(context);
}
void xm_set_max_loop_count(xm_context_t* context, uint8_t loopcnt) {
context->max_loop_count = loopcnt;
}
uint8_t xm_get_loop_count(xm_context_t* context) {
return context->loop_count;
}
void xm_seek(xm_context_t* ctx, uint8_t pot, uint8_t row, uint16_t tick) {
ctx->current_table_index = pot;
ctx->current_row = row;
ctx->current_tick = tick;
ctx->remaining_samples_in_tick = 0;
}
bool xm_mute_channel(xm_context_t* ctx, uint16_t channel, bool mute) {
CHECK_CHANNEL(ctx, channel);
bool old = ctx->channels[channel - 1].muted;
ctx->channels[channel - 1].muted = mute;
return old;
}
bool xm_mute_instrument(xm_context_t* ctx, uint16_t instr, bool mute) {
CHECK_INSTRUMENT(ctx, instr);
bool old = ctx->module.instruments[instr - 1].muted;
ctx->module.instruments[instr - 1].muted = mute;
return old;
}
#if XM_STRINGS
const char* xm_get_module_name(xm_context_t* ctx) {
return ctx->module.name;
}
const char* xm_get_tracker_name(xm_context_t* ctx) {
return ctx->module.trackername;
}
#else
const char* xm_get_module_name(xm_context_t* ctx) {
return NULL;
}
const char* xm_get_tracker_name(xm_context_t* ctx) {
return NULL;
}
#endif
uint16_t xm_get_number_of_channels(xm_context_t* ctx) {
return ctx->module.num_channels;
}
uint16_t xm_get_module_length(xm_context_t* ctx) {
return ctx->module.length;
}
uint16_t xm_get_number_of_patterns(xm_context_t* ctx) {
return ctx->module.num_patterns;
}
uint16_t xm_get_number_of_rows(xm_context_t* ctx, uint16_t pattern) {
if(pattern < ctx->module.num_patterns)
return ctx->module.patterns[pattern].num_rows;
return DEFAULT_PATTERN_LENGTH;
}
uint16_t xm_get_number_of_instruments(xm_context_t* ctx) {
return ctx->module.num_instruments;
}
uint16_t xm_get_number_of_samples(xm_context_t* ctx, uint16_t instrument) {
CHECK_INSTRUMENT(ctx, instrument);
return ctx->module.instruments[instrument - 1].num_samples;
}
void* xm_get_sample_waveform(xm_context_t* ctx, uint16_t i, uint16_t s, size_t* size, uint8_t* bits) {
CHECK_SAMPLE(ctx, i, s);
*size = ctx->module.instruments[i - 1].samples[s].length;
*bits = ctx->module.instruments[i - 1].samples[s].bits;
return ctx->module.instruments[i - 1].samples[s].data8;
}
void xm_get_playing_speed(xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo) {
if(bpm) *bpm = ctx->bpm;
if(tempo) *tempo = ctx->tempo;
}
void xm_get_position(xm_context_t* ctx, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples) {
if(pattern_index) *pattern_index = ctx->current_table_index;
if(pattern) *pattern = ctx->module.pattern_table[ctx->current_table_index];
if(row) *row = ctx->current_row;
if(samples) *samples = ctx->generated_samples;
}
uint64_t xm_get_latest_trigger_of_instrument(xm_context_t* ctx, uint16_t instr) {
CHECK_INSTRUMENT(ctx, instr);
return ctx->module.instruments[instr - 1].latest_trigger;
}
uint64_t xm_get_latest_trigger_of_sample(xm_context_t* ctx, uint16_t instr, uint16_t sample) {
CHECK_SAMPLE(ctx, instr, sample);
return ctx->module.instruments[instr - 1].samples[sample].latest_trigger;
}
uint64_t xm_get_latest_trigger_of_channel(xm_context_t* ctx, uint16_t chn) {
CHECK_CHANNEL(ctx, chn);
return ctx->channels[chn - 1].latest_trigger;
}
bool xm_is_channel_active(xm_context_t* ctx, uint16_t chn) {
CHECK_CHANNEL(ctx, chn);
xm_channel_context_t* ch = ctx->channels + (chn - 1);
return ch->instrument != NULL && ch->sample != NULL && ch->sample_position >= 0;
}
float xm_get_frequency_of_channel(xm_context_t* ctx, uint16_t chn) {
CHECK_CHANNEL(ctx, chn);
return ctx->channels[chn - 1].frequency;
}
float xm_get_volume_of_channel(xm_context_t* ctx, uint16_t chn) {
CHECK_CHANNEL(ctx, chn);
return ctx->channels[chn - 1].volume * ctx->global_volume;
}
float xm_get_panning_of_channel(xm_context_t* ctx, uint16_t chn) {
CHECK_CHANNEL(ctx, chn);
return ctx->channels[chn - 1].panning;
}
uint16_t xm_get_instrument_of_channel(xm_context_t* ctx, uint16_t chn) {
CHECK_CHANNEL(ctx, chn);
xm_channel_context_t* ch = ctx->channels + (chn - 1);
if(ch->instrument == NULL) return 0;
return 1 + (ch->instrument - ctx->module.instruments);
}