3 #include <glib/gi18n-lib.h>
4 #include <libchimara/glk.h>
7 #include <gst/controller/gstcontroller.h>
11 #include "chimara-glk-private.h"
17 extern GPrivate *glk_data_key;
19 #ifdef GSTREAMER_SOUND
20 /* Stop any currently playing sound on this channel, and remove any
21 format-specific GStreamer elements from the channel. */
23 clean_up_after_playing_sound(schanid_t chan)
25 if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
26 WARNING_S(_("Could not set GstElement state to"), "NULL");
29 gst_bin_remove(GST_BIN(chan->pipeline), chan->demux);
34 gst_bin_remove(GST_BIN(chan->pipeline), chan->decode);
39 /* This signal is thrown whenever the GStreamer pipeline generates a message.
40 Most messages are harmless. */
42 on_pipeline_message(GstBus *bus, GstMessage *message, schanid_t s)
44 /* g_printerr("Got %s message\n", GST_MESSAGE_TYPE_NAME(message)); */
49 switch(GST_MESSAGE_TYPE(message)) {
50 case GST_MESSAGE_ERROR:
52 gst_message_parse_error(message, &err, &debug_message);
53 IO_WARNING(_("GStreamer error"), err->message, debug_message);
55 g_free(debug_message);
56 clean_up_after_playing_sound(s);
59 case GST_MESSAGE_WARNING:
61 gst_message_parse_warning(message, &err, &debug_message);
62 IO_WARNING(_("GStreamer warning"), err->message, debug_message);
64 g_free(debug_message);
67 case GST_MESSAGE_INFO:
69 gst_message_parse_info(message, &err, &debug_message);
70 g_message("GStreamer info \"%s\": %s", err->message, debug_message);
72 g_free(debug_message);
75 case GST_MESSAGE_EOS: /* End of stream */
76 /* Decrease repeats if not set to forever */
77 if(s->repeats != (glui32)-1)
80 if(!gst_element_seek_simple(s->pipeline, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, 0)) {
81 WARNING(_("Could not execute GStreamer seek"));
82 clean_up_after_playing_sound(s);
85 clean_up_after_playing_sound(s);
86 /* Sound ended normally, send a notification if requested */
88 event_throw(s->glk, evtype_SoundNotify, NULL, s->resource, s->notify);
92 /* unhandled message */
97 /* This signal is thrown when the OGG demuxer element has decided what kind of
98 outputs it will output. We connect the decoder element dynamically. */
100 on_ogg_demuxer_pad_added(GstElement *demux, GstPad *pad, schanid_t s)
104 /* We can now link this pad with the vorbis-decoder sink pad */
105 sinkpad = gst_element_get_static_pad(s->decode, "sink");
106 if(gst_pad_link(pad, sinkpad) != GST_PAD_LINK_OK)
107 WARNING(_("Could not link OGG demuxer with Vorbis decoder"));
108 gst_object_unref(sinkpad);
111 /* This signal is thrown when the typefinder element has found the type of its
112 input. Now that we know what kind of input stream we have, we can connect the
113 proper demuxer/decoder elements. */
115 on_type_found(GstElement *typefind, guint probability, GstCaps *caps, schanid_t s)
117 gchar *type = gst_caps_to_string(caps);
118 if(strcmp(type, "application/ogg") == 0) {
119 s->demux = gst_element_factory_make("oggdemux", NULL);
120 s->decode = gst_element_factory_make("vorbisdec", NULL);
121 if(!s->demux || !s->decode) {
122 WARNING(_("Could not create one or more GStreamer elements"));
125 gst_bin_add_many(GST_BIN(s->pipeline), s->demux, s->decode, NULL);
126 if(!gst_element_link(s->typefind, s->demux) || !gst_element_link(s->decode, s->convert)) {
127 WARNING(_("Could not link GStreamer elements"));
130 /* We link the demuxer and decoder together dynamically, since the
131 demuxer doesn't know what source pads it will have until it starts
132 demuxing the stream */
133 g_signal_connect(s->demux, "pad-added", G_CALLBACK(on_ogg_demuxer_pad_added), s);
134 } else if(strcmp(type, "audio/x-aiff") == 0) {
135 s->decode = gst_element_factory_make("aiffparse", NULL);
137 WARNING(_("Could not create 'aiffparse' GStreamer element"));
140 gst_bin_add(GST_BIN(s->pipeline), s->decode);
141 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL)) {
142 WARNING(_("Could not link GStreamer elements"));
145 } else if(strcmp(type, "audio/x-mod") == 0) {
146 s->decode = gst_element_factory_make("modplug", NULL);
148 WARNING(_("Could not create 'modplug' GStreamer element"));
151 gst_bin_add(GST_BIN(s->pipeline), s->decode);
152 if(!gst_element_link_many(s->typefind, s->decode, s->convert, NULL)) {
153 WARNING(_("Could not link GStreamer elements"));
157 WARNING_S(_("Unexpected audio type in blorb"), type);
163 #endif /* GSTREAMER_SOUND */
166 * glk_schannel_create:
167 * @rock: The rock value to give the new sound channel.
169 * This creates a sound channel, about as you'd expect.
171 * Remember that it is possible that the library will be unable to create a new
172 * channel, in which case glk_schannel_create() will return %NULL.
174 * Returns: A new sound channel, or %NULL.
177 glk_schannel_create(glui32 rock)
179 return glk_schannel_create_ext(rock, 0x10000);
183 * glk_schannel_create_ext:
184 * @rock: The rock value to give the new sound channel.
185 * @volume: Integer representing the volume; 0x10000 is 100%.
189 * The glk_schannel_create_ext() call lets you create a channel with the volume
190 * already set at a given level.
192 * Returns: A new sound channel, or %NULL.
195 glk_schannel_create_ext(glui32 rock, glui32 volume)
197 #ifdef GSTREAMER_SOUND
198 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
200 schanid_t s = g_new0(struct glk_schannel_struct, 1);
201 s->magic = MAGIC_SCHANNEL;
203 if(glk_data->register_obj)
204 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
206 /* Add it to the global sound channel list */
207 glk_data->schannel_list = g_list_prepend(glk_data->schannel_list, s);
208 s->schannel_list = glk_data->schannel_list;
210 /* Add a pointer to the ChimaraGlk widget, for convenience */
211 s->glk = glk_data->self;
213 /* Create a GStreamer pipeline for the sound channel */
214 gchar *pipeline_name = g_strdup_printf("pipeline-%p", s);
215 s->pipeline = gst_pipeline_new(pipeline_name);
216 g_free(pipeline_name);
218 /* Watch for messages from the pipeline */
219 GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(s->pipeline));
220 gst_bus_add_signal_watch(bus);
221 g_signal_connect(bus, "message", G_CALLBACK(on_pipeline_message), s);
222 gst_object_unref(bus);
224 /* Create GStreamer elements to put in the pipeline */
225 s->source = gst_element_factory_make("giostreamsrc", NULL);
226 s->typefind = gst_element_factory_make("typefind", NULL);
227 s->convert = gst_element_factory_make("audioconvert", NULL);
228 s->filter = gst_element_factory_make("volume", NULL);
229 s->sink = gst_element_factory_make("autoaudiosink", NULL);
230 if(!s->source || !s->typefind || !s->convert || !s->filter || !s->sink) {
231 WARNING(_("Could not create one or more GStreamer elements"));
235 /* Set the initial volume */
236 glk_schannel_set_volume(s, volume);
238 /* Put the elements in the pipeline and link as many together as we can
239 without knowing the type of the audio stream */
240 gst_bin_add_many(GST_BIN(s->pipeline), s->source, s->typefind, s->convert, s->filter, s->sink, NULL);
241 /* Link elements: Source -> typefinder -> ??? -> Converter -> Volume filter -> Sink */
242 if(!gst_element_link(s->source, s->typefind) || !gst_element_link_many(s->convert, s->filter, s->sink, NULL)) {
243 WARNING(_("Could not link GStreamer elements"));
246 g_signal_connect(s->typefind, "have-type", G_CALLBACK(on_type_found), s);
251 glk_schannel_destroy(s);
255 #endif /* GSTREAMER_SOUND */
259 * glk_schannel_destroy:
260 * @chan: The sound channel to destroy.
262 * Destroys the channel. If the channel is playing a sound, the sound stops
263 * immediately (with no notification event).
266 glk_schannel_destroy(schanid_t chan)
268 VALID_SCHANNEL(chan, return);
270 #ifdef GSTREAMER_SOUND
271 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
273 if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
274 WARNING_S(_("Could not set GstElement state to"), "NULL");
276 glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
278 if(glk_data->unregister_obj)
280 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
281 chan->disprock.ptr = NULL;
284 /* This also frees all the objects inside the pipeline */
286 gst_object_unref(chan->pipeline);
288 chan->magic = MAGIC_FREE;
294 * glk_schannel_iterate:
295 * @chan: A sound channel, or %NULL.
296 * @rockptr: Return location for the next sound channel's rock, or %NULL.
298 * This function can be used to iterate through the list of all open channels.
299 * See <link linkend="chimara-Iterating-Through-Opaque-Objects">Iterating
300 * Through Opaque Objects</link>.
302 * As that section describes, the order in which channels are returned is
305 * Returns: the next sound channel, or %NULL if there are no more.
308 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
310 VALID_SCHANNEL_OR_NULL(chan, return NULL);
312 #ifdef GSTREAMER_SOUND
313 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
317 retnode = glk_data->schannel_list;
319 retnode = chan->schannel_list->next;
320 schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
322 /* Store the sound channel's rock in rockptr */
323 if(retval && rockptr)
324 *rockptr = glk_schannel_get_rock(retval);
329 #endif /* GSTREAMER_SOUND */
333 * glk_schannel_get_rock:
334 * @chan: A sound channel.
336 * Retrieves the channel's rock value. See <link
337 * linkend="chimara-Rocks">Rocks</link>.
339 * Returns: A rock value.
342 glk_schannel_get_rock(schanid_t chan)
344 VALID_SCHANNEL(chan, return 0);
350 * @chan: Channel to play the sound in.
351 * @snd: Resource number of the sound to play.
353 * Begins playing the given sound on the channel. If the channel was already
354 * playing a sound (even the same one), the old sound is stopped (with no
355 * notification event.
357 * This returns 1 if the sound actually started playing, and 0 if there was any
360 * The most obvious problem is if there is no sound resource with the given
361 * identifier. But other problems can occur. For example, the MOD-playing
362 * facility in a library might be unable to handle two MODs at the same time,
363 * in which case playing a MOD resource would fail if one was already playing.
366 * Returns: 1 on success, 0 on failure.
369 glk_schannel_play(schanid_t chan, glui32 snd)
371 return glk_schannel_play_ext(chan, snd, 1, 0);
375 * glk_schannel_play_ext:
376 * @chan: Channel to play the sound in.
377 * @snd: Resource number of the sound to play.
378 * @repeats: Number of times to repeat the sound.
379 * @notify: If nonzero, requests a notification when the sound is finished.
381 * This works the same as glk_schannel_play(), but lets you specify additional
382 * options. <code>glk_schannel_play(chan, snd)</code> is exactly equivalent to
383 * <code>glk_schannel_play_ext(chan, snd, 1, 0)</code>.
385 * The @repeats value is the number of times the sound should be repeated. A
386 * repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should repeat
387 * forever. A repeat value of 0 means that the sound will not be played at all;
388 * nothing happens. (Although a previous sound on the channel will be stopped,
389 * and the function will return 1.)
391 * The @notify value should be nonzero in order to request a sound notification
392 * event. If you do this, when the sound is completed, you will get an event
393 * with type %evtype_SoundNotify. The @window will be %NULL, @val1 will be the
394 * sound's resource id, and @val2 will be the nonzero value you passed as
397 * If you request sound notification, and the repeat value is greater than one,
398 * you will get the event only after the last repetition. If the repeat value is
399 * 0 or -1, you will never get a notification event at all. Similarly, if the
400 * sound is stopped or interrupted, or if the channel is destroyed while the
401 * sound is playing, there will be no notification event.
403 * Not all libraries support sound notification. You should test the
404 * %gestalt_SoundNotify selector before you rely on it; see <link
405 * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
406 * Capabilities</link>.
408 * Returns: 1 on success, 0 on failure.
411 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
413 VALID_SCHANNEL(chan, return 0);
414 #ifdef GSTREAMER_SOUND
415 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
416 GInputStream *stream;
418 /* Stop the previous sound */
419 clean_up_after_playing_sound(chan);
421 /* Don't play if repeats = 0 */
427 /* Load the sound into a GInputStream, by whatever method */
428 if(!glk_data->resource_map) {
429 if(!glk_data->resource_load_callback) {
430 WARNING(_("No resource map has been loaded yet."));
433 gchar *filename = glk_data->resource_load_callback(CHIMARA_RESOURCE_SOUND, snd, glk_data->resource_load_callback_data);
435 WARNING(_("Error loading resource from alternative location."));
440 GFile *file = g_file_new_for_path(filename);
441 stream = G_INPUT_STREAM(g_file_read(file, NULL, &err));
443 IO_WARNING(_("Error loading resource from file"), filename, err->message);
445 g_object_unref(file);
449 g_object_unref(file);
451 giblorb_result_t resource;
452 giblorb_err_t result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
453 if(result != giblorb_err_None) {
454 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
457 stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
460 chan->repeats = repeats;
461 chan->resource = snd;
462 chan->notify = notify;
463 g_object_set(chan->source, "stream", stream, NULL);
464 g_object_unref(stream); /* Now owned by GStreamer element */
466 /* Play the sound; unless the channel is paused, then pause it instead */
467 if(!gst_element_set_state(chan->pipeline, chan->paused? GST_STATE_PAUSED : GST_STATE_PLAYING)) {
468 WARNING_S(_("Could not set GstElement state to"), chan->paused? "PAUSED" : "PLAYING");
478 * glk_schannel_play_multi:
479 * @chanarray: Array of #schanid_t structures.
480 * @chancount: Length of @chanarray.
481 * @sndarray: Array of sound resource numbers.
482 * @soundcount: Length of @sndarray, must be equal to @chanarray.
483 * @notify: If nonzero, request a notification when each sound finishes.
487 * This works the same as glk_schannel_play_ext(), except that you can specify
488 * more than one sound. The channel references and sound resource numbers are
489 * given as two arrays, which must be the same length. The @notify argument
490 * applies to all the sounds; the repeats value for all the sounds is 1.
492 * All the sounds will begin at exactly the same time.
494 * This returns the number of sounds that began playing correctly. (This will be
495 * a number from 0 to @soundcount.)
498 * Note that you have to supply @chancount and @soundcount as separate
499 * arguments, even though they are required to be the same. This is an awkward
500 * consequence of the way array arguments are dispatched in Glulx.
503 * Returns: The number of sounds that started playing correctly.
506 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount, glui32 *sndarray, glui32 soundcount, glui32 notify)
508 g_return_val_if_fail(chancount == soundcount, 0);
509 g_return_val_if_fail(chanarray != NULL || chancount == 0, 0);
510 g_return_val_if_fail(sndarray != NULL || soundcount == 0, 0);
513 for(count = 0; count < chancount; count++)
514 VALID_SCHANNEL(chanarray[count], return 0);
516 #ifdef GSTREAMER_SOUND
517 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
518 GInputStream *stream;
520 if(!glk_data->resource_map && !glk_data->resource_load_callback) {
521 WARNING(_("No resource map has been loaded yet."));
525 /* We keep an array of sounds to skip if any of them have errors */
526 gboolean *skiparray = g_new0(gboolean, chancount);
528 /* Set up all the channels one by one */
529 for(count = 0; count < chancount; count++) {
530 /* Stop the previous sound */
531 clean_up_after_playing_sound(chanarray[count]);
533 /* Load the sound into a GInputStream, by whatever method */
534 if(!glk_data->resource_map) {
535 gchar *filename = glk_data->resource_load_callback(CHIMARA_RESOURCE_SOUND, sndarray[count], glk_data->resource_load_callback_data);
537 WARNING(_("Error loading resource from alternative location."));
538 skiparray[count] = TRUE;
543 GFile *file = g_file_new_for_path(filename);
544 stream = G_INPUT_STREAM(g_file_read(file, NULL, &err));
546 IO_WARNING(_("Error loading resource from file"), filename, err->message);
548 g_object_unref(file);
549 skiparray[count] = TRUE;
553 g_object_unref(file);
555 giblorb_result_t resource;
556 giblorb_err_t result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, sndarray[count]);
557 if(result != giblorb_err_None) {
558 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
559 skiparray[count] = TRUE;
562 stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
565 chanarray[count]->repeats = 1;
566 chanarray[count]->resource = sndarray[count];
567 chanarray[count]->notify = notify;
568 g_object_set(chanarray[count]->source, "stream", stream, NULL);
569 g_object_unref(stream); /* Now owned by GStreamer element */
572 /* Start all the sounds as close to each other as possible. */
573 /* FIXME: Is there a way to start them exactly at the same time? */
574 glui32 successes = 0;
575 for(count = 0; count < chancount; count++) {
578 /* Play the sound; unless the channel is paused, then pause it instead */
579 if(!gst_element_set_state(chanarray[count]->pipeline, chanarray[count]->paused? GST_STATE_PAUSED : GST_STATE_PLAYING)) {
580 WARNING_S(_("Could not set GstElement state to"), chanarray[count]->paused? "PAUSED" : "PLAYING");
581 skiparray[count] = TRUE;
595 * @chan: Channel to silence.
597 * Stops any sound playing in the channel. No notification event is generated,
598 * even if you requested one. If no sound is playing, this has no effect.
601 glk_schannel_stop(schanid_t chan)
603 VALID_SCHANNEL(chan, return);
604 #ifdef GSTREAMER_SOUND
605 clean_up_after_playing_sound(chan);
610 * glk_schannel_pause:
611 * @chan: Channel to pause.
615 * Pause any sound playing in the channel. This does not generate any
616 * notification events. If the channel is already paused, this does nothing.
618 * New sounds started in a paused channel are paused immediately.
620 * A volume change in progress is <emphasis>not</emphasis> paused, and may
621 * proceed to completion, generating a notification if appropriate.
624 glk_schannel_pause(schanid_t chan)
626 VALID_SCHANNEL(chan, return);
629 return; /* Silently do nothing */
631 /* Mark the channel as paused even if there is no sound playing yet */
635 if(gst_element_get_state(chan->pipeline, &state, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS) {
636 WARNING(_("Could not get GstElement state"));
639 if(state != GST_STATE_PLAYING)
640 return; /* Silently do nothing if no sound is playing */
642 if(!gst_element_set_state(chan->pipeline, GST_STATE_PAUSED)) {
643 WARNING_S(_("Could not set GstElement state to"), "PAUSED");
649 * glk_schannel_unpause:
650 * @chan: Channel to unpause.
654 * Unpause the channel. Any paused sounds begin playing where they left off. If
655 * the channel is not already paused, this does nothing.
658 glk_schannel_unpause(schanid_t chan)
660 VALID_SCHANNEL(chan, return);
663 return; /* Silently do nothing */
665 /* Mark the channel as not paused in any case */
666 chan->paused = FALSE;
669 if(gst_element_get_state(chan->pipeline, &state, NULL, GST_CLOCK_TIME_NONE) != GST_STATE_CHANGE_SUCCESS) {
670 WARNING(_("Could not get GstElement state"));
673 if(state != GST_STATE_PAUSED)
674 return; /* Silently do nothing */
676 if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
677 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
683 volume_glk_to_gstreamer(glui32 volume_glk)
685 return CLAMP(((double)volume_glk / 0x10000), 0.0, 10.0);
689 channel_set_volume_immediately(schanid_t chan, double volume, glui32 notify)
691 g_object_set(chan->filter, "volume", volume, NULL);
694 /* Send a notification */
699 * glk_schannel_set_volume:
700 * @chan: Channel to set the volume of.
701 * @vol: Integer representing the volume; 0x10000 is 100%.
703 * Sets the volume in the channel. When you create a channel, it has full
704 * volume, represented by the value 0x10000. Half volume would be 0x8000,
705 * three-quarters volume would be 0xC000, and so on. A volume of zero represents
706 * silence, although the sound is still considered to be playing.
708 * You can call this function between sounds, or while a sound is playing. The
709 * effect is immediate.
711 * You can overdrive the volume of a channel by setting a volume greater than
712 * 0x10000. However, this is not recommended; the library may be unable to
713 * increase the volume past full, or the sound may become distorted. You should
714 * always create sound resources with the maximum volume you will need, and then
715 * call glk_schannel_set_volume() to reduce the volume when appropriate.
717 * Not all libraries support this function. You should test the
718 * %gestalt_SoundVolume selector before you rely on it; see <link
719 * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
720 * Capabilities</link>.
722 * <note><title>Chimara</title>
723 * <para>Chimara supports volumes from 0 to 1000%, that is, values of
724 * @vol up to 0xA0000.</para>
728 glk_schannel_set_volume(schanid_t chan, glui32 vol)
730 VALID_SCHANNEL(chan, return);
731 /* Silently ignore out-of-range volume values */
733 #ifdef GSTREAMER_SOUND
734 double volume = volume_glk_to_gstreamer(vol);
735 channel_set_volume_immediately(chan, volume, 0);
740 * glk_schannel_set_volume_ext:
741 * @chan: Channel to set the volume of.
742 * @vol: Integer representing the volume; 0x10000 is 100%.
743 * @duration: Length of volume change in milliseconds, or 0 for immediate.
744 * @notify: If nonzero, requests a notification when the volume change finishes.
748 * Sets the volume in the channel, from 0 (silence) to 0x10000 (full volume).
749 * Again, you can overdrive the volume by setting a value greater than 0x10000,
750 * but this is not recommended.
752 * If the @duration is zero, the change is immediate. Otherwise, the change
753 * begins immediately, and occurs smoothly over the next @duration milliseconds.
755 * The @notify value should be nonzero in order to request a volume notification
756 * event. If you do this, when the volume change is completed, you will get an
757 * event with type #evtype_VolumeNotify. The window will be %NULL, @val1 will be
758 * zero, and @val2 will be the nonzero value you passed as @notify.
760 * The glk_schannel_set_volume() does not include @duration and @notify values.
761 * Both are assumed to be zero: immediate change, no notification.
763 * You can call these functions between sounds, or while a sound is playing.
764 * However, a zero-duration change while a sound is playing may produce
767 * At most one volume change can be occurring on a sound channel at any time. If
768 * you call one of these functions while a previous volume change is in
769 * progress, the previous change is interrupted. The beginning point of the new
770 * volume change should be wherever the previous volume change was interrupted
771 * (rather than the previous change's beginning or ending point).
773 * Not all libraries support these functions. You should test the appropriate
774 * gestalt selectors before you rely on them; see "Testing for Sound
778 glk_schannel_set_volume_ext(schanid_t chan, glui32 vol, glui32 duration, glui32 notify)
780 VALID_SCHANNEL(chan, return);
781 /* Silently ignore out-of-range volume values */
783 #ifdef GSTREAMER_SOUND
784 double volume = volume_glk_to_gstreamer(vol);
786 /* TODO: If channel is not playing, change the volume anyway */
789 channel_set_volume_immediately(chan, volume, notify);
793 /* Get the volume levels as GValues */
794 GValue current_volume = { 0 };
795 GValue target_volume = { 0 };
796 g_value_init(¤t_volume, G_TYPE_DOUBLE);
797 g_value_init(&target_volume, G_TYPE_DOUBLE);
798 g_object_get_property(G_OBJECT(chan->filter), "volume", ¤t_volume);
799 g_value_set_double(&target_volume, volume);
801 /* Make a controller for the volume */
802 GstController *controller = gst_object_control_properties(G_OBJECT(chan->filter), "volume", NULL);
803 if(controller == NULL) {
804 WARNING(_("Couldn't get controller for volume change"));
807 GstInterpolationControlSource *csource = gst_interpolation_control_source_new();
808 gst_interpolation_control_source_set_interpolation_mode(csource, GST_INTERPOLATE_LINEAR);
809 if(!gst_controller_set_control_source(controller, "volume", GST_CONTROL_SOURCE(csource))) {
810 WARNING(_("Couldn't set control source for volume change"));
814 /* Get the current time on the pipeline */
815 GstClock *clock = gst_pipeline_get_clock(GST_PIPELINE(chan->pipeline));
816 GstClockTime current = gst_clock_get_time(clock);
817 g_object_unref(clock);
819 if(!gst_interpolation_control_source_set(csource, current, ¤t_volume)) {
820 WARNING(_("Couldn't program volume change"));
823 if(!gst_interpolation_control_source_set(csource, current + duration * GST_MSECOND, &target_volume)) {
824 WARNING(_("Couldn't program volume change"));
828 /* TODO: SET UP NOTIFICATION */
833 /* Changing the volume dynamically didn't work; just do it immediately. */
834 channel_set_volume_immediately(chan, volume, notify);
839 * glk_sound_load_hint:
840 * @snd: Resource number of a sound.
841 * @flag: Nonzero to tell the library to load the sound, zero to tell the
842 * library to unload it.
844 * This gives the library a hint about whether the given sound should be loaded
845 * or not. If the @flag is nonzero, the library may preload the sound or do
846 * other initialization, so that glk_schannel_play() will be faster. If the
847 * @flag is zero, the library may release memory or other resources associated
848 * with the sound. Calling this function is always optional, and it has no
849 * effect on what the library actually plays.
852 glk_sound_load_hint(glui32 snd, glui32 flag)
854 #ifdef GSTREAMER_SOUND
855 ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
856 giblorb_result_t resource;
857 giblorb_err_t result;
859 /* Sound load hints only work for Blorb resource maps */
860 if(!glk_data->resource_map)
864 /* The sound load hint simply loads the resource from the resource map;
865 loading a chunk more than once does nothing */
866 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_Memory, &resource, giblorb_ID_Snd, snd);
867 if(result != giblorb_err_None) {
868 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
872 /* Get the Blorb chunk number by loading the resource with
873 method_DontLoad, then unload that chunk - has no effect if the chunk
875 result = giblorb_load_resource(glk_data->resource_map, giblorb_method_DontLoad, &resource, giblorb_ID_Snd, snd);
876 if(result != giblorb_err_None) {
877 WARNING_S( _("Error loading resource"), giblorb_get_error_message(result) );
880 result = giblorb_unload_chunk(glk_data->resource_map, resource.chunknum);
881 if(result != giblorb_err_None) {
882 WARNING_S( _("Error unloading chunk"), giblorb_get_error_message(result) );
886 #endif /* GSTREAMER_SOUND */