1 /*
2 * libInstPatch
3 * Copyright (C) 1999-2014 Element Green <element@elementsofsound.org>
4 *
5 * Author of this file: (C) 2012 BALATON Zoltan <balaton@eik.bme.hu>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public License
9 * as published by the Free Software Foundation; version 2.1
10 * of the License only.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02110-1301, USA or on the web at http://www.gnu.org.
21 */
22 /**
23 * SECTION: IpatchSLIWriter
24 * @short_description: Spectralis SLI/SLC instrument file writer
25 * @see_also: #IpatchSLI
26 *
27 * Writes an SLI instrument object tree (#IpatchSLI) to an SLI or SLC file.
28 */
29 #include <glib.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <libinstpatch/IpatchSLIWriter.h>
34 #include <libinstpatch/IpatchRiff.h>
35 #include <libinstpatch/IpatchSF2GenItem.h>
36 #include <libinstpatch/IpatchSampleStoreFile.h>
37 #include "IpatchSLIFile_priv.h"
38 #include "compat.h"
39 #include "ipatch_priv.h"
40 #include "i18n.h"
41
42 /* NOTICE: A duplicate SLI object is used for saving. This
43 * solves all multi-thread issues and allows one to continue editing
44 * even while a file is being saved. It also means that the
45 * duplicate SLI object can be accessed directly without
46 * locking. Sample data objects are not duplicated though, so they
47 * still need to be dealt with properly.
48 */
49
50 /* Hash value used for sample_hash */
51 typedef struct
52 {
53 guint index; /* sample index */
54 guint position; /* position in file */
55 guint offset; /* offset in chunk */
56 guint length; /* data length in bytes */
57 guint channels; /* channel count */
58 } SampleHashValue;
59
60 #define FORMAT_16BIT IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_SIGNED | IPATCH_SAMPLE_LENDIAN
61
62 static void ipatch_sli_writer_finalize(GObject *object);
63
64 static GQuark ipatch_sli_writer_error_quark(void);
65 static SampleHashValue *ipatch_sli_writer_sample_hash_value_new(void);
66 static void ipatch_sli_writer_sample_hash_value_destroy(gpointer value);
67 static gint ipatch_sli_writer_inst_find_func(gconstpointer a, gconstpointer b);
68
69 static GSList *ipatch_sli_writer_find_groups(IpatchSLI *sli);
70 static gboolean ipatch_sli_writer_write_group(IpatchSLIWriter *writer,
71 GPtrArray *ig, GError **err);
72 static gboolean ipatch_sli_writer_write_sifi(IpatchFileHandle *handle,
73 guint ignum, GError **err);
74 static void ipatch_sli_writer_write_siig(IpatchFileHandle *handle,
75 IpatchSLISiIg *siig);
76 static void ipatch_sli_writer_write_inst_header(IpatchFileHandle *handle,
77 IpatchSLIInstHeader *ihdr);
78 static void ipatch_sli_writer_write_zone_header(IpatchFileHandle *handle,
79 IpatchSLIZone *zone,
80 guint sample_idx);
81 static void ipatch_sli_writer_write_sample_header(IpatchFileHandle *handle,
82 SampleHashValue *sample_info,
83 IpatchSLISample *sample);
84 static void ipatch_sli_writer_write_sidp(IpatchFileHandle *handle,
85 IpatchSLISiDp *sidp);
86 static gboolean ipatch_sli_writer_write_sample_data(IpatchFileHandle *handle,
87 IpatchSLISample *sample,
88 GError **err);
89
G_DEFINE_TYPE(IpatchSLIWriter,ipatch_sli_writer,G_TYPE_OBJECT)90 G_DEFINE_TYPE(IpatchSLIWriter, ipatch_sli_writer, G_TYPE_OBJECT)
91
92 static void
93 ipatch_sli_writer_class_init(IpatchSLIWriterClass *klass)
94 {
95 GObjectClass *obj_class = G_OBJECT_CLASS(klass);
96 obj_class->finalize = ipatch_sli_writer_finalize;
97 }
98
99 static void
ipatch_sli_writer_init(IpatchSLIWriter * writer)100 ipatch_sli_writer_init(IpatchSLIWriter *writer)
101 {
102 writer->sli = NULL;
103 writer->sample_hash = g_hash_table_new_full(NULL, NULL, NULL,
104 ipatch_sli_writer_sample_hash_value_destroy);
105 }
106
107 static void
ipatch_sli_writer_finalize(GObject * object)108 ipatch_sli_writer_finalize(GObject *object)
109 {
110 IpatchSLIWriter *writer = IPATCH_SLI_WRITER(object);
111
112 if(writer->handle)
113 {
114 ipatch_file_close(writer->handle); /* -- unref file object */
115 }
116
117 if(writer->orig_sli)
118 {
119 g_object_unref(writer->orig_sli);
120 writer->orig_sli = NULL;
121 }
122
123 if(writer->sli)
124 {
125 g_object_unref(writer->sli);
126 writer->sli = NULL;
127 }
128
129 if(writer->sample_hash)
130 {
131 g_hash_table_destroy(writer->sample_hash);
132 writer->sample_hash = NULL;
133 }
134
135 if(writer->store_list)
136 {
137 g_object_unref(writer->store_list);
138 writer->store_list = NULL;
139 }
140
141 if(G_OBJECT_CLASS(ipatch_sli_writer_parent_class)->finalize)
142 {
143 G_OBJECT_CLASS(ipatch_sli_writer_parent_class)->finalize(object);
144 }
145 }
146
147 static GQuark
ipatch_sli_writer_error_quark(void)148 ipatch_sli_writer_error_quark(void)
149 {
150 static GQuark q = 0;
151
152 if(q == 0)
153 {
154 q = g_quark_from_static_string("sli-writer-error-quark");
155 }
156
157 return (q);
158 }
159
160 static SampleHashValue *
ipatch_sli_writer_sample_hash_value_new(void)161 ipatch_sli_writer_sample_hash_value_new(void)
162 {
163 return (g_slice_new0(SampleHashValue));
164 }
165
166 static void
ipatch_sli_writer_sample_hash_value_destroy(gpointer value)167 ipatch_sli_writer_sample_hash_value_destroy(gpointer value)
168 {
169 g_slice_free(SampleHashValue, value);
170 }
171
172 /**
173 * ipatch_sli_writer_new:
174 * @handle: SLI file handle to save to or %NULL to set later, taken over by
175 * writer object and will be closed on finalize.
176 * @sli: SLI object to save or %NULL to set later
177 *
178 * Create a new SLI file writer.
179 *
180 * Returns: The new SLI writer
181 */
182 IpatchSLIWriter *
ipatch_sli_writer_new(IpatchFileHandle * handle,IpatchSLI * sli)183 ipatch_sli_writer_new(IpatchFileHandle *handle, IpatchSLI *sli)
184 {
185 IpatchSLIWriter *writer;
186
187 g_return_val_if_fail(!handle || IPATCH_IS_SLI_FILE(handle->file), NULL);
188 g_return_val_if_fail(!sli || IPATCH_IS_SLI(sli), NULL);
189
190 writer = g_object_new(IPATCH_TYPE_SLI_WRITER, NULL);
191
192 if(handle)
193 {
194 ipatch_sli_writer_set_file_handle(writer, handle);
195 }
196
197 if(sli)
198 {
199 ipatch_sli_writer_set_patch(writer, sli);
200 }
201
202 return (writer);
203 }
204
205 /**
206 * ipatch_sli_writer_set_patch:
207 * @writer: SLI writer object
208 * @sli: SLI patch to save
209 *
210 * Set the SLI patch object to save with a SLI writer.
211 */
212 void
ipatch_sli_writer_set_patch(IpatchSLIWriter * writer,IpatchSLI * sli)213 ipatch_sli_writer_set_patch(IpatchSLIWriter *writer, IpatchSLI *sli)
214 {
215 g_return_if_fail(IPATCH_IS_SLI_WRITER(writer));
216 g_return_if_fail(IPATCH_IS_SLI(sli));
217
218 if(writer->orig_sli)
219 {
220 g_object_unref(writer->orig_sli);
221 }
222
223 writer->orig_sli = g_object_ref(sli);
224 }
225
226 /**
227 * ipatch_sli_writer_set_file_handle:
228 * @writer: SLI writer object
229 * @handle: (nullable): SLI file handle or %NULL to clear
230 *
231 * Set the SLI file handle of an SLI writer.
232 */
233 void
ipatch_sli_writer_set_file_handle(IpatchSLIWriter * writer,IpatchFileHandle * handle)234 ipatch_sli_writer_set_file_handle(IpatchSLIWriter *writer, IpatchFileHandle *handle)
235 {
236 g_return_if_fail(IPATCH_IS_SLI_WRITER(writer));
237 g_return_if_fail(handle && IPATCH_IS_SLI_FILE(handle->file));
238
239 /* Close old handle, if any */
240 if(writer->handle)
241 {
242 ipatch_file_close(writer->handle);
243 }
244
245 writer->handle = handle;
246 }
247
248 /**
249 * ipatch_sli_writer_save:
250 * @writer: SLI writer object
251 * @err: Location to store error info or %NULL
252 *
253 * Write an SLI object to a file.
254 *
255 * Returns: %TRUE on success, %FALSE on error
256 */
257 gboolean
ipatch_sli_writer_save(IpatchSLIWriter * writer,GError ** err)258 ipatch_sli_writer_save(IpatchSLIWriter *writer, GError **err)
259 {
260 GSList *ig, *igs = NULL;
261 guint32 len;
262 gboolean ret;
263
264 g_return_val_if_fail(IPATCH_IS_SLI_WRITER(writer), FALSE);
265 g_return_val_if_fail(!err || !*err, FALSE);
266 g_return_val_if_fail(writer->handle != NULL, FALSE);
267 g_return_val_if_fail(writer->orig_sli != NULL, FALSE);
268
269 if(writer->sli)
270 {
271 g_object_unref(writer->sli); /* shouldn't be set, but.. */
272 }
273
274 /* ++ref new duplicate sli object */
275 writer->sli = IPATCH_SLI(ipatch_item_duplicate(IPATCH_ITEM(writer->orig_sli)));
276 g_return_val_if_fail(writer->sli != NULL, FALSE);
277
278 /* build instrument groups and write main file header */
279 igs = ipatch_sli_writer_find_groups(writer->sli); /* ++ ref igs */
280
281 if(!igs)
282 {
283 g_set_error(err, ipatch_sli_writer_error_quark(),
284 IPATCH_RIFF_ERROR_INVALID_DATA,
285 "Could not determine groups for SLI");
286 return (FALSE);
287 }
288
289 if(!ipatch_sli_writer_write_sifi(writer->handle, g_slist_length(igs), err))
290 {
291 ret = FALSE;
292 goto end;
293 }
294
295 /* write groups */
296 for(ig = igs; ig; ig = g_slist_next(ig))
297 {
298 if(!ipatch_sli_writer_write_group(writer, ig->data, err))
299 {
300 ret = FALSE;
301 goto end;
302 }
303 }
304
305 /* fixup cklen field in SiFi header */
306 len = ipatch_file_get_position(writer->handle);
307
308 if(!ipatch_file_seek(writer->handle, IPATCH_RIFF_FOURCC_SIZE, G_SEEK_SET, err) ||
309 !ipatch_file_write_u32(writer->handle, len, err))
310 {
311 ret = FALSE;
312 goto end;
313 }
314
315 ret = TRUE;
316 g_object_set(writer->orig_sli,
317 "changed", FALSE, /* file and object are in sync */
318 "saved", TRUE, /* has now been saved */
319 NULL);
320 end:
321 g_slist_free_full(igs, (GDestroyNotify)g_ptr_array_unref); /* -- unref igs */
322
323 /* keep duplicated sli object for create_stores unless there was an error */
324 if(!ret)
325 {
326 g_object_unref(writer->sli); /* -- unref duplicate sli object */
327 writer->sli = NULL;
328 }
329
330 return ret;
331 }
332
333
334 /**
335 * ipatch_sli_writer_create_stores:
336 * @writer: SLI writer object
337 *
338 * Create sample stores and add them to applicable #IpatchSampleData objects and return object list.
339 * This function can be called multiple times, additional calls will return the same list.
340 *
341 * Returns: (transfer full): List of sample stores which the caller owns a reference to or %NULL
342 */
343 IpatchList *
ipatch_sli_writer_create_stores(IpatchSLIWriter * writer)344 ipatch_sli_writer_create_stores(IpatchSLIWriter *writer)
345 {
346 SampleHashValue *hash_value;
347 IpatchSample *newstore;
348 IpatchSLISample *sample;
349 IpatchIter iter;
350 IpatchList *list;
351 int rate, format;
352 guint size;
353
354 g_return_val_if_fail(writer->sli != NULL, NULL);
355
356 /* Return existing store list (if this function has been called before) */
357 if(writer->store_list)
358 {
359 return (g_object_ref(writer->store_list)); /* ++ ref for caller */
360 }
361
362 if(!ipatch_container_init_iter(IPATCH_CONTAINER(writer->sli), &iter,
363 IPATCH_TYPE_SLI_SAMPLE))
364 {
365 return (NULL);
366 }
367
368 list = ipatch_list_new(); /* ++ ref list */
369
370 /* traverse samples */
371 for(sample = ipatch_sli_sample_first(&iter); sample;
372 sample = ipatch_sli_sample_next(&iter))
373 {
374 hash_value = g_hash_table_lookup(writer->sample_hash, sample);
375
376 /* hash_value should never be NULL, but.. */
377 if(!hash_value)
378 {
379 continue;
380 }
381
382 g_object_get(sample,
383 "sample-format", &format,
384 "sample-size", &size,
385 "sample-rate", &rate,
386 NULL);
387
388 /* ++ ref newstore */
389 newstore = ipatch_sample_store_file_new(writer->handle->file,
390 hash_value->position);
391 format &= IPATCH_SAMPLE_CHANNEL_MASK;
392 format |= FORMAT_16BIT;
393 g_object_set(newstore,
394 "sample-format", format,
395 "sample-size", size,
396 "sample-rate", rate,
397 NULL);
398
399 ipatch_sample_data_add(sample->sample_data, (IpatchSampleStore *)newstore);
400 /* !! list takes over reference */
401 list->items = g_list_prepend(list->items, newstore);
402 }
403
404 writer->store_list = g_object_ref(list); /* ++ ref for writer object */
405 return (list); /* !! caller takes over reference */
406 }
407
408 static gint
ipatch_sli_writer_inst_find_func(gconstpointer a,gconstpointer b)409 ipatch_sli_writer_inst_find_func(gconstpointer a, gconstpointer b)
410 {
411 const GPtrArray *arr = a;
412 guint i;
413
414 for(i = 0; i < arr->len; i++)
415 if(g_ptr_array_index(arr, i) == b)
416 {
417 return 0;
418 }
419
420 return -1;
421 }
422
423 static GSList *
ipatch_sli_writer_find_groups(IpatchSLI * sli)424 ipatch_sli_writer_find_groups(IpatchSLI *sli)
425 {
426 IpatchIter inst_iter, iz_iter;
427 IpatchItem *item;
428 GSList *ig, *igs = NULL;
429 GPtrArray *insts;
430 IpatchList *inst_zones, *zlist;
431 IpatchSLIZone *iz, *z;
432
433 if(!ipatch_container_init_iter((IpatchContainer *)sli, &inst_iter,
434 IPATCH_TYPE_SLI_INST))
435 {
436 return (NULL);
437 }
438
439 for(item = ipatch_iter_first(&inst_iter);
440 item;
441 item = ipatch_iter_next(&inst_iter))
442 {
443 ig = g_slist_find_custom(igs, item, ipatch_sli_writer_inst_find_func);
444
445 if(ig) /* found in a group */
446 {
447 insts = ig->data;
448 }
449 else /* if not already in a group then we create a new one */
450 {
451 insts = g_ptr_array_new();
452 g_ptr_array_add(insts, item);
453 igs = g_slist_prepend(igs, insts);
454 }
455
456 inst_zones = ipatch_sli_inst_get_zones(item); /* ++ ref inst_zones */
457 ipatch_list_init_iter(inst_zones, &iz_iter);
458
459 for(iz = ipatch_sli_zone_first(&iz_iter);
460 iz;
461 iz = ipatch_sli_zone_next(&iz_iter))
462 {
463 IpatchIter iter;
464 IpatchItem *inst;
465 /* ++ ref zlist (list of zones also referencing sample of this zone) */
466 zlist = ipatch_sli_get_zone_references(ipatch_sli_zone_peek_sample(iz));
467 ipatch_list_init_iter(zlist, &iter);
468
469 for(z = ipatch_sli_zone_first(&iter); z; z = ipatch_sli_zone_next(&iter))
470 {
471 inst = ipatch_item_peek_parent(IPATCH_ITEM(z));
472 ig = g_slist_find_custom(igs, inst, ipatch_sli_writer_inst_find_func);
473
474 if(!ig) /* not yet in any group: add to current */
475 {
476 g_ptr_array_add(insts, inst);
477 }
478 else if(ig->data != insts) /* already in another group: merge groups */
479 {
480 GPtrArray *igrp = ig->data;
481 int i;
482
483 for(i = igrp->len - 1; i >= 0; i--)
484 {
485 g_ptr_array_add(insts, g_ptr_array_index(igrp, i));
486 g_ptr_array_remove_index_fast(igrp, i);
487 }
488
489 g_ptr_array_free(igrp, TRUE);
490 igs = g_slist_delete_link(igs, ig);
491 }
492 }
493
494 g_object_unref(zlist); /* -- unref zlist */
495 }
496
497 g_object_unref(inst_zones); /* -- unref inst_zones */
498 }
499
500 igs = g_slist_reverse(igs); /* fixup for prepending to preserve order */
501 return (igs);
502 }
503
504 static gboolean
ipatch_sli_writer_write_group(IpatchSLIWriter * writer,GPtrArray * ig,GError ** err)505 ipatch_sli_writer_write_group(IpatchSLIWriter *writer, GPtrArray *ig, GError **err)
506 {
507 guint i, cnt, allzones = 0, maxzones = 0, smpdata_size = 0;
508 IpatchList *inst_zones;
509 IpatchIter iz_iter;
510 IpatchSLIInst *inst;
511 IpatchSLIZone *zone;
512 IpatchSLISample *sample = NULL;
513 SampleHashValue *sample_info;
514 GPtrArray *samples;
515 IpatchSLISiIg siig;
516 IpatchSLIInstHeader ihdr;
517 IpatchSLISiDp sidp = { IPATCH_SLI_FOURCC_SIDP, IPATCH_SLI_SIDP_SIZE,
518 IPATCH_SLI_SPECHDR_VAL, 0
519 };
520 guint pos;
521
522 pos = ipatch_file_get_position(writer->handle);
523 samples = g_ptr_array_new();
524 /* prepare buffer for headers */
525 ipatch_file_buf_zero(writer->handle, IPATCH_SLI_HEAD_SIZE);
526
527 /* loop over instruments in group */
528 for(i = 0; i < ig->len; i++)
529 {
530 inst = g_ptr_array_index(ig, i);
531 inst_zones = ipatch_sli_inst_get_zones(inst); /* ++ ref inst_zones */
532 ipatch_list_init_iter(inst_zones, &iz_iter);
533 cnt = ipatch_iter_count(&iz_iter);
534 /* write instrument header */
535 strncpy(ihdr.name, inst->name, IPATCH_SLI_NAME_SIZE);
536 ihdr.sound_id = (inst->sound_id ? inst->sound_id : g_str_hash(inst->name));
537 ihdr.unused1 = 0;
538 ihdr.category = inst->category;
539 ihdr.unused2 = 0;
540 ihdr.zone_idx = allzones;
541 ihdr.zones_num = cnt;
542
543 if(cnt > maxzones)
544 {
545 maxzones = cnt;
546 }
547
548 allzones += cnt;
549 ipatch_file_buf_seek(writer->handle,
550 IPATCH_SLI_SIIG_SIZE + i * IPATCH_SLI_INST_SIZE,
551 G_SEEK_SET);
552 ipatch_sli_writer_write_inst_header(writer->handle, &ihdr);
553 /* loop over zones of this instrument */
554 ipatch_file_buf_seek(writer->handle,
555 IPATCH_SLI_SIIG_SIZE +
556 ig->len * IPATCH_SLI_INST_SIZE +
557 ihdr.zone_idx * IPATCH_SLI_ZONE_SIZE,
558 G_SEEK_SET);
559
560 for(zone = ipatch_sli_zone_first(&iz_iter);
561 zone;
562 zone = ipatch_sli_zone_next(&iz_iter))
563 {
564 int format;
565 /* write zone header */
566 sample = ipatch_sli_zone_peek_sample(zone);
567 sample_info = g_hash_table_lookup(writer->sample_hash, sample);
568 ipatch_sli_writer_write_zone_header(writer->handle, zone,
569 (sample_info ? sample_info->index : samples->len));
570
571 /* check referenced sample: if already counted then continue */
572 if(sample_info)
573 {
574 continue;
575 }
576
577 /* else check sample format and add info to sample hash */
578 format = ipatch_sample_get_format((IpatchSample *)sample);
579
580 if(IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format) > 2)
581 {
582 char *n = ipatch_sli_sample_get_name(sample);
583 g_set_error(err, IPATCH_ERROR, IPATCH_ERROR_UNSUPPORTED,
584 _("Unsupported channel count in sample '%s'"), n);
585
586 if(n)
587 {
588 free(n);
589 }
590
591 g_object_unref(inst_zones); /* -- unref inst_zones */
592 return (FALSE);
593 }
594
595 format &= IPATCH_SAMPLE_CHANNEL_MASK;
596 format |= FORMAT_16BIT;
597 sample_info = ipatch_sli_writer_sample_hash_value_new();
598 sample_info->index = samples->len;
599 sample_info->channels = IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format);
600 g_hash_table_insert(writer->sample_hash, sample, sample_info);
601 g_ptr_array_add(samples, sample);
602 sample_info->offset = smpdata_size;
603 sample_info->position = pos + sample_info->offset;
604 sample_info->length =
605 ipatch_sample_get_size(IPATCH_SAMPLE(sample), NULL) *
606 ipatch_sample_format_size(format);
607 /* plus 64 zero bytes written for each channel*/
608 smpdata_size += sample_info->length + sample_info->channels * 64;
609 }
610
611 g_object_unref(inst_zones); /* -- unref inst_zones */
612 }
613
614 /* now rewind to the beginning and write group header */
615 ipatch_file_buf_seek(writer->handle, 0, G_SEEK_SET);
616 siig.ckid = IPATCH_SLI_FOURCC_SIIG;
617 siig.cklen = IPATCH_SLI_SIIG_SIZE; /* group header */
618 siig.cklen += ig->len * IPATCH_SLI_INST_SIZE; /* instrument headers */
619 siig.cklen += allzones * IPATCH_SLI_ZONE_SIZE; /* zone headers */
620 siig.cklen += samples->len * IPATCH_SLI_SMPL_SIZE; /* sample headers */
621
622 if(siig.cklen >= IPATCH_SLI_HEAD_SIZE)
623 {
624 g_set_error(err, IPATCH_ERROR, IPATCH_RIFF_ERROR_SIZE_EXCEEDED,
625 _("Too many instruments, zones or samples. Header size exceeded."));
626 return (FALSE);
627 }
628
629 siig.cklen += smpdata_size; /* sample data */
630 siig.spechdr = IPATCH_SLI_SPECHDR_VAL;
631 siig.unused1 = 0;
632 siig.inst_offs = IPATCH_SLI_SIIG_SIZE;
633 siig.instnum = ig->len;
634 siig.zones_offs = siig.inst_offs + siig.instnum * IPATCH_SLI_INST_SIZE;
635 siig.allzones_num = allzones;
636 siig.smphdr_offs = siig.zones_offs + siig.allzones_num * IPATCH_SLI_ZONE_SIZE;
637 siig.maxzones_num = maxzones;
638 siig.smpdata_offs = siig.smphdr_offs + samples->len * IPATCH_SLI_SMPL_SIZE;
639 siig.unused2 = 0;
640 ipatch_sli_writer_write_siig(writer->handle, &siig);
641
642 /* write sample headers */
643 ipatch_file_buf_seek(writer->handle, siig.smphdr_offs, G_SEEK_SET);
644
645 for(i = 0; i < samples->len; i++)
646 {
647 sample_info = g_hash_table_lookup(writer->sample_hash, sample);
648 sample_info->position += siig.smphdr_offs;
649 ipatch_sli_writer_write_sample_header(writer->handle, sample_info,
650 g_ptr_array_index(samples, i));
651 }
652
653 /* finished assembling headers, commit to the file now */
654 ipatch_file_buf_set_size(writer->handle,
655 ipatch_file_get_position(writer->handle) - pos);
656
657 if(!ipatch_file_buf_commit(writer->handle, err))
658 {
659 return FALSE;
660 }
661
662 /* write sample data */
663 /* write sample headers */
664 for(i = 0; i < samples->len; i++)
665 if(!ipatch_sli_writer_write_sample_data(writer->handle,
666 g_ptr_array_index(samples, i),
667 err))
668 {
669 return FALSE;
670 }
671
672 /* write SiDp headers (one for each instrument) */
673 for(i = 0; i < ig->len; i++)
674 {
675 ipatch_sli_writer_write_sidp(writer->handle, &sidp);
676 }
677
678 if(!ipatch_file_buf_commit(writer->handle, err))
679 {
680 return FALSE;
681 }
682
683 return TRUE;
684 }
685
686 static gboolean
ipatch_sli_writer_write_sifi(IpatchFileHandle * handle,guint ignum,GError ** err)687 ipatch_sli_writer_write_sifi(IpatchFileHandle *handle, guint ignum, GError **err)
688 {
689 guint32 header[2] = {IPATCH_SLI_FOURCC_SIFI, 0};
690
691 /* Header is written without possible swapping for endianness
692 * Fixup cklen later after writing whole file */
693 ipatch_file_buf_write(handle, header, 8);
694 ipatch_file_buf_write_u16(handle, IPATCH_SLI_SPECHDR_VAL);
695 ipatch_file_buf_write_u16(handle, 0);
696 ipatch_file_buf_write_u16(handle, ignum);
697 ipatch_file_buf_write_u16(handle,
698 IPATCH_RIFF_HEADER_SIZE + IPATCH_SLI_SIFI_SIZE);
699 return ipatch_file_buf_commit(handle, err);
700 }
701
702 static void
ipatch_sli_writer_write_siig(IpatchFileHandle * handle,IpatchSLISiIg * siig)703 ipatch_sli_writer_write_siig(IpatchFileHandle *handle, IpatchSLISiIg *siig)
704 {
705 /* id is written without possible swapping for endianness */
706 ipatch_file_buf_write(handle, &(siig->ckid), 4);
707 ipatch_file_buf_write_u32(handle, siig->cklen);
708 ipatch_file_buf_write_u16(handle, siig->spechdr);
709 ipatch_file_buf_write_u16(handle, siig->unused1);
710 ipatch_file_buf_write_u16(handle, siig->inst_offs);
711 ipatch_file_buf_write_u16(handle, siig->instnum);
712 ipatch_file_buf_write_u16(handle, siig->zones_offs);
713 ipatch_file_buf_write_u16(handle, siig->allzones_num);
714 ipatch_file_buf_write_u16(handle, siig->smphdr_offs);
715 ipatch_file_buf_write_u16(handle, siig->maxzones_num);
716 ipatch_file_buf_write_u16(handle, siig->smpdata_offs);
717 ipatch_file_buf_write_u16(handle, siig->unused2);
718 }
719
720 static void
ipatch_sli_writer_write_inst_header(IpatchFileHandle * handle,IpatchSLIInstHeader * ihdr)721 ipatch_sli_writer_write_inst_header(IpatchFileHandle *handle, IpatchSLIInstHeader *ihdr)
722 {
723 ipatch_file_buf_write(handle, ihdr->name, IPATCH_SLI_NAME_SIZE);
724 ipatch_file_buf_write_u32(handle, ihdr->sound_id);
725 ipatch_file_buf_write_u32(handle, ihdr->unused1);
726 ipatch_file_buf_write_u16(handle, ihdr->category);
727 ipatch_file_buf_write_u16(handle, ihdr->unused2);
728 ipatch_file_buf_write_u16(handle, ihdr->zone_idx);
729 ipatch_file_buf_write_u16(handle, ihdr->zones_num);
730 }
731
732 static void
ipatch_sli_writer_write_zone_header(IpatchFileHandle * handle,IpatchSLIZone * zone,guint sample_idx)733 ipatch_sli_writer_write_zone_header(IpatchFileHandle *handle, IpatchSLIZone *zone, guint sample_idx)
734 {
735 IpatchSF2GenItem *item;
736 IpatchSF2GenAmount amount;
737 guint32 offs;
738
739 item = IPATCH_SF2_GEN_ITEM(zone);
740
741 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_RANGE, &amount);
742 ipatch_file_buf_write_u8(handle, amount.range.low); /* keyrange_low */
743 ipatch_file_buf_write_u8(handle, amount.range.high); /* keyrange_high */
744
745 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VELOCITY_RANGE, &amount);
746 ipatch_file_buf_write_u8(handle, amount.range.low); /* velrange_low */
747 ipatch_file_buf_write_u8(handle, amount.range.high); /* velrange_high */
748
749 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SAMPLE_COARSE_START, &amount);
750 offs = amount.uword << 16;
751 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SAMPLE_START, &amount);
752 offs += amount.uword << 1;
753 ipatch_file_buf_write_u32(handle, offs); /* start_offs1 */
754 ipatch_file_buf_write_u32(handle, offs); /* start_offs2 */
755
756 ipatch_file_buf_write_u32(handle, 0); /* unknown1 */
757 ipatch_file_buf_write_u32(handle, 0); /* unknown2 */
758
759 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_COARSE_TUNE, &amount);
760 ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* coarse_tune1 */
761
762 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount);
763 ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* fine_tune1 */
764
765 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SAMPLE_MODES, &amount);
766
767 if(amount.uword & IPATCH_SF2_GEN_SAMPLE_MODE_LOOP)
768 {
769 zone->flags |= IPATCH_SF2_GEN_SAMPLE_MODE_LOOP;
770 }
771
772 ipatch_file_buf_write_u8(handle, zone->flags); /* sample_modes */
773
774 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_ROOT_NOTE_OVERRIDE, &amount))
775 {
776 amount.sword = 0;
777 }
778
779 ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* root_note */
780
781 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_SCALE_TUNE, &amount))
782 {
783 amount.uword = 0;
784 }
785
786 ipatch_file_buf_write_u16(handle, amount.uword); /* scale_tuning */
787
788 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_COARSE_TUNE, &amount);
789 ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* coarse_tune2 */
790
791 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FINE_TUNE_OVERRIDE, &amount);
792 ipatch_file_buf_write_s8(handle, (guint8)amount.sword); /* fine_tune2 */
793
794 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_TO_PITCH, &amount);
795 ipatch_file_buf_write_s16(handle, amount.sword); /* modLfoToPitch */
796
797 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VIB_LFO_TO_PITCH, &amount);
798 ipatch_file_buf_write_s16(handle, amount.sword); /* vibLfoToPitch */
799
800 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_TO_PITCH, &amount);
801 ipatch_file_buf_write_s16(handle, amount.sword); /* modEnvToPitch */
802
803 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FILTER_CUTOFF, &amount))
804 {
805 amount.uword = 0;
806 }
807
808 ipatch_file_buf_write_u16(handle, amount.uword); /* initialFilterFc */
809
810 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_FILTER_Q, &amount);
811 ipatch_file_buf_write_u16(handle, amount.uword); /* initialFilterQ */
812
813 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_TO_FILTER_CUTOFF, &amount);
814 ipatch_file_buf_write_s16(handle, amount.sword); /* modLfoToFilterFc */
815
816 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_TO_FILTER_CUTOFF, &amount);
817 ipatch_file_buf_write_s16(handle, amount.sword); /* modEnvToFilterFc */
818
819 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_TO_VOLUME, &amount);
820 ipatch_file_buf_write_s16(handle, amount.sword); /* modLfoToVolume */
821
822 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_FREQ, &amount);
823 ipatch_file_buf_write_s16(handle, amount.sword); /* freqModLfo */
824
825 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VIB_LFO_FREQ, &amount);
826 ipatch_file_buf_write_s16(handle, amount.sword); /* freqVibLfo */
827
828 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_SUSTAIN, &amount);
829 ipatch_file_buf_write_u16(handle, amount.uword); /* sustainModEnv */
830
831 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_HOLD, &amount);
832 ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToModEnvHold */
833
834 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_MOD_ENV_DECAY, &amount);
835 ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToModEnvDecay */
836
837 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_SUSTAIN, &amount);
838 ipatch_file_buf_write_u16(handle, amount.uword); /* sustainVolEnv */
839
840 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_HOLD, &amount);
841 ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToVolEnvHold */
842
843 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_NOTE_TO_VOL_ENV_DECAY, &amount);
844 ipatch_file_buf_write_s16(handle, amount.sword); /* keynumToVolEnvDecay */
845
846 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_PAN, &amount);
847 ipatch_file_buf_write_s8(handle, amount.sword / 5); /* pan */
848
849 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_LFO_DELAY, &amount))
850 {
851 amount.sword = 0;
852 }
853
854 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* delayModLfo */
855
856 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VIB_LFO_DELAY, &amount))
857 {
858 amount.sword = 0;
859 }
860
861 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* delayVibLfo */
862
863 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_ATTACK, &amount))
864 {
865 amount.sword = 0;
866 }
867
868 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* attackModEnv */
869
870 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_HOLD, &amount))
871 {
872 amount.sword = 0;
873 }
874
875 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* holdModEnv */
876
877 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_DECAY, &amount))
878 {
879 amount.sword = 0;
880 }
881
882 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* decayModEnv */
883
884 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_MOD_ENV_RELEASE, &amount))
885 {
886 amount.sword = 0;
887 }
888
889 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* releaseModEnv */
890
891 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_ATTACK, &amount))
892 {
893 amount.sword = 0;
894 }
895
896 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* attackVolEnv */
897
898 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_HOLD, &amount))
899 {
900 amount.sword = 0;
901 }
902
903 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* holdVolEnv */
904
905 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_DECAY, &amount))
906 {
907 amount.sword = 0;
908 }
909
910 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* decayVolEnv */
911
912 if(!ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_VOL_ENV_RELEASE, &amount))
913 {
914 amount.sword = 0;
915 }
916
917 ipatch_file_buf_write_s8(handle, amount.sword / 100); /* releaseVolEnv */
918
919 ipatch_sf2_gen_item_get_amount(item, IPATCH_SF2_GEN_ATTENUATION, &amount);
920 ipatch_file_buf_write_u8(handle, amount.uword / 10); /* initialAttenuation */
921
922 ipatch_file_buf_write_u16(handle, sample_idx); /* sample_idx */
923 ipatch_file_buf_write_u16(handle, 0); /* unused */
924 }
925
926 static void
ipatch_sli_writer_write_sample_header(IpatchFileHandle * handle,SampleHashValue * sample_info,IpatchSLISample * sample)927 ipatch_sli_writer_write_sample_header(IpatchFileHandle *handle,
928 SampleHashValue *sample_info,
929 IpatchSLISample *sample)
930 {
931 char sname[IPATCH_SLI_NAME_SIZE];
932
933 strncpy(sname, sample->name, IPATCH_SLI_NAME_SIZE);
934 ipatch_file_buf_write(handle, sname, IPATCH_SLI_NAME_SIZE);
935 ipatch_file_buf_write_u32(handle, sample_info->offset);
936 ipatch_file_buf_write_u32(handle, sample_info->offset + sample_info->length);
937 ipatch_file_buf_write_u32(handle, sample->loop_start * 2);
938 ipatch_file_buf_write_u32(handle, sample->loop_end * 2);
939 ipatch_file_buf_write_s8(handle, sample->fine_tune);
940 ipatch_file_buf_write_u8(handle, sample->root_note);
941 ipatch_file_buf_write_u8(handle, sample_info->channels);
942 ipatch_file_buf_write_u8(handle, 16); /* bits per sample is verified above */
943 ipatch_file_buf_write_u32(handle, sample->rate);
944 }
945
946 static void
ipatch_sli_writer_write_sidp(IpatchFileHandle * handle,IpatchSLISiDp * sidp)947 ipatch_sli_writer_write_sidp(IpatchFileHandle *handle, IpatchSLISiDp *sidp)
948 {
949 /* id is written without possible swapping for endianness */
950 ipatch_file_buf_write(handle, &(sidp->ckid), 4);
951 ipatch_file_buf_write_u32(handle, sidp->cklen);
952 ipatch_file_buf_write_u16(handle, sidp->spechdr);
953 ipatch_file_buf_write_u16(handle, sidp->unused);
954 }
955
956 static gboolean
ipatch_sli_writer_write_sample_data(IpatchFileHandle * handle,IpatchSLISample * sample,GError ** err)957 ipatch_sli_writer_write_sample_data(IpatchFileHandle *handle, IpatchSLISample *sample, GError **err)
958 {
959 IpatchSampleHandle shandle;
960 gpointer buf;
961 guint samsize, fmt_size, read_size, offs;
962 int format;
963
964 samsize = ipatch_sample_get_size(IPATCH_SAMPLE(sample->sample_data), NULL);
965 format = ipatch_sample_get_format(IPATCH_SAMPLE(sample->sample_data));
966 format &= IPATCH_SAMPLE_CHANNEL_MASK;
967 format |= FORMAT_16BIT;
968 fmt_size = ipatch_sample_format_size(format);
969
970 /* ++ open sample handle */
971 if(!ipatch_sample_data_open_native_sample(sample->sample_data,
972 &shandle, 'r', format,
973 IPATCH_SAMPLE_UNITY_CHANNEL_MAP,
974 err))
975 {
976 return FALSE;
977 }
978
979 read_size = ipatch_sample_handle_get_max_frames(&shandle);
980
981 for(offs = 0; offs < samsize; offs += read_size)
982 {
983 if(samsize - offs < read_size) /* check for last partial fragment */
984 {
985 read_size = samsize - offs;
986 }
987
988 /* read and transform (if necessary) audio data from sample store */
989 if(!(buf = ipatch_sample_handle_read(&shandle, offs, read_size,
990 NULL, err)) ||
991 !ipatch_file_write(handle, buf, read_size * fmt_size, err))
992 {
993 ipatch_sample_handle_close(&shandle); /* -- close sample handle */
994 return FALSE;
995 }
996 }
997
998 ipatch_sample_handle_close(&shandle); /* -- close sample handle */
999
1000 /* write 64 "zero" samples (for each channel) following sample data */
1001 ipatch_file_buf_zero(handle,
1002 64 * IPATCH_SAMPLE_FORMAT_GET_CHANNEL_COUNT(format));
1003 return ipatch_file_buf_commit(handle, err);
1004 }
1005