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: IpatchSndFile
22 * @short_description: libsndfile file object
23 * @see_also:
24 * @stability: Stable
25 *
26 * Object type for libsndfile audio file identification.
27 */
28 #include <string.h>
29 #include <glib.h>
30 #include <glib-object.h>
31
32 #include <sndfile.h>
33
34 #include "IpatchSndFile.h"
35 #include "sample.h"
36 #include "ipatch_priv.h"
37 #include "i18n.h"
38 #include "misc.h"
39
40 static gboolean ipatch_snd_file_identify(IpatchFile *file, IpatchFileHandle *handle,
41 GError **err);
42
G_DEFINE_TYPE(IpatchSndFile,ipatch_snd_file,IPATCH_TYPE_FILE)43 G_DEFINE_TYPE(IpatchSndFile, ipatch_snd_file, IPATCH_TYPE_FILE)
44
45
46 /* Get type of dynamic libsndfile file format enum (register it as needed) */
47 GType
48 ipatch_snd_file_format_get_type(void)
49 {
50 static GType type = 0;
51
52 if(!type)
53 {
54 GEnumValue *values;
55 SF_FORMAT_INFO finfo;
56 int major_count;
57 int value_index = 0;
58 int i;
59
60 sf_command(NULL, SFC_GET_FORMAT_MAJOR_COUNT, &major_count, sizeof(int));
61
62 values = g_new(GEnumValue, major_count + 1);
63
64 for(i = 0; i < major_count; i++)
65 {
66 finfo.format = i;
67 sf_command(NULL, SFC_GET_FORMAT_MAJOR, &finfo, sizeof(finfo));
68
69 /* Skip RAW format since we use IpatchSampleStoreFile instead, for more flexibility */
70 if(finfo.format == SF_FORMAT_RAW)
71 {
72 continue;
73 }
74
75 values[value_index].value = finfo.format;
76 values[value_index].value_name = finfo.extension;
77 values[value_index].value_nick = finfo.extension;
78 value_index++;
79 }
80
81 values[value_index].value = 0;
82 values[value_index].value_name = NULL;
83 values[value_index].value_nick = NULL;
84
85 type = g_enum_register_static("IpatchSndFileFormat", values);
86 }
87
88 return (type);
89 }
90
91 /* Get type of dynamic libsndfile file sub format enum (register it as needed) */
92 GType
ipatch_snd_file_sub_format_get_type(void)93 ipatch_snd_file_sub_format_get_type(void)
94 {
95 static GType type = 0;
96 char *name, *s;
97
98 if(!type)
99 {
100 GEnumValue *values;
101 SF_FORMAT_INFO sinfo;
102 int subtype_count;
103 int value_index = 0;
104 int i;
105
106 sf_command(NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof(int));
107
108 values = g_new(GEnumValue, subtype_count + 1);
109
110 for(i = 0; i < subtype_count; i++)
111 {
112 sinfo.format = i;
113 sf_command(NULL, SFC_GET_FORMAT_SUBTYPE, &sinfo, sizeof(sinfo));
114
115 name = g_ascii_strdown(sinfo.name, -1); /* ++ alloc forever */
116
117 /* Replace spaces and '.' with dashes */
118 for(s = name; *s; s++)
119 if(*s == ' ' || *s == '.')
120 {
121 *s = '-';
122 }
123
124 values[value_index].value = sinfo.format;
125 values[value_index].value_name = name;
126 values[value_index].value_nick = name;
127 value_index++;
128 }
129
130 values[value_index].value = 0;
131 values[value_index].value_name = NULL;
132 values[value_index].value_nick = NULL;
133
134 type = g_enum_register_static("IpatchSndFileSubFormat", values);
135 }
136
137 return (type);
138 }
139
140 static void
ipatch_snd_file_class_init(IpatchSndFileClass * klass)141 ipatch_snd_file_class_init(IpatchSndFileClass *klass)
142 {
143 IpatchFileClass *file_class = IPATCH_FILE_CLASS(klass);
144 file_class->identify = ipatch_snd_file_identify;
145
146 /* Set to last execution (subtract another 100, since we really want to be last) */
147 file_class->identify_order = IPATCH_FILE_IDENTIFY_ORDER_LAST - 100;
148 }
149
150 static void
ipatch_snd_file_init(IpatchSndFile * file)151 ipatch_snd_file_init(IpatchSndFile *file)
152 {
153 }
154
155 /* Identify if this file format is known by libsndfile */
156 static gboolean
ipatch_snd_file_identify(IpatchFile * file,IpatchFileHandle * handle,GError ** err)157 ipatch_snd_file_identify(IpatchFile *file, IpatchFileHandle *handle, GError **err)
158 {
159 SNDFILE *sfhandle;
160 SF_INFO info = { 0 };
161 char *filename;
162
163 filename = ipatch_file_get_name(file); /* ++ alloc file name */
164
165 if(!filename)
166 {
167 return (FALSE);
168 }
169
170 sfhandle = sf_open(filename, SFM_READ, &info);
171
172 if(sfhandle)
173 {
174 sf_close(sfhandle);
175 }
176
177 g_free(filename); /* -- free file name */
178
179 return (sfhandle != NULL);
180 }
181
182 /**
183 * ipatch_snd_file_new:
184 *
185 * Create a new libsndfile file object.
186 *
187 * Returns: New libsndfile file object (derived from IpatchFile) with a
188 * reference count of 1. Caller owns the reference and removing it will
189 * destroy the item.
190 */
191 IpatchSndFile *
ipatch_snd_file_new(void)192 ipatch_snd_file_new(void)
193 {
194 return (IPATCH_SND_FILE(g_object_new(IPATCH_TYPE_SND_FILE, NULL)));
195 }
196
197 /**
198 * ipatch_snd_file_format_get_sub_formats:
199 * @format: (type IpatchSndFileFormat): "IpatchSndFileFormat" GEnum to get sub formats of
200 * @size: (out): Location to store size of returned sub formats array
201 *
202 * Get supported sub formats of a given libsndfile format.
203 *
204 * Returns: (array length=size): Newly allocated list of sub format
205 * enum values or %NULL if @format is invalid
206 */
207 int *
ipatch_snd_file_format_get_sub_formats(int format,guint * size)208 ipatch_snd_file_format_get_sub_formats(int format, guint *size)
209 {
210 SF_FORMAT_INFO info;
211 SF_INFO sfinfo;
212 GArray *array;
213 int subtype_count, s;
214
215 if(size)
216 {
217 *size = 0; /* In case of error */
218 }
219
220 g_return_val_if_fail(size != NULL, NULL);
221
222 format &= SF_FORMAT_TYPEMASK; /* Mask out everything but file type */
223 array = g_array_new(FALSE, FALSE, sizeof(int)); /* ++ alloc array */
224
225 memset(&sfinfo, 0, sizeof(sfinfo));
226 sf_command(NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof(int));
227 sfinfo.channels = 1;
228
229 for(s = 0; s < subtype_count; s++)
230 {
231 info.format = s;
232 sf_command(NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof(info));
233
234 sfinfo.format = format | info.format;
235
236 if(sf_format_check(&sfinfo))
237 {
238 g_array_append_val(array, info.format);
239 }
240 }
241
242 *size = array->len;
243
244 return ((int *)g_array_free(array, FALSE)); /* !! caller takes over alloc */
245 }
246
247 /* FIXME-GIR: @file_format accepts -1 as well! */
248
249 /**
250 * ipatch_snd_file_sample_format_to_sub_format:
251 * @sample_format: libinstpatch sample format (see #sample)
252 * @file_format: libsndfile format GEnum "IpatchSndFileFormat" value or -1 to
253 * not limit sub formats to a given file format.
254 *
255 * Get the optimal libsndfile sub format for a libinstpatch sample format. The
256 * returned value may not be an exact equivalent, in the case of unsigned
257 * sample data with bit widths greater than 8, but will return the optimal
258 * format in those cases. If @file_format is not -1 then the resulting sub
259 * format is guaranteed to be valid for it.
260 *
261 * Returns: Optimal libsndfile sub format enum value or -1 on error (invalid
262 * @sample_format).
263 */
264 int
ipatch_snd_file_sample_format_to_sub_format(int sample_format,int file_format)265 ipatch_snd_file_sample_format_to_sub_format(int sample_format, int file_format)
266 {
267 int sub_format;
268 int *formats;
269 guint i, size;
270
271 g_return_val_if_fail(ipatch_sample_format_verify(sample_format), -1);
272
273 switch(IPATCH_SAMPLE_FORMAT_GET_WIDTH(sample_format))
274 {
275 case IPATCH_SAMPLE_8BIT:
276 sub_format = SF_FORMAT_PCM_S8;
277 break;
278
279 case IPATCH_SAMPLE_16BIT:
280 sub_format = SF_FORMAT_PCM_16;
281 break;
282
283 case IPATCH_SAMPLE_24BIT:
284 case IPATCH_SAMPLE_REAL24BIT:
285 sub_format = SF_FORMAT_PCM_24;
286 break;
287
288 case IPATCH_SAMPLE_32BIT:
289 sub_format = SF_FORMAT_PCM_32;
290 break;
291
292 case IPATCH_SAMPLE_FLOAT:
293 sub_format = SF_FORMAT_FLOAT;
294 break;
295
296 case IPATCH_SAMPLE_DOUBLE:
297 sub_format = SF_FORMAT_DOUBLE;
298 break;
299
300 default:
301 sub_format = SF_FORMAT_PCM_16;
302 break;
303 }
304
305 if(file_format)
306 {
307 /* ++ alloc array of valid sub formats for this file format */
308 formats = ipatch_snd_file_format_get_sub_formats(file_format, &size);
309
310 if(!formats)
311 {
312 return (-1); /* Invalid file_format value */
313 }
314
315 for(i = 0; i < size; i++)
316 if(formats[i] == sub_format)
317 {
318 break;
319 }
320
321 if(i == size)
322 {
323 sub_format = formats[0]; /* sub format not found? Just use first one. FIXME? */
324 }
325
326 g_free(formats); /* -- free formats array */
327 }
328
329 return (sub_format);
330 }
331