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