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