Fix audio; save qoa and wav; separate out idea of pcm and playable sound

This commit is contained in:
John Alanbrook 2024-10-08 02:46:59 -05:00
parent 8465fb53f7
commit 39511e0d3b
17 changed files with 207 additions and 3149 deletions

View file

@ -20,7 +20,7 @@ STEAMAPI =
LDFLAGS += -lstdc++ LDFLAGS += -lstdc++
ifeq ($(CROSS)$(CC), emcc) ifeq ($(CROSS)$(CC), emcc)
LDFLAGS += --shell-file shell.html --closure 1 LDFLAGS += --shell-file shell.html --embed-file ld56@/ #--closure 1
CPPFLAGS += -Wbad-function-cast -Wcast-function-type -sSTACK_SIZE=1MB -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128MB -pthread -s USE_PTHREADS=1 CPPFLAGS += -Wbad-function-cast -Wcast-function-type -sSTACK_SIZE=1MB -sALLOW_MEMORY_GROWTH -sINITIAL_MEMORY=128MB -pthread -s USE_PTHREADS=1
NDEBUG = 1 NDEBUG = 1
ARCH:= wasm ARCH:= wasm

View file

@ -233,6 +233,7 @@ var pipe_shaders = new WeakMap();
render.use_shader = function use_shader(shader, pipeline) { render.use_shader = function use_shader(shader, pipeline) {
pipeline ??= base_pipeline; pipeline ??= base_pipeline;
if (typeof shader === "string") shader = make_shader(shader); if (typeof shader === "string") shader = make_shader(shader);
if (cur.shader === shader) return;
if (!pipe_shaders.has(shader)) pipe_shaders.set(shader, new WeakMap()); if (!pipe_shaders.has(shader)) pipe_shaders.set(shader, new WeakMap());
var shader_pipelines = pipe_shaders.get(shader); var shader_pipelines = pipe_shaders.get(shader);
@ -570,14 +571,13 @@ function shader_apply_material(shader, material = {}, old = {}) {
// Creates a binding object for a given mesh and shader // Creates a binding object for a given mesh and shader
var bcache = new WeakMap(); var bcache = new WeakMap();
function sg_bind(mesh, ssbo) { function sg_bind(mesh, ssbo) {
/* if (cur.bind && cur.mesh === mesh && cur.ssbo === ssbo) { if (cur.bind && cur.mesh === mesh && cur.ssbo === ssbo) {
cur.bind.ssbo = [ssbo]; cur.bind.ssbo = [ssbo];
cur.bind.images = cur.images; cur.bind.images = cur.images;
cur.bind.samplers = cur.samplers; cur.bind.samplers = cur.samplers;
console.info(json.encode(cur.bind));
render.setbind(cur.bind); render.setbind(cur.bind);
return; return;
}*/ }
/* if (bcache.has(cur.shader) && bcache.get(cur.shader).has(mesh)) { /* if (bcache.has(cur.shader) && bcache.get(cur.shader).has(mesh)) {
cur.bind = bcache.get(cur.shader).get(mesh); cur.bind = bcache.get(cur.shader).get(mesh);
@ -870,7 +870,7 @@ render.rectangle = function render_rectangle(lowerleft, upperright, color, shade
queued_shader = shader; queued_shader = shader;
queued_pipe = pipe; queued_pipe = pipe;
flush_poly(); check_flush(flush_poly);
}; };
render.rect = function(rect, color, shader, pipe) render.rect = function(rect, color, shader, pipe)
@ -1053,9 +1053,6 @@ render.image = function image(image, pos, scale, rotation = 0, color = Color.whi
e.image = image; e.image = image;
e.shade = color; e.shade = color;
flush_img();
lasttex = undefined;
return; return;
var bb = {}; var bb = {};
bb.b = pos.y; bb.b = pos.y;

View file

@ -5,18 +5,29 @@ Object.readonly(audio, "channels");
Object.readonly(audio, "buffer_frames"); Object.readonly(audio, "buffer_frames");
var sources = []; var sources = [];
var pcms = {};
audio.pcm = function(file)
{
file = Resources.find_sound(file);
if (!file) return;
if (pcms[file]) return pcms[file];
var newpcm = os.make_pcm(file);
if (!newpcm) return;
pcms[file] = newpcm;
newpcm.format(audio.samplerate, audio.channels);
return newpcm;
}
audio.play = function (file, bus = audio.bus.master) { audio.play = function (file, bus = audio.bus.master) {
var filename = file; var pcm = audio.pcm(file);
file = Resources.find_sound(file); if (!pcm) return;
if (!file) { var src = audio.dsp.source(pcm);
console.error(`Cannot play sound ${file}: does not exist.`);
return;
}
var src = audio.dsp.source(file);
src.plugin(bus); src.plugin(bus);
src.guid = prosperon.guid(); src.guid = prosperon.guid();
src.name = file; src.name = file;
src._pcm = pcm;
src.type = "source"; src.type = "source";
sources.push(src); sources.push(src);
return src; return src;
@ -57,7 +68,6 @@ audio.dsp.mix().__proto__.imgui = function () {
}; };
audio.cry = function (file, bus = audio.bus.sfx) { audio.cry = function (file, bus = audio.bus.sfx) {
file = Resources.find_sound(file);
var player = audio.play(file, bus); var player = audio.play(file, bus);
if (!player) return; if (!player) return;
player.ended = function () { player.ended = function () {
@ -86,7 +96,7 @@ audio.music = function (file, fade = 0.5) {
if (song) song.volume = 0; if (song) song.volume = 0;
return; return;
} }
file = Resources.find_sound(file);
if (!fade) { if (!fade) {
song = audio.play(file, audio.bus.music); song = audio.play(file, audio.bus.music);
song.loop = true; song.loop = true;

View file

@ -13,7 +13,7 @@ void vert()
void frag() void frag()
{ {
color = texture(sampler2D(diffuse,smp), uv); color = texture(sampler2D(diffuse,smp), uv);
if (color.a != 0) discard; if (color.a == 0) discard;
} }
@end @end

View file

@ -119,6 +119,7 @@ QJSCLASS(gameobject)
QJSCLASS(transform) QJSCLASS(transform)
QJSCLASS(dsp_node) QJSCLASS(dsp_node)
QJSCLASS(texture) QJSCLASS(texture)
QJSCLASS(pcm)
QJSCLASS(font) QJSCLASS(font)
QJSCLASS(warp_gravity) QJSCLASS(warp_gravity)
QJSCLASS(warp_damp) QJSCLASS(warp_damp)
@ -1127,7 +1128,7 @@ JSC_CCALL(render_make_particle_ssbo,
.type = SG_BUFFERTYPE_STORAGEBUFFER, .type = SG_BUFFERTYPE_STORAGEBUFFER,
.size = size+desc.size, .size = size+desc.size,
.usage = SG_USAGE_STREAM, .usage = SG_USAGE_STREAM,
.label = "transform buffer" .label = "particle ssbo buffer"
}); });
} }
@ -1166,7 +1167,7 @@ JSC_CCALL(render_make_sprite_ssbo,
.type = SG_BUFFERTYPE_STORAGEBUFFER, .type = SG_BUFFERTYPE_STORAGEBUFFER,
.size = size+desc.size, .size = size+desc.size,
.usage = SG_USAGE_STREAM, .usage = SG_USAGE_STREAM,
.label = "transform buffer" .label = "sprite ssbo buffer"
}); });
} }
@ -1995,8 +1996,6 @@ JSValue js_io_chmod(JSContext *js, JSValue self, int argc, JSValue *argv)
return JS_UNDEFINED; return JS_UNDEFINED;
} }
JSC_SCALL(io_save_qoa, save_qoa(str))
JSC_SCALL(io_pack_start, pack_start(str)) JSC_SCALL(io_pack_start, pack_start(str))
JSC_SCALL(io_pack_add, pack_add(str)) JSC_SCALL(io_pack_add, pack_add(str))
JSC_CCALL(io_pack_end, pack_end()) JSC_CCALL(io_pack_end, pack_end())
@ -2018,7 +2017,6 @@ static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, slurp, 1), MIST_FUNC_DEF(io, slurp, 1),
MIST_FUNC_DEF(io, slurpbytes, 1), MIST_FUNC_DEF(io, slurpbytes, 1),
MIST_FUNC_DEF(io, slurpwrite, 2), MIST_FUNC_DEF(io, slurpwrite, 2),
MIST_FUNC_DEF(io, save_qoa,1),
MIST_FUNC_DEF(io, pack_start, 1), MIST_FUNC_DEF(io, pack_start, 1),
MIST_FUNC_DEF(io, pack_add, 1), MIST_FUNC_DEF(io, pack_add, 1),
MIST_FUNC_DEF(io, pack_end, 0), MIST_FUNC_DEF(io, pack_end, 0),
@ -2253,17 +2251,39 @@ static const JSCFunctionListEntry js_dsp_node_funcs[] = {
}; };
JSC_GETSET(sound, loop, boolean) JSC_GETSET(sound, loop, boolean)
JSC_GETSET(sound, timescale, number)
JSC_GETSET(sound, frame, number) JSC_GETSET(sound, frame, number)
JSC_CCALL(sound_frames, return number2js(js2sound(self)->data->frames)) JSC_CCALL(sound_frames, return number2js(js2sound(self)->data->frames))
static const JSCFunctionListEntry js_sound_funcs[] = { static const JSCFunctionListEntry js_sound_funcs[] = {
CGETSET_ADD(sound, loop), CGETSET_ADD(sound, loop),
CGETSET_ADD(sound, timescale),
CGETSET_ADD(sound, frame), CGETSET_ADD(sound, frame),
MIST_FUNC_DEF(sound, frames, 0), MIST_FUNC_DEF(sound, frames, 0),
}; };
JSC_GET(pcm, ch, number)
JSC_GET(pcm, samplerate, number)
JSC_GET(pcm, frames, number)
JSC_CCALL(pcm_format,
pcm_format(js2pcm(self), js2number(argv[0]), js2number(argv[1]));
)
JSC_SCALL(pcm_save_qoa,
save_qoa(str, js2pcm(self));
)
JSC_SCALL(pcm_save_wav,
save_wav(str, js2pcm(self));
)
static const JSCFunctionListEntry js_pcm_funcs[] = {
MIST_GET(pcm, ch),
MIST_GET(pcm, samplerate),
MIST_GET(pcm, frames),
MIST_FUNC_DEF(pcm, format, 2),
MIST_FUNC_DEF(pcm, save_qoa, 1),
MIST_FUNC_DEF(pcm, save_wav, 1)
};
static JSValue js_window_get_fullscreen(JSContext *js, JSValue self) { return boolean2js(js2window(self)->fullscreen); } static JSValue js_window_get_fullscreen(JSContext *js, JSValue self) { return boolean2js(js2window(self)->fullscreen); }
static JSValue js_window_set_fullscreen(JSContext *js, JSValue self, JSValue v) { window_setfullscreen(js2window(self), js2boolean(v)); return JS_UNDEFINED; } static JSValue js_window_set_fullscreen(JSContext *js, JSValue self, JSValue v) { window_setfullscreen(js2window(self), js2boolean(v)); return JS_UNDEFINED; }
@ -2663,8 +2683,8 @@ JSC_CCALL(dspsound_delay,
) )
JSC_CCALL(dspsound_fwd_delay, return dsp_node2js(dsp_fwd_delay(js2number(argv[0]), js2number(argv[1])))) JSC_CCALL(dspsound_fwd_delay, return dsp_node2js(dsp_fwd_delay(js2number(argv[0]), js2number(argv[1]))))
JSC_SCALL(dspsound_source, JSC_CCALL(dspsound_source,
ret = dsp_node2js(dsp_source(str)); ret = dsp_node2js(dsp_source(js2pcm(argv[0])));
JS_SetPrototype(js, ret, sound_proto); JS_SetPrototype(js, ret, sound_proto);
) )
JSC_CCALL(dspsound_mix, return dsp_node2js(make_node(NULL,NULL,NULL))) JSC_CCALL(dspsound_mix, return dsp_node2js(make_node(NULL,NULL,NULL)))
@ -3332,6 +3352,10 @@ JSC_SCALL(os_make_texture,
JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0])); JS_SetPropertyStr(js, ret, "path", JS_DupValue(js,argv[0]));
) )
JSC_SCALL(os_make_pcm,
ret = pcm2js(make_pcm(str));
)
JSC_SCALL(os_make_aseprite, JSC_SCALL(os_make_aseprite,
) )
@ -3629,6 +3653,7 @@ static const JSCFunctionListEntry js_os_funcs[] = {
MIST_FUNC_DEF(os, make_poly2d, 2), MIST_FUNC_DEF(os, make_poly2d, 2),
MIST_FUNC_DEF(os, make_seg2d, 1), MIST_FUNC_DEF(os, make_seg2d, 1),
MIST_FUNC_DEF(os, make_texture, 1), MIST_FUNC_DEF(os, make_texture, 1),
MIST_FUNC_DEF(os, make_pcm, 1),
MIST_FUNC_DEF(os, texture_swap, 2), MIST_FUNC_DEF(os, texture_swap, 2),
MIST_FUNC_DEF(os, make_tex_data, 3), MIST_FUNC_DEF(os, make_tex_data, 3),
MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_font, 2),
@ -3691,6 +3716,7 @@ void ffi_load() {
QJSCLASSPREP_FUNCS(warp_gravity); QJSCLASSPREP_FUNCS(warp_gravity);
QJSCLASSPREP_FUNCS(warp_damp); QJSCLASSPREP_FUNCS(warp_damp);
QJSCLASSPREP_FUNCS(texture); QJSCLASSPREP_FUNCS(texture);
QJSCLASSPREP_FUNCS(pcm);
QJSCLASSPREP_FUNCS(font); QJSCLASSPREP_FUNCS(font);
QJSCLASSPREP_FUNCS(cpConstraint); QJSCLASSPREP_FUNCS(cpConstraint);
QJSCLASSPREP_FUNCS(window); QJSCLASSPREP_FUNCS(window);

View file

@ -366,22 +366,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
), bytes, &p); ), bytes, &p);
for (int c = 0; c < channels; c++) { for (unsigned int c = 0; c < channels; c++) {
/* If the weights have grown too large, reset them to 0. This may happen
with certain high-frequency sounds. This is a last resort and will
introduce quite a bit of noise, but should at least prevent pops/clicks */
int weights_sum =
qoa->lms[c].weights[0] * qoa->lms[c].weights[0] +
qoa->lms[c].weights[1] * qoa->lms[c].weights[1] +
qoa->lms[c].weights[2] * qoa->lms[c].weights[2] +
qoa->lms[c].weights[3] * qoa->lms[c].weights[3];
if (weights_sum > 0x2fffffff) {
qoa->lms[c].weights[0] = 0;
qoa->lms[c].weights[1] = 0;
qoa->lms[c].weights[2] = 0;
qoa->lms[c].weights[3] = 0;
}
/* Write the current LMS state */ /* Write the current LMS state */
qoa_uint64_t weights = 0; qoa_uint64_t weights = 0;
qoa_uint64_t history = 0; qoa_uint64_t history = 0;
@ -395,9 +380,9 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
/* We encode all samples with the channels interleaved on a slice level. /* We encode all samples with the channels interleaved on a slice level.
E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/ E.g. for stereo: (ch-0, slice 0), (ch 1, slice 0), (ch 0, slice 1), ...*/
for (int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) { for (unsigned int sample_index = 0; sample_index < frame_len; sample_index += QOA_SLICE_LEN) {
for (int c = 0; c < channels; c++) { for (unsigned int c = 0; c < channels; c++) {
int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index); int slice_len = qoa_clamp(QOA_SLICE_LEN, 0, frame_len - sample_index);
int slice_start = sample_index * channels + c; int slice_start = sample_index * channels + c;
int slice_end = (sample_index + slice_len) * channels + c; int slice_end = (sample_index + slice_len) * channels + c;
@ -405,10 +390,13 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
/* Brute for search for the best scalefactor. Just go through all /* Brute for search for the best scalefactor. Just go through all
16 scalefactors, encode all samples for the current slice and 16 scalefactors, encode all samples for the current slice and
meassure the total squared error. */ meassure the total squared error. */
qoa_uint64_t best_rank = -1;
#ifdef QOA_RECORD_TOTAL_ERROR
qoa_uint64_t best_error = -1; qoa_uint64_t best_error = -1;
qoa_uint64_t best_slice; #endif
qoa_uint64_t best_slice = 0;
qoa_lms_t best_lms; qoa_lms_t best_lms;
int best_scalefactor; int best_scalefactor = 0;
for (int sfi = 0; sfi < 16; sfi++) { for (int sfi = 0; sfi < 16; sfi++) {
/* There is a strong correlation between the scalefactors of /* There is a strong correlation between the scalefactors of
@ -421,7 +409,10 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
state when encoding. */ state when encoding. */
qoa_lms_t lms = qoa->lms[c]; qoa_lms_t lms = qoa->lms[c];
qoa_uint64_t slice = scalefactor; qoa_uint64_t slice = scalefactor;
qoa_uint64_t current_rank = 0;
#ifdef QOA_RECORD_TOTAL_ERROR
qoa_uint64_t current_error = 0; qoa_uint64_t current_error = 0;
#endif
for (int si = slice_start; si < slice_end; si += channels) { for (int si = slice_start; si < slice_end; si += channels) {
int sample = sample_data[si]; int sample = sample_data[si];
@ -434,9 +425,27 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
int dequantized = qoa_dequant_tab[scalefactor][quantized]; int dequantized = qoa_dequant_tab[scalefactor][quantized];
int reconstructed = qoa_clamp_s16(predicted + dequantized); int reconstructed = qoa_clamp_s16(predicted + dequantized);
/* If the weights have grown too large, we introduce a penalty
here. This prevents pops/clicks in certain problem cases */
int weights_penalty = ((
lms.weights[0] * lms.weights[0] +
lms.weights[1] * lms.weights[1] +
lms.weights[2] * lms.weights[2] +
lms.weights[3] * lms.weights[3]
) >> 18) - 0x8ff;
if (weights_penalty < 0) {
weights_penalty = 0;
}
long long error = (sample - reconstructed); long long error = (sample - reconstructed);
current_error += error * error; qoa_uint64_t error_sq = error * error;
if (current_error > best_error) {
current_rank += error_sq + weights_penalty * weights_penalty;
#ifdef QOA_RECORD_TOTAL_ERROR
current_error += error_sq;
#endif
if (current_rank > best_rank) {
break; break;
} }
@ -444,8 +453,11 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned
slice = (slice << 3) | quantized; slice = (slice << 3) | quantized;
} }
if (current_error < best_error) { if (current_rank < best_rank) {
best_rank = current_rank;
#ifdef QOA_RECORD_TOTAL_ERROR
best_error = current_error; best_error = current_error;
#endif
best_slice = slice; best_slice = slice;
best_lms = lms; best_lms = lms;
best_scalefactor = scalefactor; best_scalefactor = scalefactor;
@ -490,7 +502,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len)
unsigned char *bytes = QOA_MALLOC(encoded_size); unsigned char *bytes = QOA_MALLOC(encoded_size);
for (int c = 0; c < qoa->channels; c++) { for (unsigned int c = 0; c < qoa->channels; c++) {
/* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the
prediction of the first few ms of a file. */ prediction of the first few ms of a file. */
qoa->lms[c].weights[0] = 0; qoa->lms[c].weights[0] = 0;
@ -513,7 +525,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len)
#endif #endif
int frame_len = QOA_FRAME_LEN; int frame_len = QOA_FRAME_LEN;
for (int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) { for (unsigned int sample_index = 0; sample_index < qoa->samples; sample_index += frame_len) {
frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index); frame_len = qoa_clamp(QOA_FRAME_LEN, 0, qoa->samples - sample_index);
const short *frame_samples = sample_data + sample_index * qoa->channels; const short *frame_samples = sample_data + sample_index * qoa->channels;
unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p); unsigned int frame_size = qoa_encode_frame(frame_samples, qoa, frame_len, bytes + p);
@ -576,14 +588,14 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa
/* Read and verify the frame header */ /* Read and verify the frame header */
qoa_uint64_t frame_header = qoa_read_u64(bytes, &p); qoa_uint64_t frame_header = qoa_read_u64(bytes, &p);
int channels = (frame_header >> 56) & 0x0000ff; unsigned int channels = (frame_header >> 56) & 0x0000ff;
int samplerate = (frame_header >> 32) & 0xffffff; unsigned int samplerate = (frame_header >> 32) & 0xffffff;
int samples = (frame_header >> 16) & 0x00ffff; unsigned int samples = (frame_header >> 16) & 0x00ffff;
int frame_size = (frame_header ) & 0x00ffff; unsigned int frame_size = (frame_header ) & 0x00ffff;
int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels; unsigned int data_size = frame_size - 8 - QOA_LMS_LEN * 4 * channels;
int num_slices = data_size / 8; unsigned int num_slices = data_size / 8;
int max_total_samples = num_slices * QOA_SLICE_LEN; unsigned int max_total_samples = num_slices * QOA_SLICE_LEN;
if ( if (
channels != qoa->channels || channels != qoa->channels ||
@ -596,7 +608,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa
/* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */ /* Read the LMS state: 4 x 2 bytes history, 4 x 2 bytes weights per channel */
for (int c = 0; c < channels; c++) { for (unsigned int c = 0; c < channels; c++) {
qoa_uint64_t history = qoa_read_u64(bytes, &p); qoa_uint64_t history = qoa_read_u64(bytes, &p);
qoa_uint64_t weights = qoa_read_u64(bytes, &p); qoa_uint64_t weights = qoa_read_u64(bytes, &p);
for (int i = 0; i < QOA_LMS_LEN; i++) { for (int i = 0; i < QOA_LMS_LEN; i++) {
@ -609,17 +621,19 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa
/* Decode all slices for all channels in this frame */ /* Decode all slices for all channels in this frame */
for (int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) { for (unsigned int sample_index = 0; sample_index < samples; sample_index += QOA_SLICE_LEN) {
for (int c = 0; c < channels; c++) { for (unsigned int c = 0; c < channels; c++) {
qoa_uint64_t slice = qoa_read_u64(bytes, &p); qoa_uint64_t slice = qoa_read_u64(bytes, &p);
int scalefactor = (slice >> 60) & 0xf; int scalefactor = (slice >> 60) & 0xf;
slice <<= 4;
int slice_start = sample_index * channels + c; int slice_start = sample_index * channels + c;
int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c; int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c;
for (int si = slice_start; si < slice_end; si += channels) { for (int si = slice_start; si < slice_end; si += channels) {
int predicted = qoa_lms_predict(&qoa->lms[c]); int predicted = qoa_lms_predict(&qoa->lms[c]);
int quantized = (slice >> 57) & 0x7; int quantized = (slice >> 61) & 0x7;
int dequantized = qoa_dequant_tab[scalefactor][quantized]; int dequantized = qoa_dequant_tab[scalefactor][quantized];
int reconstructed = qoa_clamp_s16(predicted + dequantized); int reconstructed = qoa_clamp_s16(predicted + dequantized);

View file

@ -49,15 +49,19 @@ void *zipbuf;
sfetch_handle_t game_h; sfetch_handle_t game_h;
#define MAXGAMESIZE 15*1024*1024
static void response_cb(const sfetch_response_t *r) static void response_cb(const sfetch_response_t *r)
{ {
if (r->fetched) { if (r->fetched) {
YughInfo("FINISHED FETCH\n");
zipbuf = malloc(r->data.size); zipbuf = malloc(r->data.size);
memcpy(zipbuf, r->data.ptr, r->data.size); memcpy(zipbuf, r->data.ptr, r->data.size);
mz_zip_reader_init_mem(&game_cdb, zipbuf, r->data.size,0); mz_zip_reader_init_mem(&game_cdb, zipbuf, r->data.size,0);
} }
if (r->finished) { if (r->finished) {
YughInfo("FINISHED RESPONSE\n");
LOADED_GAME = 1; LOADED_GAME = 1;
void *buf = sfetch_unbind_buffer(r->handle); void *buf = sfetch_unbind_buffer(r->handle);
free(buf); free(buf);
@ -78,13 +82,14 @@ void resources_init() {
mz_zip_reader_init_mem(&corecdb, core_cdb, core_cdb_len, 0); mz_zip_reader_init_mem(&corecdb, core_cdb, core_cdb_len, 0);
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
gamebuf = malloc(8*1024*1024); gamebuf = malloc(MAXGAMESIZE);
YughInfo("GRABBING GAME.ZIP\n");
game_h = sfetch_send(&(sfetch_request_t){ game_h = sfetch_send(&(sfetch_request_t){
.path="game.zip", .path="game.zip",
.callback = response_cb, .callback = response_cb,
.buffer = { .buffer = {
.ptr = gamebuf, .ptr = gamebuf,
.size = 8*1024*1024 .size = MAXGAMESIZE
} }
}); });
#else #else

View file

@ -336,29 +336,6 @@ dsp_node *dsp_pitchshift(float octaves)
return make_node(oct, filter_pitchshift, NULL); return make_node(oct, filter_pitchshift, NULL);
} }
struct timescale
{
float rate;
SRC_STATE *src;
};
static long *src_cb(struct timescale *ts, float **data)
{
return NULL;
}
void filter_timescale(struct timescale *ts, soundbyte *buffer, int frames)
{
}
dsp_node *dsp_timescale(float scale)
{
struct timescale *ts = malloc(sizeof(*ts));
ts->rate = scale;
ts->src = src_callback_new(src_cb, SRC_SINC_FASTEST, scale, NULL, ts);
return make_node(ts, filter_timescale, NULL);
}
soundbyte iir_filter(struct dsp_iir iir, soundbyte val) soundbyte iir_filter(struct dsp_iir iir, soundbyte val)
{ {
iir.y[0] = 0.0; iir.y[0] = 0.0;

View file

@ -12,8 +12,6 @@
pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER;
#include "samplerate.h"
#include "stb_ds.h" #include "stb_ds.h"
#include "dsp.h" #include "dsp.h"
@ -31,7 +29,6 @@ pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER;
#define TML_IMPLEMENTATION #define TML_IMPLEMENTATION
#include "tml.h" #include "tml.h"
#define DR_WAV_NO_STDIO
#define DR_WAV_IMPLEMENTATION #define DR_WAV_IMPLEMENTATION
#include "dr_wav.h" #include "dr_wav.h"
@ -48,17 +45,23 @@ pthread_mutex_t soundrun = PTHREAD_MUTEX_INITIALIZER;
#endif #endif
#ifndef NQOA #ifndef NQOA
#define QOA_NO_STDIO
#define QOA_IMPLEMENTATION #define QOA_IMPLEMENTATION
#include "qoa.h" #include "qoa.h"
#endif #endif
static struct { void short_to_float_array(const short *in, float *out, int frames, int channels)
char *key; {
struct wav *value; for (int i = 0; i < frames * channels; i++)
} *wavhash = NULL; out[i] = (float)in[i] / 32768.0f;
}
void change_channels(struct wav *w, int ch) { void float_to_short_array(float *in, short *out, int frames, int channels)
{
for (int i = 0; i < frames*channels; i++)
out[i] = (float)in[i]*32768;
}
void change_channels(struct pcm *w, int ch) {
if (w->ch == ch) return; if (w->ch == ch) return;
soundbyte *data = w->data; soundbyte *data = w->data;
int samples = ch * w->frames; int samples = ch * w->frames;
@ -81,26 +84,30 @@ void change_channels(struct wav *w, int ch) {
w->data = new; w->data = new;
} }
void resample(soundbyte *in, soundbyte *out, int in_frames, int out_frames, int channels) void resample_pcm(soundbyte *in, soundbyte *out, int in_frames, int out_frames, int channels)
{ {
float ratio = (float)in_frames / out_frames; float ratio = (float)in_frames / out_frames;
SRC_DATA ssrc; for (int i = 0; i < out_frames; i++) {
ssrc.data_in = in; // Find the position in the input buffer.
ssrc.data_out = out; float in_pos = i * ratio;
ssrc.input_frames = in_frames; int in_index = (int)in_pos; // Get the integer part of the position.
ssrc.output_frames = out_frames; float frac = in_pos - in_index; // Get the fractional part for interpolation.
ssrc.src_ratio = ratio;
int err = src_simple(&ssrc, SRC_LINEAR, channels); for (int ch = 0; ch < channels; ch++) {
if (err) // Linear interpolation between two input samples.
YughError("Resampling error code %d: %s", err, src_strerror(err)); soundbyte sample1 = in[in_index * channels + ch];
soundbyte sample2 = in[(in_index + 1) * channels + ch];
out[i * channels + ch] = (soundbyte)((1.0f - frac) * sample1 + frac * sample2);
}
}
} }
void change_samplerate(struct wav *w, int rate) { void change_samplerate(struct pcm *w, int rate) {
if (rate == w->samplerate) return; if (rate == w->samplerate) return;
float ratio = (float)rate / w->samplerate; float ratio = (float)rate / w->samplerate;
int outframes = w->frames * ratio; int outframes = w->frames * ratio;
soundbyte *resampled = malloc(w->ch*outframes*sizeof(soundbyte)); soundbyte *resampled = malloc(w->ch*outframes*sizeof(soundbyte));
resample(w->data, resampled, w->frames, outframes, w->ch); resample_pcm(w->data, resampled, w->frames, outframes, w->ch);
free(w->data); free(w->data);
w->data = resampled; w->data = resampled;
@ -143,30 +150,8 @@ void sound_init() {
BUF_FRAMES = saudio_buffer_frames(); BUF_FRAMES = saudio_buffer_frames();
} }
typedef struct { struct pcm *make_pcm(const char *wav) {
int channels; if (!wav) return NULL;
int samplerate;
void *f;
} stream;
void mp3_filter(stream *mp3, soundbyte *buffer, int frames)
{
if (mp3->samplerate == SAMPLERATE) {
drmp3_read_pcm_frames_f32(mp3->f, frames, buffer);
return;
}
int in_frames = (float)mp3->samplerate/SAMPLERATE;
soundbyte *decode = malloc(sizeof(*decode)*in_frames*mp3->channels);
drmp3_read_pcm_frames_f32(mp3->f, in_frames, decode);
resample(decode, buffer, in_frames, frames, CHANNELS);
}
struct wav *make_sound(const char *wav) {
int index = shgeti(wavhash, wav);
if (index != -1)
return wavhash[index].value;
char *ext = strrchr(wav, '.')+1; char *ext = strrchr(wav, '.')+1;
if(!ext) { if(!ext) {
@ -174,14 +159,15 @@ struct wav *make_sound(const char *wav) {
return NULL; return NULL;
} }
struct wav *mwav = malloc(sizeof(*mwav));
size_t rawlen; size_t rawlen;
void *raw = slurp_file(wav, &rawlen); void *raw = slurp_file(wav, &rawlen);
if (!raw) { if (!raw) {
YughError("Could not find file %s.", wav); YughWarn("Could not find file %s.", wav);
return NULL; return NULL;
} }
struct pcm *mwav = malloc(sizeof(*mwav));
if (!strcmp(ext, "wav")) if (!strcmp(ext, "wav"))
mwav->data = drwav_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav->ch, &mwav->samplerate, &mwav->frames, NULL); mwav->data = drwav_open_memory_and_read_pcm_frames_f32(raw, rawlen, &mwav->ch, &mwav->samplerate, &mwav->frames, NULL);
else if (!strcmp(ext, "flac")) { else if (!strcmp(ext, "flac")) {
@ -207,9 +193,9 @@ struct wav *make_sound(const char *wav) {
short *qoa_data = qoa_decode(raw, rawlen, &qoa); short *qoa_data = qoa_decode(raw, rawlen, &qoa);
mwav->ch = qoa.channels; mwav->ch = qoa.channels;
mwav->samplerate = qoa.samplerate; mwav->samplerate = qoa.samplerate;
mwav->frames = qoa.samples; mwav->frames = qoa.samples/mwav->ch;
mwav->data = malloc(sizeof(soundbyte) * mwav->frames * mwav->ch); mwav->data = malloc(sizeof(soundbyte) * mwav->frames * mwav->ch);
src_short_to_float_array(qoa_data, mwav->data, mwav->frames*mwav->ch); short_to_float_array(qoa_data, mwav->data, mwav->frames,mwav->ch);
free(qoa_data); free(qoa_data);
#else #else
YughWarn("Could not load %s because Primum was built without QOA support.", wav); YughWarn("Could not load %s because Primum was built without QOA support.", wav);
@ -222,38 +208,47 @@ struct wav *make_sound(const char *wav) {
} }
free(raw); free(raw);
change_samplerate(mwav, SAMPLERATE);
change_channels(mwav, CHANNELS);
if (shlen(wavhash) == 0) sh_new_arena(wavhash);
shput(wavhash, wav, mwav);
return mwav; return mwav;
} }
void save_qoa(char *file) void pcm_format(pcm *pcm, int samplerate, int channels)
{ {
wav *wav = make_sound(file); change_samplerate(pcm, samplerate);
qoa_desc q; change_channels(pcm, channels);
q.channels = wav->ch;
q.samples = wav->frames;
q.samplerate = wav->samplerate;
unsigned int len;
void *raw = qoa_encode(wav->data, &q, &len);
file = str_replace_ext(file, ".qoa");
slurp_write(raw, file, len);
free(raw);
free_sound(wav);
} }
void free_sound(const char *wav) { void save_qoa(char *file, pcm *pcm)
struct wav *w = shget(wavhash, wav); {
if (w == NULL) return; qoa_desc q;
short *out = malloc(sizeof(short)*pcm->ch*pcm->frames);
float_to_short_array(pcm->data, out, pcm->frames, pcm->ch);
q.channels = pcm->ch;
q.samples = pcm->frames;
q.samplerate = pcm->samplerate;
int encoded = qoa_write(file, out, &q);
free(out);
}
free(w->data); void save_wav(char *file, pcm *pcm)
free(w); {
shdel(wavhash, wav); drwav wav;
drwav_data_format fmt = {0};
fmt.format = DR_WAVE_FORMAT_PCM;
fmt.channels = pcm->ch;
fmt.sampleRate = pcm->samplerate;
fmt.bitsPerSample = 32;
drwav_int32 *out = malloc(sizeof(*out)*pcm->ch*pcm->frames);
drwav_f32_to_s32(out, pcm->data, pcm->frames*pcm->ch);
drwav_init_file_write_sequential_pcm_frames(&wav, file, &fmt, pcm->frames, NULL);
drwav_write_pcm_frames(&wav, pcm->frames, out);
drwav_uninit(&wav);
free(out);
}
void pcm_free(pcm *pcm)
{
free(pcm->data);
free(pcm);
} }
void sound_fillbuf(struct sound *s, soundbyte *buf, int n) { void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
@ -265,11 +260,6 @@ void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
else else
end = 1; end = 1;
if (s->timescale != 1) {
src_callback_read(s->src, s->timescale, frames, buf);
return;
}
soundbyte *in = s->data->data; soundbyte *in = s->data->data;
for (int i = 0; i < frames; i++) { for (int i = 0; i < frames; i++) {
@ -287,26 +277,16 @@ void sound_fillbuf(struct sound *s, soundbyte *buf, int n) {
void free_source(struct sound *s) void free_source(struct sound *s)
{ {
src_delete(s->src);
free(s); free(s);
} }
static long src_cb(struct sound *s, float **data) struct dsp_node *dsp_source(pcm *pcm)
{
long needed = BUF_FRAMES/s->timescale;
*data = s->data->data+s->frame;
s->frame += needed;
return needed;
}
struct dsp_node *dsp_source(const char *path)
{ {
if (!pcm) return NULL;
struct sound *self = malloc(sizeof(*self)); struct sound *self = malloc(sizeof(*self));
self->frame = 0; self->frame = 0;
self->data = make_sound(path); self->data = pcm;
self->loop = false; self->loop = false;
self->src = src_callback_new(src_cb, SRC_SINC_MEDIUM_QUALITY, 2, NULL, self);
self->timescale = 1;
dsp_node *n = make_node(self, sound_fillbuf, free_source); dsp_node *n = make_node(self, sound_fillbuf, free_source);
return n; return n;
} }
@ -315,16 +295,6 @@ int sound_finished(const struct sound *s) {
return s->frame == s->data->frames; return s->frame == s->data->frames;
} }
struct mp3 make_music(const char *mp3) {
// drmp3 new;
// if (!drmp3_init_file(&new, mp3, NULL)) {
// YughError("Could not open mp3 file %s.", mp3);
// }
struct mp3 newmp3 = {};
return newmp3;
}
float short2db(short val) { float short2db(short val) {
return 20 * log10(abs(val) / SHRT_MAX); return 20 * log10(abs(val) / SHRT_MAX);
} }

View file

@ -2,7 +2,6 @@
#define SOUND_H #define SOUND_H
#include "script.h" #include "script.h"
#include "samplerate.h"
#include "pthread.h" #include "pthread.h"
typedef float soundbyte; typedef float soundbyte;
@ -13,39 +12,32 @@ struct dsp_node;
/* A bookmark into a wav, actually playing the sound */ /* A bookmark into a wav, actually playing the sound */
typedef struct sound { typedef struct sound {
unsigned int frame; /* Pointing to the current frame on the wav */ unsigned int frame; /* Pointing to the current frame on the wav */
struct wav *data; struct pcm *data;
int loop; int loop;
float timescale;
SRC_STATE *src;
} sound; } sound;
/* Represents a sound file source, fulled loaded*/ /* Represents a sound file source, fulled loaded*/
typedef struct wav { typedef struct pcm {
unsigned int ch; unsigned int ch;
unsigned int samplerate; unsigned int samplerate;
unsigned long long frames; unsigned long long frames;
soundbyte *data; soundbyte *data;
} wav; } pcm;
/* Represents a sound file stream */
typedef struct mp3 {
} mp3;
void sound_init(); void sound_init();
void audio_open(const char *device); void audio_open(const char *device);
void audio_close(); void audio_close();
struct wav *make_sound(const char *wav); struct pcm *make_pcm(const char *file);
void free_sound(const char *wav); void pcm_free(pcm *pcm);
void wav_norm_gain(struct wav *w, double lv); void pcm_norm_gain(struct pcm *w, double lv);
struct dsp_node *dsp_source(const char *path); void pcm_format(pcm *pcm, int samplerate, int channels);
struct dsp_node *dsp_source(pcm *pcm);
struct dsp_node *dsp_mod(const char *path); struct dsp_node *dsp_mod(const char *path);
int sound_finished(const struct sound *s); int sound_finished(const struct sound *s);
struct mp3 make_mp3(const char *mp3);
float short2db(short val); float short2db(short val);
short db2short(float db); short db2short(float db);
short short_gain(short val, float db); short short_gain(short val, float db);
@ -56,6 +48,7 @@ float db2float(float db);
float pct2db(float pct); float pct2db(float pct);
float pct2mult(float pct); float pct2mult(float pct);
void save_qoa(char *file); void save_qoa(char *file, pcm *pcm);
void save_wav(char *file, pcm *pcm);
#endif #endif

View file

@ -1,189 +0,0 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/
/*
** API documentation is available here:
** http://libsndfile.github.io/libsamplerate/api.html
*/
#ifndef SAMPLERATE_H
#define SAMPLERATE_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
/* Opaque data type SRC_STATE. */
typedef struct SRC_STATE_tag SRC_STATE ;
/* SRC_DATA is used to pass data to src_simple() and src_process(). */
typedef struct
{ const float *data_in ;
float *data_out ;
long input_frames, output_frames ;
long input_frames_used, output_frames_gen ;
int end_of_input ;
double src_ratio ;
} SRC_DATA ;
/*
** User supplied callback function type for use with src_callback_new()
** and src_callback_read(). First parameter is the same pointer that was
** passed into src_callback_new(). Second parameter is pointer to a
** pointer. The user supplied callback function must modify *data to
** point to the start of the user supplied float array. The user supplied
** function must return the number of frames that **data points to.
*/
typedef long (*src_callback_t) (void *cb_data, float **data) ;
/*
** Standard initialisation function : return an anonymous pointer to the
** internal state of the converter. Choose a converter from the enums below.
** Error returned in *error.
*/
SRC_STATE* src_new (int converter_type, int channels, int *error) ;
/*
** Clone a handle : return an anonymous pointer to a new converter
** containing the same internal state as orig. Error returned in *error.
*/
SRC_STATE* src_clone (SRC_STATE* orig, int *error) ;
/*
** Initilisation for callback based API : return an anonymous pointer to the
** internal state of the converter. Choose a converter from the enums below.
** The cb_data pointer can point to any data or be set to NULL. Whatever the
** value, when processing, user supplied function "func" gets called with
** cb_data as first parameter.
*/
SRC_STATE* src_callback_new (src_callback_t func, int converter_type, int channels,
int *error, void* cb_data) ;
/*
** Cleanup all internal allocations.
** Always returns NULL.
*/
SRC_STATE* src_delete (SRC_STATE *state) ;
/*
** Standard processing function.
** Returns non zero on error.
*/
int src_process (SRC_STATE *state, SRC_DATA *data) ;
/*
** Callback based processing function. Read up to frames worth of data from
** the converter int *data and return frames read or -1 on error.
*/
long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ;
/*
** Simple interface for performing a single conversion from input buffer to
** output buffer at a fixed conversion ratio.
** Simple interface does not require initialisation as it can only operate on
** a single buffer worth of audio.
*/
int src_simple (SRC_DATA *data, int converter_type, int channels) ;
/*
** This library contains a number of different sample rate converters,
** numbered 0 through N.
**
** Return a string giving either a name or a more full description of each
** sample rate converter or NULL if no sample rate converter exists for
** the given value. The converters are sequentially numbered from 0 to N.
*/
const char *src_get_name (int converter_type) ;
const char *src_get_description (int converter_type) ;
const char *src_get_version (void) ;
/*
** Set a new SRC ratio. This allows step responses
** in the conversion ratio.
** Returns non zero on error.
*/
int src_set_ratio (SRC_STATE *state, double new_ratio) ;
/*
** Get the current channel count.
** Returns negative on error, positive channel count otherwise
*/
int src_get_channels (SRC_STATE *state) ;
/*
** Reset the internal SRC state.
** Does not modify the quality settings.
** Does not free any memory allocations.
** Returns non zero on error.
*/
int src_reset (SRC_STATE *state) ;
/*
** Return TRUE if ratio is a valid conversion ratio, FALSE
** otherwise.
*/
int src_is_valid_ratio (double ratio) ;
/*
** Return an error number.
*/
int src_error (SRC_STATE *state) ;
/*
** Convert the error number into a string.
*/
const char* src_strerror (int error) ;
/*
** The following enums can be used to set the interpolator type
** using the function src_set_converter().
*/
enum
{
SRC_SINC_BEST_QUALITY = 0,
SRC_SINC_MEDIUM_QUALITY = 1,
SRC_SINC_FASTEST = 2,
SRC_ZERO_ORDER_HOLD = 3,
SRC_LINEAR = 4,
} ;
/*
** Extra helper functions for converting from short to float and
** back again.
*/
void src_short_to_float_array (const short *in, float *out, int len) ;
void src_float_to_short_array (const float *in, short *out, int len) ;
void src_int_to_float_array (const int *in, float *out, int len) ;
void src_float_to_int_array (const float *in, int *out, int len) ;
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* SAMPLERATE_H */

View file

@ -1,218 +0,0 @@
/*
** Copyright (c) 2002-2021, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/
#ifndef COMMON_H_INCLUDED
#define COMMON_H_INCLUDED
#include <stdint.h>
#ifdef HAVE_STDBOOL_H
#include <stdbool.h>
#endif
#if HAVE_IMMINTRIN_H
#include <immintrin.h>
#endif
/* Uncomment to enable specific converters */
//#define ENABLE_SINC_BEST_CONVERTER
//#define ENABLE_SINC_FAST_CONVERTER
//#define ENABLE_SINC_MEDIUM_CONVERTER
#include <math.h>
#ifdef HAVE_VISIBILITY
#define LIBSAMPLERATE_DLL_PRIVATE __attribute__ ((visibility ("hidden")))
#elif defined (__APPLE__)
#define LIBSAMPLERATE_DLL_PRIVATE __private_extern__
#else
#define LIBSAMPLERATE_DLL_PRIVATE
#endif
#define SRC_MAX_RATIO 256
#define SRC_MAX_RATIO_STR "256"
#define SRC_MIN_RATIO_DIFF (1e-20)
#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif
#define ARRAY_LEN(x) ((int) (sizeof (x) / sizeof ((x) [0])))
#define OFFSETOF(type,member) ((int) (&((type*) 0)->member))
#define MAKE_MAGIC(a,b,c,d,e,f) ((a) + ((b) << 4) + ((c) << 8) + ((d) << 12) + ((e) << 16) + ((f) << 20))
/*
** Inspiration : http://sourcefrog.net/weblog/software/languages/C/unused.html
*/
#ifdef UNUSED
#elif defined (__GNUC__)
# define UNUSED(x) UNUSED_ ## x __attribute__ ((unused))
#elif defined (__LCLINT__)
# define UNUSED(x) /*@unused@*/ x
#else
# define UNUSED(x) x
#endif
#ifdef __GNUC__
# define WARN_UNUSED __attribute__ ((warn_unused_result))
#else
# define WARN_UNUSED
#endif
#include "samplerate.h"
enum
{ SRC_FALSE = 0,
SRC_TRUE = 1,
} ;
enum SRC_MODE
{
SRC_MODE_PROCESS = 0,
SRC_MODE_CALLBACK = 1
} ;
typedef enum SRC_ERROR
{
SRC_ERR_NO_ERROR = 0,
SRC_ERR_MALLOC_FAILED,
SRC_ERR_BAD_STATE,
SRC_ERR_BAD_DATA,
SRC_ERR_BAD_DATA_PTR,
SRC_ERR_NO_PRIVATE,
SRC_ERR_BAD_SRC_RATIO,
SRC_ERR_BAD_PROC_PTR,
SRC_ERR_SHIFT_BITS,
SRC_ERR_FILTER_LEN,
SRC_ERR_BAD_CONVERTER,
SRC_ERR_BAD_CHANNEL_COUNT,
SRC_ERR_SINC_BAD_BUFFER_LEN,
SRC_ERR_SIZE_INCOMPATIBILITY,
SRC_ERR_BAD_PRIV_PTR,
SRC_ERR_BAD_SINC_STATE,
SRC_ERR_DATA_OVERLAP,
SRC_ERR_BAD_CALLBACK,
SRC_ERR_BAD_MODE,
SRC_ERR_NULL_CALLBACK,
SRC_ERR_NO_VARIABLE_RATIO,
SRC_ERR_SINC_PREPARE_DATA_BAD_LEN,
SRC_ERR_BAD_INTERNAL_STATE,
/* This must be the last error number. */
SRC_ERR_MAX_ERROR
} SRC_ERROR ;
typedef struct SRC_STATE_VT_tag
{
/* Varispeed process function. */
SRC_ERROR (*vari_process) (SRC_STATE *state, SRC_DATA *data) ;
/* Constant speed process function. */
SRC_ERROR (*const_process) (SRC_STATE *state, SRC_DATA *data) ;
/* State reset. */
void (*reset) (SRC_STATE *state) ;
/* State clone. */
SRC_STATE *(*copy) (SRC_STATE *state) ;
/* State close. */
void (*close) (SRC_STATE *state) ;
} SRC_STATE_VT ;
struct SRC_STATE_tag
{
SRC_STATE_VT *vt ;
double last_ratio, last_position ;
SRC_ERROR error ;
int channels ;
/* SRC_MODE_PROCESS or SRC_MODE_CALLBACK */
enum SRC_MODE mode ;
/* Data specific to SRC_MODE_CALLBACK. */
src_callback_t callback_func ;
void *user_callback_data ;
long saved_frames ;
const float *saved_data ;
/* Pointer to data to converter specific data. */
void *private_data ;
} ;
/* In src_sinc.c */
const char* sinc_get_name (int src_enum) ;
const char* sinc_get_description (int src_enum) ;
SRC_STATE *sinc_state_new (int converter_type, int channels, SRC_ERROR *error) ;
/* In src_linear.c */
const char* linear_get_name (int src_enum) ;
const char* linear_get_description (int src_enum) ;
SRC_STATE *linear_state_new (int channels, SRC_ERROR *error) ;
/* In src_zoh.c */
const char* zoh_get_name (int src_enum) ;
const char* zoh_get_description (int src_enum) ;
SRC_STATE *zoh_state_new (int channels, SRC_ERROR *error) ;
/*----------------------------------------------------------
** SIMD optimized math functions.
*/
static inline int psf_lrintf (float x)
{
#ifdef HAVE_IMMINTRIN_H
return _mm_cvtss_si32 (_mm_load_ss (&x)) ;
#else
return lrintf (x) ;
#endif
} /* psf_lrintf */
static inline int psf_lrint (double x)
{
#ifdef HAVE_IMMINTRIN_H
return _mm_cvtsd_si32 (_mm_load_sd (&x)) ;
#else
return lrint (x) ;
#endif
} /* psf_lrint */
/*----------------------------------------------------------
** Common static inline functions.
*/
static inline double
fmod_one (double x)
{ double res ;
res = x - psf_lrint (x) ;
if (res < 0.0)
return res + 1.0 ;
return res ;
} /* fmod_one */
static inline int
is_bad_src_ratio (double ratio)
{ return (ratio < (1.0 / SRC_MAX_RATIO) || ratio > (1.0 * SRC_MAX_RATIO)) ;
} /* is_bad_src_ratio */
#endif /* COMMON_H_INCLUDED */

View file

@ -1,172 +0,0 @@
/* src/config.h. Generated from config.h.in by configure. */
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* Host processor clips on negative float to int conversion. */
#define CPU_CLIPS_NEGATIVE 0
/* Host processor clips on positive float to int conversion. */
#define CPU_CLIPS_POSITIVE 0
/* Host processor is big endian. */
#define CPU_IS_BIG_ENDIAN 0
/* Host processor is little endian. */
#define CPU_IS_LITTLE_ENDIAN 1
/* Enable sinc best converter. */
#define ENABLE_SINC_BEST_CONVERTER 0
/* Enable sinc fast converter. */
#define ENABLE_SINC_FAST_CONVERTER 0
/* Enable sinc balanced converter. */
#define ENABLE_SINC_MEDIUM_CONVERTER 0
/* Define to 1 if you have the `alarm' function. */
#define HAVE_ALARM 1
/* Set to 1 if you have alsa */
#define HAVE_ALSA 1
/* Define to 1 if you have the `calloc' function. */
#define HAVE_CALLOC 1
/* Define to 1 if you have the `ceil' function. */
/* #undef HAVE_CEIL */
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Set to 1 if you have fftw3 */
/* #undef HAVE_FFTW3 */
/* Define to 1 if you have the `floor' function. */
/* #undef HAVE_FLOOR */
/* Define to 1 if you have the `fmod' function. */
/* #undef HAVE_FMOD */
/* Define to 1 if you have the `free' function. */
#define HAVE_FREE 1
/* Define to 1 if you have the <immintrin.h> header file. */
/* #undef HAVE_IMMINTRIN_H */
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `lrint' function. */
/* #undef HAVE_LRINT */
/* Define to 1 if you have the `lrintf' function. */
/* #undef HAVE_LRINTF */
/* Define to 1 if you have the `malloc' function. */
#define HAVE_MALLOC 1
/* Define to 1 if you have the `memcpy' function. */
#define HAVE_MEMCPY 1
/* Define to 1 if you have the `memmove' function. */
#define HAVE_MEMMOVE 1
/* Define if you have signal SIGALRM. */
#define HAVE_SIGALRM 1
/* Define to 1 if you have the `signal' function. */
#define HAVE_SIGNAL 1
/* Set to 1 if you have libsndfile */
/* #undef HAVE_SNDFILE */
/* Define to 1 if you have the <stdbool.h> header file. */
#define HAVE_STDBOOL_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/times.h> header file. */
#define HAVE_SYS_TIMES_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if the compiler supports simple visibility declarations. */
#define HAVE_VISIBILITY 1
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "libsamplerate"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "erikd@mega-nerd.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "libsamplerate"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libsamplerate 0.2.2"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libsamplerate"
/* Define to the home page for this package. */
#define PACKAGE_URL "https://github.com/libsndfile/libsamplerate/"
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.2.2"
/* The size of `double', as computed by sizeof. */
#define SIZEOF_DOUBLE 8
/* The size of `float', as computed by sizeof. */
#define SIZEOF_FLOAT 4
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
/* The size of `long', as computed by sizeof. */
#define SIZEOF_LONG 8
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "0.2.2"
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* # undef WORDS_BIGENDIAN */
# endif
#endif

View file

@ -1,524 +0,0 @@
/*
** Copyright (c) 2002-2016, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "samplerate.h"
#include "common.h"
static SRC_STATE *psrc_set_converter (int converter_type, int channels, int *error) ;
SRC_STATE *
src_new (int converter_type, int channels, int *error)
{
return psrc_set_converter (converter_type, channels, error) ;
} /* src_new */
SRC_STATE*
src_clone (SRC_STATE* orig, int *error)
{
if (!orig)
{
if (error)
*error = SRC_ERR_BAD_STATE ;
return NULL ;
}
if (error)
*error = SRC_ERR_NO_ERROR ;
SRC_STATE *state = orig->vt->copy (orig) ;
if (!state)
if (error)
*error = SRC_ERR_MALLOC_FAILED ;
return state ;
}
SRC_STATE*
src_callback_new (src_callback_t func, int converter_type, int channels, int *error, void* cb_data)
{ SRC_STATE *state ;
if (func == NULL)
{ if (error)
*error = SRC_ERR_BAD_CALLBACK ;
return NULL ;
} ;
if (error != NULL)
*error = 0 ;
if ((state = src_new (converter_type, channels, error)) == NULL)
return NULL ;
src_reset (state) ;
state->mode = SRC_MODE_CALLBACK ;
state->callback_func = func ;
state->user_callback_data = cb_data ;
return state ;
} /* src_callback_new */
SRC_STATE *
src_delete (SRC_STATE *state)
{
if (state)
state->vt->close (state) ;
return NULL ;
} /* src_state */
int
src_process (SRC_STATE *state, SRC_DATA *data)
{
int error ;
if (state == NULL)
return SRC_ERR_BAD_STATE ;
if (state->mode != SRC_MODE_PROCESS)
return SRC_ERR_BAD_MODE ;
/* Check for valid SRC_DATA first. */
if (data == NULL)
return SRC_ERR_BAD_DATA ;
/* And that data_in and data_out are valid. */
if ((data->data_in == NULL && data->input_frames > 0)
|| (data->data_out == NULL && data->output_frames > 0))
return SRC_ERR_BAD_DATA_PTR ;
/* Check src_ratio is in range. */
if (is_bad_src_ratio (data->src_ratio))
return SRC_ERR_BAD_SRC_RATIO ;
if (data->input_frames < 0)
data->input_frames = 0 ;
if (data->output_frames < 0)
data->output_frames = 0 ;
if (data->data_in < data->data_out)
{ if (data->data_in + data->input_frames * state->channels > data->data_out)
{ /*-printf ("\n\ndata_in: %p data_out: %p\n",
(void*) (data->data_in + data->input_frames * psrc->channels), (void*) data->data_out) ;-*/
return SRC_ERR_DATA_OVERLAP ;
} ;
}
else if (data->data_out + data->output_frames * state->channels > data->data_in)
{ /*-printf ("\n\ndata_in : %p ouput frames: %ld data_out: %p\n", (void*) data->data_in, data->output_frames, (void*) data->data_out) ;
printf ("data_out: %p (%p) data_in: %p\n", (void*) data->data_out,
(void*) (data->data_out + data->input_frames * psrc->channels), (void*) data->data_in) ;-*/
return SRC_ERR_DATA_OVERLAP ;
} ;
/* Set the input and output counts to zero. */
data->input_frames_used = 0 ;
data->output_frames_gen = 0 ;
/* Special case for when last_ratio has not been set. */
if (state->last_ratio < (1.0 / SRC_MAX_RATIO))
state->last_ratio = data->src_ratio ;
/* Now process. */
if (fabs (state->last_ratio - data->src_ratio) < 1e-15)
error = state->vt->const_process (state, data) ;
else
error = state->vt->vari_process (state, data) ;
return error ;
} /* src_process */
long
src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data)
{
SRC_DATA src_data ;
long output_frames_gen ;
int error = 0 ;
if (state == NULL)
return 0 ;
if (frames <= 0)
return 0 ;
if (state->mode != SRC_MODE_CALLBACK)
{ state->error = SRC_ERR_BAD_MODE ;
return 0 ;
} ;
if (state->callback_func == NULL)
{ state->error = SRC_ERR_NULL_CALLBACK ;
return 0 ;
} ;
memset (&src_data, 0, sizeof (src_data)) ;
/* Check src_ratio is in range. */
if (is_bad_src_ratio (src_ratio))
{ state->error = SRC_ERR_BAD_SRC_RATIO ;
return 0 ;
} ;
/* Switch modes temporarily. */
src_data.src_ratio = src_ratio ;
src_data.data_out = data ;
src_data.output_frames = frames ;
src_data.data_in = state->saved_data ;
src_data.input_frames = state->saved_frames ;
output_frames_gen = 0 ;
while (output_frames_gen < frames)
{ /* Use a dummy array for the case where the callback function
** returns without setting the ptr.
*/
float dummy [1] ;
if (src_data.input_frames == 0)
{ float *ptr = dummy ;
src_data.input_frames = state->callback_func (state->user_callback_data, &ptr) ;
src_data.data_in = ptr ;
if (src_data.input_frames == 0)
src_data.end_of_input = 1 ;
} ;
/*
** Now call process function. However, we need to set the mode
** to SRC_MODE_PROCESS first and when we return set it back to
** SRC_MODE_CALLBACK.
*/
state->mode = SRC_MODE_PROCESS ;
error = src_process (state, &src_data) ;
state->mode = SRC_MODE_CALLBACK ;
if (error != 0)
break ;
src_data.data_in += src_data.input_frames_used * state->channels ;
src_data.input_frames -= src_data.input_frames_used ;
src_data.data_out += src_data.output_frames_gen * state->channels ;
src_data.output_frames -= src_data.output_frames_gen ;
output_frames_gen += src_data.output_frames_gen ;
if (src_data.end_of_input == SRC_TRUE && src_data.output_frames_gen == 0)
break ;
} ;
state->saved_data = src_data.data_in ;
state->saved_frames = src_data.input_frames ;
if (error != 0)
{ state->error = (SRC_ERROR) error ;
return 0 ;
} ;
return output_frames_gen ;
} /* src_callback_read */
/*==========================================================================
*/
int
src_set_ratio (SRC_STATE *state, double new_ratio)
{
if (state == NULL)
return SRC_ERR_BAD_STATE ;
if (is_bad_src_ratio (new_ratio))
return SRC_ERR_BAD_SRC_RATIO ;
state->last_ratio = new_ratio ;
return SRC_ERR_NO_ERROR ;
} /* src_set_ratio */
int
src_get_channels (SRC_STATE *state)
{
if (state == NULL)
return -SRC_ERR_BAD_STATE ;
return state->channels ;
} /* src_get_channels */
int
src_reset (SRC_STATE *state)
{
if (state == NULL)
return SRC_ERR_BAD_STATE ;
state->vt->reset (state) ;
state->last_position = 0.0 ;
state->last_ratio = 0.0 ;
state->saved_data = NULL ;
state->saved_frames = 0 ;
state->error = SRC_ERR_NO_ERROR ;
return SRC_ERR_NO_ERROR ;
} /* src_reset */
/*==============================================================================
** Control functions.
*/
const char *
src_get_name (int converter_type)
{ const char *desc ;
if ((desc = sinc_get_name (converter_type)) != NULL)
return desc ;
if ((desc = zoh_get_name (converter_type)) != NULL)
return desc ;
if ((desc = linear_get_name (converter_type)) != NULL)
return desc ;
return NULL ;
} /* src_get_name */
const char *
src_get_description (int converter_type)
{ const char *desc ;
if ((desc = sinc_get_description (converter_type)) != NULL)
return desc ;
if ((desc = zoh_get_description (converter_type)) != NULL)
return desc ;
if ((desc = linear_get_description (converter_type)) != NULL)
return desc ;
return NULL ;
} /* src_get_description */
const char *
src_get_version (void)
{ return "VERSION";//PACKAGE "-" VERSION " (c) 2002-2008 Erik de Castro Lopo" ;
} /* src_get_version */
int
src_is_valid_ratio (double ratio)
{
if (is_bad_src_ratio (ratio))
return SRC_FALSE ;
return SRC_TRUE ;
} /* src_is_valid_ratio */
/*==============================================================================
** Error reporting functions.
*/
int
src_error (SRC_STATE *state)
{ if (state)
return state->error ;
return SRC_ERR_NO_ERROR ;
} /* src_error */
const char*
src_strerror (int error)
{
switch (error)
{ case SRC_ERR_NO_ERROR :
return "No error." ;
case SRC_ERR_MALLOC_FAILED :
return "Malloc failed." ;
case SRC_ERR_BAD_STATE :
return "SRC_STATE pointer is NULL." ;
case SRC_ERR_BAD_DATA :
return "SRC_DATA pointer is NULL." ;
case SRC_ERR_BAD_DATA_PTR :
return "SRC_DATA->data_out or SRC_DATA->data_in is NULL." ;
case SRC_ERR_NO_PRIVATE :
return "Internal error. No private data." ;
case SRC_ERR_BAD_SRC_RATIO :
return "SRC ratio outside [1/" SRC_MAX_RATIO_STR ", " SRC_MAX_RATIO_STR "] range." ;
case SRC_ERR_BAD_SINC_STATE :
return "src_process() called without reset after end_of_input." ;
case SRC_ERR_BAD_PROC_PTR :
return "Internal error. No process pointer." ;
case SRC_ERR_SHIFT_BITS :
return "Internal error. SHIFT_BITS too large." ;
case SRC_ERR_FILTER_LEN :
return "Internal error. Filter length too large." ;
case SRC_ERR_BAD_CONVERTER :
return "Bad converter number." ;
case SRC_ERR_BAD_CHANNEL_COUNT :
return "Channel count must be >= 1." ;
case SRC_ERR_SINC_BAD_BUFFER_LEN :
return "Internal error. Bad buffer length. Please report this." ;
case SRC_ERR_SIZE_INCOMPATIBILITY :
return "Internal error. Input data / internal buffer size difference. Please report this." ;
case SRC_ERR_BAD_PRIV_PTR :
return "Internal error. Private pointer is NULL. Please report this." ;
case SRC_ERR_DATA_OVERLAP :
return "Input and output data arrays overlap." ;
case SRC_ERR_BAD_CALLBACK :
return "Supplied callback function pointer is NULL." ;
case SRC_ERR_BAD_MODE :
return "Calling mode differs from initialisation mode (ie process v callback)." ;
case SRC_ERR_NULL_CALLBACK :
return "Callback function pointer is NULL in src_callback_read ()." ;
case SRC_ERR_NO_VARIABLE_RATIO :
return "This converter only allows constant conversion ratios." ;
case SRC_ERR_SINC_PREPARE_DATA_BAD_LEN :
return "Internal error : Bad length in prepare_data ()." ;
case SRC_ERR_BAD_INTERNAL_STATE :
return "Error : Someone is trampling on my internal state." ;
case SRC_ERR_MAX_ERROR :
return "Placeholder. No error defined for this error number." ;
default : break ;
}
return NULL ;
} /* src_strerror */
/*==============================================================================
** Simple interface for performing a single conversion from input buffer to
** output buffer at a fixed conversion ratio.
*/
int
src_simple (SRC_DATA *src_data, int converter, int channels)
{ SRC_STATE *src_state ;
int error ;
if ((src_state = src_new (converter, channels, &error)) == NULL)
return error ;
src_data->end_of_input = 1 ; /* Only one buffer worth of input. */
error = src_process (src_state, src_data) ;
src_delete (src_state) ;
return error ;
} /* src_simple */
void
src_short_to_float_array (const short *in, float *out, int len)
{
for (int i = 0 ; i < len ; i++)
{ out [i] = (float) (in [i] / ((float)0x8000)) ;
} ;
return ;
} /* src_short_to_float_array */
void
src_float_to_short_array (const float *in, short *out, int len)
{
for (int i = 0 ; i < len ; i++)
{ float scaled_value ;
scaled_value = in [i] * 32768.f ;
if (scaled_value >= 32767.f)
out [i] = 32767 ;
else if (scaled_value <= -32768.f)
out [i] = -32768 ;
else
out [i] = (short) (psf_lrintf (scaled_value)) ;
}
} /* src_float_to_short_array */
void
src_int_to_float_array (const int *in, float *out, int len)
{
for (int i = 0 ; i < len ; i++)
{ out [i] = (float) (in [i] / (8.0 * 0x10000000)) ;
} ;
return ;
} /* src_int_to_float_array */
void
src_float_to_int_array (const float *in, int *out, int len)
{ double scaled_value ;
for (int i = 0 ; i < len ; i++)
{ scaled_value = in [i] * (8.0 * 0x10000000) ;
#if CPU_CLIPS_POSITIVE == 0
if (scaled_value >= (1.0 * 0x7FFFFFFF))
{ out [i] = 0x7fffffff ;
continue ;
} ;
#endif
#if CPU_CLIPS_NEGATIVE == 0
if (scaled_value <= (-8.0 * 0x10000000))
{ out [i] = -1 - 0x7fffffff ;
continue ;
} ;
#endif
out [i] = (int) psf_lrint (scaled_value) ;
} ;
} /* src_float_to_int_array */
/*==============================================================================
** Private functions.
*/
static SRC_STATE *
psrc_set_converter (int converter_type, int channels, int *error)
{
SRC_ERROR temp_error;
SRC_STATE *state ;
switch (converter_type)
{
#ifdef ENABLE_SINC_BEST_CONVERTER
case SRC_SINC_BEST_QUALITY :
state = sinc_state_new (converter_type, channels, &temp_error) ;
break ;
#endif
#ifdef ENABLE_SINC_MEDIUM_CONVERTER
case SRC_SINC_MEDIUM_QUALITY :
state = sinc_state_new (converter_type, channels, &temp_error) ;
break ;
#endif
#ifdef ENABLE_SINC_FAST_CONVERTER
case SRC_SINC_FASTEST :
state = sinc_state_new (converter_type, channels, &temp_error) ;
break ;
#endif
case SRC_ZERO_ORDER_HOLD :
state = zoh_state_new (channels, &temp_error) ;
break ;
case SRC_LINEAR :
state = linear_state_new (channels, &temp_error) ;
break ;
default :
temp_error = SRC_ERR_BAD_CONVERTER ;
state = NULL ;
break ;
}
if (error)
*error = (int) temp_error ;
return state ;
} /* psrc_set_converter */

View file

@ -1,297 +0,0 @@
/*
** Copyright (c) 2002-2021, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "common.h"
static SRC_ERROR linear_vari_process (SRC_STATE *state, SRC_DATA *data) ;
static void linear_reset (SRC_STATE *state) ;
static SRC_STATE *linear_copy (SRC_STATE *state) ;
static void linear_close (SRC_STATE *state) ;
/*========================================================================================
*/
#define LINEAR_MAGIC_MARKER MAKE_MAGIC ('l', 'i', 'n', 'e', 'a', 'r')
#define SRC_DEBUG 0
typedef struct
{ int linear_magic_marker ;
int dirty ;
long in_count, in_used ;
long out_count, out_gen ;
float *last_value ;
} LINEAR_DATA ;
static SRC_STATE_VT linear_state_vt =
{
linear_vari_process,
linear_vari_process,
linear_reset,
linear_copy,
linear_close
} ;
/*----------------------------------------------------------------------------------------
*/
static SRC_ERROR
linear_vari_process (SRC_STATE *state, SRC_DATA *data)
{ LINEAR_DATA *priv ;
double src_ratio, input_index, rem ;
int ch ;
if (data->input_frames <= 0)
return SRC_ERR_NO_ERROR ;
if (state->private_data == NULL)
return SRC_ERR_NO_PRIVATE ;
priv = (LINEAR_DATA*) state->private_data ;
if (!priv->dirty)
{ /* If we have just been reset, set the last_value data. */
for (ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = data->data_in [ch] ;
priv->dirty = 1 ;
} ;
priv->in_count = data->input_frames * state->channels ;
priv->out_count = data->output_frames * state->channels ;
priv->in_used = priv->out_gen = 0 ;
src_ratio = state->last_ratio ;
if (is_bad_src_ratio (src_ratio))
return SRC_ERR_BAD_INTERNAL_STATE ;
input_index = state->last_position ;
/* Calculate samples before first sample in input array. */
while (input_index < 1.0 && priv->out_gen < priv->out_count)
{
if (priv->in_used + state->channels * (1.0 + input_index) >= priv->in_count)
break ;
if (priv->out_count > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + priv->out_gen * (data->src_ratio - state->last_ratio) / priv->out_count ;
for (ch = 0 ; ch < state->channels ; ch++)
{ data->data_out [priv->out_gen] = (float) (priv->last_value [ch] + input_index *
((double) data->data_in [ch] - priv->last_value [ch])) ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
} ;
rem = fmod_one (input_index) ;
priv->in_used += state->channels * psf_lrint (input_index - rem) ;
input_index = rem ;
/* Main processing loop. */
while (priv->out_gen < priv->out_count && priv->in_used + state->channels * input_index < priv->in_count)
{
if (priv->out_count > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + priv->out_gen * (data->src_ratio - state->last_ratio) / priv->out_count ;
#if SRC_DEBUG
if (priv->in_used < state->channels && input_index < 1.0)
{ printf ("Whoops!!!! in_used : %ld channels : %d input_index : %f\n", priv->in_used, state->channels, input_index) ;
exit (1) ;
} ;
#endif
for (ch = 0 ; ch < state->channels ; ch++)
{ data->data_out [priv->out_gen] = (float) (data->data_in [priv->in_used - state->channels + ch] + input_index *
((double) data->data_in [priv->in_used + ch] - data->data_in [priv->in_used - state->channels + ch])) ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
rem = fmod_one (input_index) ;
priv->in_used += state->channels * psf_lrint (input_index - rem) ;
input_index = rem ;
} ;
if (priv->in_used > priv->in_count)
{ input_index += (priv->in_used - priv->in_count) / state->channels ;
priv->in_used = priv->in_count ;
} ;
state->last_position = input_index ;
if (priv->in_used > 0)
for (ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = data->data_in [priv->in_used - state->channels + ch] ;
/* Save current ratio rather then target ratio. */
state->last_ratio = src_ratio ;
data->input_frames_used = priv->in_used / state->channels ;
data->output_frames_gen = priv->out_gen / state->channels ;
return SRC_ERR_NO_ERROR ;
} /* linear_vari_process */
/*------------------------------------------------------------------------------
*/
LIBSAMPLERATE_DLL_PRIVATE const char*
linear_get_name (int src_enum)
{
if (src_enum == SRC_LINEAR)
return "Linear Interpolator" ;
return NULL ;
} /* linear_get_name */
LIBSAMPLERATE_DLL_PRIVATE const char*
linear_get_description (int src_enum)
{
if (src_enum == SRC_LINEAR)
return "Linear interpolator, very fast, poor quality." ;
return NULL ;
} /* linear_get_descrition */
static LINEAR_DATA *
linear_data_new (int channels)
{
assert (channels > 0) ;
LINEAR_DATA *priv = (LINEAR_DATA *) calloc (1, sizeof (LINEAR_DATA)) ;
if (priv)
{
priv->linear_magic_marker = LINEAR_MAGIC_MARKER ;
priv->last_value = (float *) calloc (channels, sizeof (float)) ;
if (!priv->last_value)
{
free (priv) ;
priv = NULL ;
}
}
return priv ;
}
LIBSAMPLERATE_DLL_PRIVATE SRC_STATE *
linear_state_new (int channels, SRC_ERROR *error)
{
assert (channels > 0) ;
assert (error != NULL) ;
SRC_STATE *state = (SRC_STATE *) calloc (1, sizeof (SRC_STATE)) ;
if (!state)
{
*error = SRC_ERR_MALLOC_FAILED ;
return NULL ;
}
state->channels = channels ;
state->mode = SRC_MODE_PROCESS ;
state->private_data = linear_data_new (state->channels) ;
if (!state->private_data)
{
free (state) ;
*error = SRC_ERR_MALLOC_FAILED ;
return NULL ;
}
state->vt = &linear_state_vt ;
linear_reset (state) ;
*error = SRC_ERR_NO_ERROR ;
return state ;
}
/*===================================================================================
*/
static void
linear_reset (SRC_STATE *state)
{ LINEAR_DATA *priv = NULL ;
priv = (LINEAR_DATA*) state->private_data ;
if (priv == NULL)
return ;
priv->dirty = 0 ;
memset (priv->last_value, 0, sizeof (priv->last_value [0]) * state->channels) ;
return ;
} /* linear_reset */
SRC_STATE *
linear_copy (SRC_STATE *state)
{
assert (state != NULL) ;
if (state->private_data == NULL)
return NULL ;
SRC_STATE *to = (SRC_STATE *) calloc (1, sizeof (SRC_STATE)) ;
if (!to)
return NULL ;
memcpy (to, state, sizeof (SRC_STATE)) ;
LINEAR_DATA* from_priv = (LINEAR_DATA*) state->private_data ;
LINEAR_DATA *to_priv = (LINEAR_DATA *) calloc (1, sizeof (LINEAR_DATA)) ;
if (!to_priv)
{
free (to) ;
return NULL ;
}
memcpy (to_priv, from_priv, sizeof (LINEAR_DATA)) ;
to_priv->last_value = (float *) malloc (sizeof (float) * state->channels) ;
if (!to_priv->last_value)
{
free (to) ;
free (to_priv) ;
return NULL ;
}
memcpy (to_priv->last_value, from_priv->last_value, sizeof (float) * state->channels) ;
to->private_data = to_priv ;
return to ;
} /* linear_copy */
static void
linear_close (SRC_STATE *state)
{
if (state)
{
LINEAR_DATA *linear = (LINEAR_DATA *) state->private_data ;
if (linear)
{
if (linear->last_value)
{
free (linear->last_value) ;
linear->last_value = NULL ;
}
free (linear) ;
linear = NULL ;
}
free (state) ;
state = NULL ;
}
} /* linear_close */

File diff suppressed because it is too large Load diff

View file

@ -1,286 +0,0 @@
/*
** Copyright (c) 2002-2021, Erik de Castro Lopo <erikd@mega-nerd.com>
** All rights reserved.
**
** This code is released under 2-clause BSD license. Please see the
** file at : https://github.com/libsndfile/libsamplerate/blob/master/COPYING
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "common.h"
static SRC_ERROR zoh_vari_process (SRC_STATE *state, SRC_DATA *data) ;
static void zoh_reset (SRC_STATE *state) ;
static SRC_STATE *zoh_copy (SRC_STATE *state) ;
static void zoh_close (SRC_STATE *state) ;
/*========================================================================================
*/
#define ZOH_MAGIC_MARKER MAKE_MAGIC ('s', 'r', 'c', 'z', 'o', 'h')
typedef struct
{ int zoh_magic_marker ;
int dirty ;
long in_count, in_used ;
long out_count, out_gen ;
float *last_value ;
} ZOH_DATA ;
static SRC_STATE_VT zoh_state_vt =
{
zoh_vari_process,
zoh_vari_process,
zoh_reset,
zoh_copy,
zoh_close
} ;
/*----------------------------------------------------------------------------------------
*/
static SRC_ERROR
zoh_vari_process (SRC_STATE *state, SRC_DATA *data)
{ ZOH_DATA *priv ;
double src_ratio, input_index, rem ;
int ch ;
if (data->input_frames <= 0)
return SRC_ERR_NO_ERROR ;
if (state->private_data == NULL)
return SRC_ERR_NO_PRIVATE ;
priv = (ZOH_DATA*) state->private_data ;
if (!priv->dirty)
{ /* If we have just been reset, set the last_value data. */
for (ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = data->data_in [ch] ;
priv->dirty = 1 ;
} ;
priv->in_count = data->input_frames * state->channels ;
priv->out_count = data->output_frames * state->channels ;
priv->in_used = priv->out_gen = 0 ;
src_ratio = state->last_ratio ;
if (is_bad_src_ratio (src_ratio))
return SRC_ERR_BAD_INTERNAL_STATE ;
input_index = state->last_position ;
/* Calculate samples before first sample in input array. */
while (input_index < 1.0 && priv->out_gen < priv->out_count)
{
if (priv->in_used + state->channels * input_index >= priv->in_count)
break ;
if (priv->out_count > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + priv->out_gen * (data->src_ratio - state->last_ratio) / priv->out_count ;
for (ch = 0 ; ch < state->channels ; ch++)
{ data->data_out [priv->out_gen] = priv->last_value [ch] ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
} ;
rem = fmod_one (input_index) ;
priv->in_used += state->channels * psf_lrint (input_index - rem) ;
input_index = rem ;
/* Main processing loop. */
while (priv->out_gen < priv->out_count && priv->in_used + state->channels * input_index <= priv->in_count)
{
if (priv->out_count > 0 && fabs (state->last_ratio - data->src_ratio) > SRC_MIN_RATIO_DIFF)
src_ratio = state->last_ratio + priv->out_gen * (data->src_ratio - state->last_ratio) / priv->out_count ;
for (ch = 0 ; ch < state->channels ; ch++)
{ data->data_out [priv->out_gen] = data->data_in [priv->in_used - state->channels + ch] ;
priv->out_gen ++ ;
} ;
/* Figure out the next index. */
input_index += 1.0 / src_ratio ;
rem = fmod_one (input_index) ;
priv->in_used += state->channels * psf_lrint (input_index - rem) ;
input_index = rem ;
} ;
if (priv->in_used > priv->in_count)
{ input_index += (priv->in_used - priv->in_count) / state->channels ;
priv->in_used = priv->in_count ;
} ;
state->last_position = input_index ;
if (priv->in_used > 0)
for (ch = 0 ; ch < state->channels ; ch++)
priv->last_value [ch] = data->data_in [priv->in_used - state->channels + ch] ;
/* Save current ratio rather then target ratio. */
state->last_ratio = src_ratio ;
data->input_frames_used = priv->in_used / state->channels ;
data->output_frames_gen = priv->out_gen / state->channels ;
return SRC_ERR_NO_ERROR ;
} /* zoh_vari_process */
/*------------------------------------------------------------------------------
*/
LIBSAMPLERATE_DLL_PRIVATE const char*
zoh_get_name (int src_enum)
{
if (src_enum == SRC_ZERO_ORDER_HOLD)
return "ZOH Interpolator" ;
return NULL ;
} /* zoh_get_name */
LIBSAMPLERATE_DLL_PRIVATE const char*
zoh_get_description (int src_enum)
{
if (src_enum == SRC_ZERO_ORDER_HOLD)
return "Zero order hold interpolator, very fast, poor quality." ;
return NULL ;
} /* zoh_get_descrition */
static ZOH_DATA *
zoh_data_new (int channels)
{
assert (channels > 0) ;
ZOH_DATA *priv = (ZOH_DATA *) calloc (1, sizeof (ZOH_DATA)) ;
if (priv)
{
priv->zoh_magic_marker = ZOH_MAGIC_MARKER ;
priv->last_value = (float *) calloc (channels, sizeof (float)) ;
if (!priv->last_value)
{
free (priv) ;
priv = NULL ;
}
}
return priv ;
}
LIBSAMPLERATE_DLL_PRIVATE SRC_STATE *
zoh_state_new (int channels, SRC_ERROR *error)
{
assert (channels > 0) ;
assert (error != NULL) ;
SRC_STATE *state = (SRC_STATE *) calloc (1, sizeof (SRC_STATE)) ;
if (!state)
{
*error = SRC_ERR_MALLOC_FAILED ;
return NULL ;
}
state->channels = channels ;
state->mode = SRC_MODE_PROCESS ;
state->private_data = zoh_data_new (state->channels) ;
if (!state->private_data)
{
free (state) ;
*error = SRC_ERR_MALLOC_FAILED ;
return NULL ;
}
state->vt = &zoh_state_vt ;
zoh_reset (state) ;
*error = SRC_ERR_NO_ERROR ;
return state ;
}
/*===================================================================================
*/
static void
zoh_reset (SRC_STATE *state)
{ ZOH_DATA *priv ;
priv = (ZOH_DATA*) state->private_data ;
if (priv == NULL)
return ;
priv->dirty = 0 ;
memset (priv->last_value, 0, sizeof (float) * state->channels) ;
return ;
} /* zoh_reset */
static SRC_STATE *
zoh_copy (SRC_STATE *state)
{
assert (state != NULL) ;
if (state->private_data == NULL)
return NULL ;
SRC_STATE *to = (SRC_STATE *) calloc (1, sizeof (SRC_STATE)) ;
if (!to)
return NULL ;
memcpy (to, state, sizeof (SRC_STATE)) ;
ZOH_DATA* from_priv = (ZOH_DATA*) state->private_data ;
ZOH_DATA *to_priv = (ZOH_DATA *) calloc (1, sizeof (ZOH_DATA)) ;
if (!to_priv)
{
free (to) ;
return NULL ;
}
memcpy (to_priv, from_priv, sizeof (ZOH_DATA)) ;
to_priv->last_value = (float *) malloc (sizeof (float) * state->channels) ;
if (!to_priv->last_value)
{
free (to) ;
free (to_priv) ;
return NULL ;
}
memcpy (to_priv->last_value, from_priv->last_value, sizeof (float) * state->channels) ;
to->private_data = to_priv ;
return to ;
} /* zoh_copy */
static void
zoh_close (SRC_STATE *state)
{
if (state)
{
ZOH_DATA *zoh = (ZOH_DATA *) state->private_data ;
if (zoh)
{
if (zoh->last_value)
{
free (zoh->last_value) ;
zoh->last_value = NULL ;
}
free (zoh) ;
zoh = NULL ;
}
free (state) ;
state = NULL ;
}
} /* zoh_close */