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~/Makefile | 3 +- tms5220~/tms5220~-help.pd | 260 +++++++++++++++++++++++++++++----------------- tms5220~/tms5220~.c | 181 +++++++++++++++++++++++++++++--- 3 files changed, 334 insertions(+), 110 deletions(-) diff --git a/tms5220~/Makefile b/tms5220~/Makefile index 4c72e4d..eb775d4 100644 --- a/tms5220~/Makefile +++ b/tms5220~/Makefile @@ -32,6 +32,7 @@ MANUAL = # automatically included EXTRA_DIST = +LIBS = -lsamplerate #------------------------------------------------------------------------------# @@ -58,7 +59,7 @@ INSTALL_DIR = $(INSTALL) -p -m 755 -d CFLAGS = -DPD -I$(PD_PATH)/src -Wall -W -g LDFLAGS = -LIBS = +#LIBS = ALLSOURCES := $(SOURCES) $(SOURCES_android) $(SOURCES_cygwin) $(SOURCES_macosx) \ $(SOURCES_iphoneos) $(SOURCES_linux) $(SOURCES_windows) diff --git a/tms5220~/tms5220~-help.pd b/tms5220~/tms5220~-help.pd index d1dcbd0..edf216a 100644 --- a/tms5220~/tms5220~-help.pd +++ b/tms5220~/tms5220~-help.pd @@ -1,109 +1,179 @@ -#N canvas 4 100 590 524 10; -#X obj 283 167 tms5220~; -#X obj 283 456 dac~; -#X obj 350 409 hsl 128 15 0 1 0 0 empty empty OUTPUT_LEVEL -2 -8 0 +#N canvas 221 103 826 512 10; +#X obj 53 93 tms5220~; +#X obj 53 382 dac~; +#X obj 120 335 hsl 128 15 0 1 0 0 empty empty OUTPUT_LEVEL -2 -8 0 10 -262144 -1 -1 12700 1; -#X obj 283 408 *~ 0; -#X msg 347 449 \; pd dsp 1; -#X text 377 199 <-- interrupt; -#X text 376 221 <-- ready; -#X text 376 244 <-- status bits; -#X msg 310 127 reset; +#X obj 53 334 *~ 0; +#X msg 117 375 \; pd dsp 1; +#X text 147 125 <-- interrupt; +#X text 146 147 <-- ready; +#X text 146 170 <-- status bits; +#X msg 80 62 reset; #X text 43 473 (C) Federico Ferri - 2010; -#X obj 47 316 * 16; -#X msg 47 79 0; -#X text 85 76 nop; -#X text 133 43 (check the TMS5220 datasheet for description of commands) -; -#X obj 47 357 |; -#X obj 139 221 vradio 8 1 0 16 empty empty empty 0 -8 0 10 -262144 --1 -1 4; -#X text 162 274 << address; -#X msg 47 99 1; -#X msg 47 119 2; -#X msg 47 139 3; -#X msg 47 159 4; -#X msg 47 179 5; -#X msg 47 199 6; -#X msg 47 219 7; -#X text 85 96 read byte; -#X text 85 116 nop; -#X text 85 136 read & branch; -#X text 85 156 load address; -#X text 85 176 speak; -#X text 85 196 speak external; -#X text 85 216 reset; -#X text 46 45 COMMANDS:; -#X obj 298 242 t a a a; -#X obj 298 339 & 16; -#X obj 317 300 & 32; -#X obj 337 261 & 64; -#X obj 328 196 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#X obj 68 168 t a a a; +#X obj 68 265 & 16; +#X obj 87 226 & 32; +#X obj 107 187 & 64; +#X obj 98 122 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; -#X obj 313 219 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 +#X obj 83 145 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 1; -#X obj 337 280 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 64 +#X obj 107 206 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 64 64; -#X obj 317 319 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 32 +#X obj 87 245 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 32 32; -#X obj 298 358 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +#X obj 68 284 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 1; -#X text 368 280 <-- talk_status; -#X text 348 318 <-- buffer_low; -#X text 327 359 <-- buffer_empty; -#N canvas 761 102 450 299 rand.test 0; -#X obj 50 37 inlet; -#X obj 52 162 random 16; -#X obj 51 125 until; -#X obj 62 270 outlet; -#X msg 138 105 96; -#X obj 54 188 | 64; -#X msg 54 101 9; -#X obj 57 68 t b b b; -#X msg 181 105 reset; -#X msg 47 221 write \$1; -#X connect 0 0 7 0; -#X connect 1 0 5 0; -#X connect 2 0 1 0; -#X connect 4 0 9 0; -#X connect 5 0 9 0; -#X connect 6 0 2 0; -#X connect 7 0 6 0; -#X connect 7 1 4 0; -#X connect 7 2 8 0; -#X connect 8 0 3 0; -#X connect 9 0 3 0; -#X restore 77 411 pd rand.test; -#X obj 77 385 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 +#X text 138 206 <-- talk_status; +#X text 118 244 <-- buffer_low; +#X text 97 285 <-- buffer_empty; +#X obj 53 35 r \$0.in; +#X obj 344 123 s \$0.in; +#X msg 344 36 silent; +#X msg 361 64 stop; +#X msg 334 273 repeat \$1 \$2; +#X obj 334 299 s \$0.in; +#X obj 382 223 hsl 64 15 0 63 0 0 empty empty PITCH -2 -8 0 10 -262144 +-1 -1 2600 1; +#X obj 337 194 hsl 64 15 1 14 0 0 empty empty ENERGY -2 -8 0 10 -262144 +-1 -1 4400 1; +#X obj 334 248 pack f f; +#X obj 491 379 s \$0.in; +#X obj 539 78 hsl 64 15 0 63 0 0 empty empty PITCH -2 -8 0 10 -262144 +-1 -1 1400 1; +#X obj 494 49 hsl 64 15 1 14 0 0 empty empty ENERGY -2 -8 0 10 -262144 +-1 -1 600 1; +#X obj 491 103 pack f f; +#X obj 431 74 metro 25; +#X obj 430 50 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +1; +#X obj 491 139 list append; +#X obj 566 139 hsl 64 15 0 31 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 567 159 hsl 64 15 0 31 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 1900 1; +#X obj 567 179 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 5100 1; +#X obj 568 199 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 1600 1; +#X obj 491 159 list append; +#X obj 491 179 list append; +#X obj 491 199 list append; +#X obj 568 219 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 3500 1; +#X obj 491 219 list append; +#X obj 568 239 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 1500 1; +#X obj 491 239 list append; +#X obj 568 259 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 2700 1; +#X obj 491 259 list append; +#X obj 568 279 hsl 64 15 0 7 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 491 279 list append; +#X obj 568 299 hsl 64 15 0 7 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 100 1; +#X obj 491 299 list append; +#X obj 568 319 hsl 64 15 0 7 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 600 1; +#X obj 491 319 list append; +#X obj 491 338 list prepend voiced; +#X obj 491 359 list trim; +#X obj 651 379 s \$0.in; +#X obj 699 78 hsl 64 15 0 63 0 0 empty empty PITCH -2 -8 0 10 -262144 +-1 -1 3300 1; +#X obj 654 49 hsl 64 15 1 14 0 0 empty empty ENERGY -2 -8 0 10 -262144 +-1 -1 6300 1; +#X obj 651 103 pack f f; +#X obj 651 139 list append; +#X obj 726 139 hsl 64 15 0 31 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 4100 1; +#X obj 727 159 hsl 64 15 0 31 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 727 179 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 5800 1; +#X obj 728 199 hsl 64 15 0 15 0 1 empty empty kn -2 -8 0 10 -262144 +-1 -1 0 1; +#X obj 651 159 list append; +#X obj 651 179 list append; +#X obj 651 199 list append; +#X obj 651 359 list trim; +#X obj 651 338 list prepend unvoiced; +#X obj 621 71 metro 25; +#X obj 620 47 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +1; +#X obj 297 220 metro 25; +#X obj 296 196 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 +1; +#X obj 503 74 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 +-1; +#X obj 677 75 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1 -1; -#X obj 283 69 r \$0.in; -#X obj 47 443 s \$0.in; #X connect 0 0 3 0; -#X connect 0 1 32 0; -#X connect 0 2 37 0; -#X connect 0 3 36 0; +#X connect 0 1 10 0; +#X connect 0 2 15 0; +#X connect 0 3 14 0; #X connect 2 0 3 1; #X connect 2 0 4 0; #X connect 3 0 1 0; #X connect 3 0 1 1; #X connect 8 0 0 0; -#X connect 10 0 14 0; -#X connect 11 0 10 0; -#X connect 14 0 47 0; -#X connect 15 0 14 1; -#X connect 17 0 10 0; -#X connect 18 0 10 0; -#X connect 19 0 10 0; -#X connect 20 0 10 0; -#X connect 21 0 10 0; -#X connect 22 0 10 0; -#X connect 23 0 10 0; -#X connect 32 0 33 0; -#X connect 32 1 34 0; -#X connect 32 2 35 0; -#X connect 33 0 40 0; -#X connect 34 0 39 0; -#X connect 35 0 38 0; -#X connect 44 0 47 0; -#X connect 45 0 44 0; -#X connect 46 0 0 0; +#X connect 10 0 11 0; +#X connect 10 1 12 0; +#X connect 10 2 13 0; +#X connect 11 0 18 0; +#X connect 12 0 17 0; +#X connect 13 0 16 0; +#X connect 22 0 0 0; +#X connect 24 0 23 0; +#X connect 25 0 23 0; +#X connect 26 0 27 0; +#X connect 28 0 30 1; +#X connect 29 0 30 0; +#X connect 30 0 26 0; +#X connect 32 0 34 1; +#X connect 33 0 34 0; +#X connect 34 0 37 0; +#X connect 35 0 34 0; +#X connect 36 0 35 0; +#X connect 37 0 42 0; +#X connect 38 0 37 1; +#X connect 39 0 42 1; +#X connect 40 0 43 1; +#X connect 41 0 44 1; +#X connect 42 0 43 0; +#X connect 43 0 44 0; +#X connect 44 0 46 0; +#X connect 45 0 46 1; +#X connect 46 0 48 0; +#X connect 47 0 48 1; +#X connect 48 0 50 0; +#X connect 49 0 50 1; +#X connect 50 0 52 0; +#X connect 51 0 52 1; +#X connect 52 0 54 0; +#X connect 53 0 54 1; +#X connect 54 0 56 0; +#X connect 55 0 56 1; +#X connect 56 0 57 0; +#X connect 57 0 58 0; +#X connect 58 0 31 0; +#X connect 60 0 62 1; +#X connect 61 0 62 0; +#X connect 62 0 63 0; +#X connect 63 0 68 0; +#X connect 64 0 63 1; +#X connect 65 0 68 1; +#X connect 66 0 69 1; +#X connect 67 0 70 1; +#X connect 68 0 69 0; +#X connect 69 0 70 0; +#X connect 70 0 72 0; +#X connect 71 0 59 0; +#X connect 72 0 71 0; +#X connect 73 0 62 0; +#X connect 74 0 73 0; +#X connect 75 0 30 0; +#X connect 76 0 75 0; +#X connect 77 0 34 0; +#X connect 78 0 62 0; 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