Working on sound
[projects/chimara/chimara.git] / libchimara / schannel.c
1 #include <config.h>
2 #include <glib.h>
3 #include <glib/gi18n.h>
4 #include <libchimara/glk.h>
5 #ifdef GSTREAMER_SOUND
6 #include <gst/gst.h>
7 #endif
8 #include "magic.h"
9 #include "schannel.h"
10 #include "chimara-glk-private.h"
11 #include "gi_dispa.h"
12 #include "gi_blorb.h"
13 #include "resource.h"
14
15 extern GPrivate *glk_data_key;
16
17 static void
18 on_new_decoded_pad(GstTypeFindElement *typefind, guint probability, GstCaps *caps)
19 {
20         
21
22 /**
23  * glk_schannel_create:
24  * @rock: The rock value to give the new sound channel.
25  *
26  * This creates a sound channel, about as you'd expect.
27  *
28  * Remember that it is possible that the library will be unable to create a new
29  * channel, in which case glk_schannel_create() will return %NULL.
30  *
31  * Returns: A new sound channel, or %NULL.
32  */
33 schanid_t 
34 glk_schannel_create(glui32 rock)
35 {
36 #ifdef GSTREAMER_SOUND
37         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
38
39         schanid_t s = g_new0(struct glk_schannel_struct, 1);
40         s->magic = MAGIC_SCHANNEL;
41         s->rock = rock;
42         if(glk_data->register_obj)
43                 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
44
45         /* Add it to the global sound channel list */
46         glk_data->schannel_list = g_list_prepend(glk_data->schannel_list, s);
47         s->schannel_list = glk_data->schannel_list;
48
49         /* Create a GStreamer pipeline for the sound channel */
50         gchar *pipeline_name = g_strdup_printf("pipeline-%p", s);
51         s->pipeline = gst_pipeline_new(pipeline_name);
52         g_free(pipeline_name);
53
54         /* Create GStreamer elements to put in the pipeline */
55         s->source = gst_element_factory_make("giostreamsrc", NULL);
56         s->decode = gst_element_factory_make("decodebin2", NULL);
57         s->filter = gst_element_factory_make("volume", NULL);
58         s->sink = gst_element_factory_make("autoaudiosink", NULL);
59         if(!s->source || !s->decode || !s->filter || !s->sink) {
60                 WARNING(_("Could not create one or more GStreamer elements"));
61                 goto fail;
62         }
63                 
64         gst_bin_add_many(GST_BIN(s->pipeline), s->source, s->decode, s->filter, s->sink, NULL);
65         if(!gst_element_link(s->source, s->decode)) {
66                 WARNING(_("Could not link GStreamer elements"));
67                 goto fail;
68         }
69         if(!gst_element_link(s->filter, s->sink)) {
70                 WARNING(_("Could not link GStreamer elements"));
71                 goto fail;
72         }
73         g_signal_connect(s->decode, "new-decoded-pad", G_CALLBACK(on_new_decoded_pad), s);
74         
75         return s;
76
77 fail:
78         glk_schannel_destroy(s);
79         return NULL;
80 #else
81         return NULL;
82 #endif /* GSTREAMER_SOUND */
83 }
84
85 /**
86  * glk_schannel_destroy:
87  * @chan: The sound channel to destroy.
88  *
89  * Destroys the channel. If the channel is playing a sound, the sound stops 
90  * immediately (with no notification event).
91  */
92 void 
93 glk_schannel_destroy(schanid_t chan)
94 {
95         VALID_SCHANNEL(chan, return);
96
97 #ifdef GSTREAMER_SOUND
98         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
99
100         if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
101                 WARNING_S(_("Could not set GstElement state to"), "NULL");
102         
103         glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
104
105         if(glk_data->unregister_obj)
106         {
107                 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
108                 chan->disprock.ptr = NULL;
109         }
110         
111         if(chan->pipeline)
112                 gst_object_unref(chan->pipeline);
113         
114         chan->magic = MAGIC_FREE;
115         g_free(chan);
116 #endif
117 }
118
119 /**
120  * glk_schannel_iterate:
121  * @chan: A sound channel, or %NULL.
122  * @rockptr: Return location for the next sound channel's rock, or %NULL.
123  *
124  * This function can be used to iterate through the list of all open channels.
125  * See <link linkend="chimara-Iterating-Through-Opaque-Objects">Iterating 
126  * Through Opaque Objects</link>.
127  *
128  * As that section describes, the order in which channels are returned is 
129  * arbitrary.
130  *
131  * Returns: the next sound channel, or %NULL if there are no more.
132  */
133 schanid_t 
134 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
135 {
136         VALID_SCHANNEL_OR_NULL(chan, return NULL);
137
138 #ifdef GSTREAMER_SOUND
139         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
140         GList *retnode;
141         
142         if(chan == NULL)
143                 retnode = glk_data->schannel_list;
144         else
145                 retnode = chan->schannel_list->next;
146         schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
147                 
148         /* Store the sound channel's rock in rockptr */
149         if(retval && rockptr)
150                 *rockptr = glk_schannel_get_rock(retval);
151                 
152         return retval;
153 #else
154         return NULL;
155 #endif /* GSTREAMER_SOUND */
156 }
157
158 /**
159  * glk_schannel_get_rock:
160  * @chan: A sound channel.
161  * 
162  * Retrieves the channel's rock value. See <link 
163  * linkend="chimara-Rocks">Rocks</link>.
164  *
165  * Returns: A rock value.
166  */
167 glui32 
168 glk_schannel_get_rock(schanid_t chan)
169 {
170         VALID_SCHANNEL(chan, return 0);
171         return chan->rock;
172 }
173
174 /**
175  * glk_schannel_play:
176  * @chan: Channel to play the sound in.
177  * @snd: Resource number of the sound to play.
178  *
179  * Begins playing the given sound on the channel. If the channel was already
180  * playing a sound (even the same one), the old sound is stopped (with no
181  * notification event.
182  *
183  * This returns 1 if the sound actually started playing, and 0 if there was any
184  * problem.
185  * <note><para>
186  *   The most obvious problem is if there is no sound resource with the given
187  *   identifier. But other problems can occur. For example, the MOD-playing 
188  *   facility in a library might be unable to handle two MODs at the same time,
189  *   in which case playing a MOD resource would fail if one was already playing.
190  * </para></note>
191  *
192  * Returns: 1 on success, 0 on failure.
193  */
194 glui32 
195 glk_schannel_play(schanid_t chan, glui32 snd)
196 {
197         return glk_schannel_play_ext(chan, snd, 1, 0);
198 }
199
200 /**
201  * glk_schannel_play_ext:
202  * @chan: Channel to play the sound in.
203  * @snd: Resource number of the sound to play.
204  * @repeats: Number of times to repeat the sound.
205  * @notify: If nonzero, requests a notification when the sound is finished.
206  *
207  * This works the same as glk_schannel_play(), but lets you specify additional 
208  * options. <code>glk_schannel_play(chan, snd)</code> is exactly equivalent to 
209  * <code>glk_schannel_play_ext(chan, snd, 1, 0)</code>.
210  * 
211  * The @repeats value is the number of times the sound should be repeated. A 
212  * repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should repeat 
213  * forever. A repeat value of 0 means that the sound will not be played at all; 
214  * nothing happens. (Although a previous sound on the channel will be stopped, 
215  * and the function will return 1.)
216  * 
217  * The @notify value should be nonzero in order to request a sound notification
218  * event. If you do this, when the sound is completed, you will get an event 
219  * with type %evtype_SoundNotify. The @window will be %NULL, @val1 will be the 
220  * sound's resource id, and @val2 will be the nonzero value you passed as 
221  * @notify.
222  * 
223  * If you request sound notification, and the repeat value is greater than one, 
224  * you will get the event only after the last repetition. If the repeat value is
225  * 0 or -1, you will never get a notification event at all. Similarly, if the 
226  * sound is stopped or interrupted, or if the channel is destroyed while the 
227  * sound is playing, there will be no notification event.
228  *
229  * Not all libraries support sound notification. You should test the
230  * %gestalt_SoundNotify selector before you rely on it; see <link
231  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound 
232  * Capabilities</link>.
233  * 
234  * Returns: 1 on success, 0 on failure.
235  */
236 glui32 
237 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
238 {
239         VALID_SCHANNEL(chan, return 0);
240 #ifdef GSTREAMER_SOUND
241         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
242         
243         if(!glk_data->resource_map) {
244                 if(!glk_data->resource_load_callback) {
245                         WARNING(_("No resource map has been loaded yet."));
246                         return 0;
247                 }
248                 WARNING(_("Loading sound resources from alternative location not yet supported."));
249                 return 0;
250         }
251         
252         giblorb_result_t resource;
253         giblorb_err_t result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
254         if(result != giblorb_err_None) {
255                 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
256                 return 0;
257         }
258         g_printerr("playing sound resource %d at %p, length %x\n", snd, resource.data.ptr, resource.length);
259         GInputStream *stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
260         g_object_set(chan->source, "stream", stream, NULL);
261         
262         if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
263                 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
264                 return 0;
265         }
266         return 1;
267 #else
268         return 0;
269 #endif
270 }
271
272 /**
273  * glk_schannel_stop:
274  * @chan: Channel to silence.
275  *
276  * Stops any sound playing in the channel. No notification event is generated,
277  * even if you requested one. If no sound is playing, this has no effect.
278  */
279 void 
280 glk_schannel_stop(schanid_t chan)
281 {
282         VALID_SCHANNEL(chan, return);
283 #ifdef GSTREAMER_SOUND
284         if(!gst_element_set_state(chan->pipeline, GST_STATE_READY))
285                 WARNING_S(_("Could not set GstElement state to"), "READY");
286 #endif
287 }
288
289 /**
290  * glk_schannel_set_volume:
291  * @chan: Channel to set the volume of.
292  * @vol: Integer representing the volume; 0x10000 is 100&percnt;.
293  *
294  * Sets the volume in the channel. When you create a channel, it has full 
295  * volume, represented by the value 0x10000. Half volume would be 0x8000, 
296  * three-quarters volume would be 0xC000, and so on. A volume of zero represents
297  * silence, although the sound is still considered to be playing.
298  *
299  * You can call this function between sounds, or while a sound is playing. The 
300  * effect is immediate.
301  * 
302  * You can overdrive the volume of a channel by setting a volume greater than 
303  * 0x10000. However, this is not recommended; the library may be unable to 
304  * increase the volume past full, or the sound may become distorted. You should 
305  * always create sound resources with the maximum volume you will need, and then
306  * call glk_schannel_set_volume() to reduce the volume when appropriate.
307  *
308  * Not all libraries support this function. You should test the
309  * %gestalt_SoundVolume selector before you rely on it; see <link
310  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
311  * Capabilities</link>.
312  *
313  * <note><title>Chimara</title>
314  *   <para>Chimara supports volumes from 0 to 1000&percnt;, that is, values of
315  *   @vol up to 0xA0000.</para>
316  * </note>
317  */
318 void 
319 glk_schannel_set_volume(schanid_t chan, glui32 vol)
320 {
321         VALID_SCHANNEL(chan, return);
322 #ifdef GSTREAMER_SOUND
323         gdouble volume_gst = (gdouble)vol / 0x10000;
324         g_printerr("Volume set to: %f\n", volume_gst);
325         g_object_set(chan->filter, "volume", CLAMP(volume_gst, 0.0, 10.0), NULL);
326 #endif
327 }
328
329 /**
330  * glk_sound_load_hint:
331  * @snd: Resource number of a sound.
332  * @flag: Nonzero to tell the library to load the sound, zero to tell the
333  * library to unload it.
334  *
335  * This gives the library a hint about whether the given sound should be loaded
336  * or not. If the @flag is nonzero, the library may preload the sound or do
337  * other initialization, so that glk_schannel_play() will be faster. If the
338  * @flag is zero, the library may release memory or other resources associated
339  * with the sound. Calling this function is always optional, and it has no
340  * effect on what the library actually plays.
341  *
342  * <warning><para>This function is not implemented yet.</para></warning>
343  */
344 void 
345 glk_sound_load_hint(glui32 snd, glui32 flag)
346 {
347 }