summaryrefslogtreecommitdiff
path: root/plugins/tta/ttaplug.c
diff options
context:
space:
mode:
authorGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-05-31 22:27:19 +0200
committerGravatar Alexey Yakovenko <wakeroid@gmail.com>2010-05-31 22:27:19 +0200
commitd1cf5097c00388345932d244e8170d35be46df3d (patch)
treed58fb82dbc3bf42d18ba86ecb4e72cd95e8f46bc /plugins/tta/ttaplug.c
parent5f86a54bab8190c4253a2c3254667cb77df5a800 (diff)
WIP tta plugin
Diffstat (limited to 'plugins/tta/ttaplug.c')
-rw-r--r--plugins/tta/ttaplug.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/plugins/tta/ttaplug.c b/plugins/tta/ttaplug.c
new file mode 100644
index 00000000..32fa3574
--- /dev/null
+++ b/plugins/tta/ttaplug.c
@@ -0,0 +1,315 @@
+/*
+ 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 <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include "ttalib.h"
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "../../deadbeef.h"
+
+#pragma GCC optimize("O0")
+
+#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,...)
+
+static DB_decoder_t plugin;
+static DB_functions_t *deadbeef;
+
+#define MAX_BSIZE (MAX_BPS>>3)
+
+typedef struct {
+ DB_fileinfo_t info;
+ tta_info tta;
+ int currentsample;
+ int startsample;
+ int endsample;
+ char buffer[PCM_BUFFER_LENGTH * MAX_BSIZE * MAX_NCH];
+ int remaining;
+} tta_info_t;
+
+static DB_fileinfo_t *
+tta_open (void) {
+ DB_fileinfo_t *_info = malloc (sizeof (tta_info_t));
+ tta_info_t *info = (tta_info_t *)_info;
+ memset (info, 0, sizeof (tta_info_t));
+ return _info;
+}
+
+static int
+tta_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
+ tta_info_t *info = (tta_info_t *)_info;
+
+ if (open_tta_file (it->fname, &info->tta, 0) != 0) {
+ fprintf (stderr, "tta: failed to open %s\n", it->fname);
+ return -1;
+ }
+
+ if (player_init (&info->tta) != 0) {
+ fprintf (stderr, "tta: failed to init player for %s\n", it->fname);
+ return -1;
+ }
+
+ _info->bps = info->tta.BPS;
+ _info->channels = info->tta.NCH;
+ _info->samplerate = info->tta.SAMPLERATE;
+ _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;
+ info->endsample = (info->tta.DATALENGTH)-1;
+ }
+ return 0;
+}
+
+static void
+tta_free (DB_fileinfo_t *_info) {
+ tta_info_t *info = (tta_info_t *)_info;
+ if (info) {
+ player_stop ();
+ close_tta_file (&info->tta);
+ free (info);
+ }
+}
+
+static int
+tta_read_int16 (DB_fileinfo_t *_info, char *bytes, int size) {
+ tta_info_t *info = (tta_info_t *)_info;
+ if (info->currentsample + size / (2 * _info->channels) > info->endsample) {
+ size = (info->endsample - info->currentsample + 1) * 2 * _info->channels;
+ if (size <= 0) {
+ return 0;
+ }
+ }
+
+ int initsize = size;
+ int out_channels = _info->channels;
+ if (out_channels > 2) {
+ out_channels = 2;
+ }
+ int sample_size = ((_info->bps >> 3) * out_channels);
+
+ while (size > 0) {
+ if (info->remaining > 0) {
+ int n = size / sample_size;
+ n = min (n, info->remaining);
+ int nn = n;
+ char *p = info->buffer;
+ while (n > 0) {
+ memcpy (bytes, p, 2);
+ bytes += 2;
+ if (_info->channels == 2) {
+ memcpy (bytes, p + 2, 2);
+ bytes += 2;
+ }
+ n--;
+ size -= sample_size;
+ p += info->tta.NCH * info->tta.BSIZE;
+ }
+ if (info->remaining > nn) {
+ memmove (info->buffer, p, (info->remaining - nn) * sizeof (float) * _info->channels);
+ }
+ info->remaining -= nn;
+ }
+
+ if (size > 0 && !info->remaining) {
+ info->remaining = get_samples (info->buffer);
+ if (!info->remaining) {
+ break;
+ }
+ }
+ }
+ return initsize-size;
+}
+
+static int
+tta_seek_sample (DB_fileinfo_t *_info, int sample) {
+ tta_info_t *info = (tta_info_t *)_info;
+ if (set_position (sample * 1000 / info->tta.SAMPLERATE / SEEK_STEP) != 0) {
+ fprintf (stderr, "tta: seek failed\n");
+ return -1;
+ }
+ info->currentsample = sample;
+ _info->readpos = (sample - info->startsample) / _info->samplerate;
+ return 0;
+}
+
+static int
+tta_seek (DB_fileinfo_t *_info, float time) {
+ tta_info_t *info = (tta_info_t *)_info;
+ return tta_seek_sample (_info, time * _info->samplerate);
+}
+
+static DB_playItem_t *
+tta_insert (DB_playItem_t *after, const char *fname) {
+ tta_info tta;
+ if (open_tta_file (fname, &tta, 0) != 0) {
+ fprintf (stderr, "tta: failed to open %s\n", fname);
+ return NULL;
+ }
+
+ if (tta.BPS != 16) {
+ fprintf (stderr, "tta: only 16 bit is supported yet, skipped %s\n", fname);
+ return NULL;
+ }
+
+ int totalsamples = tta.DATALENGTH;
+ double dur = tta.LENGTH;
+
+ 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 = "TTA";
+ deadbeef->pl_set_item_duration (it, dur);
+
+ close_tta_file (&tta);
+ DB_FILE *fp = deadbeef->fopen (fname);
+ if (fp) {
+ /*int v2err = */deadbeef->junk_id3v2_read (it, fp);
+ /*int v1err = */deadbeef->junk_id3v1_read (it, fp);
+ deadbeef->fclose (fp);
+ }
+
+ // embedded cue
+ deadbeef->pl_lock ();
+ const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet");
+ DB_playItem_t *cue = NULL;
+ if (cuesheet) {
+ cue = deadbeef->pl_insert_cue_from_buffer (after, it, cuesheet, strlen (cuesheet), totalsamples, tta.SAMPLERATE);
+ if (cue) {
+ deadbeef->pl_item_unref (it);
+ deadbeef->pl_item_unref (cue);
+ deadbeef->pl_unlock ();
+ return cue;
+ }
+ }
+ deadbeef->pl_unlock ();
+
+ cue = deadbeef->pl_insert_cue (after, it, totalsamples, tta.SAMPLERATE);
+ if (cue) {
+ deadbeef->pl_item_unref (it);
+ deadbeef->pl_item_unref (cue);
+ return cue;
+ }
+
+ deadbeef->pl_add_meta (it, "title", NULL);
+ after = deadbeef->pl_insert_item (after, it);
+ deadbeef->pl_item_unref (it);
+
+ return after;
+}
+
+static int tta_read_metadata (DB_playItem_t *it) {
+ DB_FILE *fp = deadbeef->fopen (it->fname);
+ if (!fp) {
+ return -1;
+ }
+ deadbeef->pl_delete_all_meta (it);
+ /*int v2err = */deadbeef->junk_id3v2_read (it, fp);
+ /*int v1err = */deadbeef->junk_id3v1_read (it, fp);
+ deadbeef->pl_add_meta (it, "title", NULL);
+ deadbeef->fclose (fp);
+ return 0;
+}
+
+static int tta_write_metadata (DB_playItem_t *it) {
+ // get options
+
+ int strip_id3v2 = 0;
+ int strip_id3v1 = 0;
+ int write_id3v2 = 1;
+ int write_id3v1 = 1;
+
+ uint32_t junk_flags = 0;
+ if (strip_id3v2) {
+ junk_flags |= JUNK_STRIP_ID3V2;
+ }
+ if (strip_id3v1) {
+ junk_flags |= JUNK_STRIP_ID3V1;
+ }
+ if (write_id3v2) {
+ junk_flags |= JUNK_WRITE_ID3V2;
+ }
+ if (write_id3v1) {
+ junk_flags |= JUNK_WRITE_ID3V1;
+ }
+
+ int id3v2_version = 4;
+ const char *id3v1_encoding = deadbeef->conf_get_str ("mp3.id3v1_encoding", "iso8859-1");
+ return deadbeef->junk_rewrite_tags (it, junk_flags, id3v2_version, id3v1_encoding);
+}
+
+
+static int
+tta_start (void) {
+ return 0;
+}
+
+static int
+tta_stop (void) {
+ return 0;
+}
+
+static const char * exts[] = { "tta", NULL };
+static const char *filetypes[] = { "TTA", 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 = "tta",
+ .plugin.name = "tta decoder",
+ .plugin.descr = "tta decoder using libmppdec",
+ .plugin.author = "Alexey Yakovenko",
+ .plugin.email = "waker@users.sourceforge.net",
+ .plugin.website = "http://deadbeef.sf.net",
+ .plugin.start = tta_start,
+ .plugin.stop = tta_stop,
+ .open = tta_open,
+ .init = tta_init,
+ .free = tta_free,
+ .read_int16 = tta_read_int16,
+// .read_float32 = tta_read_float32,
+ .seek = tta_seek,
+ .seek_sample = tta_seek_sample,
+ .insert = tta_insert,
+ .read_metadata = tta_read_metadata,
+ .write_metadata = tta_write_metadata,
+ .exts = exts,
+ .filetypes = filetypes
+};
+
+DB_plugin_t *
+tta_load (DB_functions_t *api) {
+ deadbeef = api;
+ return DB_PLUGIN (&plugin);
+}