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