diff options
Diffstat (limited to 'plugins/dumb/cdumb.c')
-rw-r--r-- | plugins/dumb/cdumb.c | 823 |
1 files changed, 823 insertions, 0 deletions
diff --git a/plugins/dumb/cdumb.c b/plugins/dumb/cdumb.c new file mode 100644 index 00000000..d60c7397 --- /dev/null +++ b/plugins/dumb/cdumb.c @@ -0,0 +1,823 @@ +/* + DeaDBeeF - ultimate music player for GNU/Linux systems with X11 + Copyright (C) 2009-2010 Alexey Yakovenko <waker@users.sourceforge.net> + + 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, see <http://www.gnu.org/licenses/>. +*/ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "dumb.h" +#include "internal/it.h" +#include "deadbeef.h" + +//#define trace(...) { fprintf(stderr, __VA_ARGS__); } +#define trace(fmt,...) + +static DB_decoder_t plugin; +static DB_functions_t *deadbeef; + +typedef struct { + DB_fileinfo_t info; + DUH *duh; + DUH_SIGRENDERER *renderer; +} dumb_info_t; + +//#define DUMB_RQ_ALIASING +//#define DUMB_RQ_LINEAR +//#define DUMB_RQ_CUBIC +//#define DUMB_RQ_N_LEVELS +extern int dumb_resampling_quality; +extern int dumb_it_max_to_mix; + +static int +cdumb_startrenderer (DB_fileinfo_t *_info); + +static DUH* +open_module(const char *fname, const char *ext, int *start_order, int *is_it, int *is_dos, const char **filetype); + +static DB_fileinfo_t * +cdumb_init (DB_playItem_t *it) { + trace ("cdumb_init %s\n", it->fname); + DB_fileinfo_t *_info = malloc (sizeof (dumb_info_t)); + dumb_info_t *info = (dumb_info_t *)_info; + memset (_info, 0, sizeof (dumb_info_t)); + + int start_order = 0; + int is_dos, is_it; + const char *ext = it->fname + strlen (it->fname) - 1; + while (*ext != '.' && ext > it->fname) { + ext--; + } + ext++; + const char *ftype; + info->duh = open_module(it->fname, ext, &start_order, &is_it, &is_dos, &ftype); + + dumb_it_do_initial_runthrough (info->duh); + + _info->plugin = &plugin; + _info->bps = 16; + _info->channels = 2; + _info->samplerate = deadbeef->conf_get_int ("synth.samplerate", 48000); + _info->readpos = 0; + + if (cdumb_startrenderer (_info) < 0) { + plugin.free (_info); + return NULL; + } + + trace ("cdumb_init success (ptr=%p)\n", _info); + return _info; +} + +static int +cdumb_startrenderer (DB_fileinfo_t *_info) { + dumb_info_t *info = (dumb_info_t *)_info; + // reopen + if (info->renderer) { + duh_end_sigrenderer (info->renderer); + info->renderer = NULL; + } + info->renderer = duh_start_sigrenderer (info->duh, 0, 2, 0); + if (!info->renderer) { + return -1; + } + + DUMB_IT_SIGRENDERER *itsr = duh_get_it_sigrenderer (info->renderer); + dumb_it_set_loop_callback (itsr, &dumb_it_callback_terminate, NULL); + dumb_it_set_resampling_quality (itsr, 2); + dumb_it_set_xm_speed_zero_callback (itsr, &dumb_it_callback_terminate, NULL); + dumb_it_set_global_volume_zero_callback (itsr, &dumb_it_callback_terminate, NULL); + return 0; +} + +static void +cdumb_free (DB_fileinfo_t *_info) { + trace ("cdumb_free %p\n", _info); + dumb_info_t *info = (dumb_info_t *)_info; + if (info) { + if (info->renderer) { + duh_end_sigrenderer (info->renderer); + info->renderer = NULL; + } + if (info->duh) { + unload_duh (info->duh); + info->duh = NULL; + } + free (info); + } +} + +static int +cdumb_read (DB_fileinfo_t *_info, char *bytes, int size) { + trace ("cdumb_read req %d\n", size); + dumb_info_t *info = (dumb_info_t *)_info; + int length = size / 4; + long ret; + ret = duh_render (info->renderer, 16, 0, 1, 65536.f / _info->samplerate, length, bytes); + _info->readpos += ret / (float)_info->samplerate; + trace ("cdumb_read %d\n", ret*4); + return ret*4; +} + +static int +cdumb_seek (DB_fileinfo_t *_info, float time) { + trace ("cdumb_read seek %f\n", time); + dumb_info_t *info = (dumb_info_t *)_info; + if (time < _info->readpos) { + if (cdumb_startrenderer (_info) < 0) { + return -1; + } + } + else { + time -= _info->readpos; + } + int pos = time * _info->samplerate; + duh_sigrenderer_generate_samples (info->renderer, 0, 65536.0f / _info->samplerate, pos, NULL); + _info->readpos = duh_sigrenderer_get_position (info->renderer) / 65536.f; + return 0; +} + +static const char * exts[]= +{ + "mod","mdz", + "s3m","s3z", + "stm","stz", + "it","itz", + "xm","xmz", + "ptm","ptz", + "mtm","mtz", + "669", + "psm", + "umx", + "am","j2b", + "dsm", + "amf", + NULL +}; + +// derived from mod.cpp of foo_dumb source code +static DUH * open_module(const char *fname, const char *ext, int *start_order, int *is_it, int *is_dos, const char** filetype) +{ + *filetype = NULL; + DUH * duh = 0; + + *is_it = 0; + *is_dos = 1; + + uint8_t ptr[2000]; + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + return NULL; + } + int size = deadbeef->fread (ptr, 1, 2000, fp); + deadbeef->fclose (fp); + + DUMBFILE * f = dumbfile_open (fname); + if (!f) { + return NULL; + } + +// {{{ no umr yet +#if 0 + if (size >= 4 && + ptr[0] == 0xC1 && ptr[1] == 0x83 && + ptr[2] == 0x2A && ptr[3] == 0x9E) + { + umr_mem_reader memreader(ptr, size); + umr::upkg pkg; + if (pkg.open(&memreader)) + { + for (int i = 1, j = pkg.ocount(); i <= j; i++) + { + char * classname = pkg.oclassname(i); + if (classname && !strcmp(pkg.oclassname(i), "Music")) + { + char * type = pkg.otype(i); + if (!type) continue; + /* + if (!stricmp(type, "it")) + { + is_it = true; + ptr += memdata.offset = pkg.object_offset(i); + size = memdata.size = memdata.offset + pkg.object_size(i); + duh = dumb_read_it_quick(f); + break; + } + else if (!stricmp(type, "s3m")) + { + memdata.offset = pkg.object_offset(i); + memdata.size = memdata.offset + pkg.object_size(i); + duh = dumb_read_s3m_quick(f); + break; + } + else if (!stricmp(type, "xm")) + { + memdata.offset = pkg.object_offset(i); + memdata.size = memdata.offset + pkg.object_size(i); + duh = dumb_read_xm_quick(f); + break; + } + */ + // blah, type can't be trusted + if (!stricmp(type, "it") || !stricmp(type, "s3m") || !stricmp(type, "xm")) + { + ptr += memdata.offset = pkg.object_offset(i); + size = memdata.size = memdata.offset + pkg.object_size(i); + if (size >= 4 && ptr[0] == 'I' && ptr[1] == 'M' && ptr[2] == 'P' && ptr[3] == 'M') + { + is_it = true; + duh = dumb_read_it_quick(f); + } + else if (size >= 42 && ptr[38] == 'F' && ptr[39] == 'a' && ptr[40] == 's' && ptr[41] == 't') + { + duh = dumb_read_xm_quick(f); + } + else if (size >= 48 && ptr[44] == 'S' && ptr[45] == 'C' && ptr[46] == 'R' && ptr[47] == 'M') + { + duh = dumb_read_s3m_quick(f); + } + + break; + } + } + } + } + } + else +#endif +// end of umr code +// }}} + if (size >= 4 && + ptr[0] == 'I' && ptr[1] == 'M' && + ptr[2] == 'P' && ptr[3] == 'M') + { + *is_it = 1; + duh = dumb_read_it_quick(f); + *filetype = "IT"; + } + else if (size >= 17 && !memcmp(ptr, "Extended Module: ", 17)) + { + duh = dumb_read_xm_quick(f); + *filetype = "XM"; + } + else if (size >= 0x30 && + ptr[0x2C] == 'S' && ptr[0x2D] == 'C' && + ptr[0x2E] == 'R' && ptr[0x2F] == 'M') + { + duh = dumb_read_s3m_quick(f); + *filetype = "S3M"; + } + else if (size >= 1168 && + /*ptr[28] == 0x1A &&*/ ptr[29] == 2 && + ( ! strncasecmp( ( const char * ) ptr + 20, "!Scream!", 8 ) || + ! strncasecmp( ( const char * ) ptr + 20, "BMOD2STM", 8 ) || + ! strncasecmp( ( const char * ) ptr + 20, "WUZAMOD!", 8 ) ) ) + { + duh = dumb_read_stm_quick(f); + *filetype = "STM"; + } + else if (size >= 2 && + ((ptr[0] == 0x69 && ptr[1] == 0x66) || + (ptr[0] == 0x4A && ptr[1] == 0x4E))) + { + duh = dumb_read_669_quick(f); + *filetype = "669"; + } + else if (size >= 0x30 && + ptr[0x2C] == 'P' && ptr[0x2D] == 'T' && + ptr[0x2E] == 'M' && ptr[0x2F] == 'F') + { + duh = dumb_read_ptm_quick(f); + *filetype = "PTM"; + } + else if (size >= 4 && + ptr[0] == 'P' && ptr[1] == 'S' && + ptr[2] == 'M' && ptr[3] == ' ') + { + duh = dumb_read_psm_quick(f, *start_order); + *start_order = 0; + *filetype = "PSM"; + } + else if (size >= 4 && + ptr[0] == 'P' && ptr[1] == 'S' && + ptr[2] == 'M' && ptr[3] == 254) + { + duh = dumb_read_old_psm_quick(f); + *filetype = "PSM"; + } + else if (size >= 3 && + ptr[0] == 'M' && ptr[1] == 'T' && + ptr[2] == 'M') + { + duh = dumb_read_mtm_quick(f); + *filetype = "MTM"; + } + else if ( size >= 4 && + ptr[0] == 'R' && ptr[1] == 'I' && + ptr[2] == 'F' && ptr[3] == 'F') + { + duh = dumb_read_riff_quick(f); + *filetype = "RIFF"; + } + else if ( size >= 32 && + !memcmp( ptr, "ASYLUM Music Format", 19 ) && + !memcmp( ptr + 19, " V1.0", 5 ) ) + { + duh = dumb_read_asy_quick(f); + *filetype = "ASY"; + } + + if (!duh) + { + dumbfile_close(f); + f = dumbfile_open (fname); + *is_dos = 0; + duh = dumb_read_mod_quick (f, (!strcasecmp (ext, exts[0]) || !strcasecmp (ext, exts[1])) ? 0 : 1); + *filetype = "MOD"; + } + + if (f) { + dumbfile_close(f); + } + +// {{{ no volume ramping +#if 0 + // XXX test + if (duh) + { + int ramp_mode = 0; // none + if (ramp_mode) + { + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + if (itsd) + { + if (ramp_mode > 2) + { + if ( ( itsd->flags & ( IT_WAS_AN_XM | IT_WAS_A_MOD ) ) == IT_WAS_AN_XM ) + ramp_mode = 2; + else + ramp_mode = 1; + } + for (int i = 0, j = itsd->n_samples; i < j; i++) + { + IT_SAMPLE * sample = &itsd->sample[i]; + if ( sample->flags & IT_SAMPLE_EXISTS && !( sample->flags & IT_SAMPLE_LOOP ) ) + { + double rate = 1. / double( sample->C5_speed ); + double length = double( sample->length ) * rate; + if ( length >= .1 ) + { + int k, l = sample->length; + if ( ramp_mode == 1 && ( ( rate * 16. ) < .01 ) ) + { + if (sample->flags & IT_SAMPLE_16BIT) + { + k = l - 15; + signed short * data = (signed short *) sample->data; + if (sample->flags & IT_SAMPLE_STEREO) + { + for (int shift = 1; k < l; k++, shift++) + { + data [k * 2] >>= shift; + data [k * 2 + 1] >>= shift; + } + } + else + { + for (int shift = 1; k < l; k++, shift++) + { + data [k] >>= shift; + } + } + } + else + { + k = l - 7; + signed char * data = (signed char *) sample->data; + if (sample->flags & IT_SAMPLE_STEREO) + { + for (int shift = 1; k < l; k++, shift++) + { + data [k * 2] >>= shift; + data [k * 2 + 1] >>= shift; + } + } + else + { + for (int shift = 1; k < l; k++, shift++) + { + data [k] >>= shift; + } + } + } + } + else + { + int m = int( .01 * double( sample->C5_speed ) + .5 ); + k = l - m; + if (sample->flags & IT_SAMPLE_16BIT) + { + signed short * data = (signed short *) sample->data; + if (sample->flags & IT_SAMPLE_STEREO) + { + for (; k < l; k++) + { + data [k * 2] = MulDiv( data [k * 2], l - k, m ); + data [k * 2 + 1] = MulDiv( data [k * 2 + 1], l - k, m ); + } + } + else + { + for (; k < l; k++) + { + data [k] = MulDiv( data [k], l - k, m ); + } + } + } + else + { + signed char * data = (signed char *) sample->data; + if (sample->flags & IT_SAMPLE_STEREO) + { + for (; k < l; k++) + { + data [k * 2] = MulDiv( data [k * 2], l - k, m ); + data [k * 2 + 1] = MulDiv( data [k * 2 + 1], l - k, m ); + } + } + else + { + for (; k < l; k++) + { + data [k] = MulDiv( data [k], l - k, m ); + } + } + } + } + } + } + } + } + } + } +#endif +// }}} + +// {{{ no autochip +#if 0 + if (duh && cfg_autochip) + { + int size_force = cfg_autochip_size_force; + int size_scan = cfg_autochip_size_scan; + int scan_threshold_8 = ((cfg_autochip_scan_threshold * 0x100) + 50) / 100; + int scan_threshold_16 = ((cfg_autochip_scan_threshold * 0x10000) + 50) / 100; + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + + if (itsd) + { + for (int i = 0, j = itsd->n_samples; i < j; i++) + { + IT_SAMPLE * sample = &itsd->sample[i]; + if (sample->flags & IT_SAMPLE_EXISTS) + { + int channels = sample->flags & IT_SAMPLE_STEREO ? 2 : 1; + if (sample->length < size_force) sample->max_resampling_quality = 0; + else if (sample->length < size_scan) + { + if ((sample->flags & (IT_SAMPLE_LOOP|IT_SAMPLE_PINGPONG_LOOP)) == IT_SAMPLE_LOOP) + { + int loop_start = sample->loop_start * channels; + int loop_end = sample->loop_end * channels; + int s1, s2; + if (sample->flags & IT_SAMPLE_16BIT) + { + s1 = ((signed short *)sample->data)[loop_start]; + s2 = ((signed short *)sample->data)[loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed short *)sample->data)[loop_start + 1]; + s2 = ((signed short *)sample->data)[loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + else + { + s1 = ((signed char *)sample->data)[loop_start]; + s2 = ((signed char *)sample->data)[loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed char *)sample->data)[loop_start + 1]; + s2 = ((signed char *)sample->data)[loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + } + if ((sample->flags & (IT_SAMPLE_SUS_LOOP|IT_SAMPLE_PINGPONG_SUS_LOOP)) == IT_SAMPLE_SUS_LOOP) + { + int sus_loop_start = sample->sus_loop_start * channels; + int sus_loop_end = sample->sus_loop_end * channels; + int s1, s2; + if (sample->flags & IT_SAMPLE_16BIT) + { + s1 = ((signed short *)sample->data)[sus_loop_start]; + s2 = ((signed short *)sample->data)[sus_loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed short *)sample->data)[sus_loop_start + 1]; + s2 = ((signed short *)sample->data)[sus_loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_16) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + else + { + s1 = ((signed char *)sample->data)[sus_loop_start]; + s2 = ((signed char *)sample->data)[sus_loop_end - channels]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + s1 = ((signed char *)sample->data)[sus_loop_start + 1]; + s2 = ((signed char *)sample->data)[sus_loop_end - 1]; + if (abs(s1 - s2) > scan_threshold_8) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + } + + int k, l = sample->length * channels; + if (sample->flags & IT_SAMPLE_LOOP) l = sample->loop_end * channels; + if (sample->flags & IT_SAMPLE_16BIT) + { + for (k = channels; k < l; k += channels) + { + if (abs(((signed short *)sample->data)[k - channels] - ((signed short *)sample->data)[k]) > scan_threshold_16) + { + break; + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + for (k = 2 + 1; k < l; k += 2) + { + if (abs(((signed short *)sample->data)[k - 2] - ((signed short *)sample->data)[k]) > scan_threshold_16) + { + break; + } + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + } + else + { + for (k = channels; k < l; k += channels) + { + if (abs(((signed char *)sample->data)[k - channels] - ((signed char *)sample->data)[k]) > scan_threshold_8) + { + break; + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + if (channels == 2) + { + for (k = 2 + 1; k < l; k += 2) + { + if (abs(((signed char *)sample->data)[k - 2] - ((signed char *)sample->data)[k]) > scan_threshold_8) + { + break; + } + } + } + if (k < l) + { + sample->max_resampling_quality = 0; + continue; + } + } + } + } + } + } + } +#endif +// }}} + +// {{{ no trim +#if 0 + if ( duh && cfg_trim ) + { + if ( dumb_it_trim_silent_patterns( duh ) < 0 ) + { + unload_duh( duh ); + duh = 0; + } + } +#endif +// }}} + + return duh; +} + +static const char *convstr (const char* str, int sz) { + static char out[2048]; + int i; + for (i = 0; i < sz; i++) { + if (str[i] != ' ') { + break; + } + } + if (i == sz) { + out[0] = 0; + return out; + } + + // check for utf8 (hack) + if (deadbeef->junk_iconv (str, sz, out, sizeof (out), "utf-8", "utf-8") >= 0) { + return out; + } + + if (deadbeef->junk_iconv (str, sz, out, sizeof (out), "utf-8", "iso8859-1") >= 0) { + return out; + } + + trace ("cdumb: failed to detect charset\n"); + return NULL; +} + +static DB_playItem_t * +cdumb_insert (DB_playItem_t *after, const char *fname) { + const char *ext = fname + strlen (fname) - 1; + while (*ext != '.' && ext > fname) { + ext--; + } + ext++; + int start_order = 0; + int is_it; + int is_dos; + const char *ftype; + DUH* duh = open_module(fname, ext, &start_order, &is_it, &is_dos, &ftype); + if (!duh) { + return NULL; + } + DB_playItem_t *it = deadbeef->pl_item_alloc (); + it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); + it->fname = strdup (fname); + DUMB_IT_SIGDATA * itsd = duh_get_it_sigdata(duh); + if (itsd->name[0]) { + int tl = sizeof(itsd->name); + int i; + for (i = 0; i < tl && itsd->name[i] && itsd->name[i] == ' '; i++); + if (i == tl || !itsd->name[i]) { + deadbeef->pl_add_meta (it, "title", NULL); + } + else { + deadbeef->pl_add_meta (it, "title", convstr ((char*)&itsd->name, sizeof(itsd->name))); + } + } + else { + deadbeef->pl_add_meta (it, "title", NULL); + } + dumb_it_do_initial_runthrough (duh); + deadbeef->pl_set_item_duration (it, duh_get_length (duh)/65536.0f); + it->filetype = ftype; +// printf ("duration: %f\n", _info->duration); + after = deadbeef->pl_insert_item (after, it); + deadbeef->pl_item_unref (it); + unload_duh (duh); + + return after; +} + +static DUMBFILE_SYSTEM dumb_vfs; + +static int +dumb_vfs_skip (void *f, long n) { + return deadbeef->fseek (f, n, SEEK_CUR); +} + +static int +dumb_vfs_getc (void *f) { + uint8_t c; + deadbeef->fread (&c, 1, 1, f); + return (int)c; +} + +static long +dumb_vfs_getnc (char *ptr, long n, void *f) { + return deadbeef->fread (ptr, 1, n, f); +} + +static void +dumb_vfs_close (void *f) { + deadbeef->fclose (f); +} + +static void +dumb_register_db_vfs (void) { + dumb_vfs.open = (void *(*)(const char *))deadbeef->fopen; + dumb_vfs.skip = dumb_vfs_skip; + dumb_vfs.getc = dumb_vfs_getc; + dumb_vfs.getnc = dumb_vfs_getnc; + dumb_vfs.close = dumb_vfs_close; + register_dumbfile_system (&dumb_vfs); +} + +int +cgme_start (void) { + dumb_register_db_vfs (); + return 0; +} + +int +cgme_stop (void) { + dumb_exit (); + return 0; +} + +static const char *filetypes[] = { "IT", "XM", "S3M", "STM", "669", "PTM", "PSM", "MTM", "RIFF", "ASY", "MOD", NULL }; + +// define plugin interface +static DB_decoder_t plugin = { + DB_PLUGIN_SET_API_VERSION + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DECODER, + .plugin.id = "stddumb", + .plugin.name = "DUMB module player", + .plugin.descr = "module player based on DUMB library", + .plugin.author = "Alexey Yakovenko", + .plugin.email = "waker@users.sourceforge.net", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = cgme_start, + .plugin.stop = cgme_stop, + .init = cdumb_init, + .free = cdumb_free, + .read_int16 = cdumb_read, + .seek = cdumb_seek, + .insert = cdumb_insert, + .exts = exts, + .filetypes = filetypes +}; + +DB_plugin_t * +dumb_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} |