3 #include <glib/gi18n-lib.h>
4 #include <libchimara/glk.h>
10 #include "chimara-glk-private.h"
16 extern GPrivate *glk_data_key;
18 #ifdef GSTREAMER_SOUND
19 /* Stop any currently playing sound on this channel, and remove any
20 format-specific GStreamer elements from the channel. */
22 clean_up_after_playing_sound(schanid_t chan)
24 if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
25 WARNING_S(_("Could not set GstElement state to"), "NULL");
28 gst_bin_remove(GST_BIN(chan->pipeline), chan->demux);
33 gst_bin_remove(GST_BIN(chan->pipeline), chan->decode);
38 /* This signal is thrown whenever the GStreamer pipeline generates a message.
39 Most messages are harmless. */
41 on_pipeline_message(GstBus *bus, GstMessage *message, schanid_t s)
43 /* g_printerr("Got %s message\n", GST_MESSAGE_TYPE_NAME(message)); */
48 switch(GST_MESSAGE_TYPE(message)) {
49 case GST_MESSAGE_ERROR:
51 gst_message_parse_error(message, &err, &debug_message);
52 IO_WARNING(_("GStreamer error"), err->message, debug_message);
54 g_free(debug_message);
55 clean_up_after_playing_sound(s);
58 case GST_MESSAGE_WARNING:
60 gst_message_parse_warning(message, &err, &debug_message);
61 IO_WARNING(_("GStreamer warning"), err->message, debug_message);
63 g_free(debug_message);
66 case GST_MESSAGE_INFO:
68 gst_message_parse_info(message, &err, &debug_message);
69 g_message("GStreamer info \"%s\": %s", err->message, debug_message);
71 g_free(debug_message);
74 case GST_MESSAGE_EOS: /* End of stream */
75 /* Decrease repeats if not set to forever */
76 if(s->repeats != (glui32)-1)
79 if(!gst_element_seek_simple(s->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 0)) {
80 WARNING(_("Could not execute GStreamer seek"));
81 clean_up_after_playing_sound(s);
84 clean_up_after_playing_sound(s);
85 /* Sound ended normally, send a notification if requested */
87 event_throw(s->glk, evtype_SoundNotify, NULL, s->resource, s->notify);
91 /* unhandled message */
96 /* This signal is thrown when the OGG demuxer element has decided what kind of
97 outputs it will output. We connect the decoder element dynamically. */
99 on_ogg_demuxer_pad_added(GstElement *demux, GstPad *pad, schanid_t s)
103 /* We can now link this pad with the vorbis-decoder sink pad */
104 sinkpad = gst_element_get_static_pad(s->decode, "sink");
105 if(gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK)
106 WARNING(_("Could not link OGG demuxer with Vorbis decoder"));
107 gst_object_unref(sinkpad);
110 /* This signal is thrown when the typefinder element has found the type of its
111 input. Now that we know what kind of input stream we have, we can connect the
112 proper demuxer/decoder elements. */
114 on_type_found(GstElement *typefind, guint probability, GstCaps *caps, schanid_t s)
116 gchar *type = gst_caps_to_string(caps);
117 if(strcmp(type, "application/ogg") == 0) {
118 s->demux = gst_element_factory_make("oggdemux", NULL);
119 s->decode = gst_element_factory_make("vorbisdec", NULL);
120 if(!s->demux || !s->decode) {
121 WARNING(_("Could not create one or more GStreamer elements"));
124 gst_bin_add_many(GST_BIN(s->pipeline), s->demux, s->decode, NULL);
125 if(!gst_element_link(s->typefind, s->demux) || !gst_element_link(s->decode, s->convert)) {
126 WARNING(_("Could not link GStreamer elements"));
129 /* We link the demuxer and decoder together dynamically, since the
130 demuxer doesn't know what source pads it will have until it starts
131 demuxing the stream */
132 g_signal_connect(s->demux, "pad-added", G_CALLBACK(on_ogg_demuxer_pad_added), s);
133 } else if(strcmp(type, "audio/x-aiff") == 0) {
134 s->decode = gst_element_factory_make("aiffparse", NULL);
136 WARNING(_("Could not create 'aiffparse' GStreamer element"));
139 gst_bin_add(GST_BIN(s->pipeline), s->decode);
140 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL)) {
141 WARNING(_("Could not link GStreamer elements"));
144 } else if(strcmp(type, "audio/x-mod") == 0) {
145 s->decode = gst_element_factory_make("modplug", NULL);
147 WARNING(_("Could not create 'modplug' GStreamer element"));
150 gst_bin_add(GST_BIN(s->pipeline), s->decode);
151 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL)) {
152 WARNING(_("Could not link GStreamer elements"));
156 WARNING_S(_("Unexpected audio type in blorb"), type);
162 #endif /* GSTREAMER_SOUND */
165 * glk_schannel_create:
166 * @rock: The rock value to give the new sound channel.
168 * This creates a sound channel, about as you'd expect.
170 * Remember that it is possible that the library will be unable to create a new
171 * channel, in which case glk_schannel_create() will return %NULL.
173 * Returns: A new sound channel, or %NULL.
176 glk_schannel_create(glui32 rock)
178 #ifdef GSTREAMER_SOUND
179 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
181 schanid_t s = g_new0(struct glk_schannel_struct, 1);
182 s->magic = MAGIC_SCHANNEL;
184 if(glk_data->register_obj)
185 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
187 /* Add it to the global sound channel list */
188 glk_data->schannel_list = g_list_prepend(glk_data->schannel_list, s);
189 s->schannel_list = glk_data->schannel_list;
191 /* Add a pointer to the ChimaraGlk widget, for convenience */
192 s->glk = glk_data->self;
194 /* Create a GStreamer pipeline for the sound channel */
195 gchar *pipeline_name = g_strdup_printf("pipeline-%p", s);
196 s->pipeline = gst_pipeline_new(pipeline_name);
197 g_free(pipeline_name);
199 /* Watch for messages from the pipeline */
200 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(s->pipeline));
201 gst_bus_add_signal_watch(bus);
202 g_signal_connect(bus, "message", G_CALLBACK(on_pipeline_message), s);
203 gst_object_unref(bus);
205 /* Create GStreamer elements to put in the pipeline */
206 s->source = gst_element_factory_make("giostreamsrc", NULL);
207 s->typefind = gst_element_factory_make("typefind", NULL);
208 s->convert = gst_element_factory_make("audioconvert", NULL);
209 s->filter = gst_element_factory_make("volume", NULL);
210 s->sink = gst_element_factory_make("autoaudiosink", NULL);
211 if(!s->source || !s->typefind || !s->convert || !s->filter || !s->sink) {
212 WARNING(_("Could not create one or more GStreamer elements"));
216 /* Put the elements in the pipeline and link as many together as we can
217 without knowing the type of the audio stream */
218 gst_bin_add_many(GST_BIN(s->pipeline), s->source, s->typefind, s->convert, s->filter, s->sink, NULL);
219 /* Link elements: Source -> typefinder -> ??? -> Converter -> Volume filter -> Sink */
220 if(!gst_element_link(s->source, s->typefind) || !gst_element_link_many(s->convert, s->filter, s->sink, NULL)) {
221 WARNING(_("Could not link GStreamer elements"));
224 g_signal_connect(s->typefind, "have-type", G_CALLBACK(on_type_found), s);
229 glk_schannel_destroy(s);
233 #endif /* GSTREAMER_SOUND */
237 * glk_schannel_destroy:
238 * @chan: The sound channel to destroy.
240 * Destroys the channel. If the channel is playing a sound, the sound stops
241 * immediately (with no notification event).
244 glk_schannel_destroy(schanid_t chan)
246 VALID_SCHANNEL(chan, return);
248 #ifdef GSTREAMER_SOUND
249 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
251 if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
252 WARNING_S(_("Could not set GstElement state to"), "NULL");
254 glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
256 if(glk_data->unregister_obj)
258 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
259 chan->disprock.ptr = NULL;
262 /* This also frees all the objects inside the pipeline */
264 gst_object_unref(chan->pipeline);
266 chan->magic = MAGIC_FREE;
272 * glk_schannel_iterate:
273 * @chan: A sound channel, or %NULL.
274 * @rockptr: Return location for the next sound channel's rock, or %NULL.
276 * This function can be used to iterate through the list of all open channels.
277 * See <link linkend="chimara-Iterating-Through-Opaque-Objects">Iterating
278 * Through Opaque Objects</link>.
280 * As that section describes, the order in which channels are returned is
283 * Returns: the next sound channel, or %NULL if there are no more.
286 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
288 VALID_SCHANNEL_OR_NULL(chan, return NULL);
290 #ifdef GSTREAMER_SOUND
291 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
295 retnode = glk_data->schannel_list;
297 retnode = chan->schannel_list->next;
298 schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
300 /* Store the sound channel's rock in rockptr */
301 if(retval && rockptr)
302 *rockptr = glk_schannel_get_rock(retval);
307 #endif /* GSTREAMER_SOUND */
311 * glk_schannel_get_rock:
312 * @chan: A sound channel.
314 * Retrieves the channel's rock value. See <link
315 * linkend="chimara-Rocks">Rocks</link>.
317 * Returns: A rock value.
320 glk_schannel_get_rock(schanid_t chan)
322 VALID_SCHANNEL(chan, return 0);
328 * @chan: Channel to play the sound in.
329 * @snd: Resource number of the sound to play.
331 * Begins playing the given sound on the channel. If the channel was already
332 * playing a sound (even the same one), the old sound is stopped (with no
333 * notification event.
335 * This returns 1 if the sound actually started playing, and 0 if there was any
338 * The most obvious problem is if there is no sound resource with the given
339 * identifier. But other problems can occur. For example, the MOD-playing
340 * facility in a library might be unable to handle two MODs at the same time,
341 * in which case playing a MOD resource would fail if one was already playing.
344 * Returns: 1 on success, 0 on failure.
347 glk_schannel_play(schanid_t chan, glui32 snd)
349 return glk_schannel_play_ext(chan, snd, 1, 0);
353 * glk_schannel_play_ext:
354 * @chan: Channel to play the sound in.
355 * @snd: Resource number of the sound to play.
356 * @repeats: Number of times to repeat the sound.
357 * @notify: If nonzero, requests a notification when the sound is finished.
359 * This works the same as glk_schannel_play(), but lets you specify additional
360 * options. <code>glk_schannel_play(chan, snd)</code> is exactly equivalent to
361 * <code>glk_schannel_play_ext(chan, snd, 1, 0)</code>.
363 * The @repeats value is the number of times the sound should be repeated. A
364 * repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should repeat
365 * forever. A repeat value of 0 means that the sound will not be played at all;
366 * nothing happens. (Although a previous sound on the channel will be stopped,
367 * and the function will return 1.)
369 * The @notify value should be nonzero in order to request a sound notification
370 * event. If you do this, when the sound is completed, you will get an event
371 * with type %evtype_SoundNotify. The @window will be %NULL, @val1 will be the
372 * sound's resource id, and @val2 will be the nonzero value you passed as
375 * If you request sound notification, and the repeat value is greater than one,
376 * you will get the event only after the last repetition. If the repeat value is
377 * 0 or -1, you will never get a notification event at all. Similarly, if the
378 * sound is stopped or interrupted, or if the channel is destroyed while the
379 * sound is playing, there will be no notification event.
381 * Not all libraries support sound notification. You should test the
382 * %gestalt_SoundNotify selector before you rely on it; see <link
383 * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
384 * Capabilities</link>.
386 * Returns: 1 on success, 0 on failure.
389 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
391 VALID_SCHANNEL(chan, return 0);
392 g_printerr("Play sound %d with repeats %d and notify %d\n", snd, repeats, notify);
393 #ifdef GSTREAMER_SOUND
394 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
395 GInputStream *stream;
397 /* Stop the previous sound */
398 clean_up_after_playing_sound(chan);
400 /* Don't play if repeats = 0 */
406 /* Load the sound into a GInputStream, by whatever method */
407 if(!glk_data->resource_map) {
408 if(!glk_data->resource_load_callback) {
409 WARNING(_("No resource map has been loaded yet."));
412 gchar *filename = glk_data->resource_load_callback(CHIMARA_RESOURCE_SOUND, snd, glk_data->resource_load_callback_data);
414 WARNING(_("Error loading resource from alternative location."));
419 GFile *file = g_file_new_for_path(filename);
420 stream = G_INPUT_STREAM(g_file_read(file, NULL, &err));
422 IO_WARNING(_("Error loading resource from file"), filename, err->message);
424 g_object_unref(file);
428 g_object_unref(file);
430 giblorb_result_t resource;
431 giblorb_err_t result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
432 if(result != giblorb_err_None) {
433 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
436 stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
439 chan->repeats = repeats;
440 chan->resource = snd;
441 chan->notify = notify;
442 g_object_set(chan->source, "stream", stream, NULL);
443 g_object_unref(stream); /* Now owned by GStreamer element */
445 if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
446 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
456 * glk_schannel_play_multi:
465 * This works the same as glk_schannel_play_ext(), except that you can specify
466 * more than one sound. The channel references and sound resource numbers are
467 * given as two arrays, which must be the same length. The @notify argument
468 * applies to all the sounds; the repeats value for all the sounds is 1.
470 * All the sounds will begin at exactly the same time.
472 * This returns the number of sounds that began playing correctly. (This will be
473 * a number from 0 to @soundcount.)
476 * Note that you have to supply @chancount and @soundcount as separate
477 * arguments, even though they are required to be the same. This is an awkward
478 * consequence of the way array arguments are dispatched in Glulx.
481 * Returns: The number of sounds that started playing correctly.
484 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount, glui32 *sndarray, glui32 soundcount, glui32 notify)
486 g_return_val_if_fail(chancount == soundcount, 0);
487 g_return_val_if_fail(chanarray == NULL && chancount != 0, 0);
488 g_return_val_if_fail(sndarray == NULL && soundcount != 0, 0);
491 for(count = 0; count < chancount; count++)
492 VALID_SCHANNEL(chanarray[count], return 0);
494 g_warning("Not implemented");
500 * @chan: Channel to silence.
502 * Stops any sound playing in the channel. No notification event is generated,
503 * even if you requested one. If no sound is playing, this has no effect.
506 glk_schannel_stop(schanid_t chan)
508 VALID_SCHANNEL(chan, return);
509 #ifdef GSTREAMER_SOUND
510 clean_up_after_playing_sound(chan);
515 * glk_schannel_pause:
516 * @chan: Channel to pause.
520 * Pause any sound playing in the channel. This does not generate any
521 * notification events. If the channel is already paused, this does nothing.
523 * New sounds started in a paused channel are paused immediately.
525 * A volume change in progress is <emphasis>not</emphasis> paused, and may
526 * proceed to completion, generating a notification if appropriate.
529 glk_schannel_pause(schanid_t chan)
531 VALID_SCHANNEL(chan, return);
533 /* Not implemented */
537 * glk_schannel_unpause:
538 * @chan: Channel to unpause.
542 * Unpause the channel. Any paused sounds begin playing where they left off. If
543 * the channel is not already paused, this does nothing.
546 glk_schannel_unpause(schanid_t chan)
548 VALID_SCHANNEL(chan, return);
550 /* Not implemented */
554 * glk_schannel_set_volume:
555 * @chan: Channel to set the volume of.
556 * @vol: Integer representing the volume; 0x10000 is 100%.
558 * Sets the volume in the channel. When you create a channel, it has full
559 * volume, represented by the value 0x10000. Half volume would be 0x8000,
560 * three-quarters volume would be 0xC000, and so on. A volume of zero represents
561 * silence, although the sound is still considered to be playing.
563 * You can call this function between sounds, or while a sound is playing. The
564 * effect is immediate.
566 * You can overdrive the volume of a channel by setting a volume greater than
567 * 0x10000. However, this is not recommended; the library may be unable to
568 * increase the volume past full, or the sound may become distorted. You should
569 * always create sound resources with the maximum volume you will need, and then
570 * call glk_schannel_set_volume() to reduce the volume when appropriate.
572 * Not all libraries support this function. You should test the
573 * %gestalt_SoundVolume selector before you rely on it; see <link
574 * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
575 * Capabilities</link>.
577 * <note><title>Chimara</title>
578 * <para>Chimara supports volumes from 0 to 1000%, that is, values of
579 * @vol up to 0xA0000.</para>
583 glk_schannel_set_volume(schanid_t chan, glui32 vol)
585 glk_schannel_set_volume_ext(chan, vol, 0, 0);
589 * glk_schannel_set_volume_ext:
590 * @chan: Channel to set the volume of.
591 * @vol: Integer representing the volume; 0x10000 is 100%.
592 * @duration: Length of volume change in milliseconds, or 0 for immediate.
593 * @notify: If nonzero, requests a notification when the volume change finishes.
597 * Sets the volume in the channel, from 0 (silence) to 0x10000 (full volume).
598 * Again, you can overdrive the volume by setting a value greater than 0x10000,
599 * but this is not recommended.
601 * If the @duration is zero, the change is immediate. Otherwise, the change
602 * begins immediately, and occurs smoothly over the next @duration milliseconds.
604 * The @notify value should be nonzero in order to request a volume notification
605 * event. If you do this, when the volume change is completed, you will get an
606 * event with type #evtype_VolumeNotify. The window will be %NULL, @val1 will be
607 * zero, and @val2 will be the nonzero value you passed as @notify.
609 * The glk_schannel_set_volume() does not include @duration and @notify values.
610 * Both are assumed to be zero: immediate change, no notification.
612 * You can call these functions between sounds, or while a sound is playing.
613 * However, a zero-duration change while a sound is playing may produce
616 * At most one volume change can be occurring on a sound channel at any time. If
617 * you call one of these functions while a previous volume change is in
618 * progress, the previous change is interrupted. The beginning point of the new
619 * volume change should be wherever the previous volume change was interrupted
620 * (rather than the previous change's beginning or ending point).
622 * Not all libraries support thse functions. You should test the appropriate
623 * gestalt selectors before you rely on them; see "Testing for Sound
627 glk_schannel_set_volume_ext(schanid_t chan, glui32 vol, glui32 duration, glui32 notify)
629 VALID_SCHANNEL(chan, return);
630 /* Silently ignore out-of-range volume values */
632 #ifdef GSTREAMER_SOUND
633 gdouble volume_gst = (gdouble)vol / 0x10000;
634 g_printerr("Volume set to: %f\n", volume_gst);
635 g_object_set(chan->filter, "volume", CLAMP(volume_gst, 0.0, 10.0), NULL);
638 /* Not implemented */
642 * glk_sound_load_hint:
643 * @snd: Resource number of a sound.
644 * @flag: Nonzero to tell the library to load the sound, zero to tell the
645 * library to unload it.
647 * This gives the library a hint about whether the given sound should be loaded
648 * or not. If the @flag is nonzero, the library may preload the sound or do
649 * other initialization, so that glk_schannel_play() will be faster. If the
650 * @flag is zero, the library may release memory or other resources associated
651 * with the sound. Calling this function is always optional, and it has no
652 * effect on what the library actually plays.
655 glk_sound_load_hint(glui32 snd, glui32 flag)
657 #ifdef GSTREAMER_SOUND
658 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
659 giblorb_result_t resource;
660 giblorb_err_t result;
662 /* Sound load hints only work for Blorb resource maps */
663 if(!glk_data->resource_map)
667 /* The sound load hint simply loads the resource from the resource map;
668 loading a chunk more than once does nothing */
669 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
670 if(result != giblorb_err_None) {
671 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
675 /* Get the Blorb chunk number by loading the resource with
676 method_DontLoad, then unload that chunk - has no effect if the chunk
678 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_DontLoad, &resource, giblorb_ID_Snd, snd);
679 if(result != giblorb_err_None) {
680 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
683 result = giblorb_unload_chunk(glk_data->resource_map, resource.chunknum);
684 if(result != giblorb_err_None) {
685 WARNING_S( _("Error unloading chunk"), giblorb_get_error_message(result) );
689 #endif /* GSTREAMER_SOUND */