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: IpatchSampleTransform
22  * @short_description: Audio format conversion instance
23  * @see_also:
24  * @stability: Stable
25  *
26  * A structure for converting between audio formats (for example the bit width
27  * or number of channels).  This structure is initialized with the source and
28  * destination audio formats, multi-channel mapping and conversion buffers.
29  */
30 #include <string.h>
31 #include <glib.h>
32 #include <glib-object.h>
33 #include "IpatchSampleTransform.h"
34 #include "sample.h"
35 #include "ipatch_priv.h"
36 
37 
38 G_LOCK_DEFINE_STATIC(transform_pool);
39 static GList *transform_pool = NULL;
40 
41 static void _ipatch_sample_transform_free_transform(IpatchSampleTransform *data,
42                                                     gpointer user_data);
43 
44 /* ----- Initialization/deinitialization of list ----------------------------*/
45 /* Initialize transform_pool */
_ipatch_sample_transform_init(void)46 void _ipatch_sample_transform_init(void)
47 {
48     transform_pool = NULL;
49 }
50 
51 /* Free transform_pool */
_ipatch_sample_transform_deinit(void)52 void _ipatch_sample_transform_deinit(void)
53 {
54     g_list_foreach(transform_pool,
55                    (GFunc)_ipatch_sample_transform_free_transform, NULL);
56     g_list_free(transform_pool);
57 }
58 
59 /* free one conv_maps data */
_ipatch_sample_transform_free_transform(IpatchSampleTransform * data,gpointer user_data)60 static void _ipatch_sample_transform_free_transform(IpatchSampleTransform *data,
61                                                     gpointer user_data)
62 {
63     ipatch_sample_transform_free(data);
64 }
65 
66 /*------ IpatchSampleTransform object funtions -------------------------------*/
67 
68 GType
ipatch_sample_transform_get_type(void)69 ipatch_sample_transform_get_type(void)
70 {
71     static GType type = 0;
72 
73     if(!type)
74         type = g_boxed_type_register_static("IpatchSampleTransform",
75                                             (GBoxedCopyFunc)ipatch_sample_transform_duplicate,
76                                             (GBoxedFreeFunc)ipatch_sample_transform_free);
77 
78     return (type);
79 }
80 
81 /**
82  * ipatch_sample_transform_new:
83  * @src_format: Source sample format or 0 to set later
84  * @dest_format: Destination sample format or 0 to set later
85  * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP
86  *   to map all input channels to the same output channels, see
87  *   #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values)
88  *
89  * Create a new sample transform object. If @src_format and @dest_format are
90  * not 0 then the transform is initialized for the given source and destination
91  * formats, otherwise they are expected to be set later with
92  * ipatch_sample_transform_set_formats().
93  *
94  * Returns: New allocated sample transform structure which should be freed with
95  *   ipatch_sample_transform_free() when done with it.
96  */
97 IpatchSampleTransform *
ipatch_sample_transform_new(int src_format,int dest_format,guint32 channel_map)98 ipatch_sample_transform_new(int src_format, int dest_format, guint32 channel_map)
99 {
100     IpatchSampleTransform *trans;
101     int i;
102 
103     trans = g_slice_new0(IpatchSampleTransform);
104 
105     /* Setup straight through 1:1 channel mapping */
106     for(i = 0; i < 8; i++)
107     {
108         trans->channel_map[i] = i;
109     }
110 
111     if(src_format != 0 && dest_format != 0)
112     {
113         ipatch_sample_transform_set_formats(trans, src_format, dest_format, channel_map);
114     }
115 
116     return (trans);
117 }
118 
119 /**
120  * ipatch_sample_transform_free:
121  * @transform: Transform structure
122  *
123  * Free a transform structure as allocated by ipatch_sample_transform_new() and
124  * its allocated resources.
125  */
126 void
ipatch_sample_transform_free(IpatchSampleTransform * transform)127 ipatch_sample_transform_free(IpatchSampleTransform *transform)
128 {
129     g_return_if_fail(transform != NULL);
130 
131     if(transform->free_buffers)
132     {
133         g_free(transform->buf1);
134     }
135 
136     g_slice_free(IpatchSampleTransform, transform);
137 }
138 
139 /**
140  * ipatch_sample_transform_duplicate:
141  * @transform: Transform structure
142  *
143  * Duplicate a sample transform.
144  *
145  * Returns: New duplicated sample transform structure.
146  *
147  * Since: 1.1.0
148  */
149 IpatchSampleTransform *
ipatch_sample_transform_duplicate(const IpatchSampleTransform * transform)150 ipatch_sample_transform_duplicate(const IpatchSampleTransform *transform)
151 {
152     IpatchSampleTransform *new;
153     guint32 channel_map = 0;
154     int i;
155 
156     // Convert channel map byte array to guint32
157     for(i = 0; i < 8; i++)
158     {
159         channel_map |= (transform->channel_map[i] & 0x07) << (i * 3);
160     }
161 
162     new = ipatch_sample_transform_new(transform->src_format, transform->dest_format,
163                                       channel_map);
164 
165     if(transform->max_frames > 0)
166     {
167         ipatch_sample_transform_alloc(new, transform->max_frames);
168     }
169 
170     return (new);
171 }
172 
173 /**
174  * ipatch_sample_transform_init: (skip)
175  * @transform: Sample transform to initialize
176  *
177  * Initialize a sample transform structure. Usually only used to initialize transform
178  * structures allocated on the stack, which is done to avoid mallocs.
179  */
180 void
ipatch_sample_transform_init(IpatchSampleTransform * transform)181 ipatch_sample_transform_init(IpatchSampleTransform *transform)
182 {
183     int i;
184 
185     g_return_if_fail(transform != NULL);
186 
187     memset(transform, 0, sizeof(IpatchSampleTransform));
188 
189     /* Setup straight through 1:1 channel mapping */
190     for(i = 0; i < 8; i++)
191     {
192         transform->channel_map[i] = i;
193     }
194 }
195 
196 /**
197  * ipatch_sample_transform_pool_acquire: (skip)
198  * @src_format: Source sample format
199  * @dest_format: Destination sample format
200  * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP
201  *   to map all input channels to the same output channels, see
202  *   #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values)
203  *
204  * Get an unused sample transform object from the sample transform pool.
205  * Used for quickly getting a transform object for temporary use without the
206  * overhead of allocating one.  Note though, that if no more transforms exist
207  * in the pool an allocation will occur.
208  *
209  * Returns: Sample transform object.  Should be released after use with
210  *   ipatch_sample_transform_pool_release().
211  */
212 IpatchSampleTransform *
ipatch_sample_transform_pool_acquire(int src_format,int dest_format,guint32 channel_map)213 ipatch_sample_transform_pool_acquire(int src_format, int dest_format,
214                                      guint32 channel_map)
215 {
216     IpatchSampleTransform *trans = NULL;
217 
218     g_return_val_if_fail(ipatch_sample_format_transform_verify(src_format, dest_format, channel_map), NULL);
219 
220     G_LOCK(transform_pool);
221 
222     if(transform_pool)
223     {
224         trans = (IpatchSampleTransform *)(transform_pool->data);
225         transform_pool = g_list_delete_link(transform_pool, transform_pool);
226     }
227 
228     G_UNLOCK(transform_pool);
229 
230     if(!trans)
231     {
232         trans = ipatch_sample_transform_new(src_format, dest_format, channel_map);
233         ipatch_sample_transform_alloc_size(trans, IPATCH_SAMPLE_TRANS_BUFFER_SIZE);
234     }
235     else
236     {
237         ipatch_sample_transform_set_formats(trans, src_format, dest_format, channel_map);
238     }
239 
240     return (trans);
241 }
242 
243 /**
244  * ipatch_sample_transform_pool_release: (skip)
245  * @transform: Sample transform
246  *
247  * Release a sample transform object, returned by
248  * ipatch_sample_transform_pool_grab(), back to the transform pool.
249  */
250 void
ipatch_sample_transform_pool_release(IpatchSampleTransform * transform)251 ipatch_sample_transform_pool_release(IpatchSampleTransform *transform)
252 {
253     g_return_if_fail(transform != NULL);
254 
255     G_LOCK(transform_pool);
256     transform_pool = g_list_prepend(transform_pool, transform);
257     G_UNLOCK(transform_pool);
258 }
259 
260 /**
261  * ipatch_sample_transform_set_formats:
262  * @transform: Transform object
263  * @src_format: Source audio format to convert from
264  * @dest_format: Destination audio format to convert to
265  * @channel_map: Channel mapping (use #IPATCH_SAMPLE_UNITY_CHANNEL_MAP
266  *   to map all input channels to the same output channels, see
267  *   #IPATCH_SAMPLE_MAP_CHANNEL macro for constructing channel map values)
268  *
269  * Initialize a sample transform object for converting from
270  * @src_format to @dest_format.
271  */
272 void
ipatch_sample_transform_set_formats(IpatchSampleTransform * transform,int src_format,int dest_format,guint32 channel_map)273 ipatch_sample_transform_set_formats(IpatchSampleTransform *transform,
274                                     int src_format, int dest_format,
275                                     guint32 channel_map)
276 {
277     guint buf1_max_frame, buf2_max_frame, func_count;
278     int i, chans;
279 
280     g_return_if_fail(transform != NULL);
281     g_return_if_fail(ipatch_sample_format_transform_verify(src_format, dest_format, channel_map));
282 
283     transform->src_format = src_format;
284     transform->dest_format = dest_format;
285 
286     /* Convert channel map integer to byte array */
287     for(i = 0; i < 8; i++)
288     {
289         transform->channel_map[i] = (channel_map >> (i * 3)) & 0x07;
290     }
291 
292     transform->func_count = 0;
293 
294     if(src_format == dest_format)         /* Shortcut identical formats */
295     {
296         chans = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(src_format);
297 
298         /* Straight through channel mapping? */
299         for(i = 0; i < chans; i++)
300             if(transform->channel_map[i] != i)
301             {
302                 break;
303             }
304 
305         if(i == chans)
306         {
307             transform->buf1_max_frame = ipatch_sample_format_size(src_format);
308             transform->buf2_max_frame = 0;
309             transform->max_frames = transform->combined_size
310                                     ? transform->combined_size / transform->buf1_max_frame : 0;
311             return;
312         }
313     }
314 
315     func_count = ipatch_sample_get_transform_funcs
316                  (src_format, dest_format, channel_map, &buf1_max_frame, &buf2_max_frame,
317                   transform->funcs);
318 
319     transform->buf1_max_frame = buf1_max_frame;
320     transform->buf2_max_frame = buf2_max_frame;
321     transform->func_count = func_count;
322 
323     /* update max frames if buffer is already assigned */
324     if(transform->combined_size)
325     {
326         transform->max_frames =
327             transform->combined_size / (buf1_max_frame + buf2_max_frame);
328 
329         transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame
330                           * transform->max_frames);
331     }
332     else
333     {
334         transform->max_frames = 0;
335     }
336 }
337 
338 /**
339  * ipatch_sample_transform_alloc:
340  * @transform: Sample transform object
341  * @frames: Number of frames to allocate for (max frames processed in batch)
342  *
343  * Allocate buffers for transforming between two audio formats, for
344  * which @transform has previously been initialized for with
345  * ipatch_sample_transform_new() or ipatch_sample_transform_set_formats().
346  *
347  * Note: Assigning buffers with this function allows sample formats to be
348  * changed without re-assigning the buffers.
349  */
350 void
ipatch_sample_transform_alloc(IpatchSampleTransform * transform,guint frames)351 ipatch_sample_transform_alloc(IpatchSampleTransform *transform, guint frames)
352 {
353     g_return_if_fail(transform != NULL);
354     g_return_if_fail(frames > 0);
355     g_return_if_fail(transform->src_format != 0);
356 
357     if(transform->free_buffers)
358     {
359         g_free(transform->buf1);
360     }
361 
362     transform->combined_size =
363         (transform->buf1_max_frame + transform->buf2_max_frame) * frames;
364 
365     transform->buf1 = g_malloc(transform->combined_size);
366     transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame * frames);
367     transform->free_buffers = TRUE;
368     transform->max_frames = frames;
369 }
370 
371 /**
372  * ipatch_sample_transform_alloc_size:
373  * @transform: Sample transform object
374  * @size: Maximum total size in bytes of allocation for both transform buffers.
375  *
376  * Like ipatch_sample_transform_alloc() but allocates buffers based on a
377  * maximum size and returns the maximum number of sample frames which can be
378  * converted at a time using this size.  Another difference is that conversion
379  * formats do not need to be set before calling this function.
380  *
381  * Returns: The maximum number of frames that can be converted at a time for
382  * the given @size of transform buffers.  If conversion formats have not yet
383  * been set, then this function returns 0.
384  */
385 int
ipatch_sample_transform_alloc_size(IpatchSampleTransform * transform,guint size)386 ipatch_sample_transform_alloc_size(IpatchSampleTransform *transform,
387                                    guint size)
388 {
389     g_return_val_if_fail(transform != NULL, 0);
390     g_return_val_if_fail(size > 32, 0);	/* just a somewhat sane value */
391 
392     if(transform->free_buffers)
393     {
394         g_free(transform->buf1);
395     }
396 
397     transform->combined_size = size;
398     transform->buf1 = g_malloc(size);
399     transform->free_buffers = TRUE;
400     transform->buf2 = NULL;
401     transform->max_frames = 0;
402 
403     /* update buffer split if formats already assigned */
404     if(transform->src_format && transform->dest_format)
405     {
406         transform->max_frames
407             = size / (transform->buf1_max_frame + transform->buf2_max_frame);
408         transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame
409                           * transform->max_frames);
410     }
411 
412     return (transform->max_frames);
413 }
414 
415 /**
416  * ipatch_sample_transform_free_buffers:
417  * @transform: Sample transform object
418  *
419  * Free sample transform buffers.
420  */
421 void
ipatch_sample_transform_free_buffers(IpatchSampleTransform * transform)422 ipatch_sample_transform_free_buffers(IpatchSampleTransform *transform)
423 {
424     g_return_if_fail(transform != NULL);
425 
426     if(transform->free_buffers)
427     {
428         g_free(transform->buf1);
429     }
430 
431     transform->buf1 = NULL;
432     transform->buf2 = NULL;
433     transform->combined_size = 0;
434     transform->max_frames = 0;
435 }
436 
437 /**
438  * ipatch_sample_transform_set_buffers_size:
439  * @transform: Sample transform object
440  * @buf: (array length=size) (element-type guint8) (transfer none): Buffer to
441  *    assign for transforming sample formats
442  * @size: Size of @buf in bytes
443  *
444  * Assign transform buffers using a single buffer of a
445  * specific size in bytes and determine optimal division for source and
446  * destination buffers.  Conversion formats must not be set before calling this
447  * function (can be set later).
448  *
449  * Returns: The maximum number of frames that can be converted at a time for
450  * the given @size of transform buffers.  If conversion formats have not yet
451  * been set, then this function returns 0.
452  */
453 guint
ipatch_sample_transform_set_buffers_size(IpatchSampleTransform * transform,gpointer buf,guint size)454 ipatch_sample_transform_set_buffers_size(IpatchSampleTransform *transform,
455         gpointer buf, guint size)
456 {
457     g_return_val_if_fail(transform != NULL, 0);
458     g_return_val_if_fail(buf != NULL, 0);
459     g_return_val_if_fail(size > 32, 0);		/* some slightly sane value */
460 
461     if(transform->free_buffers)
462     {
463         g_free(transform->buf1);
464     }
465 
466     transform->buf1 = buf;
467     transform->free_buffers = FALSE;
468     transform->combined_size = size;
469     transform->buf2 = NULL;
470     transform->max_frames = 0;
471 
472     /* update buffer split if formats already assigned */
473     if(transform->src_format && transform->dest_format)
474     {
475         transform->max_frames
476             = size / (transform->buf1_max_frame + transform->buf2_max_frame);
477         transform->buf2 = (guint8 *)transform->buf1 + (transform->buf1_max_frame
478                           * transform->max_frames);
479     }
480 
481     return (transform->max_frames);
482 }
483 
484 /**
485  * ipatch_sample_transform_get_buffers: (skip)
486  * @transform: Sample transform object
487  * @buf1: (out) (optional): First buffer or %NULL to ignore
488  * @buf2: (out) (optional): Second buffer or %NULL to ignore
489  *
490  * Get the sample data buffers in a sample transform object.
491  */
492 void
ipatch_sample_transform_get_buffers(IpatchSampleTransform * transform,gpointer * buf1,gpointer * buf2)493 ipatch_sample_transform_get_buffers(IpatchSampleTransform *transform,
494                                     gpointer *buf1, gpointer *buf2)
495 {
496     g_return_if_fail(transform != NULL);
497 
498     if(buf1)
499     {
500         *buf1 = transform->buf1;
501     }
502 
503     if(buf2)
504     {
505         *buf2 = transform->buf2;
506     }
507 }
508 
509 /**
510  * ipatch_sample_transform_get_frame_sizes:
511  * @transform: Initialized transform object
512  * @buf1_size: (out) (optional): Maximum frame size for buffer 1 or %NULL to ignore
513  * @buf2_size: (out) (optional): Maximum frame size for buffer 2 or %NULL to ignore
514  *
515  * Get max frame sizes for transform buffers. When transforming audio the
516  * first buffer must be at least frames * buf1_size bytes in size and the
517  * second buffer must be at least frames * buf2_size, where frames is the
518  * max number of frames to convert in batch.
519  */
520 void
ipatch_sample_transform_get_frame_sizes(IpatchSampleTransform * transform,guint * buf1_size,guint * buf2_size)521 ipatch_sample_transform_get_frame_sizes(IpatchSampleTransform *transform,
522                                         guint *buf1_size, guint *buf2_size)
523 {
524     g_return_if_fail(transform != NULL);
525 
526     if(buf1_size)
527     {
528         *buf1_size = transform->buf1_max_frame;
529     }
530 
531     if(buf2_size)
532     {
533         *buf2_size = transform->buf2_max_frame;
534     }
535 }
536 
537 /**
538  * ipatch_sample_transform_get_max_frames:
539  * @transform: Sample transform object
540  *
541  * Get the maximum frames that the @transform object can convert at a time.
542  *
543  * Returns: Max number of frames that can be converted at a time, 0 if buffers
544  *   have not been allocated yet.
545  */
546 guint
ipatch_sample_transform_get_max_frames(IpatchSampleTransform * transform)547 ipatch_sample_transform_get_max_frames(IpatchSampleTransform *transform)
548 {
549     g_return_val_if_fail(transform != NULL, 0);
550     return (transform->max_frames);
551 }
552 
553 /**
554  * ipatch_sample_transform_convert: (skip)
555  * @transform: An initialized sample transform object
556  * @src: Audio source buffer (@frames X the source frame size in bytes), can
557  *   be %NULL to use internal buffer (provided @frames is less than or equal to
558  *   the max frames that can be converted at a time), in which case buf1 of
559  *   @transform should already be loaded with source data.
560  * @dest: Converted audio destination buffer (@frames X the destination frame
561  *   size in bytes), can be %NULL to use internal buffer (provided @frames is
562  *   less than or equal to the max frames that can be converted at a time).
563  * @frames: Number of audio frames to convert
564  *
565  * Convert an arbitrary number of audio frames from user provided buffers,
566  * contrary to ipatch_sample_transform_convert_single() which uses internal
567  * buffers and converts a max of 1 buffer at a time.  The sample formats
568  * should already be assigned and internal buffers assigned or allocated.
569  *
570  * Returns: (transfer none): Pointer to converted data.  Will be @dest if it was not %NULL or
571  *   internal buffer containing the converted data otherwise.
572  */
573 gpointer
ipatch_sample_transform_convert(IpatchSampleTransform * transform,gconstpointer src,gpointer dest,guint frames)574 ipatch_sample_transform_convert(IpatchSampleTransform *transform,
575                                 gconstpointer src, gpointer dest, guint frames)
576 {
577     int i, func_count;
578     guint  block_size;
579     gpointer buf1, buf2;
580     int src_frame_size, dest_frame_size, srcchan;
581 
582     g_return_val_if_fail(transform != NULL, NULL);
583     g_return_val_if_fail(frames > 0, NULL);
584     g_return_val_if_fail(transform->buf1 != NULL, NULL);
585     g_return_val_if_fail(transform->buf2 != NULL, NULL);
586     g_return_val_if_fail(transform->max_frames > 0, NULL);
587     g_return_val_if_fail((src && dest) || frames <= transform->max_frames, NULL);
588 
589     block_size = transform->max_frames;
590     func_count = transform->func_count;
591     buf1 = transform->buf1;
592     buf2 = transform->buf2;
593 
594     if(!src)
595     {
596         src = buf1;
597     }
598 
599     src_frame_size = ipatch_sample_format_size(transform->src_format);
600     srcchan = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format);
601 
602     dest_frame_size = ipatch_sample_format_size(transform->dest_format);
603 
604     if(func_count == 0)	/* same format?  Just copy it */
605     {
606         if(dest)
607         {
608             memcpy(dest, src, frames * src_frame_size);
609             return (dest);
610         }
611         else
612         {
613             return ((gpointer)src);
614         }
615     }
616 
617     while(frames > 0)
618     {
619         if(block_size > frames)
620         {
621             block_size = frames;
622         }
623 
624         transform->frames = block_size;
625         transform->samples = block_size * srcchan;
626 
627         /* execute first transform function with src buffer directly */
628         transform->buf1 = (gpointer)src;
629         transform->buf2 = (func_count == 1 && dest) ? dest : buf2;
630         (*transform->funcs[0])(transform);
631 
632         /* execute remaining transform functions */
633         for(i = 1; i < func_count; i++)
634         {
635             if(i & 1)
636             {
637                 transform->buf1 = buf2;
638                 transform->buf2 = (i == (func_count - 1) && dest) ? dest : buf1;
639             }
640             else
641             {
642                 transform->buf1 = buf1;
643                 transform->buf2 = (i == (func_count - 1) && dest) ? dest : buf2;
644             }
645 
646             (*transform->funcs[i])(transform);
647         }
648 
649         frames -= block_size;
650         src = (guint8 *)src + (block_size * src_frame_size);
651 
652         if(dest)
653         {
654             dest = (guint8 *)dest + (block_size * dest_frame_size);
655         }
656     }
657 
658     transform->buf1 = buf1;
659     transform->buf2 = buf2;
660 
661     if(dest)
662     {
663         return (dest);
664     }
665     else
666     {
667         return ((func_count & 1) ? buf2 : buf1);
668     }
669 }
670 
671 /**
672  * ipatch_sample_transform_convert_sizes: (rename-to ipatch_sample_transform_convert)
673  * @transform: An initialized sample transform object
674  * @src: (array length=src_size) (element-type guint8) (transfer none): Audio source buffer
675  * @src_size: Size of src buffer data to convert (in bytes, must be a multiple of the source format)
676  * @dest_size: (out): Location to store size of returned converted audio buffer
677  *
678  * Convert an arbitrary number of audio frames from user provided buffer.
679  * This is like ipatch_sample_transform_convert() but friendly to GObject
680  * introspection and the destination conversion buffer is allocated.  The sample formats
681  * should already be assigned and internal buffers assigned or allocated.
682  *
683  * Returns: (array length=dest_size) (element-type guint8) (transfer full): Newly allocated
684  *   converted audio data buffer.  Free with g_free() when done with it.
685  *
686  * Since: 1.1.0
687  */
688 gpointer
ipatch_sample_transform_convert_sizes(IpatchSampleTransform * transform,gconstpointer src,guint src_size,guint * dest_size)689 ipatch_sample_transform_convert_sizes(IpatchSampleTransform *transform,
690                                       gconstpointer src, guint src_size,
691                                       guint *dest_size)
692 {
693     int src_frame_size, dest_frame_size, frames;
694     gpointer destbuf;
695 
696     g_return_val_if_fail(transform != NULL, NULL);
697     g_return_val_if_fail(src_size > 0, NULL);
698 
699     src_frame_size = ipatch_sample_format_size(transform->src_format);
700     g_return_val_if_fail(src_size % src_frame_size == 0, NULL);
701 
702     dest_frame_size = ipatch_sample_format_size(transform->dest_format);
703     g_return_val_if_fail(dest_frame_size > 0, NULL);
704 
705     frames = src_size / src_frame_size;
706 
707     destbuf = g_malloc(dest_frame_size * frames);         // ++ alloc conversion buffer
708 
709     if(dest_size)
710     {
711         *dest_size = dest_frame_size * frames;
712     }
713 
714     if(!ipatch_sample_transform_convert(transform, src, destbuf, frames))
715     {
716         g_free(destbuf);    // -- free buffer on error
717         return (NULL);
718     }
719 
720     return (destbuf);     // !! caller takes over converted buffer
721 }
722 
723 /**
724  * ipatch_sample_transform_convert_single: (skip)
725  * @transform: An initialized sample transform object
726  * @frames: Number of frames to convert (should be less than or equal to the
727  *   maximum frames which can be converted at a time
728  *   (see ipatch_sample_transform_get_max_frames()).
729  *
730  * Convert the format of a single buffer of audio. The @transform object must
731  * have had its sample formats set and buffers assigned or allocated.  Use
732  * ipatch_sample_transform_convert() to convert from one buffer to another
733  * regardless of the number of frames.
734  *
735  * Returns: (transfer none): Pointer to converted audio data (buffer is internal to @transform
736  * object).
737  */
738 gpointer
ipatch_sample_transform_convert_single(IpatchSampleTransform * transform,guint frames)739 ipatch_sample_transform_convert_single(IpatchSampleTransform *transform,
740                                        guint frames)
741 {
742     gpointer temp;
743     int i, count;
744 
745     g_return_val_if_fail(transform != NULL, NULL);
746     g_return_val_if_fail(frames > 0 && frames <= transform->max_frames, NULL);
747     g_return_val_if_fail(transform->buf1 != NULL, NULL);
748     g_return_val_if_fail(transform->buf2 != NULL, NULL);
749 
750     transform->frames = frames;
751     transform->samples = frames;
752 
753     /* multiply frames by number of channels to get # of samples */
754     transform->samples *= IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(transform->src_format);
755 
756     count = transform->func_count;
757 
758     /* execute each transform function */
759     for(i = 0; i < count; i++)
760     {
761         (*transform->funcs[i])(transform);
762 
763         /* swap buffer pointers */
764         temp = transform->buf1;
765         transform->buf1 = transform->buf2;
766         transform->buf2 = temp;
767     }
768 
769     /* swap 1 more time if odd number of functions to set buffer order right */
770     if(count & 1)
771     {
772         temp = transform->buf1;
773         transform->buf1 = transform->buf2;
774         transform->buf2 = temp;
775 
776         return (transform->buf2);
777     }
778     else
779     {
780         return (transform->buf1);
781     }
782 }
783