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