Refactor loading sound resource as GIO stream
[projects/chimara/chimara.git] / libchimara / schannel.c
1 #include <config.h>
2 #include <glib.h>
3 #include <glib/gi18n-lib.h>
4 #include <libchimara/glk.h>
5 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
6 #include <gst/gst.h>
7 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
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 #include "event.h"
15
16 #define VOLUME_TIMER_RESOLUTION 1.0 /* In milliseconds */
17
18 #ifdef GSTREAMER_0_10_SOUND
19 #define OGG_MIMETYPE "application/ogg"
20 #endif
21 #ifdef GSTREAMER_1_0_SOUND
22 #define OGG_MIMETYPE "audio/ogg"
23 #endif
24
25 extern GPrivate glk_data_key;
26
27 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
28 /* Stop any currently playing sound on this channel, and remove any
29  format-specific GStreamer elements from the channel. */
30 static void
31 clean_up_after_playing_sound(schanid_t chan)
32 {
33         if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
34                 WARNING_S(_("Could not set GstElement state to"), "NULL");
35         if(chan->source)
36         {
37                 gst_bin_remove(GST_BIN(chan->pipeline), chan->source);
38                 chan->source = NULL;
39         }
40         if(chan->demux)
41         {
42                 gst_bin_remove(GST_BIN(chan->pipeline), chan->demux);
43                 chan->demux = NULL;
44         }
45         if(chan->decode)
46         {
47                 gst_bin_remove(GST_BIN(chan->pipeline), chan->decode);
48                 chan->decode = NULL;
49         }
50 }
51
52 /* This signal is thrown whenever the GStreamer pipeline generates a message.
53  Most messages are harmless. */
54 static void
55 on_pipeline_message(GstBus *bus, GstMessage *message, schanid_t s)
56 {
57         /* g_printerr("Got %s message\n", GST_MESSAGE_TYPE_NAME(message)); */
58
59         GError *err;
60         gchar *debug_message;
61         
62         switch(GST_MESSAGE_TYPE(message)) {
63         case GST_MESSAGE_ERROR: 
64         {
65                 gst_message_parse_error(message, &err, &debug_message);
66                 IO_WARNING(_("GStreamer error"), err->message, debug_message);
67                 g_error_free(err);
68                 g_free(debug_message);
69                 clean_up_after_playing_sound(s);
70         }
71                 break;
72         case GST_MESSAGE_WARNING:
73         {
74                 gst_message_parse_warning(message, &err, &debug_message);
75                 IO_WARNING(_("GStreamer warning"), err->message, debug_message);
76                 g_error_free(err);
77                 g_free(debug_message);
78         }
79                 break;
80         case GST_MESSAGE_INFO:
81         {
82                 gst_message_parse_info(message, &err, &debug_message);
83                 g_message("GStreamer info \"%s\": %s", err->message, debug_message);
84                 g_error_free(err);
85                 g_free(debug_message);
86         }
87                 break;
88         case GST_MESSAGE_EOS: /* End of stream */
89                 /* Decrease repeats if not set to forever */
90                 if(s->repeats != (glui32)-1)
91                         s->repeats--;
92                 if(s->repeats > 0) {
93                         if(!gst_element_seek_simple(s->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 0)) {
94                                 WARNING(_("Could not execute GStreamer seek"));
95                                 clean_up_after_playing_sound(s);
96                         }
97                 } else {
98                         clean_up_after_playing_sound(s);
99                         /* Sound ended normally, send a notification if requested */
100                         if(s->notify)
101                                 event_throw(s->glk, evtype_SoundNotify, NULL, s->resource, s->notify);
102                 }
103                 break;
104         default:
105                 /* unhandled message */
106                 break;
107         }
108 }
109
110 /* This signal is thrown when the OGG demuxer element has decided what kind of
111  outputs it will output. We connect the decoder element dynamically. */
112 static void
113 on_ogg_demuxer_pad_added(GstElement *demux, GstPad *pad, schanid_t s)
114 {
115         GstPad *sinkpad;
116         
117         /* We can now link this pad with the vorbis-decoder sink pad */
118         sinkpad = gst_element_get_static_pad(s->decode, "sink");
119         if(gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK)
120                 WARNING(_("Could not link OGG demuxer with Vorbis decoder"));
121         gst_object_unref(sinkpad);
122 }
123
124 /* This signal is thrown when the typefinder element has found the type of its
125  input. Now that we know what kind of input stream we have, we can connect the
126  proper demuxer/decoder elements. */
127 static void
128 on_type_found(GstElement *typefind, guint probability, GstCaps *caps, schanid_t s)
129 {
130         gchar *type = gst_caps_to_string(caps);
131         if(strcmp(type, OGG_MIMETYPE) == 0) {
132                 s->demux = gst_element_factory_make("oggdemux", NULL);
133                 s->decode = gst_element_factory_make("vorbisdec", NULL);
134                 if(!s->demux || !s->decode) {
135                         WARNING(_("Could not create one or more GStreamer elements"));
136                         goto finally;
137                 }
138                 gst_bin_add_many(GST_BIN(s->pipeline), s->demux, s->decode, NULL);
139                 if(!gst_element_link(s->typefind, s->demux) || !gst_element_link(s->decode, s->convert)) {
140                         WARNING(_("Could not link GStreamer elements"));
141                         goto finally;
142                 }
143                 /* We link the demuxer and decoder together dynamically, since the
144                  demuxer doesn't know what source pads it will have until it starts
145                  demuxing the stream */
146                 g_signal_connect(s->demux, "pad-added", G_CALLBACK(on_ogg_demuxer_pad_added), s);
147         } else if(strcmp(type, "audio/x-aiff") == 0) {
148                 s->decode = gst_element_factory_make("aiffparse", NULL);
149                 if(!s->decode) {
150                         WARNING(_("Could not create 'aiffparse' GStreamer element"));
151                         goto finally;
152                 }
153                 gst_bin_add(GST_BIN(s->pipeline), s->decode);
154                 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL)) {
155                         WARNING(_("Could not link GStreamer elements"));
156                         goto finally;
157                 }
158         } else if(strcmp(type, "audio/x-mod") == 0) {
159                 s->decode = gst_element_factory_make("modplug", NULL);
160                 if(!s->decode) {
161                         WARNING(_("Could not create 'modplug' GStreamer element"));
162                         goto finally;
163                 }
164                 gst_bin_add(GST_BIN(s->pipeline), s->decode);
165                 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL)) {
166                         WARNING(_("Could not link GStreamer elements"));
167                         goto finally;
168                 }
169         } else {
170                 WARNING_S(_("Unexpected audio type in blorb"), type);
171         }
172
173         /* This is necessary in case this handler occurs in the middle of a state
174         change */
175         gst_element_sync_state_with_parent(s->decode);
176         if(s->demux != NULL)
177                 gst_element_sync_state_with_parent(s->demux);
178
179 finally:
180         g_free(type);
181 }
182
183 /* Load a sound resource into a GInputStream, by whatever method */
184 static GInputStream *
185 load_resource_into_giostream(glui32 snd)
186 {
187         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
188         GInputStream *retval;
189
190         if(glk_data->resource_map == NULL) {
191                 if(glk_data->resource_load_callback == NULL) {
192                         WARNING(_("No resource map has been loaded yet."));
193                         return NULL;
194                 }
195                 char *filename = glk_data->resource_load_callback(CHIMARA_RESOURCE_SOUND, snd, glk_data->resource_load_callback_data);
196                 if(filename == NULL) {
197                         WARNING(_("Error loading resource from alternative location."));
198                         return NULL;
199                 }
200
201                 GError *err = NULL;
202                 GFile *file = g_file_new_for_path(filename);
203                 retval = G_INPUT_STREAM(g_file_read(file, NULL, &err));
204                 if(retval == NULL)
205                         IO_WARNING(_("Error loading resource from file"), filename, err->message);
206                 g_free(filename);
207                 g_object_unref(file);
208         } else {
209                 giblorb_result_t resource;
210                 giblorb_err_t result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
211                 if(result != giblorb_err_None) {
212                         WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
213                         return NULL;
214                 }
215                 retval = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
216         }
217         return retval;
218 }
219 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
220
221 /**
222  * glk_schannel_create:
223  * @rock: The rock value to give the new sound channel.
224  *
225  * This creates a sound channel, about as you'd expect.
226  *
227  * Remember that it is possible that the library will be unable to create a new
228  * channel, in which case glk_schannel_create() will return %NULL.
229  *
230  * When you create a channel using glk_schannel_create(), it has full volume,
231  * represented by the value 0x10000. Half volume would be 0x8000, three-quarters
232  * volume would be 0xC000, and so on. A volume of zero represents silence.
233  *
234  * You can overdrive the volume of a channel by setting a volume greater than 
235  * 0x10000. However, this is not recommended; the library may be unable to 
236  * increase the volume past full, or the sound may become distorted. You should 
237  * always create sound resources with the maximum volume you will need, and then
238  * reduce the volume when appropriate using the channel-volume calls.
239  *
240  * <note><para>
241  *   Mathematically, these volume changes should be taken as linear
242  *   multiplication of a waveform represented as linear samples. As I
243  *   understand it, linear PCM encodes the sound pressure, and therefore a
244  *   volume of 0x8000 should represent a 6 dB drop.
245  * </para></note>
246  *
247  * Returns: A new sound channel, or %NULL.
248  */
249 schanid_t 
250 glk_schannel_create(glui32 rock)
251 {
252         return glk_schannel_create_ext(rock, 0x10000);
253 }
254
255 /**
256  * glk_schannel_create_ext:
257  * @rock: The rock value to give the new sound channel.
258  * @volume: Integer representing the volume; 0x10000 is 100&percnt;.
259  *
260  * The glk_schannel_create_ext() call lets you create a channel with the volume
261  * already set at a given level.
262  *
263  * Not all libraries support glk_schannel_create_ext(). You should test the
264  * %gestalt_Sound2 selector before you rely on it; see <link
265  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
266  * Capabilities</link>.
267  *
268  * Returns: A new sound channel, or %NULL.
269  */
270 schanid_t
271 glk_schannel_create_ext(glui32 rock, glui32 volume)
272 {
273 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
274         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
275
276         schanid_t s = g_new0(struct glk_schannel_struct, 1);
277         s->magic = MAGIC_SCHANNEL;
278         s->rock = rock;
279         if(glk_data->register_obj)
280                 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
281
282         /* Add it to the global sound channel list */
283         glk_data->schannel_list = g_list_prepend(glk_data->schannel_list, s);
284         s->schannel_list = glk_data->schannel_list;
285
286         /* Add a pointer to the ChimaraGlk widget, for convenience */
287         s->glk = glk_data->self;
288
289         /* Create a GStreamer pipeline for the sound channel */
290         gchar *pipeline_name = g_strdup_printf("pipeline-%p", s);
291         s->pipeline = gst_pipeline_new(pipeline_name);
292         g_free(pipeline_name);
293
294         /* Watch for messages from the pipeline */
295         GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(s->pipeline));
296         gst_bus_add_signal_watch(bus);
297         g_signal_connect(bus, "message", G_CALLBACK(on_pipeline_message), s);
298         gst_object_unref(bus);
299
300         /* Create GStreamer elements to put in the pipeline */
301         s->typefind = gst_element_factory_make("typefind", NULL);
302         s->convert = gst_element_factory_make("audioconvert", NULL);
303         s->filter = gst_element_factory_make("volume", NULL);
304         s->sink = gst_element_factory_make("autoaudiosink", NULL);
305         if(!s->typefind || !s->convert || !s->filter || !s->sink) {
306                 WARNING(_("Could not create one or more GStreamer elements"));
307                 goto fail;
308         }
309
310         /* Set the initial volume */
311         glk_schannel_set_volume(s, volume);
312
313         /* Put the elements in the pipeline and link as many together as we can
314          without knowing the type of the audio stream */
315         gst_bin_add_many(GST_BIN(s->pipeline), s->typefind, s->convert, s->filter, s->sink, NULL);
316
317         /* Link elements: ??? -> Converter -> Volume filter -> Sink */
318         if(!gst_element_link_many(s->convert, s->filter, s->sink, NULL)) {
319                 WARNING(_("Could not link GStreamer elements"));
320                 goto fail;
321         }
322         g_signal_connect(s->typefind, "have-type", G_CALLBACK(on_type_found), s);
323         
324         return s;
325
326 fail:
327         glk_schannel_destroy(s);
328         return NULL;
329 #else
330         return NULL;
331 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
332 }
333
334 /**
335  * glk_schannel_destroy:
336  * @chan: The sound channel to destroy.
337  *
338  * Destroys the channel. If the channel is playing a sound, the sound stops 
339  * immediately (with no notification event).
340  */
341 void 
342 glk_schannel_destroy(schanid_t chan)
343 {
344         VALID_SCHANNEL(chan, return);
345
346 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
347         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
348
349         if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
350                 WARNING_S(_("Could not set GstElement state to"), "NULL");
351         
352         glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
353
354         if(glk_data->unregister_obj)
355         {
356                 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
357                 chan->disprock.ptr = NULL;
358         }
359
360         /* This also frees all the objects inside the pipeline */
361         if(chan->pipeline)
362                 gst_object_unref(chan->pipeline);
363         
364         chan->magic = MAGIC_FREE;
365         g_free(chan);
366 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
367 }
368
369 /**
370  * glk_schannel_iterate:
371  * @chan: A sound channel, or %NULL.
372  * @rockptr: Return location for the next sound channel's rock, or %NULL.
373  *
374  * This function can be used to iterate through the list of all open channels.
375  * See <link linkend="chimara-Iterating-Through-Opaque-Objects">Iterating 
376  * Through Opaque Objects</link>.
377  *
378  * As that section describes, the order in which channels are returned is 
379  * arbitrary.
380  *
381  * Returns: the next sound channel, or %NULL if there are no more.
382  */
383 schanid_t 
384 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
385 {
386         VALID_SCHANNEL_OR_NULL(chan, return NULL);
387
388 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
389         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
390         GList *retnode;
391         
392         if(chan == NULL)
393                 retnode = glk_data->schannel_list;
394         else
395                 retnode = chan->schannel_list->next;
396         schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
397                 
398         /* Store the sound channel's rock in rockptr */
399         if(retval && rockptr)
400                 *rockptr = glk_schannel_get_rock(retval);
401                 
402         return retval;
403 #else
404         return NULL;
405 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
406 }
407
408 /**
409  * glk_schannel_get_rock:
410  * @chan: A sound channel.
411  * 
412  * Retrieves the channel's rock value. See <link 
413  * linkend="chimara-Rocks">Rocks</link>.
414  *
415  * Returns: A rock value.
416  */
417 glui32 
418 glk_schannel_get_rock(schanid_t chan)
419 {
420         VALID_SCHANNEL(chan, return 0);
421         return chan->rock;
422 }
423
424 /**
425  * glk_schannel_play:
426  * @chan: Channel to play the sound in.
427  * @snd: Resource number of the sound to play.
428  *
429  * Begins playing the given sound on the channel. If the channel was already
430  * playing a sound (even the same one), the old sound is stopped (with no
431  * notification event.
432  *
433  * This returns 1 if the sound actually started playing, and 0 if there was any
434  * problem.
435  * <note><para>
436  *   The most obvious problem is if there is no sound resource with the given
437  *   identifier. But other problems can occur. For example, the MOD-playing 
438  *   facility in a library might be unable to handle two MODs at the same time,
439  *   in which case playing a MOD resource would fail if one was already playing.
440  * </para></note>
441  *
442  * Returns: 1 on success, 0 on failure.
443  */
444 glui32 
445 glk_schannel_play(schanid_t chan, glui32 snd)
446 {
447         return glk_schannel_play_ext(chan, snd, 1, 0);
448 }
449
450 /**
451  * glk_schannel_play_ext:
452  * @chan: Channel to play the sound in.
453  * @snd: Resource number of the sound to play.
454  * @repeats: Number of times to repeat the sound.
455  * @notify: If nonzero, requests a notification when the sound is finished.
456  *
457  * This works the same as glk_schannel_play(), but lets you specify additional 
458  * options. <code>glk_schannel_play(chan, snd)</code> is exactly equivalent to 
459  * <code>glk_schannel_play_ext(chan, snd, 1, 0)</code>.
460  * 
461  * The @repeats value is the number of times the sound should be repeated. A 
462  * repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should repeat 
463  * forever. A repeat value of 0 means that the sound will not be played at all; 
464  * nothing happens. (Although a previous sound on the channel will be stopped, 
465  * and the function will return 1.)
466  * 
467  * The @notify value should be nonzero in order to request a sound notification
468  * event. If you do this, when the sound is completed, you will get an event 
469  * with type %evtype_SoundNotify. The @window will be %NULL, @val1 will be the 
470  * sound's resource id, and @val2 will be the nonzero value you passed as 
471  * @notify.
472  * 
473  * If you request sound notification, and the repeat value is greater than one, 
474  * you will get the event only after the last repetition. If the repeat value is
475  * 0 or -1, you will never get a notification event at all. Similarly, if the 
476  * sound is stopped or interrupted, or if the channel is destroyed while the 
477  * sound is playing, there will be no notification event.
478  *
479  * Not all libraries support sound notification. You should test the
480  * %gestalt_Sound2 selector before you rely on it; see <link
481  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound 
482  * Capabilities</link>.
483  *
484  * Note that you can play a sound on a channel whose volume is zero. This has
485  * no audible result, unless you later change the volume; but it produces
486  * notifications as usual. You can also play a sound on a paused channel; the
487  * sound is paused immediately, and does not progress.
488  * 
489  * Returns: 1 on success, 0 on failure.
490  */
491 glui32 
492 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
493 {
494         VALID_SCHANNEL(chan, return 0);
495 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
496         /* Stop the previous sound */
497         clean_up_after_playing_sound(chan);
498
499         /* Don't play if repeats = 0 */
500         if(repeats == 0) {
501                 chan->repeats = 0;
502                 return 1;
503         }
504
505         GInputStream *stream = load_resource_into_giostream(snd);
506         if(stream == NULL)
507                 return 0;
508
509         chan->source = gst_element_factory_make("giostreamsrc", NULL);
510         g_object_set(chan->source, "stream", stream, NULL);
511         g_object_unref(stream); /* Now owned by GStreamer element */
512         gst_bin_add(GST_BIN(chan->pipeline), chan->source);
513         if(!gst_element_link(chan->source, chan->typefind)) {
514                 WARNING(_("Could not link GStreamer elements"));
515                 clean_up_after_playing_sound(chan);
516                 return 0;
517         }
518
519         chan->repeats = repeats;
520         chan->resource = snd;
521         chan->notify = notify;
522         
523         /* Play the sound; unless the channel is paused, then pause it instead */
524         if(!gst_element_set_state(chan->pipeline, chan->paused? GST_STATE_PAUSED : GST_STATE_PLAYING)) {
525                 WARNING_S(_("Could not set GstElement state to"), chan->paused? "PAUSED" : "PLAYING");
526                 clean_up_after_playing_sound(chan);
527                 return 0;
528         }
529         return 1;
530 #else
531         return 0;
532 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
533 }
534
535 /**
536  * glk_schannel_play_multi:
537  * @chanarray: Array of #schanid_t structures.
538  * @chancount: Length of @chanarray.
539  * @sndarray: Array of sound resource numbers.
540  * @soundcount: Length of @sndarray, must be equal to @chanarray.
541  * @notify: If nonzero, request a notification when each sound finishes.
542  *
543  * This works the same as glk_schannel_play_ext(), except that you can specify
544  * more than one sound. The channel references and sound resource numbers are
545  * given as two arrays, which must be the same length. The @notify argument
546  * applies to all the sounds; the repeats value for all the sounds is 1.
547  * 
548  * All the sounds will begin at exactly the same time.
549  * 
550  * This returns the number of sounds that began playing correctly. (This will be
551  * a number from 0 to @soundcount.)
552  *
553  * <note><para>
554  *   If the @notify argument is nonzero, you will get a separate sound
555  *   notification event as each sound finishes. They will all have the same
556  *   @val2 value.
557  * </para></note>
558  * <note><para>
559  *   Note that you have to supply @chancount and @soundcount as separate
560  *   arguments, even though they are required to be the same. This is an awkward
561  *   consequence of the way array arguments are dispatched in Glulx.
562  * </para></note>
563  * 
564  * Returns: The number of sounds that started playing correctly.
565  */
566 glui32
567 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount, glui32 *sndarray, glui32 soundcount, glui32 notify)
568 {
569         g_return_val_if_fail(chancount == soundcount, 0);
570         g_return_val_if_fail(chanarray != NULL || chancount == 0, 0);
571         g_return_val_if_fail(sndarray != NULL || soundcount == 0, 0);
572
573         int count;
574         for(count = 0; count < chancount; count++)
575                 VALID_SCHANNEL(chanarray[count], return 0);
576
577 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
578         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
579         if(!glk_data->resource_map && !glk_data->resource_load_callback) {
580                 WARNING(_("No resource map has been loaded yet."));
581                 return 0;
582         }
583
584         /* We keep an array of sounds to skip if any of them have errors */
585         gboolean *skiparray = g_new0(gboolean, chancount);
586
587         /* Set up all the channels one by one */
588         for(count = 0; count < chancount; count++) {
589                 /* Stop the previous sound */
590                 clean_up_after_playing_sound(chanarray[count]);
591
592                 GInputStream *stream = load_resource_into_giostream(sndarray[count]);
593                 if(stream == NULL) {
594                         skiparray[count] = TRUE;
595                         continue;
596                 }
597
598                 chanarray[count]->source = gst_element_factory_make("giostreamsrc", NULL);
599                 g_object_set(chanarray[count]->source, "stream", stream, NULL);
600                 g_object_unref(stream); /* Now owned by GStreamer element */
601                 gst_bin_add(GST_BIN(chanarray[count]->pipeline), chanarray[count]->source);
602                 if(!gst_element_link(chanarray[count]->source, chanarray[count]->typefind)) {
603                         WARNING(_("Could not link GStreamer elements"));
604                         clean_up_after_playing_sound(chanarray[count]);
605                 }
606
607                 chanarray[count]->repeats = 1;
608                 chanarray[count]->resource = sndarray[count];
609                 chanarray[count]->notify = notify;
610         }
611
612         /* Start all the sounds as close to each other as possible. */
613         /* FIXME: Is there a way to start them exactly at the same time? */
614         glui32 successes = 0;
615         for(count = 0; count < chancount; count++) {
616                 if(skiparray[count])
617                         continue;
618                 /* Play the sound; unless the channel is paused, then pause it instead */
619                 if(!gst_element_set_state(chanarray[count]->pipeline, chanarray[count]->paused? GST_STATE_PAUSED : GST_STATE_PLAYING)) {
620                         WARNING_S(_("Could not set GstElement state to"), chanarray[count]->paused? "PAUSED" : "PLAYING");
621                         skiparray[count] = TRUE;
622                         clean_up_after_playing_sound(chanarray[count]);
623                         continue;
624                 }
625                 successes++;
626         }
627         g_free(skiparray);
628         return successes;
629 #else
630         return 0;
631 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
632 }
633
634 /**
635  * glk_schannel_stop:
636  * @chan: Channel to silence.
637  *
638  * Stops any sound playing in the channel. No notification event is generated,
639  * even if you requested one. If no sound is playing, this has no effect.
640  */
641 void 
642 glk_schannel_stop(schanid_t chan)
643 {
644         VALID_SCHANNEL(chan, return);
645 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
646         clean_up_after_playing_sound(chan);
647 #endif
648 }
649
650 /**
651  * glk_schannel_pause:
652  * @chan: Channel to pause.
653  *
654  * Pause any sound playing in the channel. This does not generate any
655  * notification events. If the channel is already paused, this does nothing.
656  * 
657  * New sounds started in a paused channel are paused immediately.
658  * 
659  * A volume change in progress is <emphasis>not</emphasis> paused, and may
660  * proceed to completion, generating a notification if appropriate.
661  */
662 void
663 glk_schannel_pause(schanid_t chan)
664 {
665         VALID_SCHANNEL(chan, return);
666
667         if(chan->paused)
668                 return; /* Silently do nothing */
669
670         /* Mark the channel as paused even if there is no sound playing yet */
671         chan->paused = TRUE;
672
673 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
674         GstState state;
675         if(gst_element_get_state(chan->pipeline, &state, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS) {
676                 WARNING(_("Could not get GstElement state"));
677                 return;
678         }
679         if(state != GST_STATE_PLAYING)
680                 return; /* Silently do nothing if no sound is playing */
681
682         if(!gst_element_set_state(chan->pipeline, GST_STATE_PAUSED)) {
683                 WARNING_S(_("Could not set GstElement state to"), "PAUSED");
684                 return;
685         }
686 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
687 }
688
689 /**
690  * glk_schannel_unpause:
691  * @chan: Channel to unpause.
692  *
693  * Unpause the channel. Any paused sounds begin playing where they left off. If
694  * the channel is not already paused, this does nothing.
695  *
696  * <note><para>
697  *   This means, for example, that you can pause a channel that is currently
698  *   not playing any sounds. If you then add a sound to the channel, it will
699  *   not start playing; it will be paused at its beginning. If you later
700  *   unpaise the channel, the sound will commence.
701  * </para></note>
702  */
703 void
704 glk_schannel_unpause(schanid_t chan)
705 {
706         VALID_SCHANNEL(chan, return);
707
708         if(!chan->paused)
709                 return; /* Silently do nothing */
710
711         /* Mark the channel as not paused in any case */
712         chan->paused = FALSE;
713
714 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
715         GstState state;
716         if(gst_element_get_state(chan->pipeline, &state, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS) {
717                 WARNING(_("Could not get GstElement state"));
718                 return;
719         }
720         if(state != GST_STATE_PAUSED)
721                 return; /* Silently do nothing */
722
723         if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
724                 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
725                 return;
726         }
727 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
728 }
729
730 /**
731  * glk_schannel_set_volume:
732  * @chan: Channel to set the volume of.
733  * @vol: Integer representing the volume; 0x10000 is 100&percnt;.
734  *
735  * Sets the volume in the channel, from 0 (silence) to 0x10000 (full volume).
736  * Again, you can overdrive the volume by setting a value greater than 0x10000,
737  * but this is not recommended.
738  *
739  * The glk_schannel_set_volume() function does not include duration and notify
740  * values. Both are assumed to be zero: immediate change, no notification.
741  *
742  * You can call this function between sounds, or while a sound is playing.
743  * However, a zero-duration change while a sound is playing may produce
744  * unpleasant clicks.
745  * 
746  * At most one volume change can be occurring on a sound channel at any time.
747  * If you call this function while a previous volume change is in progress, the
748  * previous change is interrupted.
749  *
750  * Not all libraries support this function. You should test the
751  * %gestalt_SoundVolume selector before you rely on it; see <link
752  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
753  * Capabilities</link>.
754  *
755  * <note><title>Chimara</title>
756  *   <para>Chimara supports volumes from 0 to 1000&percnt;, that is, values of
757  *   @vol up to 0xA0000.</para>
758  * </note>
759  */
760 void 
761 glk_schannel_set_volume(schanid_t chan, glui32 vol)
762 {
763         glk_schannel_set_volume_ext(chan, vol, 0, 0);
764 }
765
766 static double
767 volume_glk_to_gstreamer(glui32 volume_glk)
768 {
769         return CLAMP(((double)volume_glk / 0x10000), 0.0, 10.0);
770 }
771
772 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
773 static gboolean
774 volume_change_timeout(schanid_t chan)
775 {
776         GTimeVal now;
777         g_get_current_time(&now);
778
779         if(now.tv_sec >= chan->target_time_sec && now.tv_usec >= chan->target_time_usec) {
780                 /* We're done - make sure the volume is at the requested level */
781                 g_object_set(chan->filter, "volume", chan->target_volume, NULL);
782
783                 if(chan->volume_notify)
784                         event_throw(chan->glk, evtype_VolumeNotify, NULL, 0, chan->volume_notify);
785
786                 chan->volume_timer_id = 0;
787                 return FALSE;
788         }
789
790         /* Calculate the appropriate step every time - a busy system may delay or
791          * drop timer ticks */
792         double time_left_msec = (chan->target_time_sec - now.tv_sec) * 1000.0
793                 + (chan->target_time_usec - now.tv_usec) / 1000.0;
794         double steps_left = time_left_msec / VOLUME_TIMER_RESOLUTION;
795         double current_volume;
796         g_object_get(chan->filter, "volume", &current_volume, NULL);
797         double volume_step = (chan->target_volume - current_volume) / steps_left;
798
799         g_object_set(chan->filter, "volume", current_volume + volume_step, NULL);
800
801         return TRUE;
802 }
803 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
804
805 /**
806  * glk_schannel_set_volume_ext:
807  * @chan: Channel to set the volume of.
808  * @vol: Integer representing the volume; 0x10000 is 100&percnt;.
809  * @duration: Length of volume change in milliseconds, or 0 for immediate.
810  * @notify: If nonzero, requests a notification when the volume change finishes.
811  *
812  * Sets the volume in the channel, from 0 (silence) to 0x10000 (full volume).
813  * Again, you can overdrive the volume by setting a value greater than 0x10000,
814  * but this is not recommended.
815  *
816  * If the @duration is zero, the change is immediate. Otherwise, the change
817  * begins immediately, and occurs smoothly over the next @duration milliseconds.
818  *
819  * The @notify value should be nonzero in order to request a volume notification
820  * event. If you do this, when the volume change is completed, you will get an
821  * event with type #evtype_VolumeNotify. The window will be %NULL, @val1 will be
822  * zero, and @val2 will be the nonzero value you passed as @notify.
823  *
824  * You can call this function between sounds, or while a sound is playing.
825  * However, a zero-duration change while a sound is playing may produce
826  * unpleasant clicks.
827  *
828  * At most one volume change can be occurring on a sound channel at any time. If
829  * you call this function while a previous volume change is in progress, the
830  * previous change is interrupted. The beginning point of the new volume change
831  * should be wherever the previous volume change was interrupted (rather than
832  * the previous change's beginning or ending point).
833  *
834  * Not all libraries support these functions. You should test the appropriate
835  * gestalt selectors before you rely on them; see "Testing for Sound
836  * Capabilities".
837  */
838 void
839 glk_schannel_set_volume_ext(schanid_t chan, glui32 vol, glui32 duration, glui32 notify)
840 {
841         VALID_SCHANNEL(chan, return);
842         /* Silently ignore out-of-range volume values */
843
844 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
845         /* Interrupt a previous volume change */
846         if(chan->volume_timer_id > 0)
847                 g_source_remove(chan->volume_timer_id);
848         
849         double target_volume = volume_glk_to_gstreamer(vol);
850
851         if(duration == 0) {
852                 g_object_set(chan->filter, "volume", target_volume, NULL);
853
854                 if(notify != 0)
855                         event_throw(chan->glk, evtype_VolumeNotify, NULL, 0, notify);
856
857                 return;
858         }
859
860         GTimeVal target_time;
861         g_get_current_time(&target_time);
862         g_time_val_add(&target_time, (long)duration * 1000);
863
864         chan->target_volume = target_volume;
865         chan->target_time_sec = target_time.tv_sec;
866         chan->target_time_usec = target_time.tv_usec;
867         chan->volume_notify = notify;
868
869         /* Set up a timer for the volume */
870         chan->volume_timer_id = g_timeout_add(VOLUME_TIMER_RESOLUTION, (GSourceFunc)volume_change_timeout, chan);
871 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
872 }
873
874 /**
875  * glk_sound_load_hint:
876  * @snd: Resource number of a sound.
877  * @flag: Nonzero to tell the library to load the sound, zero to tell the
878  * library to unload it.
879  *
880  * This gives the library a hint about whether the given sound should be loaded
881  * or not. If the @flag is nonzero, the library may preload the sound or do
882  * other initialization, so that glk_schannel_play() will be faster. If the
883  * @flag is zero, the library may release memory or other resources associated
884  * with the sound. Calling this function is always optional, and it has no
885  * effect on what the library actually plays.
886  */
887 void 
888 glk_sound_load_hint(glui32 snd, glui32 flag)
889 {
890 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
891         ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
892         giblorb_result_t resource;
893         giblorb_err_t result;
894
895         /* Sound load hints only work for Blorb resource maps */
896         if(!glk_data->resource_map)
897                 return;
898
899         if(flag) {
900                 /* The sound load hint simply loads the resource from the resource map;
901                  loading a chunk more than once does nothing */
902                 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
903                 if(result != giblorb_err_None) {
904                         WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
905                         return;
906                 }
907         } else {
908                 /* Get the Blorb chunk number by loading the resource with
909                  method_DontLoad, then unload that chunk - has no effect if the chunk
910                  isn't loaded */
911                 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_DontLoad, &resource, giblorb_ID_Snd, snd);
912                 if(result != giblorb_err_None) {
913                         WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
914                         return;
915                 }
916                 result = giblorb_unload_chunk(glk_data->resource_map, resource.chunknum);
917                 if(result != giblorb_err_None) {
918                         WARNING_S( _("Error unloading chunk"), giblorb_get_error_message(result) );
919                         return;
920                 }
921         }
922 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
923 }