1 /*
2 * libInstPatch
3 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; version 2.1
8 * of the License only.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 * 02110-1301, USA or on the web at http://www.gnu.org.
19 */
20 /**
21 * SECTION: IpatchSample
22 * @short_description: Sample audio interface
23 * @see_also:
24 * @stability: Stable
25 *
26 * This interface provides a basic API for accessing audio of sample objects.
27 */
28 #include <stdio.h>
29
30 #include <string.h>
31 #include <glib.h>
32 #include <glib-object.h>
33 #include <glib/gstdio.h> /* for g_unlink */
34
35 #include "IpatchSample.h"
36 #include "IpatchSndFile.h"
37 #include "IpatchSampleStoreSndFile.h"
38 #include "builtin_enums.h"
39 #include "sample.h"
40 #include "i18n.h"
41 #include "ipatch_priv.h"
42
43
44 /* some public loop type arrays for use with IpatchSample interfaces */
45 int ipatch_sample_loop_types_standard[] =
46 {
47 IPATCH_SAMPLE_LOOP_NONE,
48 IPATCH_SAMPLE_LOOP_STANDARD,
49 IPATCH_SAMPLE_LOOP_TYPE_TERM /* terminator */
50 };
51
52 int ipatch_sample_loop_types_standard_release[] =
53 {
54 IPATCH_SAMPLE_LOOP_NONE,
55 IPATCH_SAMPLE_LOOP_STANDARD,
56 IPATCH_SAMPLE_LOOP_RELEASE,
57 IPATCH_SAMPLE_LOOP_TYPE_TERM /* terminator */
58 };
59
60
61 static void ipatch_sample_interface_init(IpatchSampleIface *sample_iface);
62
63
64 GType
ipatch_sample_get_type(void)65 ipatch_sample_get_type(void)
66 {
67 static GType itype = 0;
68
69 if(!itype)
70 {
71 static const GTypeInfo info =
72 {
73 sizeof(IpatchSampleIface),
74 NULL, /* base_init */
75 NULL, /* base_finalize */
76 (GClassInitFunc) ipatch_sample_interface_init,
77 (GClassFinalizeFunc) NULL
78 };
79
80 itype = g_type_register_static(G_TYPE_INTERFACE, "IpatchSample", &info, 0);
81 g_type_interface_add_prerequisite(itype, IPATCH_TYPE_ITEM);
82 }
83
84 return (itype);
85 }
86
87 static void
ipatch_sample_interface_init(IpatchSampleIface * sample_iface)88 ipatch_sample_interface_init(IpatchSampleIface *sample_iface)
89 {
90 g_object_interface_install_property(sample_iface,
91 ipatch_sample_new_property_param_spec("sample-data",
92 G_PARAM_READABLE));
93 g_object_interface_install_property(sample_iface,
94 ipatch_sample_new_property_param_spec("sample-size",
95 G_PARAM_READABLE));
96 g_object_interface_install_property(sample_iface,
97 ipatch_sample_new_property_param_spec("sample-format",
98 G_PARAM_READABLE));
99 g_object_interface_install_property(sample_iface,
100 ipatch_sample_new_property_param_spec("sample-rate",
101 G_PARAM_READABLE));
102 g_object_interface_install_property(sample_iface,
103 ipatch_sample_new_property_param_spec("loop-type",
104 G_PARAM_READABLE));
105 g_object_interface_install_property(sample_iface,
106 ipatch_sample_new_property_param_spec("loop-start",
107 G_PARAM_READABLE));
108 g_object_interface_install_property(sample_iface,
109 ipatch_sample_new_property_param_spec("loop-end",
110 G_PARAM_READABLE));
111 g_object_interface_install_property(sample_iface,
112 ipatch_sample_new_property_param_spec("root-note",
113 G_PARAM_READABLE));
114 g_object_interface_install_property(sample_iface,
115 ipatch_sample_new_property_param_spec("fine-tune",
116 G_PARAM_READABLE));
117 }
118
119 /**
120 * ipatch_sample_get_loop_types: (skip)
121 * @sample: Object with #IpatchSample interface
122 *
123 * Get an array of supported loop type enums for a sample object.
124 *
125 * Returns: -1 terminated array of #IpatchSampleLoopType values. If no loop
126 * types are supported, then %NULL is returned. Array is internal and should
127 * not be modified or freed.
128 */
129 int *
ipatch_sample_get_loop_types(IpatchSample * sample)130 ipatch_sample_get_loop_types(IpatchSample *sample)
131 {
132 GType type;
133 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL);
134
135 type = G_OBJECT_TYPE(sample);
136 return (ipatch_sample_type_get_loop_types(type));
137 }
138
139 /**
140 * ipatch_sample_type_get_loop_types: (skip)
141 * @type: A GType that has a #IpatchItem interface
142 *
143 * Like ipatch_sample_get_loop_types() but retrieves the supported loop types
144 * from an object type rather than an instance of an object.
145 *
146 * Returns: -1 terminated array of #IpatchSampleLoopType values. If no loop
147 * types are supported, then %NULL is returned. Array is internal and should
148 * not be modified or freed.
149 */
150 int *
ipatch_sample_type_get_loop_types(GType type)151 ipatch_sample_type_get_loop_types(GType type)
152 {
153 GObjectClass *obj_class;
154 IpatchSampleIface *iface;
155
156 g_return_val_if_fail(g_type_is_a(type, IPATCH_TYPE_SAMPLE), NULL);
157
158 obj_class = g_type_class_ref(type);
159 iface = g_type_interface_peek(obj_class, IPATCH_TYPE_SAMPLE);
160 g_type_class_unref(obj_class);
161
162 return (iface->loop_types);
163 }
164
165 /**
166 * ipatch_sample_get_loop_types_len: (rename-to ipatch_sample_get_loop_types)
167 * @sample: Object with #IpatchSample interface
168 * @len: (out) (optional): Location to store number of indeces in returned array
169 * (not including -1 terminator), can be %NULL to ignore
170 *
171 * Get an array of supported loop type enums for a sample object.
172 *
173 * Returns: (array length=len): -1 terminated array of #IpatchSampleLoopType values.
174 * If no loop types are supported, then %NULL is returned. Array is internal and should
175 * not be modified or freed.
176 *
177 * Since: 1.1.0
178 */
179 int *
ipatch_sample_get_loop_types_len(IpatchSample * sample,int * len)180 ipatch_sample_get_loop_types_len(IpatchSample *sample, int *len)
181 {
182 GType type;
183
184 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL);
185
186 type = G_OBJECT_TYPE(sample);
187 return (ipatch_sample_type_get_loop_types_len(type, len));
188 }
189
190 /**
191 * ipatch_sample_type_get_loop_types_len:
192 * @type: A GType that has a #IpatchItem interface
193 * @len: (out) (optional): Location to store number of indeces in returned array
194 * (not including -1 terminator), can be %NULL to ignore
195 *
196 * Like ipatch_sample_get_loop_types_len() but retrieves the supported loop types
197 * from an object type rather than an instance of an object.
198 *
199 * Returns: (array length=len): -1 terminated array of #IpatchSampleLoopType values.
200 * If no loop types are supported, then %NULL is returned. Array is internal and should
201 * not be modified or freed.
202 *
203 * Since: 1.1.0
204 */
205 int *
ipatch_sample_type_get_loop_types_len(GType type,int * len)206 ipatch_sample_type_get_loop_types_len(GType type, int *len)
207 {
208 GObjectClass *obj_class;
209 IpatchSampleIface *iface;
210 int *tp;
211
212 g_return_val_if_fail(g_type_is_a(type, IPATCH_TYPE_SAMPLE), NULL);
213
214 obj_class = g_type_class_ref(type);
215 iface = g_type_interface_peek(obj_class, IPATCH_TYPE_SAMPLE);
216 g_type_class_unref(obj_class);
217
218 if(!iface->loop_types)
219 {
220 return (NULL);
221 }
222
223 if(len)
224 for(*len = 0, tp = iface->loop_types; *tp != -1; *len = *len + 1);
225
226 return (iface->loop_types);
227 }
228
229 /**
230 * ipatch_sample_set_format:
231 * @sample: Sample to set format of
232 * @format: Sample format to assign to sample (see #IpatchSampleWidth, etc)
233 *
234 * Set sample format of a new sample. Should only be assigned once. Same as
235 * assigning to a sample's "sample-format" property.
236 */
237 void
ipatch_sample_set_format(IpatchSample * sample,int format)238 ipatch_sample_set_format(IpatchSample *sample, int format)
239 {
240 g_return_if_fail(IPATCH_IS_SAMPLE(sample));
241 g_object_set(sample, "sample-format", format, NULL);
242 }
243
244 /**
245 * ipatch_sample_get_format:
246 * @sample: Sample to get format of
247 *
248 * Get the sample format of a sample. Same as getting a sample's "sample-format"
249 * property.
250 *
251 * Returns: Sample format integer (see #IpatchSampleWidth, etc).
252 */
253 int
ipatch_sample_get_format(IpatchSample * sample)254 ipatch_sample_get_format(IpatchSample *sample)
255 {
256 int format;
257
258 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), 0);
259 g_object_get(sample, "sample-format", &format, NULL);
260
261 return (format);
262 }
263
264 /**
265 * ipatch_sample_set_size:
266 * @sample: Sample to set size of
267 * @size: Size to assign (in frames)
268 *
269 * Set the size of a sample. Should be done once, and only once when created.
270 */
271 void
ipatch_sample_set_size(IpatchSample * sample,guint size)272 ipatch_sample_set_size(IpatchSample *sample, guint size)
273 {
274 g_return_if_fail(IPATCH_IS_SAMPLE(sample));
275 g_object_set(sample, "sample-size", size, NULL);
276 }
277
278 /**
279 * ipatch_sample_get_size:
280 * @sample: Sample to get size of
281 * @bytes: (out) (optional): Location to store sample size in
282 * bytes (size * frame size) or %NULL to ignore
283 *
284 * Get the size of a sample. Same as getting a sample's "sample-size"
285 * property.
286 *
287 * Returns: Sample size (in frames)
288 */
289 guint
ipatch_sample_get_size(IpatchSample * sample,guint * bytes)290 ipatch_sample_get_size(IpatchSample *sample, guint *bytes)
291 {
292 guint size;
293
294 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), 0);
295 g_object_get(sample, "sample-size", &size, NULL);
296
297 if(bytes)
298 {
299 *bytes = size * ipatch_sample_get_frame_size(sample);
300 }
301
302 return (size);
303 }
304
305 /**
306 * ipatch_sample_get_frame_size:
307 * @sample: Sample to get data frame size of
308 *
309 * A convenience function to get size of a single sample frame for a given
310 * @sample. This is useful for determining buffer allocation sizes when
311 * reading or writing data.
312 *
313 * Returns: Size in bytes of a single sample frame
314 */
315 int
ipatch_sample_get_frame_size(IpatchSample * sample)316 ipatch_sample_get_frame_size(IpatchSample *sample)
317 {
318 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), 0);
319 return (ipatch_sample_format_size(ipatch_sample_get_format(sample)));
320 }
321
322 /**
323 * ipatch_sample_get_sample_data:
324 * @sample: Sample to get sample data from
325 *
326 * Get sample data object from a sample. Not every sample object supports this
327 * property, in which case %NULL is returned.
328 *
329 * Returns: (transfer full): Sample data object of the sample or %NULL if not set or unsupported
330 * by this sample type. Caller owns a reference to the returned object.
331 */
332 IpatchSampleData *
ipatch_sample_get_sample_data(IpatchSample * sample)333 ipatch_sample_get_sample_data(IpatchSample *sample)
334 {
335 IpatchSampleData *sampledata;
336
337 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL);
338 g_object_get(sample, "sample-data", &sampledata, NULL); /* ++ ref */
339
340 return (sampledata); /* !! caller takes over ref */
341 }
342
343 /**
344 * ipatch_sample_set_sample_data:
345 * @sample: Sample to set sample data of
346 *
347 * Set sample data object of a sample. Not every sample object supports writing
348 * to this property, in which case %FALSE will be returned.
349 *
350 * Returns: %TRUE if the sample supports this property and it was assigned,
351 * %FALSE otherwise.
352 */
353 gboolean
ipatch_sample_set_sample_data(IpatchSample * sample,IpatchSampleData * sampledata)354 ipatch_sample_set_sample_data(IpatchSample *sample, IpatchSampleData *sampledata)
355 {
356 GParamSpec *pspec;
357
358 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
359 g_return_val_if_fail(!sampledata || IPATCH_IS_SAMPLE_DATA(sampledata), FALSE);
360
361 pspec = g_object_class_find_property(G_OBJECT_GET_CLASS(sample), "sample-data");
362
363 if(!(pspec->flags & G_PARAM_WRITABLE))
364 {
365 return (FALSE);
366 }
367
368 g_object_set(sample, "sample-data", sampledata, NULL);
369 return (TRUE);
370 }
371
372 /**
373 * ipatch_sample_read: (skip)
374 * @sample: Sample to read from
375 * @offset: Offset in frames to read from
376 * @frames: Number of frames to read
377 * @buf: Buffer to store sample data in (should be at least @frames *
378 * sizeof (frame), the frame size can be had from
379 * ipatch_sample_get_frame_size()).
380 * @err: Location to store error info or %NULL
381 *
382 * Read sample data from a sample. This is a convenience function which
383 * opens/reads/closes a #IpatchSampleHandle and is therefore not as efficient
384 * when making multiple accesses. Sample data transform
385 * is also not handled (see ipatch_sample_read_transform()).
386 *
387 * Returns: %TRUE on success, %FALSE on error (in which case
388 * @err may be set).
389 */
390 gboolean
ipatch_sample_read(IpatchSample * sample,guint offset,guint frames,gpointer buf,GError ** err)391 ipatch_sample_read(IpatchSample *sample, guint offset, guint frames,
392 gpointer buf, GError **err)
393 {
394 IpatchSampleHandle handle;
395 gpointer retval;
396
397 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
398
399 if(!ipatch_sample_handle_open(sample, &handle, 'r', 0, 0, err))
400 {
401 return (FALSE);
402 }
403
404 retval = ipatch_sample_handle_read(&handle, offset, frames, buf, err);
405
406 ipatch_sample_handle_close(&handle);
407
408 return (retval != NULL);
409 }
410
411 /**
412 * ipatch_sample_read_size: (rename-to ipatch_sample_read)
413 * @sample: Sample to read from
414 * @offset: Offset in frames to read from
415 * @size: Size of data to read in bytes
416 * @err: Location to store error info or %NULL
417 *
418 * Read sample data from a sample. Like ipatch_sample_read() but
419 * is designed to be GObject introspection friendly and returned buffer is allocated.
420 *
421 * Returns: (array length=size) (element-type guint8) (transfer full): Newly
422 * allocated buffer with read data, %NULL on error (in which case
423 * @err may be set). Free the buffer with g_free() when finished with it.
424 *
425 * Since: 1.1.0
426 */
427 gpointer
ipatch_sample_read_size(IpatchSample * sample,guint offset,guint size,GError ** err)428 ipatch_sample_read_size(IpatchSample *sample, guint offset, guint size, GError **err)
429 {
430 int frame_size;
431 gpointer buf;
432
433 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL);
434 g_return_val_if_fail(size > 0, NULL);
435
436 frame_size = ipatch_sample_get_frame_size(sample);
437 g_return_val_if_fail(frame_size > 0, NULL);
438 g_return_val_if_fail(size % frame_size == 0, NULL);
439
440 buf = g_malloc(size); // ++ alloc buf
441
442 if(!ipatch_sample_read(sample, offset, size / frame_size, buf, err))
443 {
444 g_free(buf); // -- free buf on error
445 return (NULL);
446 }
447
448 return (buf); // !! caller takes over
449 }
450
451 /**
452 * ipatch_sample_write: (skip)
453 * @sample: Sample to write to
454 * @offset: Offset in frames to write to
455 * @frames: Number of frames to write
456 * @buf: Buffer of sample data to write (should be at least @frames *
457 * sizeof (frame), the frame size can be had from
458 * ipatch_sample_get_frame_size()).
459 * @err: Location to store error info or %NULL
460 *
461 * Write sample data to a sample. This is a convenience function which
462 * opens/writes/closes a #IpatchSampleHandle and is therefore not as efficient
463 * when making multiple accesses. Sample data transform
464 * is also not handled (see ipatch_sample_write_transform()).
465 *
466 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
467 */
468 gboolean
ipatch_sample_write(IpatchSample * sample,guint offset,guint frames,gconstpointer buf,GError ** err)469 ipatch_sample_write(IpatchSample *sample, guint offset, guint frames,
470 gconstpointer buf, GError **err)
471 {
472 IpatchSampleHandle handle;
473 gboolean retval;
474
475 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
476
477 if(!ipatch_sample_handle_open(sample, &handle, 'w', 0, 0, err))
478 {
479 return (FALSE);
480 }
481
482 retval = ipatch_sample_handle_write(&handle, offset, frames, buf, err);
483
484 ipatch_sample_handle_close(&handle);
485
486 return (retval);
487 }
488
489 /**
490 * ipatch_sample_write_size: (rename-to ipatch_sample_write)
491 * @sample: Sample to write to
492 * @offset: Offset in frames to write to
493 * @buf: (array length=size) (element-type guint8) (transfer none): Buffer of
494 * sample data to write
495 * @size: Size of buffer (must be multiple of audio frame size)
496 * @err: Location to store error info or %NULL
497 *
498 * Write sample data to a sample. Like ipatch_sample_write() but is designed
499 * to be GObject Inspection friendly.
500 *
501 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
502 *
503 * Since: 1.1.0
504 */
505 gboolean
ipatch_sample_write_size(IpatchSample * sample,guint offset,gconstpointer buf,guint size,GError ** err)506 ipatch_sample_write_size(IpatchSample *sample, guint offset,
507 gconstpointer buf, guint size, GError **err)
508 {
509 int frame_size;
510
511 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
512 g_return_val_if_fail(size > 0, FALSE);
513
514 frame_size = ipatch_sample_get_frame_size(sample);
515 g_return_val_if_fail(frame_size > 0, FALSE);
516 g_return_val_if_fail(size % frame_size == 0, FALSE);
517
518 return (ipatch_sample_write(sample, offset, size / frame_size, buf, err));
519 }
520
521 /**
522 * ipatch_sample_read_transform: (skip)
523 * @sample: Sample to read from
524 * @offset: Offset in frames to read from
525 * @frames: Number of frames to read
526 * @buf: Buffer to store sample data in (should be at least @frames *
527 * ipatch_sample_format_size() of @format).
528 * @format: Format to transform sample data to (if its the same as the native
529 * format of @sample no transformation occurs)
530 * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use
531 * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping
532 * (see ipatch_sample_get_transform_funcs() for details).
533 * @err: Location to store error info or %NULL
534 *
535 * Like ipatch_sample_read() but allows for sample transformation.
536 *
537 * Returns: %TRUE on success, %FALSE on error (in which case
538 * @err may be set).
539 */
540 gboolean
ipatch_sample_read_transform(IpatchSample * sample,guint offset,guint frames,gpointer buf,int format,guint32 channel_map,GError ** err)541 ipatch_sample_read_transform(IpatchSample *sample, guint offset, guint frames,
542 gpointer buf, int format, guint32 channel_map,
543 GError **err)
544 {
545 IpatchSampleHandle handle;
546 gpointer retval;
547
548 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
549
550 if(!ipatch_sample_handle_open(sample, &handle, 'r', format, channel_map, err))
551 {
552 return (FALSE);
553 }
554
555 retval = ipatch_sample_handle_read(&handle, offset, frames, buf, err);
556
557 ipatch_sample_handle_close(&handle);
558
559 return (retval != NULL);
560 }
561
562 /**
563 * ipatch_sample_read_transform_size: (rename-to ipatch_sample_read_transform)
564 * @sample: Sample to read from
565 * @offset: Offset in frames to read from
566 * @size: Size of sample data to read (in bytes) after conversion
567 * @format: Format to transform sample data to (if its the same as the native
568 * format of @sample no transformation occurs)
569 * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use
570 * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping
571 * (see ipatch_sample_get_transform_funcs() for details).
572 * @err: Location to store error info or %NULL
573 *
574 * Like ipatch_sample_read_transform() but is GObject Introspection friendly
575 * and audio buffer is allocated.
576 *
577 * Returns: (array length=size) (element-type guint8) (transfer full): Newly
578 * allocated buffer containing sample data or %NULL on error (in which case
579 * @err may be set).
580 *
581 * Since: 1.1.0
582 */
583 gpointer
ipatch_sample_read_transform_size(IpatchSample * sample,guint offset,guint size,int format,guint32 channel_map,GError ** err)584 ipatch_sample_read_transform_size(IpatchSample *sample, guint offset, guint size,
585 int format, guint32 channel_map, GError **err)
586 {
587 int frame_size;
588 gpointer buf;
589
590 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), NULL);
591 g_return_val_if_fail(size > 0, NULL);
592
593 frame_size = ipatch_sample_format_size(format);
594 g_return_val_if_fail(frame_size > 0, NULL);
595 g_return_val_if_fail(size % frame_size == 0, NULL);
596
597 buf = g_malloc(size); // ++ alloc buf
598
599 if(!ipatch_sample_read_transform(sample, offset, size / frame_size,
600 buf, format, channel_map, err))
601 {
602 g_free(buf); // -- free buf on error
603 return (NULL);
604 }
605
606 return (buf); // !! caller takes over
607 }
608
609 /**
610 * ipatch_sample_write_transform: (skip)
611 * @sample: Sample to write to
612 * @offset: Offset in frames to write to
613 * @frames: Number of frames to write
614 * @buf: Buffer of sample data to write (should be at least @frames *
615 * ipatch_sample_format_size() of @format).
616 * @format: Format to transform sample data from (if its the same as the native
617 * format of @sample no transformation occurs)
618 * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use
619 * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping
620 * (see ipatch_sample_get_transform_funcs() for details).
621 * @err: Location to store error info or %NULL
622 *
623 * Like ipatch_sample_write() but allows for sample transformation.
624 *
625 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
626 */
627 gboolean
ipatch_sample_write_transform(IpatchSample * sample,guint offset,guint frames,gconstpointer buf,int format,guint32 channel_map,GError ** err)628 ipatch_sample_write_transform(IpatchSample *sample, guint offset, guint frames,
629 gconstpointer buf, int format, guint32 channel_map,
630 GError **err)
631 {
632 IpatchSampleHandle handle;
633 gboolean retval;
634
635 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
636
637 if(!ipatch_sample_handle_open(sample, &handle, 'w', format, channel_map, err))
638 {
639 return (FALSE);
640 }
641
642 retval = ipatch_sample_handle_write(&handle, offset, frames, buf, err);
643
644 ipatch_sample_handle_close(&handle);
645
646 return (retval);
647 }
648
649 /**
650 * ipatch_sample_write_transform_size: (rename-to ipatch_sample_write_transform)
651 * @sample: Sample to write to
652 * @offset: Offset in frames to write to
653 * @buf: Buffer of sample data to write
654 * @size: Size of data in @buf (must be a multiple of @format frame size)
655 * @format: Format to transform sample data from (if its the same as the native
656 * format of @sample no transformation occurs)
657 * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use
658 * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping
659 * (see ipatch_sample_get_transform_funcs() for details).
660 * @err: Location to store error info or %NULL
661 *
662 * Like ipatch_sample_write() but allows for sample transformation.
663 *
664 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
665 *
666 * Since: 1.1.0
667 */
668 gboolean
ipatch_sample_write_transform_size(IpatchSample * sample,guint offset,gconstpointer buf,guint size,int format,guint32 channel_map,GError ** err)669 ipatch_sample_write_transform_size(IpatchSample *sample, guint offset,
670 gconstpointer buf, guint size, int format,
671 guint32 channel_map, GError **err)
672 {
673 int frame_size;
674
675 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
676 g_return_val_if_fail(size > 0, FALSE);
677
678 frame_size = ipatch_sample_format_size(format);
679 g_return_val_if_fail(frame_size != 0, FALSE);
680 g_return_val_if_fail(size % frame_size == 0, FALSE);
681
682 return (ipatch_sample_write_transform(sample, offset, size / frame_size,
683 buf, format, channel_map, err));
684 }
685
686 /**
687 * ipatch_sample_copy:
688 * @dest_sample: Destination sample to copy data to
689 * @src_sample: Source sample to copy data from
690 * @channel_map: Channel mapping, use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1
691 * channel mapping (see ipatch_sample_get_transform_funcs() for details).
692 * @err: Location to store error information or %NULL
693 *
694 * Copy sample data from one sample to another. The two samples may differ
695 * in format, in which case the sample data will be converted. The
696 * @dest_sample must either be the same size in frames as @src_sample or not
697 * yet assigned a size.
698 *
699 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
700 */
701 gboolean
ipatch_sample_copy(IpatchSample * dest_sample,IpatchSample * src_sample,guint32 channel_map,GError ** err)702 ipatch_sample_copy(IpatchSample *dest_sample, IpatchSample *src_sample,
703 guint32 channel_map, GError **err)
704 {
705 IpatchSampleHandle dest_handle, src_handle;
706 IpatchSampleTransform *transform;
707 int dest_size, src_size, thissize;
708 gpointer buf;
709 int src_format;
710 int sizeleft, ofs;
711 gboolean retval = FALSE;
712
713 g_return_val_if_fail(IPATCH_IS_SAMPLE(dest_sample), FALSE);
714 g_return_val_if_fail(IPATCH_IS_SAMPLE(src_sample), FALSE);
715 g_return_val_if_fail(!err || !*err, FALSE);
716
717 dest_size = ipatch_sample_get_size(dest_sample, NULL);
718 src_size = ipatch_sample_get_size(src_sample, NULL);
719 g_return_val_if_fail(src_size != 0, FALSE);
720
721 /* If destination size not yet set, assign it */
722 if(dest_size == 0)
723 {
724 dest_size = src_size;
725 ipatch_sample_set_size(dest_sample, dest_size);
726 }
727
728 g_return_val_if_fail(dest_size == src_size, FALSE);
729
730 src_format = ipatch_sample_get_format(src_sample);
731
732 if(!ipatch_sample_handle_open(dest_sample, &dest_handle, 'w', src_format,
733 channel_map, err))
734 {
735 return (FALSE);
736 }
737
738 if(!ipatch_sample_handle_open(src_sample, &src_handle, 'r', 0, 0, err))
739 {
740 ipatch_sample_handle_close(&dest_handle);
741 return (FALSE);
742 }
743
744 transform = ipatch_sample_handle_get_transform(&dest_handle); /* ++ ref */
745
746 /* Transform should always be set, since we passed a format to ipatch_sample_handle_open */
747 g_return_val_if_fail(transform != NULL, FALSE);
748
749 thissize = ipatch_sample_transform_get_max_frames(transform);
750 ipatch_sample_transform_get_buffers(transform, &buf, NULL);
751
752 sizeleft = src_size;
753 ofs = 0;
754
755 while(sizeleft > 0)
756 {
757 if(thissize > sizeleft)
758 {
759 thissize = sizeleft;
760 }
761
762 if(!ipatch_sample_handle_read(&src_handle, ofs, thissize, buf, err))
763 {
764 goto err;
765 }
766
767 if(!ipatch_sample_handle_write(&dest_handle, ofs, thissize, buf, err))
768 {
769 goto err;
770 }
771
772 ofs += thissize;
773 sizeleft -= thissize;
774 }
775
776 retval = TRUE;
777
778 err:
779
780 ipatch_sample_handle_close(&src_handle); /* -- close source handle */
781 ipatch_sample_handle_close(&dest_handle); /* -- close destination handle */
782
783 return (retval);
784 }
785
786 /* FIXME-GIR: @sub_format is a dynamic GEnum or -1 */
787
788 /**
789 * ipatch_sample_save_to_file:
790 * @sample: Sample to save to file
791 * @filename: File name to save to
792 * @file_format: (type IpatchSndFileFormat): A value from the dynamic GEnum "IpatchSndFileFormat".
793 * @sub_format: A value from the dynamic GEnum "IpatchSndFileSubFormat" or -1
794 * to calculate optimal value based on the format of @sample.
795 * @err: Location to store error info or %NULL to ignore
796 *
797 * Convenience function to save a sample to a file using libsndfile.
798 *
799 * Returns: %TRUE on success, %FALSE otherwise
800 */
801 gboolean
ipatch_sample_save_to_file(IpatchSample * sample,const char * filename,int file_format,int sub_format,GError ** err)802 ipatch_sample_save_to_file(IpatchSample *sample, const char *filename,
803 int file_format, int sub_format, GError **err)
804 {
805 IpatchSample *store;
806 int channels, samplerate, sample_format;
807 int loop_type, loop_start, loop_end, fine_tune, root_note;
808
809 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
810 g_return_val_if_fail(filename != NULL, FALSE);
811 g_return_val_if_fail(!err || !*err, FALSE);
812
813 g_object_get(sample,
814 "sample-format", &sample_format,
815 "sample-rate", &samplerate,
816 NULL);
817
818 channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(sample_format);
819 sub_format = ipatch_snd_file_sample_format_to_sub_format(sample_format, file_format);
820
821 if(sub_format == -1)
822 {
823 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM,
824 "Invalid libsndfile format parameters");
825 return (FALSE);
826 }
827
828 store = ipatch_sample_store_snd_file_new(filename); /* ++ ref new store */
829
830 if(!ipatch_sample_store_snd_file_init_write(IPATCH_SAMPLE_STORE_SND_FILE(store),
831 file_format, sub_format,
832 IPATCH_SND_FILE_ENDIAN_FILE,
833 channels, samplerate))
834 {
835 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_PROGRAM,
836 "Invalid libsndfile format parameters");
837 g_object_unref(store); /* -- unref store */
838 return (FALSE);
839 }
840
841 g_object_get(sample,
842 "loop-type", &loop_type,
843 "loop-start", &loop_start,
844 "loop-end", &loop_end,
845 "root-note", &root_note,
846 "fine-tune", &fine_tune,
847 NULL);
848
849 g_object_set(store,
850 "loop-type", loop_type,
851 "loop-start", loop_start,
852 "loop-end", loop_end,
853 "root-note", root_note,
854 "fine-tune", fine_tune,
855 NULL);
856
857 if(!ipatch_sample_copy(store, sample, IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err))
858 {
859 g_object_unref(store); /* -- unref store */
860 return (FALSE);
861 }
862
863 g_object_unref(store); /* -- unref store */
864
865 return (TRUE);
866 }
867
868 /**
869 * ipatch_sample_handle_open:
870 * @sample: Sample to open a handle to
871 * @handle: (out): Caller supplied structure to initialize
872 * @mode: Access mode to sample, 'r' for reading and 'w' for writing
873 * @format: Sample format to convert to/from (0 for no conversion or to assign
874 * a transform object with ipatch_sample_handle_set_transform()).
875 * @channel_map: Channel mapping if @format is set (set to 0 otherwise), use
876 * #IPATCH_SAMPLE_UNITY_CHANNEL_MAP for 1 to 1 channel mapping
877 * (see ipatch_sample_get_transform_funcs() for details).
878 * @err: Location to store error information
879 *
880 * Open a handle to a sample for reading or writing sample data. Can optionally
881 * provide data conversion if @format is set. If it is desirable to have more
882 * control over the transform object and buffer allocation, the transform object
883 * can be assigned with ipatch_sample_handle_set_transform(). Note that a sample
884 * transform is acquired if @format is set, even if the format is identical to
885 * the @sample format, as a convenience to always provide a data buffer.
886 *
887 * Returns: %TRUE on success, %FALSE on failure (in which case @err may be set)
888 */
889 gboolean
ipatch_sample_handle_open(IpatchSample * sample,IpatchSampleHandle * handle,char mode,int format,guint32 channel_map,GError ** err)890 ipatch_sample_handle_open(IpatchSample *sample, IpatchSampleHandle *handle,
891 char mode, int format, guint32 channel_map,
892 GError **err)
893 {
894 IpatchSampleIface *iface;
895 int sample_format;
896 guint size;
897
898 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
899 g_return_val_if_fail(handle != NULL, FALSE);
900 g_return_val_if_fail(mode == 'r' || mode == 'w', FALSE);
901 g_return_val_if_fail(!format || ipatch_sample_format_verify(format), FALSE);
902
903 /* Verify sample format was set */
904 g_object_get(sample, "sample-format", &sample_format, NULL);
905 g_return_val_if_fail(ipatch_sample_format_verify(sample_format), FALSE);
906
907 /* Verify transform formats and channel mapping, if format is set */
908 if(format)
909 {
910 if(mode == 'r')
911 {
912 g_return_val_if_fail(ipatch_sample_format_transform_verify(sample_format, format, channel_map), FALSE);
913 }
914 else
915 {
916 g_return_val_if_fail(ipatch_sample_format_transform_verify(format, sample_format, channel_map), FALSE);
917 }
918 }
919
920 /* Verify sample size is set */
921 g_object_get(sample, "sample-size", &size, NULL);
922 g_return_val_if_fail(size != 0, FALSE);
923
924 memset(handle, 0, sizeof(IpatchSampleHandle));
925 handle->sample = g_object_ref(sample); /* ++ ref sample interface object */
926 handle->read_mode = mode == 'r';
927 handle->format = format ? format : sample_format;
928 handle->channel_map = format ? channel_map : IPATCH_SAMPLE_UNITY_CHANNEL_MAP;
929
930 /* Was format specified? */
931 if(format != 0)
932 {
933 /* Acquire sample data transform in the proper direction */
934 if(handle->read_mode) /* ++ grab transform */
935 handle->transform = ipatch_sample_transform_pool_acquire(sample_format, format,
936 channel_map);
937 else
938 handle->transform = ipatch_sample_transform_pool_acquire(format, sample_format,
939 channel_map);
940
941 handle->release_transform = TRUE; /* Indicate that transform came from pool */
942 }
943
944 iface = IPATCH_SAMPLE_GET_IFACE(sample);
945
946 handle->read = iface->read;
947 handle->write = iface->write;
948 handle->close = iface->close;
949
950 /* call interface open method (if any) */
951 if(iface->open)
952 {
953 if(iface->open(handle, err))
954 {
955 return (TRUE);
956 }
957
958 /* Error occurred */
959
960 if(handle->transform) /* -- release transform */
961 {
962 ipatch_sample_transform_pool_release(handle->transform);
963 }
964
965 g_object_unref(handle->sample); /* -- unref sample */
966
967 handle->transform = NULL;
968 handle->sample = NULL;
969
970 return (FALSE);
971 }
972 else
973 {
974 return (TRUE); /* No open method, assume success */
975 }
976 }
977
978 /**
979 * ipatch_sample_handle_close:
980 * @handle: Sample handle to close
981 *
982 * Close a handle previously opened with ipatch_sample_handle_open().
983 */
984 void
ipatch_sample_handle_close(IpatchSampleHandle * handle)985 ipatch_sample_handle_close(IpatchSampleHandle *handle)
986 {
987 IpatchSampleIface *iface;
988
989 g_return_if_fail(handle != NULL);
990 g_return_if_fail(IPATCH_IS_SAMPLE(handle->sample));
991
992 iface = IPATCH_SAMPLE_GET_IFACE(handle->sample);
993
994 /* call interface close method (if any) */
995 if(iface->close)
996 {
997 iface->close(handle);
998 }
999
1000 if(handle->transform)
1001 {
1002 /* If transform came from pool, release it, unref otherwise (user assigned) */
1003 if(handle->release_transform)
1004 {
1005 ipatch_sample_transform_pool_release(handle->transform); /* -- release transform */
1006 }
1007 else
1008 {
1009 ipatch_sample_transform_free(handle->transform); /* -- free transform */
1010 }
1011 }
1012
1013 g_object_unref(handle->sample); /* -- unref sample */
1014
1015 handle->transform = NULL;
1016 handle->sample = NULL;
1017 }
1018
1019 /**
1020 * ipatch_sample_handle_get_transform:
1021 * @handle: Sample handle to get transform from
1022 *
1023 * Get sample transform from a sample handle. Only exists if sample
1024 * data conversion is taking place or even if formats are the same but was
1025 * implicitly supplied to ipatch_sample_handle_open(). Transform should not be
1026 * modified unless it was assigned via ipatch_sample_handle_set_transform().
1027 *
1028 * Returns: (transfer none): Sample transform or %NULL if none.
1029 */
1030 IpatchSampleTransform *
ipatch_sample_handle_get_transform(IpatchSampleHandle * handle)1031 ipatch_sample_handle_get_transform(IpatchSampleHandle *handle)
1032 {
1033 g_return_val_if_fail(handle != NULL, NULL);
1034 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), NULL);
1035
1036 return (handle->transform);
1037 }
1038
1039 /**
1040 * ipatch_sample_handle_set_transform:
1041 * @handle: Sample handle to set transform of
1042 * @transform: (nullable): Transform to assign, source format must match that of
1043 * the handle's sample (read mode) or destination format must match (write mode),
1044 * can be %NULL to de-activate sample transformation for @handle.
1045 *
1046 * Assign a sample transform to a sample handle. Provided for added
1047 * control over @transform allocation. A transform can also be automatically
1048 * created and assigned with ipatch_sample_handle_open(). Sample transform
1049 * allocation is taken over by @handle.
1050 */
1051 void
ipatch_sample_handle_set_transform(IpatchSampleHandle * handle,IpatchSampleTransform * transform)1052 ipatch_sample_handle_set_transform(IpatchSampleHandle *handle,
1053 IpatchSampleTransform *transform)
1054 {
1055 g_return_if_fail(handle != NULL);
1056 g_return_if_fail(!transform || transform->buf1);
1057
1058 if(handle->transform)
1059 {
1060 /* If transform came from pool, release it, free otherwise (user assigned) */
1061 if(handle->release_transform)
1062 {
1063 ipatch_sample_transform_pool_release(handle->transform); /* -- release transform */
1064 }
1065 else
1066 {
1067 ipatch_sample_transform_free(handle->transform); /* -- free transform */
1068 }
1069 }
1070
1071 if(transform)
1072 {
1073 handle->transform = transform;
1074 }
1075 else
1076 {
1077 handle->transform = NULL;
1078 }
1079
1080 handle->release_transform = FALSE;
1081 }
1082
1083 /**
1084 * ipatch_sample_handle_get_format:
1085 * @handle: Sample handle to get format of
1086 *
1087 * Get the sample format of a sample handle. May differ from the #IpatchSample
1088 * format of the handle, if it was opened with a different format and is
1089 * therefore being converted.
1090 *
1091 * Returns: Sample format integer (see #IpatchSampleWidth, etc).
1092 */
1093 int
ipatch_sample_handle_get_format(IpatchSampleHandle * handle)1094 ipatch_sample_handle_get_format(IpatchSampleHandle *handle)
1095 {
1096 g_return_val_if_fail(handle != NULL, 0);
1097 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), 0);
1098
1099 if(handle->transform)
1100 return (handle->read_mode ? handle->transform->dest_format
1101 : handle->transform->src_format);
1102 else
1103 {
1104 return (ipatch_sample_get_format(handle->sample));
1105 }
1106 }
1107
1108 /**
1109 * ipatch_sample_handle_get_frame_size:
1110 * @handle: Sample handle to get data frame size of
1111 *
1112 * A convenience function to get size of a single sample frame for a given
1113 * sample @handle. This is useful for determining buffer allocation sizes when
1114 * reading or writing data.
1115 *
1116 * Returns: Size in bytes of a single sample frame
1117 */
ipatch_sample_handle_get_frame_size(IpatchSampleHandle * handle)1118 int ipatch_sample_handle_get_frame_size(IpatchSampleHandle *handle)
1119 {
1120 return (ipatch_sample_format_size(ipatch_sample_handle_get_format(handle)));
1121 }
1122
1123 /**
1124 * ipatch_sample_handle_get_max_frames:
1125 * @handle: Sample handle to get max transform frames of
1126 *
1127 * A convenience function to get the maximum transform frames that can fit
1128 * in the sample transform of @handle.
1129 *
1130 * Returns: Maximum frames that can be read or written using the sample
1131 * transform buffers. 0 if no sample transform is assigned.
1132 */
1133 guint
ipatch_sample_handle_get_max_frames(IpatchSampleHandle * handle)1134 ipatch_sample_handle_get_max_frames(IpatchSampleHandle *handle)
1135 {
1136 g_return_val_if_fail(handle != NULL, 0);
1137 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), 0);
1138
1139 if(!handle->transform)
1140 {
1141 return 0;
1142 }
1143
1144 return (ipatch_sample_transform_get_max_frames(handle->transform));
1145 }
1146
1147 /**
1148 * ipatch_sample_handle_read: (skip)
1149 * @handle: Sample handle
1150 * @offset: Offset in frames to read from
1151 * @frames: Number of frames to read
1152 * @buf: Buffer to store sample data in (should be at least @frames *
1153 * sizeof (frame), the frame size can be had from
1154 * ipatch_sample_handle_get_frame_size()). Can be %NULL if transforming
1155 * audio data with not more than the maximum frames that can be transformed
1156 * at a time, in which case the internal transform buffer pointer will be
1157 * returned.
1158 * @err: Location to store error info or %NULL
1159 *
1160 * Read sample data from a sample handle. If the number of
1161 * frames read is within the sample transform buffer size and @buf is %NULL
1162 * then the transform buffer will be returned (extra copy not needed).
1163 *
1164 * Returns: Pointer to sample data on success, %NULL on error (in which case
1165 * @err may be set). The internal transform buffer will only be returned
1166 * if the @buf parameter is %NULL.
1167 */
1168 gpointer
ipatch_sample_handle_read(IpatchSampleHandle * handle,guint offset,guint frames,gpointer buf,GError ** err)1169 ipatch_sample_handle_read(IpatchSampleHandle *handle, guint offset,
1170 guint frames, gpointer buf, GError **err)
1171 {
1172 IpatchSampleTransform *trans;
1173 guint readframes, framesize, readbytes;
1174 gpointer transbuf, outbuf, bufptr;
1175 guint size;
1176
1177 g_return_val_if_fail(handle != NULL, NULL);
1178 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), NULL);
1179 g_return_val_if_fail(handle->read_mode, NULL);
1180 g_return_val_if_fail(!err || !*err, NULL);
1181
1182 g_return_val_if_fail(handle->read != NULL, NULL);
1183
1184 /* Make sure read does not exceed the sample size */
1185 size = ipatch_sample_get_size(handle->sample, NULL);
1186 g_return_val_if_fail(offset + frames <= size, FALSE);
1187
1188 trans = handle->transform;
1189
1190 if(trans && !handle->manual_transform) /* transforming audio data? */
1191 {
1192 readframes = trans->max_frames;
1193 transbuf = trans->buf1;
1194
1195 /* buffer pointer not supplied? */
1196 if(!buf)
1197 {
1198 /* extra descriptive for debugging purposes */
1199 g_return_val_if_fail(buf || frames <= readframes, NULL);
1200
1201 /* read the sample data */
1202 if(!handle->read(handle, offset, frames, transbuf, err))
1203 {
1204 return (NULL);
1205 }
1206
1207 /* transform the sample data and return - we done! */
1208 return (ipatch_sample_transform_convert_single(trans, frames));
1209 }
1210
1211 bufptr = buf;
1212 framesize = ipatch_sample_format_size(trans->dest_format);
1213 readbytes = readframes * framesize;
1214
1215 while(frames > 0) /* must be transformed in blocks */
1216 {
1217 if(readframes > frames)
1218 {
1219 readframes = frames;
1220 readbytes = readframes * framesize;
1221 }
1222
1223 /* read the sample data */
1224 if(!handle->read(handle, offset, readframes, transbuf, err))
1225 {
1226 return (NULL);
1227 }
1228
1229 /* transform the sample data */
1230 outbuf = ipatch_sample_transform_convert_single(trans, readframes);
1231
1232 /* copy to caller's buffer */
1233 memcpy(bufptr, outbuf, readbytes);
1234
1235 frames -= readframes;
1236 offset += readframes;
1237 bufptr = (guint8 *)bufptr + readbytes;
1238 }
1239 }
1240 else /* not transforming, do it all in one go */
1241 {
1242 g_return_val_if_fail(buf != NULL, NULL);
1243
1244 if(!handle->read(handle, offset, frames, buf, err))
1245 {
1246 return (NULL);
1247 }
1248 }
1249
1250 return (buf);
1251 }
1252
1253 /**
1254 * ipatch_sample_handle_read_size: (rename-to ipatch_sample_handle_read)
1255 * @handle: Sample handle
1256 * @offset: Offset in frames to read from
1257 * @size: Size of data to read (in bytes), must be a multiple of sample frame size
1258 * @err: Location to store error info or %NULL
1259 *
1260 * Read sample data from a sample handle. Like ipatch_sample_handle_read() but
1261 * is GObject Introspection friendly and allocates returned buffer.
1262 *
1263 * Returns: (array length=size) (element-type guint8) (transfer full): Newly allocated
1264 * sample data or %NULL on error (in which case @err may be set)
1265 *
1266 * Since: 1.1.0
1267 */
1268 gpointer
ipatch_sample_handle_read_size(IpatchSampleHandle * handle,guint offset,guint size,GError ** err)1269 ipatch_sample_handle_read_size(IpatchSampleHandle *handle, guint offset,
1270 guint size, GError **err)
1271 {
1272 gpointer buf;
1273 int frame_size;
1274
1275 g_return_val_if_fail(handle != NULL, NULL);
1276 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), NULL);
1277 g_return_val_if_fail(size > 0, NULL);
1278
1279 frame_size = ipatch_sample_handle_get_frame_size(handle);
1280 g_return_val_if_fail(frame_size > 0, NULL);
1281 g_return_val_if_fail(size % frame_size == 0, NULL);
1282
1283 buf = g_malloc(size); // ++ alloc buf
1284
1285 if(!ipatch_sample_handle_read(handle, offset, size / frame_size, buf, err))
1286 {
1287 g_free(buf); // -- free buf on error
1288 return (NULL);
1289 }
1290
1291 return (buf); // !! caller takes over allocation
1292 }
1293
1294 /**
1295 * ipatch_sample_handle_write: (skip)
1296 * @handle: Sample handle
1297 * @offset: Offset in frames to write to
1298 * @frames: Number of frames to write
1299 * @buf: Buffer of sample data to write (should be at least @frames *
1300 * sizeof (frame), the frame size can be had from
1301 * ipatch_sample_handle_get_frame_size()). Can be %NULL, in which case it is
1302 * assumed that the sample data has been loaded into the first buffer of the
1303 * handle's sample transform.
1304 * @err: Location to store error info or %NULL
1305 *
1306 * Write sample data to a sample handle.
1307 *
1308 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
1309 */
1310 gboolean
ipatch_sample_handle_write(IpatchSampleHandle * handle,guint offset,guint frames,gconstpointer buf,GError ** err)1311 ipatch_sample_handle_write(IpatchSampleHandle *handle, guint offset, guint frames,
1312 gconstpointer buf, GError **err)
1313 {
1314 IpatchSampleTransform *trans;
1315 guint writeframes, framesize, writebytes;
1316 gpointer transbuf, outbuf;
1317 gconstpointer bufptr;
1318 guint size;
1319
1320 g_return_val_if_fail(handle != NULL, FALSE);
1321 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), FALSE);
1322 g_return_val_if_fail(!handle->read_mode, FALSE);
1323 g_return_val_if_fail(!err || !*err, FALSE);
1324
1325 g_return_val_if_fail(handle->write != NULL, FALSE);
1326
1327 /* Make sure write does not exceed the sample size */
1328 size = ipatch_sample_get_size(handle->sample, NULL);
1329 g_return_val_if_fail(offset + frames <= size, FALSE);
1330
1331 trans = handle->transform;
1332
1333 if(trans && !handle->manual_transform) /* transforming audio data? */
1334 {
1335 writeframes = trans->max_frames;
1336 transbuf = trans->buf1;
1337
1338 /* buffer pointer not supplied or its the transform buffer? */
1339 if(!buf || buf == transbuf)
1340 {
1341 /* extra descriptive for debugging purposes */
1342 g_return_val_if_fail(buf || frames <= writeframes, FALSE);
1343
1344 outbuf = ipatch_sample_transform_convert_single(trans, frames);
1345
1346 /* write the sample data and return - we's done! */
1347 return (handle->write(handle, offset, frames, outbuf, err));
1348 }
1349
1350 bufptr = buf;
1351 framesize = ipatch_sample_format_size(trans->src_format);
1352 writebytes = writeframes * framesize;
1353
1354 while(frames > 0) /* must be transformed in blocks */
1355 {
1356 if(writeframes > frames)
1357 {
1358 writeframes = frames;
1359 writebytes = writeframes * framesize;
1360 }
1361
1362 /* copy the block of sample data to transform */
1363 memcpy(transbuf, bufptr, writebytes);
1364
1365 /* transform the sample data */
1366 outbuf = ipatch_sample_transform_convert_single(trans, writeframes);
1367
1368 /* write the transformed sample data */
1369 if(!handle->write(handle, offset, writeframes, outbuf, err))
1370 {
1371 return (FALSE);
1372 }
1373
1374 frames -= writeframes;
1375 offset += writeframes;
1376 bufptr = (guint8 *)bufptr + writebytes;
1377 }
1378 }
1379 else /* not transforming, do it all in one go */
1380 {
1381 g_return_val_if_fail(buf != NULL, FALSE);
1382
1383 if(!handle->write(handle, offset, frames, buf, err))
1384 {
1385 return (FALSE);
1386 }
1387 }
1388
1389 return (TRUE);
1390 }
1391
1392 /**
1393 * ipatch_sample_handle_write_size: (rename-to ipatch_sample_handle_write)
1394 * @handle: Sample handle
1395 * @offset: Offset in frames to write to
1396 * @buf: (array length=size) (element-type guint8) (transfer none): Buffer of
1397 * sample data to write
1398 * @size: Size of @buf in bytes (must be a multiple of sample frame size)
1399 * @err: Location to store error info or %NULL
1400 *
1401 * Write sample data to a sample handle. Like ipatch_sample_handle_write() but
1402 * is GObject Introspection friendly.
1403 *
1404 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set).
1405 *
1406 * Since: 1.1.0
1407 */
1408 gboolean
ipatch_sample_handle_write_size(IpatchSampleHandle * handle,guint offset,gconstpointer buf,guint size,GError ** err)1409 ipatch_sample_handle_write_size(IpatchSampleHandle *handle, guint offset,
1410 gconstpointer buf, guint size, GError **err)
1411 {
1412 int frame_size;
1413
1414 g_return_val_if_fail(handle != NULL, FALSE);
1415 g_return_val_if_fail(IPATCH_IS_SAMPLE(handle->sample), FALSE);
1416
1417 frame_size = ipatch_sample_handle_get_frame_size(handle);
1418 g_return_val_if_fail(frame_size != 0, FALSE);
1419 g_return_val_if_fail(size % frame_size == 0, FALSE);
1420
1421 return (ipatch_sample_handle_write(handle, offset, size / frame_size, buf, err));
1422 }
1423
1424 /**
1425 * ipatch_sample_handle_cascade_open: (skip)
1426 * @handle: Already open handle
1427 * @sample: The cascade sample containing the actual data
1428 * @err: Location to store error information
1429 *
1430 * This can be called from #IpatchSampleIface.open methods
1431 * for objects which contain a pointer to an #IpatchSample that contains the
1432 * sample's data.
1433 *
1434 * Returns: %TRUE on success, %FALSE on failure (in which case @err may be set)
1435 */
1436 gboolean
ipatch_sample_handle_cascade_open(IpatchSampleHandle * handle,IpatchSample * sample,GError ** err)1437 ipatch_sample_handle_cascade_open(IpatchSampleHandle *handle,
1438 IpatchSample *sample, GError **err)
1439 {
1440 IpatchSampleIface *iface;
1441
1442 g_return_val_if_fail(handle != NULL, FALSE);
1443 g_return_val_if_fail(IPATCH_IS_SAMPLE(sample), FALSE);
1444
1445 iface = IPATCH_SAMPLE_GET_IFACE(sample);
1446
1447 g_object_unref(handle->sample); /* -- unref old sample */
1448 handle->sample = g_object_ref(sample); /* ++ ref cascaded sample for new handle */
1449
1450 handle->read = iface->read;
1451 handle->write = iface->write;
1452 handle->close = iface->close;
1453
1454 /* call interface open method (if any) */
1455 if(iface->open)
1456 {
1457 return (iface->open(handle, err));
1458 }
1459 else
1460 {
1461 return (TRUE); /* No open method, assume success */
1462 }
1463 }
1464
1465 /**
1466 * ipatch_sample_install_property: (skip)
1467 * @oclass: Object class to install #IpatchSample property
1468 * @property_id: Property ID for set/get property class method
1469 * @property_name: #IpatchSample property name to install
1470 *
1471 * A helper function for objects that have an #IpatchSample interface.
1472 * Installs a #IpatchSample interface property for the given object class.
1473 * The parameter will be #G_PARAM_READWRITE.
1474 *
1475 * Returns: The newly created and installed parameter spec.
1476 */
1477 GParamSpec *
ipatch_sample_install_property(GObjectClass * oclass,guint property_id,const char * property_name)1478 ipatch_sample_install_property(GObjectClass *oclass, guint property_id,
1479 const char *property_name)
1480 {
1481 GParamSpec *pspec;
1482
1483 g_return_val_if_fail(G_IS_OBJECT_CLASS(oclass), NULL);
1484 g_return_val_if_fail(property_id != 0, NULL);
1485
1486 pspec = ipatch_sample_new_property_param_spec(property_name,
1487 G_PARAM_READWRITE);
1488 g_return_val_if_fail(pspec != NULL, NULL);
1489
1490 g_object_class_install_property(oclass, property_id, pspec);
1491
1492 return (pspec);
1493 }
1494
1495 /**
1496 * ipatch_sample_install_property_readonly: (skip)
1497 * @oclass: Object class to install #IpatchSample property
1498 * @property_id: Property ID for set/get property class method
1499 * @property_name: #IpatchSample property name to install
1500 *
1501 * A helper function for objects that have an #IpatchSample interface.
1502 * Identical to ipatch_sample_install_property() but installs the property
1503 * as readonly and uses g_object_class_override_property() instead of
1504 * creating a new #GParamSpec.
1505 *
1506 * Returns: The newly created and installed parameter spec (GParamSpecOverride).
1507 */
1508 GParamSpec *
ipatch_sample_install_property_readonly(GObjectClass * oclass,guint property_id,const char * property_name)1509 ipatch_sample_install_property_readonly(GObjectClass *oclass,
1510 guint property_id,
1511 const char *property_name)
1512 {
1513 g_return_val_if_fail(G_IS_OBJECT_CLASS(oclass), NULL);
1514 g_return_val_if_fail(property_id != 0, NULL);
1515
1516 g_object_class_override_property(oclass, property_id, property_name);
1517 return (g_object_class_find_property(oclass, property_name));
1518 }
1519
1520 /**
1521 * ipatch_sample_new_property_param_spec: (skip)
1522 * @property_name: Name of a #IpatchSample property
1523 * @flags: Flags to use for the new #GParamSpec
1524 *
1525 * Seldom used function that creates a new GParamSpec that is identical to
1526 * a #IpatchSample property by the name @property_name, except the flags
1527 * can differ.
1528 *
1529 * Returns: New GParamSpec.
1530 */
1531 GParamSpec *
ipatch_sample_new_property_param_spec(const char * property_name,GParamFlags flags)1532 ipatch_sample_new_property_param_spec(const char *property_name,
1533 GParamFlags flags)
1534 {
1535 if(strcmp(property_name, "sample-data") == 0)
1536 return g_param_spec_object("sample-data", _("Sample data"), _("Sample data"),
1537 IPATCH_TYPE_SAMPLE_DATA, flags);
1538 else if(strcmp(property_name, "sample-size") == 0)
1539 return g_param_spec_uint("sample-size", _("Size"),
1540 _("Size in frames"),
1541 0, G_MAXUINT, 0,
1542 flags);
1543 else if(strcmp(property_name, "sample-format") == 0)
1544 return g_param_spec_int("sample-format", _("Sample format"),
1545 _("Sample format"),
1546 0, G_MAXINT, IPATCH_SAMPLE_FORMAT_DEFAULT,
1547 flags);
1548 else if(strcmp(property_name, "sample-rate") == 0)
1549 return g_param_spec_int("sample-rate", _("Sample rate"),
1550 _("Sampling rate in Hertz"),
1551 IPATCH_SAMPLE_RATE_MIN, IPATCH_SAMPLE_RATE_MAX,
1552 IPATCH_SAMPLE_RATE_DEFAULT, flags);
1553 else if(strcmp(property_name, "loop-type") == 0)
1554 return g_param_spec_enum("loop-type", _("Loop type"),
1555 _("Loop method type"),
1556 IPATCH_TYPE_SAMPLE_LOOP_TYPE,
1557 IPATCH_SAMPLE_LOOP_NONE,
1558 flags);
1559 else if(strcmp(property_name, "loop-start") == 0)
1560 return g_param_spec_uint("loop-start", _("Loop start"),
1561 _("Start of loop in frames"),
1562 0, G_MAXUINT, 0,
1563 flags);
1564 else if(strcmp(property_name, "loop-end") == 0)
1565 return g_param_spec_uint("loop-end", _("Loop end"),
1566 _("Loop end in frames (after loop)"),
1567 0, G_MAXUINT, 0,
1568 flags);
1569 else if(strcmp(property_name, "root-note") == 0)
1570 return g_param_spec_int("root-note", _("Root note"),
1571 _("Root MIDI note"),
1572 0, 127, IPATCH_SAMPLE_ROOT_NOTE_DEFAULT,
1573 flags);
1574 else if(strcmp(property_name, "fine-tune") == 0)
1575 return g_param_spec_int("fine-tune", _("Fine tuning"),
1576 _("Fine tuning in cents"),
1577 -99, 99, 0,
1578 flags);
1579 else
1580 {
1581 return (NULL);
1582 }
1583 }
1584