/* DeaDBeeF - The Ultimate Music Player Copyright (C) 2009-2013 Alexey Yakovenko This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "../../deadbeef.h" #include "st.h" enum { ST_PARAM_TEMPO, ST_PARAM_PITCH, ST_PARAM_RATE, ST_PARAM_USE_AA_FILTER, ST_PARAM_AA_FILTER_LENGTH, ST_PARAM_USE_QUICKSEEK, ST_PARAM_SEQUENCE_MS, ST_PARAM_SEEKWINDOW_MS, ST_PARAM_SET_OUTPUT_SAMPLERATE, ST_PARAM_COUNT }; static DB_functions_t *deadbeef; static DB_dsp_t plugin; typedef struct { ddb_dsp_context_t ctx; void *st; float tempo; float pitch; float rate; int use_aa_filter; int aa_filter_length; int use_quickseek; int sequence_ms; int seekwindow_ms; int set_output_samplerate; int changed; } ddb_soundtouch_t; ddb_dsp_context_t* st_open (void) { ddb_soundtouch_t *st = malloc (sizeof (ddb_soundtouch_t)); DDB_INIT_DSP_CONTEXT (st,ddb_soundtouch_t,&plugin); st->st = st_alloc (); st->changed = 1; st->tempo = 0; st->rate = 0; st->pitch = 0; st->use_aa_filter = 0; st->aa_filter_length = 32; st->use_quickseek = 0; st->sequence_ms = 82; st->seekwindow_ms = 28; return (ddb_dsp_context_t *)st; } void st_close (ddb_dsp_context_t *_src) { ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; if (st->st) { st_free (st->st); } free (st); } void st_reset (ddb_dsp_context_t *_src) { ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; st_clear (st->st); } int st_process (ddb_dsp_context_t *_src, float *samples, int nframes, int maxframes, ddb_waveformat_t *fmt, float *ratio) { ddb_soundtouch_t *st = (ddb_soundtouch_t *)_src; if (st->changed) { if (st->set_output_samplerate > 0) { st_set_rate_change (st->st, 0); st_set_rate (st->st, fmt->samplerate / (float)st->set_output_samplerate); } else { st_set_rate (st->st, 1); st_set_rate_change (st->st, st->rate); } st_set_pitch_semi_tones (st->st, st->pitch); st_set_tempo_change (st->st, st->tempo); st_set_setting (st->st, SETTING_USE_AA_FILTER, st->use_aa_filter); st_set_setting (st->st, SETTING_AA_FILTER_LENGTH, st->aa_filter_length); st_set_setting (st->st, SETTING_USE_QUICKSEEK, st->use_quickseek); st_set_setting (st->st, SETTING_SEQUENCE_MS, st->sequence_ms); st_set_setting (st->st, SETTING_SEEKWINDOW_MS, st->seekwindow_ms); st->changed = 0; } *ratio = (1.f + 0.01f * st->tempo); if (st->set_output_samplerate > 0) { fmt->samplerate = st->set_output_samplerate; } else { *ratio *= (1.f + 0.01f * st->rate); } st_set_sample_rate (st->st, fmt->samplerate); st_set_channels (st->st, fmt->channels); st_put_samples (st->st, samples, nframes); int nout = 0; int n = 0; do { n = st_receive_samples (st->st, samples, maxframes); maxframes -= n; samples += n * fmt->channels; nout += n; } while (n != 0); return nout; } const char * st_get_param_name (int p) { switch (p) { case ST_PARAM_TEMPO: return "Tempo"; case ST_PARAM_PITCH: return "Pitch"; case ST_PARAM_RATE: return "Playback Rate"; case ST_PARAM_USE_AA_FILTER: return "Use AA Filter"; case ST_PARAM_AA_FILTER_LENGTH: return "AA Filter Length"; case ST_PARAM_USE_QUICKSEEK: return "Use Quickseek"; case ST_PARAM_SEQUENCE_MS: return "Time Stretch Sequence Length (ms)"; case ST_PARAM_SEEKWINDOW_MS: return "Time Stretch Seek Window Length (ms)"; case ST_PARAM_SET_OUTPUT_SAMPLERATE: return "Set Output Samplerate"; default: fprintf (stderr, "st_param_name: invalid param index (%d)\n", p); } return NULL; } int st_num_params (void) { return ST_PARAM_COUNT; } void st_set_param (ddb_dsp_context_t *ctx, int p, const char *val) { ddb_soundtouch_t *st = (ddb_soundtouch_t *)ctx; switch (p) { case ST_PARAM_TEMPO: st->tempo = atof (val); st->changed = 1; break; case ST_PARAM_PITCH: st->pitch = atof (val); st->changed = 1; break; case ST_PARAM_RATE: st->rate = atof (val); st->changed = 1; break; case ST_PARAM_USE_AA_FILTER: st->use_aa_filter = atoi (val); st->changed = 1; break; case ST_PARAM_AA_FILTER_LENGTH: st->aa_filter_length = atoi (val); st->changed = 1; break; case ST_PARAM_USE_QUICKSEEK: st->use_quickseek = atoi (val); st->changed = 1; break; case ST_PARAM_SEQUENCE_MS: st->sequence_ms = atoi (val); st->changed = 1; break; case ST_PARAM_SEEKWINDOW_MS: st->seekwindow_ms = atoi (val); st->changed = 1; break; case ST_PARAM_SET_OUTPUT_SAMPLERATE: st->set_output_samplerate = atoi (val); if (st->set_output_samplerate < 8000) { st->set_output_samplerate = 0; } else if (st->set_output_samplerate > 192000) { st->set_output_samplerate = 192000; } break; default: fprintf (stderr, "st_param: invalid param index (%d)\n", p); } } void st_get_param (ddb_dsp_context_t *ctx, int p, char *val, int sz) { ddb_soundtouch_t *st = (ddb_soundtouch_t *)ctx; switch (p) { case ST_PARAM_TEMPO: snprintf (val, sz, "%f", st->tempo); break; case ST_PARAM_PITCH: snprintf (val, sz, "%f", st->pitch); break; case ST_PARAM_RATE: snprintf (val, sz, "%f", st->rate); break; case ST_PARAM_USE_AA_FILTER: snprintf (val, sz, "%d", st->use_aa_filter); break; case ST_PARAM_AA_FILTER_LENGTH: snprintf (val, sz, "%d", st->aa_filter_length); break; case ST_PARAM_USE_QUICKSEEK: snprintf (val, sz, "%d", st->use_quickseek); break; case ST_PARAM_SEQUENCE_MS: snprintf (val, sz, "%d", st->sequence_ms); break; case ST_PARAM_SEEKWINDOW_MS: snprintf (val, sz, "%d", st->seekwindow_ms); break; case ST_PARAM_SET_OUTPUT_SAMPLERATE: snprintf (val, sz, "%d", st->set_output_samplerate); break; default: fprintf (stderr, "st_get_param: invalid param index (%d)\n", p); } } static const char settings_dlg[] = "property \"Tempo Change (%)\" spinbtn[-200,200,1] 0 0;\n" "property \"Pitch Change (semi-tones)\" spinbtn[-24,24,1] 1 0;\n" "property \"Playback Rate Change (%)\" spinbtn[-200,200,1] 2 0;\n" "property \"Absolute Samplerate (overrides Rate Change if nonzero)\" spinbtn[0,192000,1] 8 0;\n" "property \"Use AA Filter\" checkbox 3 0;\n" "property \"AA Filter Length\" spinbtn[16,64,1] 4 32;\n" "property \"Use Quickseek\" checkbox 5 0;\n" "property \"Time Stretch Sequence Length (ms)\" spinbtn[10,500,1] 6 82;\n" "property \"Time Stretch Seek Window Length (ms)\" spinbtn[10,500,1] 7 28;\n" ; static DB_dsp_t plugin = { .plugin.api_vmajor = 1, .plugin.api_vminor = 0, .open = st_open, .close = st_close, .process = st_process, .plugin.version_major = 0, .plugin.version_minor = 1, .plugin.type = DB_PLUGIN_DSP, .plugin.id = "soundtouch", .plugin.name = "Soundtouch", .plugin.descr = "Tempo/Pitch/Rate changer using SoundTouch Library (http://www.surina.net/soundtouch)", .plugin.copyright = "Copyright (C) 2009-2013 Alexey Yakovenko \n" "\n" "uses SoundTouch Library, (C) Olli Parviainen" "\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License\n" "as published by the Free Software Foundation; either version 2\n" "of the License, or (at your option) any later version.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" , .plugin.website = "http://deadbeef.sf.net", .num_params = st_num_params, .get_param_name = st_get_param_name, .set_param = st_set_param, .get_param = st_get_param, .reset = st_reset, .configdialog = settings_dlg, }; DB_plugin_t * ddb_soundtouch_load (DB_functions_t *f) { deadbeef = f; return &plugin.plugin; }