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