1 // this is a decoder plugin skeleton
2 // use to create new decoder plugins
3
4 #include <stdlib.h>
5 #include <string.h>
6 #include <deadbeef/deadbeef.h>
7
8 #define trace(...) { fprintf(stderr, __VA_ARGS__); }
9
10 static DB_decoder_t plugin;
11 static DB_functions_t *deadbeef;
12
13 typedef struct {
14 DB_fileinfo_t info;
15 int startsample;
16 int endsample;
17 int currentsample;
18 } example_info_t;
19
20 static const char * exts[] = { "example", NULL }; // e.g. mp3
21
22 // allocate codec control structure
23 static DB_fileinfo_t *
example_open(uint32_t hints)24 example_open (uint32_t hints) {
25 DB_fileinfo_t *_info = malloc (sizeof (example_info_t));
26 example_info_t *info = (example_info_t *)_info;
27 memset (info, 0, sizeof (example_info_t));
28 return _info;
29 }
30
31 // prepare to decode the track, fill in mandatory plugin fields
32 // return -1 on failure
33 static int
example_init(DB_fileinfo_t * _info,DB_playItem_t * it)34 example_init (DB_fileinfo_t *_info, DB_playItem_t *it) {
35 example_info_t *info = (example_info_t *)_info;
36
37 // take this parameters from your input file
38 // we set constants for clarity sake
39 _info->fmt.bps = 16;
40 _info->fmt.channels = 2;
41 _info->fmt.samplerate = 44100;
42 for (int i = 0; i < _info->fmt.channels; i++) {
43 _info->fmt.channelmask |= 1 << i;
44 }
45 _info->readpos = 0;
46 _info->plugin = &plugin;
47
48 if (it->endsample > 0) {
49 info->startsample = it->startsample;
50 info->endsample = it->endsample;
51 plugin.seek_sample (_info, 0);
52 }
53 else {
54 info->startsample = 0;
55 int TOTALSAMPLES = 1000; // calculate from file
56 info->endsample = TOTALSAMPLES-1;
57 }
58 return 0;
59 }
60
61 // free everything allocated in _init
62 static void
example_free(DB_fileinfo_t * _info)63 example_free (DB_fileinfo_t *_info) {
64 example_info_t *info = (example_info_t *)_info;
65 if (info) {
66 free (info);
67 }
68 }
69
70
71 // try decode `size' bytes
72 // return number of decoded bytes
73 // or 0 on EOF/error
74 static int
example_read(DB_fileinfo_t * _info,char * bytes,int size)75 example_read (DB_fileinfo_t *_info, char *bytes, int size) {
76 example_info_t *info = (example_info_t *)_info;
77 info->currentsample += size / (_info->fmt.channels * _info->fmt.bps/8);
78 return size;
79 }
80
81 // seek to specified sample (frame)
82 // return 0 on success
83 // return -1 on failure
84 static int
example_seek_sample(DB_fileinfo_t * _info,int sample)85 example_seek_sample (DB_fileinfo_t *_info, int sample) {
86 example_info_t *info = (example_info_t *)_info;
87
88 info->currentsample = sample + info->startsample;
89 _info->readpos = (float)sample / _info->fmt.samplerate;
90 return 0;
91 }
92
93 // seek to specified time in seconds
94 // return 0 on success
95 // return -1 on failure
96 static int
example_seek(DB_fileinfo_t * _info,float time)97 example_seek (DB_fileinfo_t *_info, float time) {
98 return example_seek_sample (_info, time * _info->fmt.samplerate);
99 }
100
101 // read information from the track
102 // load/process cuesheet if exists
103 // insert track into playlist
104 // return track pointer on success
105 // return NULL on failure
106
107 static DB_playItem_t *
example_insert(ddb_playlist_t * plt,DB_playItem_t * after,const char * fname)108 example_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) {
109 // open file
110 DB_FILE *fp = deadbeef->fopen (fname);
111 if (!fp) {
112 trace ("example: failed to fopen %s\n", fname);
113 return NULL;
114 }
115
116 // decoder_* functions are imaginary -- you should replace them with real
117 // decoder library calls
118 decoder_info_t *di = decoder_open ();
119 if (!di) {
120 trace ("example: failed to init decoder\n");
121 return NULL;
122 }
123 // read track info/tags
124 track_info_t ti;
125 if (decoder_read_info (&ti) < 0) {
126 trace ("example: failed to read info\n");
127 decoder_free (di);
128 return NULL;
129 }
130
131 // replace "example" with your file type (e.g. MP3, WAV, etc)
132 const char *ft = "example";
133
134 // no cuesheet, prepare track for addition
135 DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id);
136
137 deadbeef->pl_replace_meta (it, ":FILETYPE", ft);
138 deadbeef->plt_set_item_duration (plt, it, (float)ti.total_num_samples/ti.samplerate);
139
140 // now we should have track duration, and can try loading cuesheet
141 // 1st try embedded cuesheet
142 if (ti.embeddedcuesheet[0]) {
143 DB_playItem_t *cue = deadbeef->plt_insert_cue_from_buffer (plt, after, it, ti.embeddedcuesheet, strlen (ti.embeddedcuesheet), ti.total_num_samples, ti.samplerate);
144 if (cue) {
145 deadbeef->pl_item_unref (it);
146 deadbeef->pl_item_unref (cue);
147 // cuesheet loaded
148 decoder_free (di);
149 return cue;
150 }
151 }
152
153 // embedded cuesheet not found, try external one
154 DB_playItem_t *cue = deadbeef->plt_insert_cue (plt, after, it, ti.total_num_samples, ti.samplerate);
155 if (cue) {
156 // cuesheet loaded
157 deadbeef->pl_item_unref (it);
158 deadbeef->pl_item_unref (cue);
159 decoder_free (di);
160 return cue;
161 }
162
163
164 // add metainfo
165 if (!strlen (ti.title)) {
166 // title is empty, this call will set track title to filename without extension
167 deadbeef->pl_add_meta (it, "title", NULL);
168 }
169 else {
170 deadbeef->pl_add_meta (it, "title", ti.title);
171 }
172 deadbeef->pl_add_meta (it, "artist", ti.artist);
173 // ... etc ...
174
175 // free decoder
176 decoder_free (di);
177
178 // now the track is ready, insert into playlist
179 after = deadbeef->plt_insert_item (plt, after, it);
180 deadbeef->pl_item_unref (it);
181 return after;
182 }
183
184 static int
example_start(void)185 example_start (void) {
186 // do one-time plugin initialization here
187 // e.g. starting threads for background processing, subscribing to events, etc
188 // return 0 on success
189 // return -1 on failure
190 return 0;
191 }
192
193 static int
example_stop(void)194 example_stop (void) {
195 // undo everything done in _start here
196 // return 0 on success
197 // return -1 on failure
198 return 0;
199 }
200
201 // define plugin interface
202 static DB_decoder_t plugin = {
203 DB_PLUGIN_SET_API_VERSION
204 .plugin.version_major = 0,
205 .plugin.version_minor = 1,
206 .plugin.type = DB_PLUGIN_DECODER,
207 .plugin.name = "short plugin name",
208 .plugin.id = "example",
209 .plugin.descr = "plugin description",
210 .plugin.copyright = "copyright message - author(s), license, etc",
211 .plugin.start = example_start,
212 .plugin.stop = example_stop,
213 .open = example_open,
214 .init = example_init,
215 .free = example_free,
216 .read = example_read,
217 .seek = example_seek,
218 .seek_sample = example_seek_sample,
219 .insert = example_insert,
220 .exts = exts,
221 };
222
223 DB_plugin_t *
example_load(DB_functions_t * api)224 example_load (DB_functions_t *api) {
225 deadbeef = api;
226 return DB_PLUGIN (&plugin);
227 }
228
229