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