From 8b367d4a50f0a71178403b72bb65645f1478277f Mon Sep 17 00:00:00 2001 From: mescalinum Date: Fri, 20 Aug 2010 17:41:32 +0000 Subject: use libsamplerate and high level methods for building frames svn path=/trunk/externals/ffext/; revision=13873 --- tms5220~/tms5220~.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 167 insertions(+), 14 deletions(-) (limited to 'tms5220~/tms5220~.c') diff --git a/tms5220~/tms5220~.c b/tms5220~/tms5220~.c index ae9e630..5ef85a5 100644 --- a/tms5220~/tms5220~.c +++ b/tms5220~/tms5220~.c @@ -4,6 +4,18 @@ #include "tms5220/tms5220.c" +#define USE_LIBSAMPLERATE +//#define DEBUG_TMS5220_TILDE + +// rate at which tms5220 emulator produces samples (according to datasheet) +#define TMS5220_SAMPLE_RATE 8000 + +#ifdef USE_LIBSAMPLERATE +#define SRC_TYPE SRC_LINEAR //fastest +//#define SRC_TYPE SRC_SINC_BEST_QUALITY +#include +#endif + #include "m_pd.h" static t_class *tms5220_tilde_class; @@ -11,6 +23,16 @@ static t_class *tms5220_tilde_class; typedef struct _tms5220_tilde { t_object x_obj; + // sample rate + t_float sr; + t_float resample_factor; + +#ifdef USE_LIBSAMPLERATE + SRC_STATE* src; +#else +#error Missing alternative implementation of sample rate converter. Please use libsamplerate. +#endif + // status outlets t_int status; t_outlet *x_status; @@ -22,6 +44,8 @@ typedef struct _tms5220_tilde { t_float dummy; } t_tms5220_tilde; + + void tms5220_tilde_reset(t_tms5220_tilde *x) { tms5220_reset(); } @@ -31,9 +55,6 @@ void *tms5220_tilde_new(t_symbol *s, int argc, t_atom *argv) { x->status = x->ready = x->interrupt = 0; - //inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); - //floatinlet_new(&x->x_obj, &x->f_x); - outlet_new(&x->x_obj, &s_signal); x->x_status = outlet_new(&x->x_obj, &s_float); x->x_ready = outlet_new(&x->x_obj, &s_float); @@ -41,16 +62,105 @@ void *tms5220_tilde_new(t_symbol *s, int argc, t_atom *argv) { tms5220_tilde_reset(x); +#ifdef USE_LIBSAMPLERATE + if(x->src) src_delete(x->src); + + int err = -1; + x->src = src_new(SRC_TYPE, 1, &err); + + if(x->src == NULL) { + error("Failed libsamplerate initialization."); + post("libsamplerate returned error code %d (%s).", err, src_strerror(err)); + } else { +#ifdef DEBUG_TMS5220_TILDE + post("tms5220~: DEBUG: Initialized sample rate converter '%s'", src_get_name(SRC_TYPE)); +#endif + } +#endif + return (void *)x; } void tms5220_tilde_free(t_tms5220_tilde *x) { +#ifdef USE_LIBSAMPLERATE + // deallocate sample rate converter object + if(x->src) { + src_delete(x->src); +#ifdef DEBUG_TMS5220_TILDE + post("tms5220~: DEBUG: Destroyed sample rate converter '%s'", src_get_name(SRC_TYPE)); +#endif + } +#endif } void tms5220_tilde_write(t_tms5220_tilde *x, t_floatarg data) { tms5220_data_write((int)data); } +#define CHECK_ARGS(argcvar, num, errmsg) if(argcvar != num) { error(errmsg); return; } +#define CLIP_VAL(v, min, max) (((v) < (min)) ? (min) : (((v) > (max)) ? (max) : (v))) + +void tms5220_tilde_framefunc(t_tms5220_tilde *x, t_symbol *s, int argc, t_atom *argv) { + int energy, pitch, k[10], i; + + energy = argc > 0 ? argv[0].a_w.w_float : -1; + pitch = argc > 1 ? argv[1].a_w.w_float : -1; + for(i = 2; i < 12; i++) k[i - 2] = argc > i ? argv[i].a_w.w_float : -1; + + energy = CLIP_VAL(energy, 1, 14); + pitch = CLIP_VAL(pitch, 0, 63); + k[0] = CLIP_VAL(k[0], 0, 31); + k[1] = CLIP_VAL(k[1], 0, 31); + k[2] = CLIP_VAL(k[2], 0, 15); + k[3] = CLIP_VAL(k[3], 0, 15); + k[4] = CLIP_VAL(k[4], 0, 15); + k[5] = CLIP_VAL(k[5], 0, 15); + k[6] = CLIP_VAL(k[6], 0, 15); + k[7] = CLIP_VAL(k[7], 0, 7); + k[8] = CLIP_VAL(k[8], 0, 7); + k[9] = CLIP_VAL(k[9], 0, 7); + + unsigned char data[8]; + memset(&data[0], 0, 8); + + data[0] = 0x60; //speak external + + if(energy == 0 || energy == 15) { error("illegal value for energy"); return; } + + if(s == gensym("silent")) { + CHECK_ARGS(argc, 0, "usage: silent"); + data[1] = 0; + } else if(s == gensym("stop")) { + CHECK_ARGS(argc, 0, "usage: stop"); + data[1] = 0xf0; + } else if(s == gensym("repeat")) { + CHECK_ARGS(argc, 2, "usage: repeat "); + data[1] = ((energy << 4) | (1 << 3) | ((pitch & 56) >> 3)); + data[2] = (((pitch & 7) << 5)); + } else if(s == gensym("unvoiced")) { + CHECK_ARGS(argc, 6, "usage: repeat "); + data[1] = ((energy << 4) | 0 | 0); + data[2] = (k[0]); + data[3] = ((k[1] << 3) | ((k[2] & 14) >> 1)); + data[4] = (((k[2] & 1) << 7) | (k[3] << 3)); + } else if(s == gensym("voiced")) { + CHECK_ARGS(argc, 12, "usage: repeat "); + data[1] = ((energy << 4) | 0 | 0); + data[2] = (k[0]); + data[3] = ((k[1] << 3) | ((k[2] & 14) >> 1)); + data[4] = (((k[2] & 1) << 7) | (k[3] << 3) | ((k[4] & 14) >> 1)); + data[5] = (((k[4] & 1) << 7) | (k[5] << 3) | ((k[6] & 14) >> 1)); + data[6] = (((k[6] & 1) << 7) | (k[7] << 4) | (k[8] << 1) | ((k[9] & 4) >> 2)); + data[7] = (((k[9] & 3) << 6)); + } else { + error("unknown method called: %s", s->s_name); + return; + } + //tms5220_reset(); + for(i = 0; i < 8; i++) tms5220_data_write(data[i]); + //tms5220_data_write(0); +} + void tms5220_tilde_update_status(t_tms5220_tilde *x) { t_int new_status, new_ready, new_interrupt; @@ -80,24 +190,53 @@ t_int *tms5220_tilde_perform(t_int *w) { t_sample *out = (t_sample *)(w[3]); int n = (int)(w[4]); - //TODO: figure out proper resampling here: -#define RESAMPLE_FACTOR 8 - int rc = 0; // resample counter + int m = (int)(0.0 + n / x->resample_factor); - unsigned char *bytebuf = (unsigned char *)malloc(sizeof(unsigned char)*n/RESAMPLE_FACTOR); + unsigned char *bytebuf = (unsigned char *)malloc(sizeof(unsigned char) * m); + if(!bytebuf) {error("FATAL: cannot allocate signal buffer (byte)"); return w;} - if(!bytebuf) {error("FATAL: cannot allocate signal buffer"); return w;} + float *floatbuf = (float *)malloc(sizeof(float) * m); + if(!floatbuf) {error("FATAL: cannot allocate signal buffer (float)"); return w;} - tms5220_process(bytebuf, n/RESAMPLE_FACTOR); + tms5220_process(bytebuf, m); unsigned char *pb = bytebuf; + float *fb = floatbuf; + int m1 = m; + while (m1--) *fb++ = (0.5 + ((t_sample) *pb++)) / 127.5; + free(bytebuf); - while (n--) { - //FIXME: resampling without alias, please - *out++ = (0.5 + ((t_sample) *pb)) / 127.5; - if(!(rc = ((rc + 1) % RESAMPLE_FACTOR))) pb++; +#ifdef USE_LIBSAMPLERATE + if(x->src != NULL) { + SRC_DATA src_data; + src_data.data_in = floatbuf; + src_data.input_frames = m; + src_data.data_out = out; + src_data.output_frames = n; + src_data.src_ratio = x->resample_factor; + src_data.end_of_input = 0; + int err; + if(0 != (err = src_process(x->src, &src_data))) { + error("error during resample (libsamplerate)"); + post("error code: %d (%s)", err, src_strerror(err)); + } +#ifdef DEBUG_TMS5220_TILDE + // check residual: + if(src_data.input_frames_used < m) { + error("used only %d input frames, instead of %d", src_data.input_frames_used, m); + } + if(src_data.output_frames_gen < n) { + error("generated only %d output frames, instead of %d", src_data.output_frames_gen, n); + } +#endif + } else { + error("libsamplerate not initialized."); } +#else + // missing implementation of poor man's sample rate converter + // use libsamplerate for now +#endif - free(bytebuf); + free(floatbuf); tms5220_tilde_update_status(x); @@ -105,6 +244,14 @@ t_int *tms5220_tilde_perform(t_int *w) { } void tms5220_tilde_dsp(t_tms5220_tilde *x, t_signal **sp) { + // update sample rate & resample factor: + x->resample_factor = sp[0]->s_sr / TMS5220_SAMPLE_RATE; +#ifdef DEBUG_TMS5220_TILDE + if(sp[0]->s_sr != x->sr) + post("tms5220~: DEBUG: samplerate has changed to %f; new resample factor is %f.", sp[0]->s_sr, (float)x->resample_factor); +#endif + x->sr = sp[0]->s_sr; + dsp_add(tms5220_tilde_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); } @@ -121,6 +268,12 @@ void tms5220_tilde_setup(void) { //class_addfloat(tms5220_tilde_class, (t_method)tms5220_tilde_write); class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_write, gensym("write"), A_DEFFLOAT, 0); class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_reset, gensym("reset"), 0); + // frame building commands: + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_framefunc, gensym("silent"), A_GIMME, 0); + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_framefunc, gensym("stop"), A_GIMME, 0); + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_framefunc, gensym("repeat"), A_GIMME, 0); + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_framefunc, gensym("unvoiced"), A_GIMME, 0); + class_addmethod(tms5220_tilde_class, (t_method)tms5220_tilde_framefunc, gensym("voiced"), A_GIMME, 0); post("tms5220~: TSM5220 IC emulation"); post("tms5220~: external by Federico Ferri "); -- cgit v1.2.1