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