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: IpatchSF2VoiceCache
22 * @short_description: SoundFont voice cache object
23 * @see_also:
24 * @stability: Stable
25 *
26 * This is used for pre-processing instruments into arrays of SoundFont
27 * compatible voices which can then be accessed very quickly without
28 * multi-thread locking or other issues (during synthesis for example).
29 */
30 #include <stdarg.h>
31 #include <glib.h>
32 #include <glib-object.h>
33
34 #include "IpatchSF2VoiceCache.h"
35 #include "IpatchSF2Mod.h"
36 #include "IpatchTypeProp.h"
37 #include "i18n.h"
38
39 /* SoundFont voice native sample format */
40 #define VOICE_SAMPLE_FORMAT \
41 (IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_MONO | IPATCH_SAMPLE_ENDIAN_HOST)
42
43 static void ipatch_sf2_voice_cache_class_init(IpatchSF2VoiceCacheClass *klass);
44 static void ipatch_sf2_voice_cache_init(IpatchSF2VoiceCache *cache);
45 static void ipatch_sf2_voice_cache_finalize(GObject *gobject);
46
47 static gpointer parent_class = NULL;
48 static IpatchSF2Voice def_voice; /* default voice structure */
49
50
51 /* default selection info */
52 static IpatchSF2VoiceSelInfo default_sel_info[] =
53 {
54 { IPATCH_SF2_VOICE_SEL_NOTE },
55 { IPATCH_SF2_VOICE_SEL_VELOCITY }
56 };
57
58 GType
ipatch_sf2_voice_cache_get_type(void)59 ipatch_sf2_voice_cache_get_type(void)
60 {
61 static GType item_type = 0;
62
63 if(!item_type)
64 {
65 static const GTypeInfo item_info =
66 {
67 sizeof(IpatchSF2VoiceCacheClass), NULL, NULL,
68 (GClassInitFunc)ipatch_sf2_voice_cache_class_init, NULL, NULL,
69 sizeof(IpatchSF2VoiceCache),
70 0,
71 (GInstanceInitFunc)ipatch_sf2_voice_cache_init,
72 };
73
74 item_type = g_type_register_static(G_TYPE_OBJECT,
75 "IpatchSF2VoiceCache", &item_info, 0);
76
77 /* install voice update function type property */
78 ipatch_type_install_property
79 (g_param_spec_pointer("sf2voice-update-func", "sf2voice-update-func",
80 "sf2voice-update-func", G_PARAM_READWRITE));
81 }
82
83 return (item_type);
84 }
85
86 static void
ipatch_sf2_voice_cache_class_init(IpatchSF2VoiceCacheClass * klass)87 ipatch_sf2_voice_cache_class_init(IpatchSF2VoiceCacheClass *klass)
88 {
89 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
90
91 parent_class = g_type_class_peek_parent(klass);
92
93 obj_class->finalize = ipatch_sf2_voice_cache_finalize;
94
95 /* initialize default voice structure */
96 def_voice.sample_data = NULL;
97 def_voice.sample_store = NULL;
98 def_voice.sample_size = 0;
99 def_voice.loop_start = 0;
100 def_voice.loop_end = 0;
101 def_voice.rate = 44100;
102 def_voice.root_note = 60;
103 def_voice.fine_tune = 0;
104 def_voice.reserved = 0;
105
106 ipatch_sf2_gen_array_init(&def_voice.gen_array, FALSE, FALSE);
107 def_voice.mod_list = NULL;
108
109 def_voice.range_index = 0; /* init in ipatch_sf2_voice_cache_add_voice */
110 }
111
112 static void
ipatch_sf2_voice_cache_init(IpatchSF2VoiceCache * cache)113 ipatch_sf2_voice_cache_init(IpatchSF2VoiceCache *cache)
114 {
115 cache->sel_info = g_memdup(default_sel_info, sizeof(default_sel_info));
116 cache->sel_count = 2; /* default for velocity and note selection */
117 cache->voices = g_array_new(FALSE, FALSE, sizeof(IpatchSF2Voice));
118 cache->ranges = NULL; /* initialized later */
119 cache->default_mods = ipatch_sf2_mod_list_duplicate
120 (ipatch_sf2_mod_list_get_default());
121 cache->default_loop_type = IPATCH_SAMPLE_LOOP_STANDARD;
122 }
123
124 static void
ipatch_sf2_voice_cache_finalize(GObject * gobject)125 ipatch_sf2_voice_cache_finalize(GObject *gobject)
126 {
127 IpatchSF2VoiceCache *cache = IPATCH_SF2_VOICE_CACHE(gobject);
128 IpatchSF2Voice *voice;
129 guint i;
130
131 g_free(cache->sel_info);
132
133 /*-------- free IpatchSF2Voice field members -------------------------------*/
134 for(i = 0; i < cache->voices->len; i++)
135 {
136 voice = &g_array_index(cache->voices, IpatchSF2Voice, i);
137
138 /* running voice_user_data_destroy() function first ensures that the
139 function could expect that any IpatchSF2Voice field member is
140 still valid. This is necessarry if the parameter voice->user_data
141 is a pointer to one of this field.
142 */
143 if(cache->voice_user_data_destroy && voice->user_data)
144 {
145 cache->voice_user_data_destroy(voice->user_data);
146 }
147
148 if(voice->sample_data) /* -- unref sample data */
149 {
150 g_object_unref(voice->sample_data);
151 }
152
153 if(voice->sample_store) /* -- unref sample store */
154 {
155 g_object_unref(voice->sample_store);
156 }
157
158 if(voice->mod_list) /* free modulators */
159 {
160 ipatch_sf2_mod_list_free(voice->mod_list, TRUE);
161 }
162 }
163
164 g_array_free(cache->voices, TRUE);
165
166 /*-------- free IpatchSF2VoiceCache field members -----------------------*/
167
168 /* running user_data_destroy() function first ensures that the
169 function could expect that any IpatchSF2VoiceCache field member
170 is still valid. This is necessarry if the parameter cache->user_data
171 is a pointer to one of this field.
172 */
173 if(cache->user_data_destroy && cache->user_data)
174 {
175 cache->user_data_destroy(cache->user_data);
176 }
177
178 if(cache->ranges)
179 {
180 g_array_free(cache->ranges, TRUE);
181 }
182
183 if(cache->default_mods)
184 {
185 ipatch_sf2_mod_list_free(cache->default_mods, TRUE);
186 }
187
188 if(cache->override_mods)
189 {
190 ipatch_sf2_mod_list_free(cache->override_mods, TRUE);
191 }
192
193 if(G_OBJECT_CLASS(parent_class)->finalize)
194 {
195 G_OBJECT_CLASS(parent_class)->finalize(gobject);
196 }
197 }
198
199 /* FIXME-GIR: Voice cache interface isn't currently binding friendly */
200
201 /**
202 * ipatch_sf2_voice_cache_new: (skip)
203 * @info: (nullable): Array of selection info structures (length should be @sel_count), use
204 * %NULL for default selection info (MIDI note and velocity)
205 * @sel_count: Count of selection ranges for this cache (ignored if @info is %NULL)
206 *
207 * Create a new SoundFont voice cache object. The @sel_count parameter
208 * defines the number of selection ranges for the cache. Examples of selection
209 * ranges include MIDI note and velocity ranges for a voice. The @info
210 * parameter should be a pointer to an array of selection info structures
211 * which describes each selection type.
212 *
213 * Returns: New SoundFont voice cache with a reference count of 1 which the
214 * caller owns.
215 */
216 IpatchSF2VoiceCache *
ipatch_sf2_voice_cache_new(IpatchSF2VoiceSelInfo * info,int sel_count)217 ipatch_sf2_voice_cache_new(IpatchSF2VoiceSelInfo *info, int sel_count)
218 {
219 IpatchSF2VoiceCache *cache;
220
221 g_return_val_if_fail(!info || sel_count > 0, NULL);
222
223 cache = IPATCH_SF2_VOICE_CACHE(g_object_new(IPATCH_TYPE_SF2_VOICE_CACHE,
224 NULL));
225
226 if(info)
227 {
228 g_free(cache->sel_info);
229 cache->sel_info = g_memdup(info, sizeof(IpatchSF2VoiceSelInfo) * sel_count);
230 cache->sel_count = sel_count;
231 }
232
233 return (cache);
234 }
235
236 /**
237 * ipatch_sf2_voice_cache_set_default_mods: (skip)
238 * @cache: Voice cache
239 * @mods: (element-type IpatchSF2Mod) (transfer full): SoundFont modulator list
240 * to use as default (used directly)
241 *
242 * Set the default modulator list for the voice cache. Modulator list is used
243 * directly and the allocation of the list is taken over by the voice cache.
244 */
245 void
ipatch_sf2_voice_cache_set_default_mods(IpatchSF2VoiceCache * cache,GSList * mods)246 ipatch_sf2_voice_cache_set_default_mods(IpatchSF2VoiceCache *cache,
247 GSList *mods)
248 {
249 g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache));
250
251 if(cache->default_mods)
252 {
253 ipatch_sf2_mod_list_free(cache->default_mods, TRUE);
254 }
255
256 cache->default_mods = mods;
257 }
258
259 /**
260 * ipatch_sf2_voice_cache_set_override_mods: (skip)
261 * @cache: Voice cache
262 * @mods: (element-type IpatchSF2Mod) (transfer full): SoundFont modulator list
263 * which overrides rendered voice modulators (used directly)
264 *
265 * Set the override modulator list for the voice cache. Modulator list is used
266 * directly and the allocation of the list is taken over by the voice cache.
267 *
268 * Since: 1.1.0
269 */
270 void
ipatch_sf2_voice_cache_set_override_mods(IpatchSF2VoiceCache * cache,GSList * mods)271 ipatch_sf2_voice_cache_set_override_mods(IpatchSF2VoiceCache *cache,
272 GSList *mods)
273 {
274 g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache));
275
276 if(cache->override_mods)
277 {
278 ipatch_sf2_mod_list_free(cache->override_mods, TRUE);
279 }
280
281 cache->override_mods = mods;
282 }
283
284 /**
285 * ipatch_sf2_voice_cache_add_voice: (skip)
286 * @cache: Voice cache to create voice for
287 *
288 * Adds a new initialized voice to a SoundFont voice cache.
289 *
290 * Returns: (transfer none): Pointer to new voice which is owned by the @cache.
291 * The sample pointer is set to NULL, generator array is initialized to default
292 * absolute unset values, selection ranges are set to G_MININT-G_MAXINT and
293 * all other fields are initialized to defaults.
294 */
295 IpatchSF2Voice *
ipatch_sf2_voice_cache_add_voice(IpatchSF2VoiceCache * cache)296 ipatch_sf2_voice_cache_add_voice(IpatchSF2VoiceCache *cache)
297 {
298 IpatchSF2Voice *voice;
299 int *ranges;
300 int i;
301
302 g_return_val_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache), NULL);
303
304 /* create range integer array if this is the first voice */
305 if(!cache->ranges)
306 cache->ranges = g_array_new(FALSE, FALSE, 2 * sizeof(int)
307 * cache->sel_count);
308
309 g_array_append_val(cache->voices, def_voice);
310
311 voice = &g_array_index(cache->voices, IpatchSF2Voice, cache->voices->len - 1);
312
313 voice->range_index = cache->ranges->len * cache->sel_count * 2;
314
315 /* add selection ranges for this voice to range array */
316 g_array_set_size(cache->ranges, cache->ranges->len + 1);
317
318 /* initialize ranges to wildcard range */
319 ranges = &((int *)(cache->ranges->data))[voice->range_index];
320
321 for(i = 0; i < cache->sel_count; i++)
322 {
323 ranges[i * 2] = G_MININT;
324 ranges[i * 2 + 1] = G_MAXINT;
325 }
326
327 return (voice);
328 }
329
330 /**
331 * ipatch_sf2_voice_cache_set_voice_range: (skip)
332 * @cache: Voice cache object
333 * @voice: Voice pointer in cache
334 * @sel_index: Selection index
335 * @low: Range low value
336 * @high: Range high value
337 *
338 * Set a voice selection range. Selection ranges are used for selection criteria
339 * such as MIDI velocity and note ranges.
340 */
341 void
ipatch_sf2_voice_cache_set_voice_range(IpatchSF2VoiceCache * cache,IpatchSF2Voice * voice,guint sel_index,int low,int high)342 ipatch_sf2_voice_cache_set_voice_range(IpatchSF2VoiceCache *cache,
343 IpatchSF2Voice *voice, guint sel_index,
344 int low, int high)
345 {
346 int *rangep;
347
348 g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache));
349 g_return_if_fail(voice != NULL);
350 g_return_if_fail(sel_index < (guint)cache->sel_count);
351 g_return_if_fail(low <= high);
352
353 rangep = (int *)(cache->ranges->data);
354 rangep[voice->range_index + sel_index * 2] = low;
355 rangep[voice->range_index + sel_index * 2 + 1] = high;
356 }
357
358 /**
359 * ipatch_sf2_voice_set_sample_data: (skip)
360 * @voice: SoundFont voice structure
361 * @sample_data: Sample data to assign to voice
362 *
363 * Assign sample data to a SoundFont voice.
364 */
365 void
ipatch_sf2_voice_set_sample_data(IpatchSF2Voice * voice,IpatchSampleData * sample_data)366 ipatch_sf2_voice_set_sample_data(IpatchSF2Voice *voice, IpatchSampleData *sample_data)
367 {
368 g_return_if_fail(voice != NULL);
369 g_return_if_fail(IPATCH_IS_SAMPLE_DATA(sample_data));
370
371 if(voice->sample_data)
372 {
373 g_object_unref(voice->sample_data);
374 }
375
376 voice->sample_data = g_object_ref(sample_data); /* ++ ref sample data */
377
378 if(voice->sample_store)
379 {
380 g_object_unref(voice->sample_store);
381 }
382
383 voice->sample_store = NULL;
384 voice->sample_size = ipatch_sample_data_get_size(voice->sample_data);
385 }
386
387 /**
388 * ipatch_sf2_voice_cache_sample_data: (skip)
389 * @voice: SoundFont voice structure
390 * @err: Location to store error info or %NULL to ignore
391 *
392 * Cache an already assigned sample data object of a voice. The sample data is
393 * cached as 16 bit mono native endian format, if not already cached, and the
394 * new cached sample is assigned to the sample_store field.
395 *
396 * Returns: %TRUE on success, %FALSE otherwise (in which case @err may be set)
397 */
398 gboolean
ipatch_sf2_voice_cache_sample_data(IpatchSF2Voice * voice,GError ** err)399 ipatch_sf2_voice_cache_sample_data(IpatchSF2Voice *voice, GError **err)
400 {
401 g_return_val_if_fail(voice != NULL, FALSE);
402 g_return_val_if_fail(!err || !*err, FALSE);
403 g_return_val_if_fail(voice->sample_data != NULL, FALSE);
404
405 if(voice->sample_store)
406 {
407 g_object_unref(voice->sample_store);
408 }
409
410 /* FIXME - Do we need channel_map per voice? */
411 /* ++ ref cached sample store for voice */
412 voice->sample_store
413 = ipatch_sample_data_get_cache_sample(voice->sample_data, VOICE_SAMPLE_FORMAT,
414 IPATCH_SAMPLE_UNITY_CHANNEL_MAP, err);
415
416 return (voice->sample_store != NULL);
417 }
418
419 /**
420 * ipatch_sf2_voice_copy: (skip)
421 * @dest: Destination voice to copy to (initialized to 0s or already valid)
422 * @src: Source voice to copy from
423 *
424 * Copy a source (@src) voice's information to a destination voice (@dest).
425 * Does not copy selection criteria integers in parent #IpatchSF2VoiceCache
426 * objects.
427 */
428 void
ipatch_sf2_voice_copy(IpatchSF2Voice * dest,IpatchSF2Voice * src)429 ipatch_sf2_voice_copy(IpatchSF2Voice *dest, IpatchSF2Voice *src)
430 {
431 g_return_if_fail(dest != NULL);
432 g_return_if_fail(src != NULL);
433
434 if(dest->sample_data)
435 {
436 g_object_unref(dest->sample_data);
437 }
438
439 if(dest->sample_store)
440 {
441 g_object_unref(dest->sample_store);
442 }
443
444 if(src->sample_data)
445 {
446 dest->sample_data = g_object_ref(src->sample_data);
447 }
448 else
449 {
450 dest->sample_data = NULL;
451 }
452
453 if(src->sample_store)
454 {
455 dest->sample_store = g_object_ref(src->sample_store);
456 }
457 else
458 {
459 dest->sample_store = NULL;
460 }
461
462 dest->sample_size = src->sample_size;
463 dest->loop_start = src->loop_start;
464 dest->loop_end = src->loop_end;
465 dest->rate = src->rate;
466 dest->root_note = src->root_note;
467 dest->fine_tune = src->fine_tune;
468 dest->reserved = src->reserved;
469 dest->gen_array = src->gen_array;
470
471 dest->mod_list = ipatch_sf2_mod_list_duplicate(src->mod_list);
472 }
473
474 /**
475 * ipatch_sf2_voice_cache_optimize: (skip)
476 * @cache: SoundFont voice cache
477 *
478 * Can be called after all voices have been added to a voice cache.
479 * Will optimize voice cache for use with ipatch_sf2_voice_cache_select().
480 * NOTE: Currently does nothing, but will in the future..
481 */
482 void
ipatch_sf2_voice_cache_optimize(IpatchSF2VoiceCache * cache)483 ipatch_sf2_voice_cache_optimize(IpatchSF2VoiceCache *cache)
484 {
485 /* FIXME */
486 }
487
488 /**
489 * ipatch_sf2_voice_cache_select: (skip)
490 * @cache: SoundFont voice cache
491 * @select_values: Array of select values which must be the same length as
492 * the voice cache was initialized with. Each selection value is tested
493 * against each voice's selection ranges (use #IPATCH_SF2_VOICE_SEL_WILDCARD
494 * as a wildcard selection value).
495 * @index_array: Voice indexes are stored in this array.
496 * @max_indexes: Maximum number of voices to match. @index_array should be
497 * at least this value in size.
498 *
499 * Stores pointers to voices matching @select_values criteria.
500 *
501 * Returns: Number of indexes stored to @index_array.
502 */
503 int
ipatch_sf2_voice_cache_select(IpatchSF2VoiceCache * cache,int * select_values,guint16 * index_array,guint16 max_indexes)504 ipatch_sf2_voice_cache_select(IpatchSF2VoiceCache *cache, int *select_values,
505 guint16 *index_array, guint16 max_indexes)
506 {
507 IpatchSF2Voice *voice;
508 guint16 *indexp = index_array;
509 GArray *voices;
510 int *range_array;
511 int i, count, scount, sv, si, ri;
512 int matches;
513
514 g_return_val_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache), 0);
515 g_return_val_if_fail(select_values != NULL, 0);
516 g_return_val_if_fail(index_array != NULL, 0);
517 g_return_val_if_fail(max_indexes > 0, 0);
518
519 /* OPTME - This could be optimized, currently just iterates over array */
520
521 count = cache->voices->len;
522 voices = cache->voices;
523 scount = cache->sel_count;
524
525 if(!cache->ranges) /* no ranges means no voices, return */
526 {
527 return (0);
528 }
529
530 range_array = (int *)(cache->ranges->data); /* array will not change */
531
532 /* loop over all voices */
533 for(i = 0, matches = 0; i < count && matches < max_indexes; i++)
534 {
535 voice = &g_array_index(voices, IpatchSF2Voice, i);
536
537 /* loop over selection ranges */
538 for(si = 0, ri = voice->range_index; si < scount; si++, ri += 2)
539 {
540 sv = select_values[si];
541
542 /* break out if no match (not -1 and not in range) */
543 if(sv != -1 && !(sv >= range_array[ri] && sv <= range_array[ri + 1]))
544 {
545 break;
546 }
547 }
548
549 if(si == scount) /* all range selection criteria matched? */
550 {
551 *indexp = i; /* add voice index to index array */
552 indexp++;
553 matches++;
554 }
555 }
556
557 return (matches);
558 }
559
560 /**
561 * ipatch_sf2_voice_cache_updates: (skip)
562 * @cache: Voice cache to get updates for
563 * @select_values: The voice selection criteria to use, should be the same
564 * number of select values as in @cache
565 * @cache_item: Original item @cache was created from
566 * @item: Object for which a property changed (only really makes sense if it is
567 * a dependent item of @cache_item).
568 * @pspec: Parameter specification of property which changed
569 * @value: The new value of the property
570 * @updates: Output array to store updates to
571 * @max_updates: Size of @updates array (max possible update values).
572 *
573 * This function is used to re-calculate SoundFont effect generators for a
574 * single object property change. Useful for real time effect changes.
575 *
576 * Returns: Number of updates stored to @updates array or -1 if not handled
577 * (no handler for the given @item). Will be 0 if no updates required.
578 */
579 int
ipatch_sf2_voice_cache_update(IpatchSF2VoiceCache * cache,int * select_values,GObject * cache_item,GObject * item,GParamSpec * pspec,const GValue * value,IpatchSF2VoiceUpdate * updates,guint max_updates)580 ipatch_sf2_voice_cache_update(IpatchSF2VoiceCache *cache,
581 int *select_values, GObject *cache_item,
582 GObject *item, GParamSpec *pspec,
583 const GValue *value,
584 IpatchSF2VoiceUpdate *updates,
585 guint max_updates)
586 {
587 IpatchSF2VoiceCacheUpdateHandler handler;
588
589 g_return_val_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache), -1);
590 g_return_val_if_fail(select_values != NULL, -1);
591 g_return_val_if_fail(G_IS_OBJECT(cache_item), -1);
592 g_return_val_if_fail(G_IS_OBJECT(item), -1);
593 g_return_val_if_fail(G_IS_PARAM_SPEC(pspec), -1);
594 g_return_val_if_fail(G_IS_VALUE(value), -1);
595 g_return_val_if_fail(updates != NULL, -1);
596
597 ipatch_type_get(G_OBJECT_TYPE(cache_item), "sf2voice-update-func", &handler,
598 NULL);
599
600 if(!handler)
601 {
602 return (-1);
603 }
604
605 if(max_updates == 0)
606 {
607 return (0);
608 }
609
610 return handler(cache, select_values, cache_item, item, pspec, value,
611 updates, max_updates);
612 }
613
614 #ifdef IPATCH_DEBUG
615 /**
616 * ipatch_sf2_voice_cache_dump: (skip)
617 *
618 * Debugging function to dump a voice cache to stdout
619 */
620 void
ipatch_sf2_voice_cache_dump(IpatchSF2VoiceCache * cache,int start,int count)621 ipatch_sf2_voice_cache_dump(IpatchSF2VoiceCache *cache, int start, int count)
622 {
623 IpatchSF2Voice *voice;
624 char *selnames[] = { "Note", "Vel", "AftTch", "CC" };
625 int *range;
626 int i, si;
627
628 g_return_if_fail(IPATCH_IS_SF2_VOICE_CACHE(cache));
629
630 if(start == 0)
631 {
632 printf("Voice cache selection criteria:\n");
633
634 for(i = 0; i < cache->sel_count; i++)
635 {
636 switch(cache->sel_info[i].type)
637 {
638 case IPATCH_SF2_VOICE_SEL_NOTE:
639 printf("%d: Note\n", i);
640 break;
641
642 case IPATCH_SF2_VOICE_SEL_VELOCITY:
643 printf("%d: Velocity\n", i);
644 break;
645
646 case IPATCH_SF2_VOICE_SEL_AFTER_TOUCH:
647 printf("%d: After touch\n", i);
648 break;
649
650 case IPATCH_SF2_VOICE_SEL_MIDI_CC:
651 printf("%d: CC %d\n", i, cache->sel_info[i].param1);
652 break;
653 }
654 }
655 }
656
657 for(i = start; i < start + count && i < cache->voices->len; i++)
658 {
659 voice = &g_array_index(cache->voices, IpatchSF2Voice, i);
660
661 printf("%d (S:%d,SD:%p,SS:%p) L:%d-%d R:%d RN:%d T:%d\n ",
662 i, voice->sample_size, voice->sample_data, voice->sample_store,
663 voice->loop_start, voice->loop_end, voice->rate, voice->root_note,
664 voice->fine_tune);
665
666 range = &g_array_index(cache->ranges, int, voice->range_index);
667
668 for(si = 0; si < cache->sel_count; si++)
669 {
670 printf(" %s: %d-%d", selnames[cache->sel_info[si].type],
671 range[si * 2], range[si * 2 + 1]);
672 }
673
674 printf("\n");
675 }
676 }
677
678 /**
679 * ipatch_sf2_voice_cache_dump_select: (skip)
680 */
681 void
ipatch_sf2_voice_cache_dump_select(IpatchSF2VoiceCache * cache,...)682 ipatch_sf2_voice_cache_dump_select(IpatchSF2VoiceCache *cache, ...)
683 {
684 IpatchSF2Voice *voice;
685 va_list args;
686 char *selnames[] = { "Note", "Vel", "AftTch", "CC" };
687 guint16 indexes[256];
688 int selvals[8];
689 int *range;
690 int count, i, vindex, si;
691
692 va_start(args, cache);
693
694 for(i = 0; i < cache->sel_count; i++)
695 {
696 selvals[i] = va_arg(args, int);
697 }
698
699 va_end(args);
700
701 count = ipatch_sf2_voice_cache_select(cache, selvals, indexes, G_N_ELEMENTS(indexes));
702
703 printf("%d voices matched:\n", count);
704
705 for(i = 0; i < count; i++)
706 {
707 vindex = indexes[i];
708 voice = &g_array_index(cache->voices, IpatchSF2Voice, vindex);
709
710 printf("%d (S:%d,SD:%p,SS:%p) L:%d-%d R:%d RN:%d T:%d\n ",
711 vindex, voice->sample_size, voice->sample_data, voice->sample_store,
712 voice->loop_start, voice->loop_end, voice->rate, voice->root_note,
713 voice->fine_tune);
714
715 range = &g_array_index(cache->ranges, int, voice->range_index);
716
717 for(si = 0; si < cache->sel_count; si++)
718 {
719 printf(" %s: %d-%d", selnames[cache->sel_info[si].type],
720 range[si * 2], range[si * 2 + 1]);
721 }
722
723 printf("\n");
724 }
725 }
726 #endif
727