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