diff options
author | waker <wakeroid@gmail.com> | 2011-01-02 12:24:03 +0100 |
---|---|---|
committer | waker <wakeroid@gmail.com> | 2011-01-02 12:24:03 +0100 |
commit | 4c90be31a1be29701c3fcca0cd67cb2114e18e15 (patch) | |
tree | 3351347735b93706e58b5da8358514415d41f34c /examples | |
parent | be0631a8bbf54616547a54121efa1d7c7cf606bb (diff) |
slightly updater decoder_template; added dsp_template; moved plugin templates to examples folder
Diffstat (limited to 'examples')
-rw-r--r-- | examples/decoder_template.c | 221 | ||||
-rw-r--r-- | examples/dsp_template.c | 133 |
2 files changed, 354 insertions, 0 deletions
diff --git a/examples/decoder_template.c b/examples/decoder_template.c new file mode 100644 index 00000000..b6e6585b --- /dev/null +++ b/examples/decoder_template.c @@ -0,0 +1,221 @@ +// this is a decoder plugin skeleton +// use to create new decoder plugins + +#include <stdlib.h> +#include <string.h> +#include <deadbeef/deadbeef.h> + +#define trace(...) { fprintf(stderr, __VA_ARGS__); } + +static DB_decoder_t plugin; +static DB_functions_t *deadbeef; + +typedef struct { + DB_fileinfo_t info; + int startsample; + int endsample; + int currentsample; +} example_info_t; + +static const char * exts[] = { "example", NULL }; // e.g. mp3 +static const char *filetypes[] = { "example", NULL }; // e.g. MP3 + +// allocate codec control structure +static DB_fileinfo_t * +example_open (uint32_t hints) { + DB_fileinfo_t *_info = malloc (sizeof (example_info_t)); + example_info_t *info = (example_info_t *)_info; + memset (info, 0, sizeof (example_info_t)); + return _info; +} + +// prepare to decode the track, fill in mandatory plugin fields +// return -1 on failure +static int +example_init (DB_fileinfo_t *_info, DB_playItem_t *it) { + example_info_t *info = (example_info_t *)_info; + + // take this parameters from your input file + // we set constants for clarity sake + _info->fmt.bps = 16; + _info->fmt.channels = 2; + _info->fmt.samplerate = 44100; + for (int i = 0; i < _info->fmt.channels; i++) { + _info->fmt.channelmask |= 1 << i; + } + _info->readpos = 0; + _info->plugin = &plugin; + + if (it->endsample > 0) { + info->startsample = it->startsample; + info->endsample = it->endsample; + plugin.seek_sample (_info, 0); + } + else { + info->startsample = 0; + int TOTALSAMPLES = 1000; // calculate from file + info->endsample = TOTALSAMPLES-1; + } + return 0; +} + +// free everything allocated in _init +static void +example_free (DB_fileinfo_t *_info) { + example_info_t *info = (example_info_t *)_info; + if (info) { + free (info); + } +} + + +// try decode `size' bytes +// return number of decoded bytes +// or 0 on EOF/error +static int +example_read (DB_fileinfo_t *_info, char *bytes, int size) { + example_info_t *info = (example_info_t *)_info; + info->currentsample += size / (_info->fmt.channels * _info->fmt.bps/8); + return size; +} + +// seek to specified sample (frame) +// return 0 on success +// return -1 on failure +static int +example_seek_sample (DB_fileinfo_t *_info, int sample) { + example_info_t *info = (example_info_t *)_info; + + info->currentsample = sample + info->startsample; + _info->readpos = (float)sample / _info->fmt.samplerate; + return 0; +} + +// seek to specified time in seconds +// return 0 on success +// return -1 on failure +static int +example_seek (DB_fileinfo_t *_info, float time) { + return example_seek_sample (_info, time * _info->fmt.samplerate); +} + +// read information from the track +// load/process cuesheet if exists +// insert track into playlist +// return track pointer on success +// return NULL on failure + +static DB_playItem_t * +example_insert (DB_playItem_t *after, const char *fname) { + // open file + DB_FILE *fp = deadbeef->fopen (fname); + if (!fp) { + trace ("example: failed to fopen %s\n", fname); + return NULL; + } + decoder_info_t *di = decoder_open (); + if (!di) { + trace ("example: failed to init decoder\n"); + return NULL; + } + // read track info/tags + track_info_t ti; + if (decoder_read_info (&ti) < 0) { + trace ("example: failed to read info\n"); + decoder_free (di); + return NULL; + } + + // now we should have track duration, and can try loading cuesheet + // 1st try embedded cuesheet + if (ti.embeddedcuesheet[0]) { + DB_playItem_t *cue = deadbeef->pl_insert_cue_from_buffer (after, fname, ti.embeddedcuesheet, strlen (ti.embeddedcuesheet), &plugin, plugin.filetypes[0], ti.total_num_samples, ti.samplerate); + if (cue) { + // cuesheet loaded + decoder_free (di); + return cue; + } + } + + // embedded cuesheet not found, try external one + DB_playItem_t *cue = deadbeef->pl_insert_cue (after, fname, &plugin, plugin.filetypes[0], ti.total_num_samples, ti.samplerate); + if (cue) { + // cuesheet loaded + decoder_free (di); + return cue; + } + + // no cuesheet, prepare track for addition + DB_playItem_t *it = deadbeef->pl_item_alloc (); + it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id); + it->fname = strdup (fname); + it->filetype = filetypes[0]; + deadbeef->pl_set_item_duration (it, (float)ti.total_num_samples/ti.samplerate); + + // add metainfo + if (!strlen (ti.title)) { + // title is empty, this call will set track title to filename without extension + deadbeef->pl_add_meta (it, "title", NULL); + } + else { + deadbeef->pl_add_meta (it, "title", ti.title); + } + deadbeef->pl_add_meta (it, "artist", ti.artist); + // ... etc ... + + // free decoder + decoder_free (di); + + // now the track is ready, insert into playlist + after = deadbeef->pl_insert_item (after, it); + deadbeef->pl_item_unref (it); + return after; +} + +static int +example_start (void) { + // do one-time plugin initialization here + // e.g. starting threads for background processing, subscribing to events, etc + // return 0 on success + // return -1 on failure + return 0; +} + +static int +example_stop (void) { + // undo everything done in _start here + // return 0 on success + // return -1 on failure + return 0; +} + +// 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.name = "short plugin name", + .plugin.descr = "plugin description", + .plugin.author = "author name", + .plugin.email = "author email", + .plugin.website = "author/plugin website", + .plugin.start = example_start, + .plugin.stop = example_stop, + .init = example_init, + .free = example_free, + .read = example_read, + .seek = example_seek, + .seek_sample = example_seek_sample, + .insert = example_insert, + .exts = exts, + .id = "example", + .filetypes = filetypes +}; + +DB_plugin_t * +example_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} + diff --git a/examples/dsp_template.c b/examples/dsp_template.c new file mode 100644 index 00000000..24b53759 --- /dev/null +++ b/examples/dsp_template.c @@ -0,0 +1,133 @@ +// this is a dsp plugin skeleton/example +// use to create new dsp plugins + +// usage: +// 1. copy to your plugin folder +// 2. s/example/plugname/g +// 3. s/EXAMPLE/PLUGNAME/g + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <deadbeef/deadbeef.h> + +enum { + EXAMPLE_PARAM_LEVEL, + EXAMPLE_PARAM_COUNT +}; + +static DB_functions_t *deadbeef; +static DB_dsp_t plugin; + +typedef struct { + ddb_dsp_context_t ctx; + // instance-specific variables here + float level; // this is example +} ddb_example_t; + +ddb_dsp_context_t* +example_open (void) { + ddb_example_t *example = malloc (sizeof (ddb_example_t)); + DDB_INIT_DSP_CONTEXT (example,ddb_example_t,&plugin); + + // initialize + example->level = 0.5; + + return (ddb_dsp_context_t *)example; +} + +void +example_close (ddb_dsp_context_t *ctx) { + ddb_example_t *example = (ddb_example_t *)ctx; + + // free instance-specific allocations + + free (example); +} + +void +example_reset (ddb_dsp_context_t *ctx) { + // use this method to flush dsp buffers, reset filters, etc +} + +int +example_process (ddb_dsp_context_t *ctx, float *samples, int nframes, ddb_waveformat_t *fmt) { + ddb_example_t *example = (ddb_example_t *)ctx; + for (int i = 0; i < nframes*fmt->channels; i++) { + samples[i] *= example->level; + } + return nframes; +} + +const char * +example_get_param_name (int p) { + switch (p) { + case EXAMPLE_PARAM_LEVEL: + return "Volume level"; + default: + fprintf (stderr, "example_param_name: invalid param index (%d)\n", p); + } + return NULL; +} + +int +example_num_params (void) { + return EXAMPLE_PARAM_COUNT; +} + +void +example_set_param (ddb_dsp_context_t *ctx, int p, const char *val) { + ddb_example_t *example = (ddb_example_t *)ctx; + switch (p) { + case EXAMPLE_PARAM_LEVEL: + example->level = atof (val); + break; + default: + fprintf (stderr, "example_param: invalid param index (%d)\n", p); + } +} + +void +example_get_param (ddb_dsp_context_t *ctx, int p, char *val, int sz) { + ddb_example_t *example = (ddb_example_t *)ctx; + switch (p) { + case EXAMPLE_PARAM_LEVEL: + snprintf (val, sz, "%f", example->level); + break; + default: + fprintf (stderr, "example_get_param: invalid param index (%d)\n", p); + } +} + +static const char settings_dlg[] = + "property \"Volume Level\" spinbtn[0,2,0.1] 0 0.5;\n" +; + +static DB_dsp_t plugin = { + .plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.api_vminor = DB_API_VERSION_MINOR, + .open = example_open, + .close = example_close, + .process = example_process, + .plugin.version_major = 0, + .plugin.version_minor = 1, + .plugin.type = DB_PLUGIN_DSP, + .plugin.id = "example", + .plugin.name = "example", + .plugin.descr = "example DSP Plugin", + .plugin.author = "", + .plugin.email = "", + .plugin.website = "", + .num_params = example_num_params, + .get_param_name = example_get_param_name, + .set_param = example_set_param, + .get_param = example_get_param, + .reset = example_reset, + .configdialog = settings_dlg, +}; + +DB_plugin_t * +example_load (DB_functions_t *f) { + deadbeef = f; + return &plugin.plugin; +} |