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