1 /* GStreamer libsndfile plugin
2 * Copyright (C) 1999,2000 Erik Walthinsen <omega@cse.ogi.edu>
3 * 2000,2005 Wim Taymans <wim@fluendo.com>
4 * 2003,2007 Andy Wingo <wingo@pobox.com>
5 *
6 * gstsfsrc.c:
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include <gst/gst-i18n-plugin.h>
29
30 #include "gstsfsrc.h"
31
32 enum
33 {
34 PROP_0,
35 PROP_LOCATION
36 };
37
38 static GstStaticPadTemplate sf_src_factory = GST_STATIC_PAD_TEMPLATE ("src",
39 GST_PAD_SRC,
40 GST_PAD_ALWAYS,
41 GST_STATIC_CAPS ("audio/x-raw-float, "
42 "rate = (int) [ 1, MAX ], "
43 "channels = (int) [ 1, MAX ], "
44 "endianness = (int) BYTE_ORDER, "
45 "width = (int) 32; "
46 "audio/x-raw-int, "
47 "rate = (int) [ 1, MAX ], "
48 "channels = (int) [ 1, MAX ], "
49 "endianness = (int) BYTE_ORDER, "
50 "width = (int) {16, 32}, "
51 "depth = (int) {16, 32}, " "signed = (boolean) true")
52 );
53
54
55 GST_DEBUG_CATEGORY_STATIC (gst_sf_src_debug);
56 #define GST_CAT_DEFAULT gst_sf_src_debug
57
58
59 #define DEFAULT_BUFFER_FRAMES (256)
60
61
62 static void gst_sf_src_finalize (GObject * object);
63 static void gst_sf_src_set_property (GObject * object, guint prop_id,
64 const GValue * value, GParamSpec * pspec);
65 static void gst_sf_src_get_property (GObject * object, guint prop_id,
66 GValue * value, GParamSpec * pspec);
67
68 static gboolean gst_sf_src_start (GstBaseSrc * bsrc);
69 static gboolean gst_sf_src_stop (GstBaseSrc * bsrc);
70 static gboolean gst_sf_src_is_seekable (GstBaseSrc * bsrc);
71 static gboolean gst_sf_src_get_size (GstBaseSrc * bsrc, guint64 * size);
72 static GstFlowReturn gst_sf_src_create (GstBaseSrc * bsrc, guint64 offset,
73 guint length, GstBuffer ** buffer);
74 static GstCaps *gst_sf_src_get_caps (GstBaseSrc * bsrc);
75 static gboolean gst_sf_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps);
76 static gboolean gst_sf_src_check_get_range (GstBaseSrc * bsrc);
77 static void gst_sf_src_fixate (GstBaseSrc * bsrc, GstCaps * caps);
78
79 GST_BOILERPLATE (GstSFSrc, gst_sf_src, GstBaseSrc, GST_TYPE_BASE_SRC);
80
81 static void
gst_sf_src_base_init(gpointer g_class)82 gst_sf_src_base_init (gpointer g_class)
83 {
84 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
85
86 gst_element_class_add_static_pad_template (gstelement_class, &sf_src_factory);
87
88 gst_element_class_set_static_metadata (gstelement_class, "Sndfile source",
89 "Source/Audio",
90 "Read audio streams from disk using libsndfile",
91 "Andy Wingo <wingo at pobox dot com>");
92 GST_DEBUG_CATEGORY_INIT (gst_sf_src_debug, "sfsrc", 0, "sfsrc element");
93 }
94
95 static void
gst_sf_src_class_init(GstSFSrcClass * klass)96 gst_sf_src_class_init (GstSFSrcClass * klass)
97 {
98 GObjectClass *gobject_class;
99 GstBaseSrcClass *gstbasesrc_class;
100
101 gobject_class = G_OBJECT_CLASS (klass);
102 gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
103
104 gobject_class->set_property = gst_sf_src_set_property;
105 gobject_class->get_property = gst_sf_src_get_property;
106
107 g_object_class_install_property (gobject_class, PROP_LOCATION,
108 g_param_spec_string ("location", "File Location",
109 "Location of the file to read", NULL, G_PARAM_READWRITE));
110
111 gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_sf_src_finalize);
112
113 gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_sf_src_start);
114 gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_sf_src_stop);
115 gstbasesrc_class->is_seekable = GST_DEBUG_FUNCPTR (gst_sf_src_is_seekable);
116 gstbasesrc_class->get_size = GST_DEBUG_FUNCPTR (gst_sf_src_get_size);
117 gstbasesrc_class->create = GST_DEBUG_FUNCPTR (gst_sf_src_create);
118 gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_sf_src_get_caps);
119 gstbasesrc_class->set_caps = GST_DEBUG_FUNCPTR (gst_sf_src_set_caps);
120 gstbasesrc_class->check_get_range =
121 GST_DEBUG_FUNCPTR (gst_sf_src_check_get_range);
122 gstbasesrc_class->fixate = GST_DEBUG_FUNCPTR (gst_sf_src_fixate);
123 }
124
125 static void
gst_sf_src_init(GstSFSrc * src,GstSFSrcClass * g_class)126 gst_sf_src_init (GstSFSrc * src, GstSFSrcClass * g_class)
127 {
128 }
129
130 static void
gst_sf_src_finalize(GObject * object)131 gst_sf_src_finalize (GObject * object)
132 {
133 GstSFSrc *src;
134
135 src = GST_SF_SRC (object);
136
137 g_free (src->location);
138
139 G_OBJECT_CLASS (parent_class)->finalize (object);
140 }
141
142 static void
gst_sf_src_set_location(GstSFSrc * this,const gchar * location)143 gst_sf_src_set_location (GstSFSrc * this, const gchar * location)
144 {
145 if (this->file)
146 goto was_open;
147
148 g_free (this->location);
149
150 this->location = location ? g_strdup (location) : NULL;
151
152 return;
153
154 was_open:
155 {
156 g_warning ("Changing the `location' property on sfsrc when "
157 "a file is open not supported.");
158 return;
159 }
160 }
161
162 static void
gst_sf_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)163 gst_sf_src_set_property (GObject * object, guint prop_id, const GValue * value,
164 GParamSpec * pspec)
165 {
166 GstSFSrc *this = GST_SF_SRC (object);
167
168 switch (prop_id) {
169 case PROP_LOCATION:
170 gst_sf_src_set_location (this, g_value_get_string (value));
171 break;
172
173 default:
174 break;
175 }
176 }
177
178 static void
gst_sf_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)179 gst_sf_src_get_property (GObject * object, guint prop_id, GValue * value,
180 GParamSpec * pspec)
181 {
182 GstSFSrc *this = GST_SF_SRC (object);
183
184 switch (prop_id) {
185 case PROP_LOCATION:
186 g_value_set_string (value, this->location);
187 break;
188
189 default:
190 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
191 break;
192 }
193 }
194
195 static GstFlowReturn
gst_sf_src_create(GstBaseSrc * bsrc,guint64 offset,guint length,GstBuffer ** buffer)196 gst_sf_src_create (GstBaseSrc * bsrc, guint64 offset, guint length,
197 GstBuffer ** buffer)
198 {
199 GstSFSrc *this;
200 GstBuffer *buf;
201 /* FIXME discont is set but not used */
202 #if 0
203 gboolean discont = FALSE;
204 #endif
205 sf_count_t bytes_read;
206
207 this = GST_SF_SRC (bsrc);
208
209 if (G_UNLIKELY (offset % this->bytes_per_frame))
210 goto bad_offset;
211 if (G_UNLIKELY (length % this->bytes_per_frame))
212 goto bad_length;
213
214 offset /= this->bytes_per_frame;
215
216 if (G_UNLIKELY (this->offset != offset)) {
217 sf_count_t pos;
218
219 pos = sf_seek (this->file, offset, SEEK_SET);
220
221 if (G_UNLIKELY (pos < 0 || pos != offset))
222 goto seek_failed;
223
224 this->offset = offset;
225 #if 0
226 discont = TRUE;
227 #endif
228 }
229
230 buf = gst_buffer_new_and_alloc (length);
231
232 /* now make length in frames */
233 length /= this->bytes_per_frame;
234
235 bytes_read = this->reader (this->file, GST_BUFFER_DATA (buf), length);
236 if (G_UNLIKELY (bytes_read < 0))
237 goto could_not_read;
238
239 if (G_UNLIKELY (bytes_read == 0 && length > 0))
240 goto eos;
241
242 GST_BUFFER_SIZE (buf) = bytes_read * this->bytes_per_frame;
243
244 GST_BUFFER_OFFSET (buf) = offset;
245 GST_BUFFER_OFFSET_END (buf) = offset + length;
246 GST_BUFFER_TIMESTAMP (buf) = gst_util_uint64_scale_int (offset,
247 GST_SECOND, this->rate);
248 GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (offset + length,
249 GST_SECOND, this->rate) - GST_BUFFER_TIMESTAMP (buf);
250
251 gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (bsrc)));
252
253 *buffer = buf;
254
255 this->offset += length;
256
257 return GST_FLOW_OK;
258
259 /* ERROR */
260 bad_offset:
261 {
262 GST_ELEMENT_ERROR (this, RESOURCE, SEEK,
263 (NULL), ("offset %" G_GUINT64_FORMAT " not divisible by %d bytes per "
264 "frame", offset, this->bytes_per_frame));
265 return GST_FLOW_ERROR;
266 }
267 bad_length:
268 {
269 GST_ELEMENT_ERROR (this, RESOURCE, SEEK, (NULL),
270 ("length %u not divisible by %d bytes per frame", length,
271 this->bytes_per_frame));
272 return GST_FLOW_ERROR;
273 }
274 seek_failed:
275 {
276 GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
277 return GST_FLOW_ERROR;
278 }
279 could_not_read:
280 {
281 GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
282 gst_buffer_unref (buf);
283 return GST_FLOW_ERROR;
284 }
285 eos:
286 {
287 GST_DEBUG ("EOS, baby");
288 gst_buffer_unref (buf);
289 return GST_FLOW_UNEXPECTED;
290 }
291 }
292
293 static gboolean
gst_sf_src_is_seekable(GstBaseSrc * basesrc)294 gst_sf_src_is_seekable (GstBaseSrc * basesrc)
295 {
296 return TRUE;
297 }
298
299 static gboolean
gst_sf_src_get_size(GstBaseSrc * basesrc,guint64 * size)300 gst_sf_src_get_size (GstBaseSrc * basesrc, guint64 * size)
301 {
302 GstSFSrc *this;
303 sf_count_t end;
304
305 this = GST_SF_SRC (basesrc);
306
307 end = sf_seek (this->file, 0, SEEK_END);
308
309 sf_seek (this->file, this->offset, SEEK_SET);
310
311 *size = end * this->bytes_per_frame;
312
313 return TRUE;
314 }
315
316 static gboolean
gst_sf_src_open_file(GstSFSrc * this)317 gst_sf_src_open_file (GstSFSrc * this)
318 {
319 int mode;
320 SF_INFO info;
321
322 g_return_val_if_fail (this->file == NULL, FALSE);
323
324 if (!this->location)
325 goto no_filename;
326
327 mode = SFM_READ;
328 info.format = 0;
329
330 this->file = sf_open (this->location, mode, &info);
331
332 if (!this->file)
333 goto open_failed;
334
335 this->channels = info.channels;
336 this->rate = info.samplerate;
337 /* do something with info.seekable? */
338
339 return TRUE;
340
341 no_filename:
342 {
343 GST_ELEMENT_ERROR (this, RESOURCE, NOT_FOUND,
344 (_("No file name specified for writing.")), (NULL));
345 return FALSE;
346 }
347 open_failed:
348 {
349 GST_ELEMENT_ERROR (this, RESOURCE, OPEN_WRITE,
350 (_("Could not open file \"%s\" for writing."), this->location),
351 ("soundfile error: %s", sf_strerror (NULL)));
352 return FALSE;
353 }
354 }
355
356 static void
gst_sf_src_close_file(GstSFSrc * this)357 gst_sf_src_close_file (GstSFSrc * this)
358 {
359 int err = 0;
360
361 g_return_if_fail (this->file != NULL);
362
363 GST_INFO_OBJECT (this, "Closing file %s", this->location);
364
365 if ((err = sf_close (this->file)))
366 goto close_failed;
367
368 this->file = NULL;
369 this->offset = 0;
370 this->channels = 0;
371 this->rate = 0;
372
373 return;
374
375 close_failed:
376 {
377 GST_ELEMENT_ERROR (this, RESOURCE, CLOSE,
378 ("Could not close file file \"%s\".", this->location),
379 ("soundfile error: %s", sf_error_number (err)));
380 return;
381 }
382 }
383
384 static gboolean
gst_sf_src_start(GstBaseSrc * basesrc)385 gst_sf_src_start (GstBaseSrc * basesrc)
386 {
387 GstSFSrc *this = GST_SF_SRC (basesrc);
388
389 return gst_sf_src_open_file (this);
390 }
391
392 /* unmap and close the file */
393 static gboolean
gst_sf_src_stop(GstBaseSrc * basesrc)394 gst_sf_src_stop (GstBaseSrc * basesrc)
395 {
396 GstSFSrc *this = GST_SF_SRC (basesrc);
397
398 gst_sf_src_close_file (this);
399
400 return TRUE;
401 }
402
403 static GstCaps *
gst_sf_src_get_caps(GstBaseSrc * bsrc)404 gst_sf_src_get_caps (GstBaseSrc * bsrc)
405 {
406 GstSFSrc *this;
407 GstCaps *ret;
408
409 this = GST_SF_SRC (bsrc);
410
411 ret = gst_caps_copy (gst_pad_get_pad_template_caps (bsrc->srcpad));
412
413 if (this->file) {
414 GstStructure *s;
415 gint i;
416
417 for (i = 0; i < gst_caps_get_size (ret); i++) {
418 s = gst_caps_get_structure (ret, i);
419 gst_structure_set (s, "channels", G_TYPE_INT, this->channels,
420 "rate", G_TYPE_INT, this->rate, NULL);
421 }
422 }
423
424 return ret;
425 }
426
427 static gboolean
gst_sf_src_set_caps(GstBaseSrc * bsrc,GstCaps * caps)428 gst_sf_src_set_caps (GstBaseSrc * bsrc, GstCaps * caps)
429 {
430 GstSFSrc *this = (GstSFSrc *) bsrc;
431 GstStructure *structure;
432 gint width;
433
434 structure = gst_caps_get_structure (caps, 0);
435
436 if (!this->file)
437 goto file_not_open;
438
439 if (!gst_structure_get_int (structure, "width", &width))
440 goto impossible;
441
442 if (gst_structure_has_name (structure, "audio/x-raw-int")) {
443 switch (width) {
444 case 16:
445 this->reader = (GstSFReader) sf_readf_short;
446 break;
447 case 32:
448 this->reader = (GstSFReader) sf_readf_int;
449 break;
450 default:
451 goto impossible;
452 }
453 } else {
454 switch (width) {
455 case 32:
456 this->reader = (GstSFReader) sf_readf_float;
457 break;
458 default:
459 goto impossible;
460 }
461 }
462
463 this->bytes_per_frame = width * this->channels / 8;
464
465 return TRUE;
466
467 impossible:
468 {
469 g_warning ("something impossible happened");
470 return FALSE;
471 }
472 file_not_open:
473 {
474 GST_WARNING_OBJECT (this, "file has to be open in order to set caps");
475 return FALSE;
476 }
477 }
478
479 static gboolean
gst_sf_src_check_get_range(GstBaseSrc * bsrc)480 gst_sf_src_check_get_range (GstBaseSrc * bsrc)
481 {
482 return TRUE;
483 }
484
485 static void
gst_sf_src_fixate(GstBaseSrc * bsrc,GstCaps * caps)486 gst_sf_src_fixate (GstBaseSrc * bsrc, GstCaps * caps)
487 {
488 GstStructure *s;
489 gint width, depth;
490
491 s = gst_caps_get_structure (caps, 0);
492
493 gst_structure_fixate_field_nearest_int (s, "width", 16);
494
495 /* fields for int */
496 if (gst_structure_has_field (s, "depth")) {
497 gst_structure_get_int (s, "width", &width);
498 /* round width to nearest multiple of 8 for the depth */
499 depth = GST_ROUND_UP_8 (width);
500 gst_structure_fixate_field_nearest_int (s, "depth", depth);
501 }
502 if (gst_structure_has_field (s, "signed"))
503 gst_structure_fixate_field_boolean (s, "signed", TRUE);
504 if (gst_structure_has_field (s, "endianness"))
505 gst_structure_fixate_field_nearest_int (s, "endianness", G_BYTE_ORDER);
506 }
507