summaryrefslogtreecommitdiff
path: root/plugins/ao/plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/ao/plugin.c')
-rw-r--r--plugins/ao/plugin.c338
1 files changed, 338 insertions, 0 deletions
diff --git a/plugins/ao/plugin.c b/plugins/ao/plugin.c
new file mode 100644
index 00000000..5f6a12bf
--- /dev/null
+++ b/plugins/ao/plugin.c
@@ -0,0 +1,338 @@
+/*
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+#include <stdlib.h>
+#include <string.h>
+#include "../../deadbeef.h"
+#include "ao.h"
+#include "eng_protos.h"
+
+#define min(x,y) ((x)<(y)?(x):(y))
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define trace(...) { fprintf(stderr, __VA_ARGS__); }
+//#define trace(fmt,...)
+
+DB_functions_t *deadbeef;
+static DB_decoder_t plugin;
+
+static const char * exts[] = { "psf", "psf2", "spu", "ssf", "minidsf", "qsf", "dsf", "miniqsf", NULL };
+static const char *filetypes[] = { "PSF", "PSF2", "SPU", "SSF", "QSF", "DSF", NULL };
+
+typedef struct {
+ DB_fileinfo_t info;
+ int currentsample;
+ uint32 type;
+ void *decoder;
+ char *filebuffer;
+ size_t filesize;
+ char buffer[735*4]; // psf2 decoder only works with 735 samples buffer
+ int remaining;
+ int skipsamples;
+ float duration;
+} aoplug_info_t;
+
+static DB_fileinfo_t *
+aoplug_open (void) {
+ DB_fileinfo_t *_info = malloc (sizeof (aoplug_info_t));
+ aoplug_info_t *info = (aoplug_info_t *)_info;
+ memset (info, 0, sizeof (aoplug_info_t));
+ return _info;
+}
+
+static int
+aoplug_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
+ aoplug_info_t *info = (aoplug_info_t *)_info;
+
+ _info->bps = 16;
+ _info->channels = 2;
+ _info->samplerate = 44100;
+ _info->readpos = 0;
+ _info->plugin = &plugin;
+ info->duration = deadbeef->pl_get_item_duration (it);
+
+ DB_FILE *file = deadbeef->fopen (it->fname);
+ if (!file) {
+ trace ("psf: failed to fopen %s\n", it->fname);
+ return -1;
+ }
+
+ info->filesize = deadbeef->fgetlength (file);
+ info->filebuffer = malloc (info->filesize);
+ if (!info->filebuffer) {
+ fprintf(stderr, "psf: could not allocate %d bytes of memory\n", (int)info->filesize);
+ deadbeef->fclose (file);
+ return -1;
+ }
+
+ if (deadbeef->fread(info->filebuffer, 1, info->filesize, file) != info->filesize) {
+ fprintf(stderr, "psf: file read error: %s\n", it->fname);
+ deadbeef->fclose (file);
+ return -1;
+ }
+ deadbeef->fclose (file);
+
+ info->type = ao_identify (info->filebuffer);
+ if (info->type < 0) {
+ fprintf (stderr, "psf: ao_identify failed\n");
+ return -1;
+ }
+
+ info->decoder = ao_start (info->type, it->fname, (uint8 *)info->filebuffer, info->filesize);
+ if (!info->decoder) {
+ fprintf (stderr, "psf: ao_start failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+aoplug_free (DB_fileinfo_t *_info) {
+ aoplug_info_t *info = (aoplug_info_t *)_info;
+ if (info) {
+ if (info->filebuffer) {
+ ao_stop (info->type, info->decoder);
+ free (info->filebuffer);
+ info->filebuffer = NULL;
+ }
+ free (info);
+ }
+}
+
+static int
+aoplug_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
+ aoplug_info_t *info = (aoplug_info_t *)_info;
+// printf ("aoplug_read_int16 %d samples, curr %d, end %d\n", size/4, info->currentsample, (int)(info->duration * _info->samplerate));
+
+ if (info->currentsample >= info->duration * _info->samplerate) {
+ return 0;
+ }
+
+ int initsize = size;
+
+ while (size > 0) {
+ if (info->remaining > 0) {
+ if (info->skipsamples > 0) {
+ int n = min (info->skipsamples, info->remaining);
+ if (info->remaining > n) {
+ memmove (info->buffer, info->buffer+n*4, (info->remaining - n)*4);
+ }
+ info->remaining -= n;
+ info->skipsamples -= n;
+ continue;
+ }
+ int n = size / 4;
+ n = min (info->remaining, n);
+ memcpy (bytes, info->buffer, n * 4);
+ if (info->remaining > n) {
+ memmove (info->buffer, info->buffer+n*4, (info->remaining - n)*4);
+ }
+ info->remaining -= n;
+ bytes += n*4;
+ size -= n*4;
+ }
+ if (!info->remaining) {
+ ao_decode (info->type, info->decoder, (int16_t *)info->buffer, 735);
+ info->remaining = 735;
+ }
+ }
+ info->currentsample += (initsize-size) / (_info->channels * _info->bps/8);
+ return initsize-size;
+}
+
+static int
+aoplug_seek_sample (DB_fileinfo_t *_info, int sample) {
+ aoplug_info_t *info = (aoplug_info_t *)_info;
+ if (sample > info->currentsample) {
+ info->skipsamples = sample-info->currentsample;
+ }
+ else {
+ // restart song
+ ao_command (info->type, info->decoder, COMMAND_RESTART, 0);
+ info->skipsamples = sample;
+ }
+ info->currentsample = sample;
+ _info->readpos = (float)sample / _info->samplerate;
+ return 0;
+}
+
+static int
+aoplug_seek (DB_fileinfo_t *_info, float time) {
+ return aoplug_seek_sample (_info, time * _info->samplerate);
+}
+
+static DB_playItem_t *
+aoplug_insert (DB_playItem_t *after, const char *fname) {
+ DB_FILE *fp = deadbeef->fopen (fname);
+ if (!fp) {
+ trace ("psf: failed to fopen %s\n", fname);
+ return NULL;
+ }
+
+ size_t size = deadbeef->fgetlength (fp);
+ char *buffer = malloc (size);
+ if (!buffer) {
+ deadbeef->fclose (fp);
+ fprintf(stderr, "psf: could not allocate %d bytes of memory\n", (int)size);
+ return NULL;
+ }
+
+ if (deadbeef->fread(buffer, 1, size, fp) != size) {
+ deadbeef->fclose (fp);
+ fprintf(stderr, "psf: file read error: %s\n", fname);
+ return NULL;
+ }
+
+ deadbeef->fclose (fp);
+
+ int type = ao_identify (buffer);
+ if (type < 0) {
+ free (buffer);
+ return NULL;
+ }
+
+ void *dec = ao_start (type, fname, (uint8*)buffer, size);
+ if (!dec) {
+ free (buffer);
+ return NULL;
+ }
+ ao_display_info info;
+ int have_info = 0;
+ if (ao_get_info (type, dec, &info) == AO_SUCCESS) {
+ have_info = 1;
+ }
+
+ ao_stop (type, dec);
+ dec = NULL;
+
+ free (buffer);
+
+ DB_playItem_t *it = deadbeef->pl_item_alloc ();
+ it->decoder_id = deadbeef->plug_get_decoder_id (plugin.plugin.id);
+ it->fname = strdup (fname);
+ const char *ext = fname + strlen (fname);
+ while (*ext != '.' && ext > fname) {
+ ext--;
+ }
+ if (*ext == '.') {
+ ext++;
+ if (!strcasecmp (ext, "psf")) {
+ it->filetype = filetypes[0];
+ }
+ else if (!strcasecmp (ext, "psf2")) {
+ it->filetype = filetypes[1];
+ }
+ else if (!strcasecmp (ext, "spu")) {
+ it->filetype = filetypes[2];
+ }
+ else if (!strcasecmp (ext, "ssf")) {
+ it->filetype = filetypes[3];
+ }
+ else if (!strcasecmp (ext, "dsf") || !strcasecmp (ext, "minidsf")) {
+ it->filetype = filetypes[5];
+ }
+ else if (!strcasecmp (ext, "qsf") || !strcasecmp (ext, "miniqsf")) {
+ it->filetype = filetypes[4];
+ }
+ }
+ else {
+ it->filetype = filetypes[0];
+ }
+
+ float duration = 120;
+
+ if (have_info) {
+ int i;
+ for (i = 1; i < 9; i++) {
+ if (!strncasecmp (info.title[i], "Length: ", 8)) {
+ int min, sec;
+ if (sscanf (info.info[i], "%d:%d", &min, &sec) == 2) {
+ duration = min * 60 + sec;
+ }
+ }
+ else if (!strncasecmp (info.title[i], "Name: ", 6) || !strncasecmp (info.title[i], "Song: ", 6)) {
+ deadbeef->pl_add_meta (it, "title", info.info[i]);
+ }
+ else if (!strncasecmp (info.title[i], "Game: ", 6)) {
+ deadbeef->pl_add_meta (it, "album", info.info[i]);
+ }
+ else if (!strncasecmp (info.title[i], "Artist: ", 8)) {
+ deadbeef->pl_add_meta (it, "artist", info.info[i]);
+ }
+ else if (!strncasecmp (info.title[i], "Copyright: ", 11)) {
+ deadbeef->pl_add_meta (it, "copyright", info.info[i]);
+ }
+ else if (!strncasecmp (info.title[i], "Year: ", 6)) {
+ deadbeef->pl_add_meta (it, "date", info.info[i]);
+ }
+ else if (!strncasecmp (info.title[i], "Year: ", 6)) {
+ deadbeef->pl_add_meta (it, "date", info.info[i]);
+ }
+ char s[1024];
+ snprintf (s, sizeof (s), "%s%s", info.title[i], info.info[i]);
+ deadbeef->pl_append_meta (it, "comment", s);
+ }
+ }
+ deadbeef->pl_set_item_duration (it, duration);
+ deadbeef->pl_add_meta (it, "title", NULL);
+ after = deadbeef->pl_insert_item (after, it);
+ deadbeef->pl_item_unref (it);
+ return after;
+}
+
+static int
+aoplug_start (void) {
+ return 0;
+}
+
+static int
+aoplug_stop (void) {
+ return 0;
+}
+
+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 = "psf",
+ .plugin.name = "Audio Overload plugin",
+ .plugin.descr = "psf, psf2, spu, ssf, minidsf player based on Audio Overload library",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = aoplug_start,
+ .plugin.stop = aoplug_stop,
+ .open = aoplug_open,
+ .init = aoplug_init,
+ .free = aoplug_free,
+ .read_int16 = aoplug_read_int16,
+ .seek = aoplug_seek,
+ .seek_sample = aoplug_seek_sample,
+ .insert = aoplug_insert,
+ .exts = exts,
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+ao_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}
+