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: IpatchSampleStoreRam
22  * @short_description: Sample store object for audio data in RAM
23  * @see_also:
24  * @stability: Stable
25  */
26 #include <string.h>
27 #include <stdlib.h>
28 #include <glib.h>
29 #include <glib-object.h>
30 #include "IpatchSampleStoreRam.h"
31 #include "ipatch_priv.h"
32 #include "i18n.h"
33 
34 /* properties */
35 enum
36 {
37     PROP_0,
38     PROP_LOCATION,
39     PROP_FREE_DATA
40 };
41 
42 
43 static void ipatch_sample_store_ram_sample_iface_init(IpatchSampleIface *iface);
44 static void ipatch_sample_store_ram_set_property
45 (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
46 static void ipatch_sample_store_ram_get_property
47 (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
48 static void ipatch_sample_store_ram_finalize(GObject *object);
49 static gboolean ipatch_sample_store_ram_sample_iface_open
50 (IpatchSampleHandle *handle, GError **err);
51 static gboolean ipatch_sample_store_ram_sample_iface_read
52 (IpatchSampleHandle *handle, guint offset, guint frames, gpointer buf, GError **err);
53 static gboolean ipatch_sample_store_ram_sample_iface_write
54 (IpatchSampleHandle *handle, guint offset, guint frames, gconstpointer buf, GError **err);
55 
56 
G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreRam,ipatch_sample_store_ram,IPATCH_TYPE_SAMPLE_STORE,G_IMPLEMENT_INTERFACE (IPATCH_TYPE_SAMPLE,ipatch_sample_store_ram_sample_iface_init))57 G_DEFINE_TYPE_WITH_CODE(IpatchSampleStoreRam, ipatch_sample_store_ram,
58                         IPATCH_TYPE_SAMPLE_STORE,
59                         G_IMPLEMENT_INTERFACE(IPATCH_TYPE_SAMPLE,
60                                 ipatch_sample_store_ram_sample_iface_init))
61 
62 static void
63 ipatch_sample_store_ram_sample_iface_init(IpatchSampleIface *iface)
64 {
65     iface->open = ipatch_sample_store_ram_sample_iface_open;
66     iface->read = ipatch_sample_store_ram_sample_iface_read;
67     iface->write = ipatch_sample_store_ram_sample_iface_write;
68 }
69 
70 static void
ipatch_sample_store_ram_class_init(IpatchSampleStoreRamClass * klass)71 ipatch_sample_store_ram_class_init(IpatchSampleStoreRamClass *klass)
72 {
73     GObjectClass *obj_class = G_OBJECT_CLASS(klass);
74     IpatchItemClass *item_class = IPATCH_ITEM_CLASS(klass);
75 
76     obj_class->finalize = ipatch_sample_store_ram_finalize;
77     obj_class->get_property = ipatch_sample_store_ram_get_property;
78     item_class->item_set_property = ipatch_sample_store_ram_set_property;
79 
80     g_object_class_install_property(obj_class, PROP_LOCATION,
81                                     g_param_spec_pointer("location", "Location", "Sample data pointer",
82                                             G_PARAM_READWRITE));
83     g_object_class_install_property(obj_class, PROP_FREE_DATA,
84                                     g_param_spec_boolean("free-data", "Free data", "Free data when object destroyed",
85                                             FALSE, G_PARAM_READWRITE));
86 }
87 
88 static void
ipatch_sample_store_ram_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)89 ipatch_sample_store_ram_set_property(GObject *object, guint property_id,
90                                      const GValue *value, GParamSpec *pspec)
91 {
92     IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(object);
93 
94     switch(property_id)
95     {
96     case PROP_LOCATION:
97         g_return_if_fail(store->location == NULL);
98 
99         /* Lock not needed, should be set only once before use */
100         store->location = g_value_get_pointer(value);
101         break;
102 
103     case PROP_FREE_DATA:
104         ipatch_item_set_flags(object, IPATCH_SAMPLE_STORE_RAM_ALLOCATED);
105         break;
106 
107     default:
108         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
109     }
110 }
111 
112 static void
ipatch_sample_store_ram_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)113 ipatch_sample_store_ram_get_property(GObject *object, guint property_id,
114                                      GValue *value, GParamSpec *pspec)
115 {
116     IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(object);
117 
118     switch(property_id)
119     {
120     case PROP_LOCATION:
121         /* Lock not needed, should be set only once before use */
122         g_value_set_pointer(value, store->location);
123         break;
124 
125     case PROP_FREE_DATA:
126         g_value_set_boolean(value, (ipatch_item_get_flags((IpatchItem *)object)
127                                     & IPATCH_SAMPLE_STORE_RAM_ALLOCATED) != 0);
128         break;
129 
130     default:
131         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
132     }
133 }
134 
135 static void
ipatch_sample_store_ram_init(IpatchSampleStoreRam * store)136 ipatch_sample_store_ram_init(IpatchSampleStoreRam *store)
137 {
138 }
139 
140 static void
ipatch_sample_store_ram_finalize(GObject * object)141 ipatch_sample_store_ram_finalize(GObject *object)
142 {
143     IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(object);
144 
145     if(ipatch_item_get_flags(IPATCH_ITEM(store)) & IPATCH_SAMPLE_STORE_RAM_ALLOCATED)
146     {
147         g_free(store->location);
148         store->location = NULL;
149     }
150 
151     if(G_OBJECT_CLASS(ipatch_sample_store_ram_parent_class)->finalize)
152     {
153         G_OBJECT_CLASS(ipatch_sample_store_ram_parent_class)->finalize(object);
154     }
155 }
156 
157 static gboolean
ipatch_sample_store_ram_sample_iface_open(IpatchSampleHandle * handle,GError ** err)158 ipatch_sample_store_ram_sample_iface_open(IpatchSampleHandle *handle,
159         GError **err)
160 {
161     IpatchSampleStoreRam *store = IPATCH_SAMPLE_STORE_RAM(handle->sample);
162     guint bytes;
163 
164     g_return_val_if_fail(!handle->read_mode || store->location, FALSE);
165 
166     /* Locking not needed, since new samples will be written with audio before
167      * being used by multiple threads */
168     if(!store->location)
169     {
170         ipatch_item_set_flags(IPATCH_ITEM(store), IPATCH_SAMPLE_STORE_RAM_ALLOCATED);
171         ipatch_sample_get_size(handle->sample, &bytes);
172         store->location = g_malloc0(bytes);
173     }
174 
175     /* Store frame size to data1 */
176     handle->data1 = GUINT_TO_POINTER(ipatch_sample_format_size(ipatch_sample_store_get_format(store)));
177 
178     return (TRUE);
179 }
180 
181 static gboolean
ipatch_sample_store_ram_sample_iface_read(IpatchSampleHandle * handle,guint offset,guint frames,gpointer buf,GError ** err)182 ipatch_sample_store_ram_sample_iface_read(IpatchSampleHandle *handle,
183         guint offset, guint frames,
184         gpointer buf, GError **err)
185 {
186     IpatchSampleStoreRam *store = (IpatchSampleStoreRam *)(handle->sample);
187     guint8 frame_size = GPOINTER_TO_UINT(handle->data1);
188 
189     /* No need to lock, sample data should not change after initial load */
190 
191     memcpy(buf, &((gint8 *)(store->location))[offset * frame_size],
192            frames * frame_size);
193 
194     return (TRUE);
195 }
196 
197 static gboolean
ipatch_sample_store_ram_sample_iface_write(IpatchSampleHandle * handle,guint offset,guint frames,gconstpointer buf,GError ** err)198 ipatch_sample_store_ram_sample_iface_write(IpatchSampleHandle *handle,
199         guint offset, guint frames,
200         gconstpointer buf, GError **err)
201 {
202     IpatchSampleStoreRam *store = (IpatchSampleStoreRam *)(handle->sample);
203     guint8 frame_size = GPOINTER_TO_UINT(handle->data1);
204 
205     /* No need to lock, sample data written only once and before used by
206        multiple threads */
207 
208     memcpy(&((gint8 *)(store->location))[offset * frame_size], buf,
209            frames * frame_size);
210 
211     return (TRUE);
212 }
213 
214 /**
215  * ipatch_sample_store_ram_new:
216  * @location: (nullable): Location of existing sample data or %NULL if the sample buffer
217  *   should be allocated (in which case the sample must be written to first).
218  * @free_data: %TRUE if sample data at @location should be freed when object
219  *   is destroyed
220  *
221  * Creates a new RAM sample store.
222  *
223  * Returns: (type IpatchSampleStoreRam): New RAM sample store,
224  *   cast as a #IpatchSample for convenience.
225  */
226 IpatchSample *
ipatch_sample_store_ram_new(gpointer location,gboolean free_data)227 ipatch_sample_store_ram_new(gpointer location, gboolean free_data)
228 {
229     return (IPATCH_SAMPLE(g_object_new(IPATCH_TYPE_SAMPLE_STORE_RAM,
230                                        "location", location,
231                                        "free-data", free_data,
232                                        NULL)));
233 }
234 
235 /**
236  * ipatch_sample_store_ram_get_blank:
237  *
238  * Get blank mono RAM sample object.  Return's a sample object
239  * with 48 stereo 16 bit samples of silent audio.  Only creates it on
240  * the first call, subsequent calls return the same sample object. Therefore it
241  * should not be modified.
242  *
243  * Returns: (transfer full): The blank sample object. Remember to unref it when not
244  * using it anymore with g_object_unref().
245  */
246 IpatchSample *
ipatch_sample_store_ram_get_blank(void)247 ipatch_sample_store_ram_get_blank(void)
248 {
249     static IpatchSample *blank_sample = NULL;
250     gpointer dataptr;
251 
252     if(!blank_sample)
253     {
254         dataptr = g_malloc(48 * 2);
255         blank_sample = ipatch_sample_store_ram_new(dataptr, TRUE);
256 
257         g_object_set(blank_sample,
258                      "sample-size", 48,
259                      "sample-format", IPATCH_SAMPLE_16BIT | IPATCH_SAMPLE_ENDIAN_HOST,
260                      "sample-rate", IPATCH_SAMPLE_RATE_DEFAULT,
261                      NULL);
262     }
263     else
264     {
265         g_object_ref(blank_sample);
266     }
267 
268     return (blank_sample);
269 }
270