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: IpatchSampleStoreSplit24
22  * @short_description: Sample storage object for 24 bit audio in 16 and 8 bit segments
23  * @see_also:
24  * @stability: Stable
25  *
26  * SoundFont 2.04 adds support for 24 bit audio.  This is done in a semi
27  * backwards compatible fashion where the most significant 16 bits is stored
28  * separately from the remaining 8 bit segments.  This storage object handles
29  * this transparently.
30  */
31 #include <errno.h>
32 #include <glib.h>
33 #include <glib-object.h>
34 #include "IpatchSampleStoreSplit24.h"
35 #include "ipatch_priv.h"
36 #include "i18n.h"
37 
38 enum
39 {
40     PROP_0,
41     PROP_LOCATION_LSBYTES
42 };
43 
44 /* Size of allocated copy buffer for each open sample handle */
45 #define READBUF_SIZE  16384
46 
47 static void ipatch_sample_store_split24_sample_iface_init(IpatchSampleIface *iface);
48 static void ipatch_sample_store_split24_class_init(IpatchSampleStoreSplit24Class *klass);
49 static void ipatch_sample_store_split24_set_property
50 (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
51 static void ipatch_sample_store_split24_get_property
52 (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
53 static gboolean ipatch_sample_store_split24_sample_iface_open
54 (IpatchSampleHandle *handle, GError **err);
55 static void ipatch_sample_store_split24_sample_iface_close(IpatchSampleHandle *handle);
56 static gboolean ipatch_sample_store_split24_sample_iface_read
57 (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err);
58 
59 
G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreSplit24,ipatch_sample_store_split24,IPATCH_TYPE_SAMPLE_STORE_FILE,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_sample_store_split24_sample_iface_init))60 G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreSplit24, ipatch_sample_store_split24,
61                         IPATCH_TYPE_SAMPLE_STORE_FILE,
62                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
63                                 ipatch_sample_store_split24_sample_iface_init))
64 
65 static void
66 ipatch_sample_store_split24_sample_iface_init(IpatchSampleIface *iface)
67 {
68     iface->open = ipatch_sample_store_split24_sample_iface_open;
69     iface->close = ipatch_sample_store_split24_sample_iface_close;
70     iface->read = ipatch_sample_store_split24_sample_iface_read;
71     iface->write = NULL;
72 }
73 
74 static void
ipatch_sample_store_split24_class_init(IpatchSampleStoreSplit24Class * klass)75 ipatch_sample_store_split24_class_init(IpatchSampleStoreSplit24Class *klass)
76 {
77     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
78     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
79 
80     obj_class->get_property = ipatch_sample_store_split24_get_property;
81     item_class->item_set_property = ipatch_sample_store_split24_set_property;
82 
83     g_object_class_install_property(obj_class, PROP_LOCATION_LSBYTES,
84                                     g_param_spec_uint("location-lsbytes", "Location LS-Bytes",
85                                             "LS byte sample data file position",
86                                             0, G_MAXUINT, 0, G_PARAM_READWRITE));
87 }
88 
89 static void
ipatch_sample_store_split24_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)90 ipatch_sample_store_split24_set_property(GObject *object, guint property_id,
91         const GValue *value, GParamSpec *pspec)
92 {
93     IpatchSampleStoreSplit24 *split24 = IPATCH_SAMPLE_STORE_SPLIT24(object);
94 
95     switch(property_id)
96     {
97     case PROP_LOCATION_LSBYTES:
98         g_return_if_fail(split24->loc_lsbytes == 0);
99 
100         /* Only set once before use, no lock required */
101         split24->loc_lsbytes = g_value_get_uint(value);
102         break;
103 
104     default:
105         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
106     }
107 }
108 
109 static void
ipatch_sample_store_split24_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)110 ipatch_sample_store_split24_get_property(GObject *object, guint property_id,
111         GValue *value, GParamSpec *pspec)
112 {
113     IpatchSampleStoreSplit24 *split24 = IPATCH_SAMPLE_STORE_SPLIT24(object);
114 
115     switch(property_id)
116     {
117     case PROP_LOCATION_LSBYTES:
118         /* Only set once before use, no lock required */
119         g_value_set_uint(value, split24->loc_lsbytes);
120         break;
121 
122     default:
123         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
124     }
125 }
126 
127 static void
ipatch_sample_store_split24_init(IpatchSampleStoreSplit24 * store)128 ipatch_sample_store_split24_init(IpatchSampleStoreSplit24 *store)
129 {
130 }
131 
132 static gboolean
ipatch_sample_store_split24_sample_iface_open(IpatchSampleHandle * handle,GError ** err)133 ipatch_sample_store_split24_sample_iface_open(IpatchSampleHandle *handle, GError **err)
134 {
135     IpatchSampleStoreSplit24 *split24_store = (IpatchSampleStoreSplit24 *)(handle->sample);
136     IpatchSampleStoreFile *file_store = (IpatchSampleStoreFile *)split24_store;
137     int fmt;
138 
139     g_return_val_if_fail(file_store->file != NULL, FALSE);
140     g_return_val_if_fail(file_store->location != 0, FALSE);
141     g_return_val_if_fail(split24_store->loc_lsbytes != 0, FALSE);
142 
143     fmt = ipatch_sample_store_get_format(split24_store);
144     fmt &= ~IPATCH_SAMPLE_ENDIAN_MASK;  /* we can do either endian */
145     g_return_val_if_fail(fmt == IPATCH_SAMPLE_24BIT, FALSE);
146 
147     /* No lock needed - file object set only once */
148     handle->data1 = ipatch_file_open(file_store->file, NULL,
149                                      handle->read_mode ? "r" : "w", err);
150 
151     if(!handle->data1)
152     {
153         return (FALSE);
154     }
155 
156     handle->data2 = g_malloc(READBUF_SIZE);
157 
158     return (TRUE);
159 }
160 
161 static void
ipatch_sample_store_split24_sample_iface_close(IpatchSampleHandle * handle)162 ipatch_sample_store_split24_sample_iface_close(IpatchSampleHandle *handle)
163 {
164     if(handle->data1)
165     {
166         ipatch_file_close(handle->data1);
167         g_free(handle->data2);
168         handle->data1 = NULL;
169         handle->data2 = NULL;
170     }
171 }
172 
173 static gboolean
ipatch_sample_store_split24_sample_iface_read(IpatchSampleHandle * handle,guint offset,guint frames,gpointer buf,GError ** err)174 ipatch_sample_store_split24_sample_iface_read(IpatchSampleHandle *handle,
175         guint offset, guint frames,
176         gpointer buf, GError **err)
177 {
178     IpatchSampleStoreSplit24 *split24_store = (IpatchSampleStoreSplit24 *)(handle->sample);
179     IpatchSampleStoreFile *file_store = (IpatchSampleStoreFile *)split24_store;
180     guint samplepos, thissize, curofs;
181     gboolean lilendian;
182     IpatchFileHandle *fhandle = (IpatchFileHandle *)(handle->data1);
183     guint8 *readbuf = (guint8 *)(handle->data2);
184     guint8 *i8p;
185     guint i;
186 
187     lilendian = (ipatch_sample_store_get_format(split24_store)
188                  & IPATCH_SAMPLE_ENDIAN_MASK) == IPATCH_SAMPLE_LENDIAN;
189 
190     samplepos = 0;
191     curofs = offset;
192     thissize = READBUF_SIZE / 2;	// 16 bit samples
193 
194     /* copy 16 bit sample data */
195     while(samplepos < frames)
196     {
197         if(frames - samplepos < thissize)
198         {
199             thissize = frames - samplepos;
200         }
201 
202         if(!ipatch_file_seek(fhandle, file_store->location + curofs * 2, G_SEEK_SET, err))
203         {
204             return (FALSE);
205         }
206 
207         if(!ipatch_file_read(fhandle, readbuf, thissize * 2, err))
208         {
209             return (FALSE);
210         }
211 
212         i8p = (guint8 *)buf + samplepos * 4;
213 
214         if(lilendian)
215         {
216             for(i = 0; i < thissize; i++)
217             {
218                 i8p[i * 4 + 1] = readbuf[i * 2];
219                 i8p[i * 4 + 2] = readbuf[i * 2 + 1];
220                 i8p[i * 4 + 3] = 0;
221             }
222         }
223         else
224         {
225             for(i = 0; i < thissize; i++)
226             {
227                 i8p[i * 4 + 2] = readbuf[i * 2];
228                 i8p[i * 4 + 1] = readbuf[i * 2 + 1];
229                 i8p[i * 4 + 0] = 0;
230             }
231         }
232 
233         samplepos += thissize;
234         curofs += thissize;
235     }
236 
237 
238     samplepos = 0;
239     curofs = offset;
240     thissize = READBUF_SIZE;
241 
242     /* copy upper byte of 24 bit samples */
243     while(samplepos < frames)
244     {
245         if(frames - samplepos < thissize)
246         {
247             thissize = frames - samplepos;
248         }
249 
250         if(!ipatch_file_seek(fhandle, split24_store->loc_lsbytes + curofs, G_SEEK_SET, err))
251         {
252             return (FALSE);
253         }
254 
255         if(!ipatch_file_read(fhandle, readbuf, thissize, err))
256         {
257             return (FALSE);
258         }
259 
260         i8p = (guint8 *)buf + samplepos * 4;
261 
262         if(lilendian)
263         {
264             for(i = 0; i < thissize; i++)
265             {
266                 i8p[i * 4] = readbuf[i];
267             }
268         }
269         else
270         {
271             for(i = 0; i < thissize; i++)
272             {
273                 i8p[i * 4 + 3] = readbuf[i];
274             }
275         }
276 
277         samplepos += thissize;
278         curofs += thissize;
279     }
280 
281     return (TRUE);
282 }
283 
284 /**
285  * ipatch_sample_store_split24_new:
286  * @file: File object
287  * @loc_16bit: Location of 16 bit audio data
288  * @loc_lsbytes: Location of 24 bit LS bytes
289  *
290  * Creates a new split 24 bit sample store (lower byte of 24 bit
291  * samples is stored in a separate block).  New SoundFont 2.04 uses this method.
292  *
293  * Returns: (type IpatchSampleStoreSplit24): New split 24 sample store
294  */
295 IpatchSample *
ipatch_sample_store_split24_new(IpatchFile * file,guint loc_16bit,guint loc_lsbytes)296 ipatch_sample_store_split24_new(IpatchFile *file, guint loc_16bit,
297                                 guint loc_lsbytes)
298 {
299     return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_SPLIT24,
300                                        "file", file,
301                                        "location", loc_16bit,
302                                        "location-lsbytes", loc_lsbytes,
303                                        NULL)));
304 }
305