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: IpatchSampleStoreFile
22  * @short_description: Sample store object type for audio in files on disk
23  * @see_also:
24  * @stability: Stable
25  */
26 #include <glib.h>
27 #include <glib-object.h>
28 #include <errno.h>
29 #include <string.h>
30 
31 #include "IpatchSampleStoreFile.h"
32 #include "ipatch_priv.h"
33 #include "i18n.h"
34 
35 enum
36 {
37     PROP_0,
38     PROP_TITLE,
39     PROP_FILE,
40     PROP_LOCATION
41 };
42 
43 static void ipatch_sample_store_file_sample_iface_init(IpatchSampleIface *iface);
44 static void ipatch_sample_store_file_get_title(IpatchSampleStoreFile *store,
45         GValue *value);
46 static void ipatch_sample_store_file_set_property
47 (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
48 static void ipatch_sample_store_file_get_property
49 (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
50 static void ipatch_sample_store_file_finalize(GObject *object);
51 static gboolean ipatch_sample_store_file_sample_iface_open
52 (IpatchSampleHandle *handle, GError **err);
53 static void ipatch_sample_store_file_sample_iface_close(IpatchSampleHandle *handle);
54 static gboolean ipatch_sample_store_file_sample_iface_read
55 (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err);
56 static gboolean ipatch_sample_store_file_sample_iface_write
57 (IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err);
58 
59 
G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreFile,ipatch_sample_store_file,IPATCH_TYPE_SAMPLE_STORE,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_sample_store_file_sample_iface_init))60 G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreFile, ipatch_sample_store_file,
61                         IPATCH_TYPE_SAMPLE_STORE,
62                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
63                                 ipatch_sample_store_file_sample_iface_init))
64 
65 static void
66 ipatch_sample_store_file_sample_iface_init(IpatchSampleIface *iface)
67 {
68     iface->open = ipatch_sample_store_file_sample_iface_open;
69     iface->close = ipatch_sample_store_file_sample_iface_close;
70     iface->read = ipatch_sample_store_file_sample_iface_read;
71     iface->write = ipatch_sample_store_file_sample_iface_write;
72 }
73 
74 static void
ipatch_sample_store_file_class_init(IpatchSampleStoreFileClass * klass)75 ipatch_sample_store_file_class_init(IpatchSampleStoreFileClass *klass)
76 {
77     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
78     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
79 
80     obj_class->finalize = ipatch_sample_store_file_finalize;
81     obj_class->get_property = ipatch_sample_store_file_get_property;
82     item_class->item_set_property = ipatch_sample_store_file_set_property;
83 
84     g_object_class_override_property(obj_class, PROP_TITLE, "title");
85     g_object_class_install_property(obj_class, PROP_FILE,
86                                     g_param_spec_object("file", "File", "File object",
87                                             IPATCH_TYPE_FILE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
88     g_object_class_install_property(obj_class, PROP_LOCATION,
89                                     g_param_spec_uint("location", "Location", "Sample data file location",
90                                             0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
91 }
92 
93 static void
ipatch_sample_store_file_get_title(IpatchSampleStoreFile * store,GValue * value)94 ipatch_sample_store_file_get_title(IpatchSampleStoreFile *store, GValue *value)
95 {
96     char *filename, *s, *basename = NULL;
97 
98     if(store->file)
99     {
100         filename = ipatch_file_get_name(store->file);       /* ++ alloc filename */
101 
102         if(filename)
103         {
104             basename = g_path_get_basename(filename);   /* ++ alloc basename */
105 
106             s = strrchr(basename, '.');  /* search for dot delimiter */
107 
108             if(s && s > basename)
109             {
110                 *s = '\0';    /* terminate string at dot */
111             }
112 
113             g_free(filename);           /* -- free filename */
114         }
115     }
116 
117     g_value_take_string(value, basename);         /* !! caller takes over alloc */
118 }
119 
120 static void
ipatch_sample_store_file_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)121 ipatch_sample_store_file_set_property(GObject *object, guint property_id,
122                                       const GValue *value, GParamSpec *pspec)
123 {
124     IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(object);
125 
126     /* No lock required since they should be set only once before use */
127 
128     switch(property_id)
129     {
130     case PROP_FILE:
131         g_return_if_fail(store->file == NULL);
132         store->file = g_value_get_object(value);
133         ipatch_file_ref_from_object(store->file, object);         // ++ ref file from store
134 
135         /* IpatchItem notify for "title" property */
136         {
137             GValue titleval = { 0 };
138             g_value_init(&titleval, G_TYPE_STRING);
139             ipatch_sample_store_file_get_title(store, &titleval);
140             ipatch_item_prop_notify((IpatchItem *)store, ipatch_item_pspec_title,
141                                     &titleval, NULL);
142             g_value_unset(&titleval);
143         }
144         break;
145 
146     case PROP_LOCATION:
147         g_return_if_fail(store->location == 0);
148         store->location = g_value_get_uint(value);
149         break;
150 
151     default:
152         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
153     }
154 }
155 
156 static void
ipatch_sample_store_file_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)157 ipatch_sample_store_file_get_property(GObject *object, guint property_id,
158                                       GValue *value, GParamSpec *pspec)
159 {
160     IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(object);
161 
162     /* No lock required since they should be set only once before use */
163 
164     switch(property_id)
165     {
166     case PROP_TITLE:
167         ipatch_sample_store_file_get_title(store, value);
168         break;
169 
170     case PROP_FILE:
171         g_value_set_object(value, store->file);
172         break;
173 
174     case PROP_LOCATION:
175         g_value_set_uint(value, store->location);
176         break;
177 
178     default:
179         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
180     }
181 }
182 
183 static void
ipatch_sample_store_file_init(IpatchSampleStoreFile * store)184 ipatch_sample_store_file_init(IpatchSampleStoreFile *store)
185 {
186 }
187 
188 static void
ipatch_sample_store_file_finalize(GObject * object)189 ipatch_sample_store_file_finalize(GObject *object)
190 {
191     IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(object);
192 
193     if(store->file)
194     {
195         ipatch_file_unref_from_object(store->file, object);    // -- unref from object
196     }
197 
198     if(G_OBJECT_CLASS(ipatch_sample_store_file_parent_class)->finalize)
199     {
200         G_OBJECT_CLASS(ipatch_sample_store_file_parent_class)->finalize(object);
201     }
202 }
203 
204 static gboolean
ipatch_sample_store_file_sample_iface_open(IpatchSampleHandle * handle,GError ** err)205 ipatch_sample_store_file_sample_iface_open(IpatchSampleHandle *handle,
206         GError **err)
207 {
208     IpatchSampleStoreFile *store = IPATCH_SAMPLE_STORE_FILE(handle->sample);
209 
210     g_return_val_if_fail(store->file != NULL, FALSE);
211 
212     /* No lock needed - file object set only once */
213     handle->data1 = ipatch_file_open(store->file, NULL,
214                                      handle->read_mode ? "r" : "w", err);
215 
216     if(!handle->data1)
217     {
218         return (FALSE);
219     }
220 
221     handle->data2 = GUINT_TO_POINTER(ipatch_sample_format_size(ipatch_sample_store_get_format(store)));
222 
223     return (TRUE);
224 }
225 
226 static void
ipatch_sample_store_file_sample_iface_close(IpatchSampleHandle * handle)227 ipatch_sample_store_file_sample_iface_close(IpatchSampleHandle *handle)
228 {
229     if(handle->data1)
230     {
231         ipatch_file_close(handle->data1);
232         handle->data1 = NULL;
233     }
234 }
235 
236 static gboolean
ipatch_sample_store_file_sample_iface_read(IpatchSampleHandle * handle,guint offset,guint frames,gpointer buf,GError ** err)237 ipatch_sample_store_file_sample_iface_read(IpatchSampleHandle *handle,
238         guint offset, guint frames,
239         gpointer buf, GError **err)
240 {
241     IpatchSampleStoreFile *store = (IpatchSampleStoreFile *)(handle->sample);
242     IpatchFileHandle *fh = (IpatchFileHandle *)(handle->data1);
243     guint8 frame_size = GPOINTER_TO_UINT(handle->data2);
244 
245     if(!ipatch_file_seek(fh, store->location + offset * frame_size, G_SEEK_SET, err))
246     {
247         return (FALSE);
248     }
249 
250     if(!ipatch_file_read(fh, buf, frames * frame_size, err))
251     {
252         return (FALSE);
253     }
254 
255     return (TRUE);
256 }
257 
258 static gboolean
ipatch_sample_store_file_sample_iface_write(IpatchSampleHandle * handle,guint offset,guint frames,gconstpointer buf,GError ** err)259 ipatch_sample_store_file_sample_iface_write(IpatchSampleHandle *handle,
260         guint offset, guint frames,
261         gconstpointer buf, GError **err)
262 {
263     IpatchSampleStoreFile *store = (IpatchSampleStoreFile *)(handle->sample);
264     IpatchFileHandle *fh = (IpatchFileHandle *)(handle->data1);
265     guint8 frame_size = GPOINTER_TO_UINT(handle->data2);
266 
267     if(!ipatch_file_seek(fh, store->location + offset * frame_size, G_SEEK_SET, err))
268     {
269         return (FALSE);
270     }
271 
272     if(!ipatch_file_write(fh, buf, frames * frame_size, err))
273     {
274         return (FALSE);
275     }
276 
277     return (TRUE);
278 }
279 
280 /**
281  * ipatch_sample_store_file_new:
282  * @file: File object to use for file sample store
283  * @location: Location in file of audio data
284  *
285  * Creates a new file sample store.
286  *
287  * Returns: (type IpatchSampleStoreFile): New file sample store, cast
288  *   as a #IpatchSample for convenience.
289  */
290 IpatchSample *
ipatch_sample_store_file_new(IpatchFile * file,guint location)291 ipatch_sample_store_file_new(IpatchFile *file, guint location)
292 {
293     return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_FILE,
294                                        "file", file,
295                                        "location", location,
296                                        NULL)));
297 }
298