Implemented sound repeats
[projects/chimara/chimara.git] / libchimara / schannel.c
1 #include <config.h>
2 #include <glib.h>
3 #include <glib/gi18n.h>
4 #include <libchimara/glk.h>
5 #ifdef GSTREAMER_SOUND
6 #include <gst/gst.h>
7 #endif
8 #include "magic.h"
9 #include "schannel.h"
10 #include "chimara-glk-private.h"
11 #include "gi_dispa.h"
12 #include "gi_blorb.h"
13 #include "resource.h"
14
15 extern GPrivate *glk_data_key;
16
17 #ifdef GSTREAMER_SOUND
18 /* Stop any currently playing sound on this channel, and remove any
19  format-specific GStreamer elements from the channel. */
20 static void
21 clean_up_after_playing_sound(schanid_t chan)
22 {
23         if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
24                 WARNING_S(_("Could not set GstElement state to"), "NULL");
25         if(chan->demux)
26         {
27                 gst_bin_remove(GST_BIN(chan->pipeline), chan->demux);
28                 chan->demux = NULL;
29         }
30         if(chan->decode)
31         {
32                 gst_bin_remove(GST_BIN(chan->pipeline), chan->decode);
33                 chan->decode = NULL;
34         }
35 }
36
37 /* This signal is thrown whenever the GStreamer pipeline generates a message.
38  Most messages are harmless. */
39 static void
40 on_pipeline_message(GstBus *bus, GstMessage *message, schanid_t s)
41 {
42         /* g_printerr("Got %s message\n", GST_MESSAGE_TYPE_NAME(message)); */
43
44         GError *err;
45         gchar *debug_message;
46         
47         switch(GST_MESSAGE_TYPE(message)) {
48         case GST_MESSAGE_ERROR: 
49         {
50                 gst_message_parse_error(message, &err, &debug_message);
51                 IO_WARNING(_("GStreamer error"), err->message, debug_message);
52                 g_error_free(err);
53                 g_free(debug_message);
54                 clean_up_after_playing_sound(s);
55         }
56                 break;
57         case GST_MESSAGE_WARNING:
58         {
59                 gst_message_parse_warning(message, &err, &debug_message);
60                 IO_WARNING(_("GStreamer warning"), err->message, debug_message);
61                 g_error_free(err);
62                 g_free(debug_message);
63         }
64                 break;
65         case GST_MESSAGE_INFO:
66         {
67                 gst_message_parse_info(message, &err, &debug_message);
68                 g_message("GStreamer info \"%s\": %s", err->message, debug_message);
69                 g_error_free(err);
70                 g_free(debug_message);
71         }
72                 break;
73         case GST_MESSAGE_EOS: /* End of stream */
74                 /* Decrease repeats if not set to forever */
75                 if(s->repeats != (glui32)-1)
76                         s->repeats--;
77                 if(s->repeats > 0) {
78                         if(!gst_element_seek_simple(s->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 0)) {
79                                 WARNING(_("Could not execute GStreamer seek"));
80                                 clean_up_after_playing_sound(s);
81                         }
82                 } else {
83                         clean_up_after_playing_sound(s);
84                 }
85                 break;
86         default:
87                 /* unhandled message */
88                 break;
89         }
90 }
91
92 /* This signal is thrown when the OGG demuxer element has decided what kind of
93  outputs it will output. We connect the decoder element dynamically. */
94 static void
95 on_ogg_demuxer_pad_added(GstElement *demux, GstPad *pad, schanid_t s)
96 {
97         GstPad *sinkpad;
98         
99         /* We can now link this pad with the vorbis-decoder sink pad */
100         sinkpad = gst_element_get_static_pad(s->decode, "sink");
101         if(gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK)
102                 WARNING(_("Could not link OGG demuxer with Vorbis decoder"));
103         gst_object_unref(sinkpad);
104 }
105
106 /* This signal is thrown when the typefinder element has found the type of its
107  input. Now that we know what kind of input stream we have, we can connect the
108  proper demuxer/decoder elements. */
109 static void
110 on_type_found(GstElement *typefind, guint probability, GstCaps *caps, schanid_t s)
111 {
112         gchar *type = gst_caps_to_string(caps);
113         if(strcmp(type, "application/ogg") == 0) 
114         {
115                 s->demux = gst_element_factory_make("oggdemux", NULL);
116                 s->decode = gst_element_factory_make("vorbisdec", NULL);
117                 if(!s->demux || !s->decode)
118                 {
119                         WARNING(_("Could not create one or more GStreamer elements"));
120                         goto finally;
121                 }
122                 gst_bin_add_many(GST_BIN(s->pipeline), s->demux, s->decode, NULL);
123                 if(!gst_element_link(s->typefind, s->demux) || !gst_element_link(s->decode, s->convert))
124                 {
125                         WARNING(_("Could not link GStreamer elements"));
126                         goto finally;
127                 }
128                 /* We link the demuxer and decoder together dynamically, since the
129                  demuxer doesn't know what source pads it will have until it starts
130                  demuxing the stream */
131                 g_signal_connect(s->demux, "pad-added", G_CALLBACK(on_ogg_demuxer_pad_added), s);
132         }
133         else if(strcmp(type, "audio/x-aiff") == 0)
134         {
135                 s->decode = gst_element_factory_make("aiffparse", NULL);
136                 if(!s->decode)
137                 {
138                         WARNING(_("Could not create 'aiffparse' GStreamer element"));
139                         goto finally;
140                 }
141                 gst_bin_add(GST_BIN(s->pipeline), s->decode);
142                 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL))
143                 {
144                         WARNING(_("Could not link GStreamer elements"));
145                         goto finally;
146                 }
147         }
148         else
149         {
150                 WARNING_S(_("Unexpected audio type in blorb"), type);
151         }
152
153 finally:
154         g_free(type);
155 }
156 #endif /* GSTREAMER_SOUND */
157
158 /**
159  * glk_schannel_create:
160  * @rock: The rock value to give the new sound channel.
161  *
162  * This creates a sound channel, about as you'd expect.
163  *
164  * Remember that it is possible that the library will be unable to create a new
165  * channel, in which case glk_schannel_create() will return %NULL.
166  *
167  * Returns: A new sound channel, or %NULL.
168  */
169 schanid_t 
170 glk_schannel_create(glui32 rock)
171 {
172 #ifdef GSTREAMER_SOUND
173         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
174
175         schanid_t s = g_new0(struct glk_schannel_struct, 1);
176         s->magic = MAGIC_SCHANNEL;
177         s->rock = rock;
178         if(glk_data->register_obj)
179                 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
180
181         /* Add it to the global sound channel list */
182         glk_data->schannel_list = g_list_prepend(glk_data->schannel_list, s);
183         s->schannel_list = glk_data->schannel_list;
184
185         /* Create a GStreamer pipeline for the sound channel */
186         gchar *pipeline_name = g_strdup_printf("pipeline-%p", s);
187         s->pipeline = gst_pipeline_new(pipeline_name);
188         g_free(pipeline_name);
189
190         /* Watch for messages from the pipeline */
191         GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(s->pipeline));
192         gst_bus_add_signal_watch(bus);
193         g_signal_connect(bus, "message", G_CALLBACK(on_pipeline_message), s);
194         gst_object_unref(bus);
195
196         /* Create GStreamer elements to put in the pipeline */
197         s->source = gst_element_factory_make("giostreamsrc", NULL);
198         s->typefind = gst_element_factory_make("typefind", NULL);
199         s->convert = gst_element_factory_make("audioconvert", NULL);
200         s->filter = gst_element_factory_make("volume", NULL);
201         s->sink = gst_element_factory_make("autoaudiosink", NULL);
202         if(!s->source || !s->typefind || !s->convert || !s->filter || !s->sink) {
203                 WARNING(_("Could not create one or more GStreamer elements"));
204                 goto fail;
205         }
206
207         /* Put the elements in the pipeline and link as many together as we can
208          without knowing the type of the audio stream */
209         gst_bin_add_many(GST_BIN(s->pipeline), s->source, s->typefind, s->convert, s->filter, s->sink, NULL);
210         /* Link elements: Source -> typefinder -> ??? -> Converter -> Volume filter -> Sink */
211         if(!gst_element_link(s->source, s->typefind) || !gst_element_link_many(s->convert, s->filter, s->sink, NULL)) {
212                 WARNING(_("Could not link GStreamer elements"));
213                 goto fail;
214         }
215         g_signal_connect(s->typefind, "have-type", G_CALLBACK(on_type_found), s);
216         
217         return s;
218
219 fail:
220         glk_schannel_destroy(s);
221         return NULL;
222 #else
223         return NULL;
224 #endif /* GSTREAMER_SOUND */
225 }
226
227 /**
228  * glk_schannel_destroy:
229  * @chan: The sound channel to destroy.
230  *
231  * Destroys the channel. If the channel is playing a sound, the sound stops 
232  * immediately (with no notification event).
233  */
234 void 
235 glk_schannel_destroy(schanid_t chan)
236 {
237         VALID_SCHANNEL(chan, return);
238
239 #ifdef GSTREAMER_SOUND
240         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
241
242         if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
243                 WARNING_S(_("Could not set GstElement state to"), "NULL");
244         
245         glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
246
247         if(glk_data->unregister_obj)
248         {
249                 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
250                 chan->disprock.ptr = NULL;
251         }
252
253         /* This also frees all the objects inside the pipeline */
254         if(chan->pipeline)
255                 gst_object_unref(chan->pipeline);
256         
257         chan->magic = MAGIC_FREE;
258         g_free(chan);
259 #endif
260 }
261
262 /**
263  * glk_schannel_iterate:
264  * @chan: A sound channel, or %NULL.
265  * @rockptr: Return location for the next sound channel's rock, or %NULL.
266  *
267  * This function can be used to iterate through the list of all open channels.
268  * See <link linkend="chimara-Iterating-Through-Opaque-Objects">Iterating 
269  * Through Opaque Objects</link>.
270  *
271  * As that section describes, the order in which channels are returned is 
272  * arbitrary.
273  *
274  * Returns: the next sound channel, or %NULL if there are no more.
275  */
276 schanid_t 
277 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
278 {
279         VALID_SCHANNEL_OR_NULL(chan, return NULL);
280
281 #ifdef GSTREAMER_SOUND
282         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
283         GList *retnode;
284         
285         if(chan == NULL)
286                 retnode = glk_data->schannel_list;
287         else
288                 retnode = chan->schannel_list->next;
289         schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
290                 
291         /* Store the sound channel's rock in rockptr */
292         if(retval && rockptr)
293                 *rockptr = glk_schannel_get_rock(retval);
294                 
295         return retval;
296 #else
297         return NULL;
298 #endif /* GSTREAMER_SOUND */
299 }
300
301 /**
302  * glk_schannel_get_rock:
303  * @chan: A sound channel.
304  * 
305  * Retrieves the channel's rock value. See <link 
306  * linkend="chimara-Rocks">Rocks</link>.
307  *
308  * Returns: A rock value.
309  */
310 glui32 
311 glk_schannel_get_rock(schanid_t chan)
312 {
313         VALID_SCHANNEL(chan, return 0);
314         return chan->rock;
315 }
316
317 /**
318  * glk_schannel_play:
319  * @chan: Channel to play the sound in.
320  * @snd: Resource number of the sound to play.
321  *
322  * Begins playing the given sound on the channel. If the channel was already
323  * playing a sound (even the same one), the old sound is stopped (with no
324  * notification event.
325  *
326  * This returns 1 if the sound actually started playing, and 0 if there was any
327  * problem.
328  * <note><para>
329  *   The most obvious problem is if there is no sound resource with the given
330  *   identifier. But other problems can occur. For example, the MOD-playing 
331  *   facility in a library might be unable to handle two MODs at the same time,
332  *   in which case playing a MOD resource would fail if one was already playing.
333  * </para></note>
334  *
335  * Returns: 1 on success, 0 on failure.
336  */
337 glui32 
338 glk_schannel_play(schanid_t chan, glui32 snd)
339 {
340         return glk_schannel_play_ext(chan, snd, 1, 0);
341 }
342
343 /**
344  * glk_schannel_play_ext:
345  * @chan: Channel to play the sound in.
346  * @snd: Resource number of the sound to play.
347  * @repeats: Number of times to repeat the sound.
348  * @notify: If nonzero, requests a notification when the sound is finished.
349  *
350  * This works the same as glk_schannel_play(), but lets you specify additional 
351  * options. <code>glk_schannel_play(chan, snd)</code> is exactly equivalent to 
352  * <code>glk_schannel_play_ext(chan, snd, 1, 0)</code>.
353  * 
354  * The @repeats value is the number of times the sound should be repeated. A 
355  * repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should repeat 
356  * forever. A repeat value of 0 means that the sound will not be played at all; 
357  * nothing happens. (Although a previous sound on the channel will be stopped, 
358  * and the function will return 1.)
359  * 
360  * The @notify value should be nonzero in order to request a sound notification
361  * event. If you do this, when the sound is completed, you will get an event 
362  * with type %evtype_SoundNotify. The @window will be %NULL, @val1 will be the 
363  * sound's resource id, and @val2 will be the nonzero value you passed as 
364  * @notify.
365  * 
366  * If you request sound notification, and the repeat value is greater than one, 
367  * you will get the event only after the last repetition. If the repeat value is
368  * 0 or -1, you will never get a notification event at all. Similarly, if the 
369  * sound is stopped or interrupted, or if the channel is destroyed while the 
370  * sound is playing, there will be no notification event.
371  *
372  * Not all libraries support sound notification. You should test the
373  * %gestalt_SoundNotify selector before you rely on it; see <link
374  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound 
375  * Capabilities</link>.
376  * 
377  * Returns: 1 on success, 0 on failure.
378  */
379 glui32 
380 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
381 {
382         VALID_SCHANNEL(chan, return 0);
383         g_printerr("Play sound %d with repeats %d and notify %d\n", snd, repeats, notify);
384 #ifdef GSTREAMER_SOUND
385         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
386         GInputStream *stream;
387
388         /* Stop the previous sound */
389         clean_up_after_playing_sound(chan);
390
391         /* Don't play if repeats = 0 */
392         if(repeats == 0) {
393                 chan->repeats = 0;
394                 return 1;
395         }
396
397         /* Load the sound into a GInputStream, by whatever method */
398         if(!glk_data->resource_map) {
399                 if(!glk_data->resource_load_callback) {
400                         WARNING(_("No resource map has been loaded yet."));
401                         return 0;
402                 }
403                 WARNING(_("Loading sound resources from alternative location not yet supported."));
404                 return 0;
405         } else {
406                 giblorb_result_t resource;
407                 giblorb_err_t result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
408                 if(result != giblorb_err_None) {
409                         WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
410                         return 0;
411                 }
412                 stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
413         }
414
415         chan->repeats = repeats;
416         g_object_set(chan->source, "stream", stream, NULL);
417         
418         if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
419                 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
420                 return 0;
421         }
422         return 1;
423 #else
424         return 0;
425 #endif
426 }
427
428 /**
429  * glk_schannel_stop:
430  * @chan: Channel to silence.
431  *
432  * Stops any sound playing in the channel. No notification event is generated,
433  * even if you requested one. If no sound is playing, this has no effect.
434  */
435 void 
436 glk_schannel_stop(schanid_t chan)
437 {
438         VALID_SCHANNEL(chan, return);
439 #ifdef GSTREAMER_SOUND
440         clean_up_after_playing_sound(chan);
441 #endif
442 }
443
444 /**
445  * glk_schannel_set_volume:
446  * @chan: Channel to set the volume of.
447  * @vol: Integer representing the volume; 0x10000 is 100&percnt;.
448  *
449  * Sets the volume in the channel. When you create a channel, it has full 
450  * volume, represented by the value 0x10000. Half volume would be 0x8000, 
451  * three-quarters volume would be 0xC000, and so on. A volume of zero represents
452  * silence, although the sound is still considered to be playing.
453  *
454  * You can call this function between sounds, or while a sound is playing. The 
455  * effect is immediate.
456  * 
457  * You can overdrive the volume of a channel by setting a volume greater than 
458  * 0x10000. However, this is not recommended; the library may be unable to 
459  * increase the volume past full, or the sound may become distorted. You should 
460  * always create sound resources with the maximum volume you will need, and then
461  * call glk_schannel_set_volume() to reduce the volume when appropriate.
462  *
463  * Not all libraries support this function. You should test the
464  * %gestalt_SoundVolume selector before you rely on it; see <link
465  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
466  * Capabilities</link>.
467  *
468  * <note><title>Chimara</title>
469  *   <para>Chimara supports volumes from 0 to 1000&percnt;, that is, values of
470  *   @vol up to 0xA0000.</para>
471  * </note>
472  */
473 void 
474 glk_schannel_set_volume(schanid_t chan, glui32 vol)
475 {
476         VALID_SCHANNEL(chan, return);
477 #ifdef GSTREAMER_SOUND
478         gdouble volume_gst = (gdouble)vol / 0x10000;
479         g_printerr("Volume set to: %f\n", volume_gst);
480         g_object_set(chan->filter, "volume", CLAMP(volume_gst, 0.0, 10.0), NULL);
481 #endif
482 }
483
484 /**
485  * glk_sound_load_hint:
486  * @snd: Resource number of a sound.
487  * @flag: Nonzero to tell the library to load the sound, zero to tell the
488  * library to unload it.
489  *
490  * This gives the library a hint about whether the given sound should be loaded
491  * or not. If the @flag is nonzero, the library may preload the sound or do
492  * other initialization, so that glk_schannel_play() will be faster. If the
493  * @flag is zero, the library may release memory or other resources associated
494  * with the sound. Calling this function is always optional, and it has no
495  * effect on what the library actually plays.
496  */
497 void 
498 glk_sound_load_hint(glui32 snd, glui32 flag)
499 {
500 #ifdef GSTREAMER_SOUND
501         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
502         giblorb_result_t resource;
503         giblorb_err_t result;
504
505         /* Sound load hints only work for Blorb resource maps */
506         if(!glk_data->resource_map)
507                 return;
508
509         if(flag) {
510                 /* The sound load hint simply loads the resource from the resource map;
511                  loading a chunk more than once does nothing */
512                 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
513                 if(result != giblorb_err_None) {
514                         WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
515                         return;
516                 }
517         } else {
518                 /* Get the Blorb chunk number by loading the resource with
519                  method_DontLoad, then unload that chunk - has no effect if the chunk
520                  isn't loaded */
521                 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_DontLoad, &resource, giblorb_ID_Snd, snd);
522                 if(result != giblorb_err_None) {
523                         WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
524                         return;
525                 }
526                 result = giblorb_unload_chunk(glk_data->resource_map, resource.chunknum);
527                 if(result != giblorb_err_None) {
528                         WARNING_S( _("Error unloading chunk"), giblorb_get_error_message(result) );
529                         return;
530                 }
531         }
532 #endif /* GSTREAMER_SOUND */
533 }