3 #include <glib/gi18n-lib.h>
4 #include <libchimara/glk.h>
5 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
7 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
10 #include "chimara-glk-private.h"
16 #define VOLUME_TIMER_RESOLUTION 1.0 /* In milliseconds */
18 #ifdef GSTREAMER_0_10_SOUND
19 #define OGG_MIMETYPE "application/ogg"
21 #ifdef GSTREAMER_1_0_SOUND
22 #define OGG_MIMETYPE "audio/ogg"
25 extern GPrivate glk_data_key;
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. */
31 clean_up_after_playing_sound(schanid_t chan)
33 if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
34 WARNING_S(_("Could not set GstElement state to"), "NULL");
37 gst_bin_remove(GST_BIN(chan->pipeline), chan->source);
42 gst_bin_remove(GST_BIN(chan->pipeline), chan->demux);
47 gst_bin_remove(GST_BIN(chan->pipeline), chan->decode);
52 /* This signal is thrown whenever the GStreamer pipeline generates a message.
53 Most messages are harmless. */
55 on_pipeline_message(GstBus *bus, GstMessage *message, schanid_t s)
57 /* g_printerr("Got %s message\n", GST_MESSAGE_TYPE_NAME(message)); */
62 switch(GST_MESSAGE_TYPE(message)) {
63 case GST_MESSAGE_ERROR:
65 gst_message_parse_error(message, &err, &debug_message);
66 IO_WARNING(_("GStreamer error"), err->message, debug_message);
68 g_free(debug_message);
69 clean_up_after_playing_sound(s);
72 case GST_MESSAGE_WARNING:
74 gst_message_parse_warning(message, &err, &debug_message);
75 IO_WARNING(_("GStreamer warning"), err->message, debug_message);
77 g_free(debug_message);
80 case GST_MESSAGE_INFO:
82 gst_message_parse_info(message, &err, &debug_message);
83 g_message("GStreamer info \"%s\": %s", err->message, debug_message);
85 g_free(debug_message);
88 case GST_MESSAGE_EOS: /* End of stream */
89 /* Decrease repeats if not set to forever */
90 if(s->repeats != (glui32)-1)
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);
98 clean_up_after_playing_sound(s);
99 /* Sound ended normally, send a notification if requested */
101 event_throw(s->glk, evtype_SoundNotify, NULL, s->resource, s->notify);
105 /* unhandled message */
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. */
113 on_ogg_demuxer_pad_added(GstElement *demux, GstPad *pad, schanid_t s)
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);
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. */
128 on_type_found(GstElement *typefind, guint probability, GstCaps *caps, schanid_t s)
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"));
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"));
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);
150 WARNING(_("Could not create 'aiffparse' GStreamer element"));
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"));
158 } else if(strcmp(type, "audio/x-mod") == 0) {
159 s->decode = gst_element_factory_make("modplug", NULL);
161 WARNING(_("Could not create 'modplug' GStreamer element"));
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"));
170 WARNING_S(_("Unexpected audio type in blorb"), type);
173 /* This is necessary in case this handler occurs in the middle of a state
175 gst_element_sync_state_with_parent(s->decode);
177 gst_element_sync_state_with_parent(s->demux);
182 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
185 * glk_schannel_create:
186 * @rock: The rock value to give the new sound channel.
188 * This creates a sound channel, about as you'd expect.
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.
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.
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.
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.
210 * Returns: A new sound channel, or %NULL.
213 glk_schannel_create(glui32 rock)
215 return glk_schannel_create_ext(rock, 0x10000);
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%.
223 * The glk_schannel_create_ext() call lets you create a channel with the volume
224 * already set at a given level.
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>.
231 * Returns: A new sound channel, or %NULL.
234 glk_schannel_create_ext(glui32 rock, glui32 volume)
236 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
237 ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
239 schanid_t s = g_new0(struct glk_schannel_struct, 1);
240 s->magic = MAGIC_SCHANNEL;
242 if(glk_data->register_obj)
243 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
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;
249 /* Add a pointer to the ChimaraGlk widget, for convenience */
250 s->glk = glk_data->self;
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);
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);
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"));
273 /* Set the initial volume */
274 glk_schannel_set_volume(s, volume);
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);
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"));
285 g_signal_connect(s->typefind, "have-type", G_CALLBACK(on_type_found), s);
290 glk_schannel_destroy(s);
294 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
298 * glk_schannel_destroy:
299 * @chan: The sound channel to destroy.
301 * Destroys the channel. If the channel is playing a sound, the sound stops
302 * immediately (with no notification event).
305 glk_schannel_destroy(schanid_t chan)
307 VALID_SCHANNEL(chan, return);
309 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
310 ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
312 if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
313 WARNING_S(_("Could not set GstElement state to"), "NULL");
315 glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
317 if(glk_data->unregister_obj)
319 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
320 chan->disprock.ptr = NULL;
323 /* This also frees all the objects inside the pipeline */
325 gst_object_unref(chan->pipeline);
327 chan->magic = MAGIC_FREE;
329 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
333 * glk_schannel_iterate:
334 * @chan: A sound channel, or %NULL.
335 * @rockptr: Return location for the next sound channel's rock, or %NULL.
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>.
341 * As that section describes, the order in which channels are returned is
344 * Returns: the next sound channel, or %NULL if there are no more.
347 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
349 VALID_SCHANNEL_OR_NULL(chan, return NULL);
351 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
352 ChimaraGlkPrivate *glk_data = g_private_get(&glk_data_key);
356 retnode = glk_data->schannel_list;
358 retnode = chan->schannel_list->next;
359 schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
361 /* Store the sound channel's rock in rockptr */
362 if(retval && rockptr)
363 *rockptr = glk_schannel_get_rock(retval);
368 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
372 * glk_schannel_get_rock:
373 * @chan: A sound channel.
375 * Retrieves the channel's rock value. See <link
376 * linkend="chimara-Rocks">Rocks</link>.
378 * Returns: A rock value.
381 glk_schannel_get_rock(schanid_t chan)
383 VALID_SCHANNEL(chan, return 0);
389 * @chan: Channel to play the sound in.
390 * @snd: Resource number of the sound to play.
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.
396 * This returns 1 if the sound actually started playing, and 0 if there was any
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.
405 * Returns: 1 on success, 0 on failure.
408 glk_schannel_play(schanid_t chan, glui32 snd)
410 return glk_schannel_play_ext(chan, snd, 1, 0);
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.
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>.
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.)
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
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.
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>.
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.
452 * Returns: 1 on success, 0 on failure.
455 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
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;
462 /* Stop the previous sound */
463 clean_up_after_playing_sound(chan);
465 /* Don't play if repeats = 0 */
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."));
477 gchar *filename = glk_data->resource_load_callback(CHIMARA_RESOURCE_SOUND, snd, glk_data->resource_load_callback_data);
479 WARNING(_("Error loading resource from alternative location."));
484 GFile *file = g_file_new_for_path(filename);
485 stream = G_INPUT_STREAM(g_file_read(file, NULL, &err));
487 IO_WARNING(_("Error loading resource from file"), filename, err->message);
489 g_object_unref(file);
493 g_object_unref(file);
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) );
501 stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
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);
514 chan->repeats = repeats;
515 chan->resource = snd;
516 chan->notify = notify;
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);
527 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
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.
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.
543 * All the sounds will begin at exactly the same time.
545 * This returns the number of sounds that began playing correctly. (This will be
546 * a number from 0 to @soundcount.)
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
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.
559 * Returns: The number of sounds that started playing correctly.
562 glk_schannel_play_multi(schanid_t *chanarray, glui32 chancount, glui32 *sndarray, glui32 soundcount, glui32 notify)
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);
569 for(count = 0; count < chancount; count++)
570 VALID_SCHANNEL(chanarray[count], return 0);
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;
576 if(!glk_data->resource_map && !glk_data->resource_load_callback) {
577 WARNING(_("No resource map has been loaded yet."));
581 /* We keep an array of sounds to skip if any of them have errors */
582 gboolean *skiparray = g_new0(gboolean, chancount);
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]);
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);
593 WARNING(_("Error loading resource from alternative location."));
594 skiparray[count] = TRUE;
599 GFile *file = g_file_new_for_path(filename);
600 stream = G_INPUT_STREAM(g_file_read(file, NULL, &err));
602 IO_WARNING(_("Error loading resource from file"), filename, err->message);
604 g_object_unref(file);
605 skiparray[count] = TRUE;
609 g_object_unref(file);
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;
618 stream = g_memory_input_stream_new_from_data(resource.data.ptr, resource.length, NULL);
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]);
630 chanarray[count]->repeats = 1;
631 chanarray[count]->resource = sndarray[count];
632 chanarray[count]->notify = notify;
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++) {
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]);
654 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
659 * @chan: Channel to silence.
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.
665 glk_schannel_stop(schanid_t chan)
667 VALID_SCHANNEL(chan, return);
668 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
669 clean_up_after_playing_sound(chan);
674 * glk_schannel_pause:
675 * @chan: Channel to pause.
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.
680 * New sounds started in a paused channel are paused immediately.
682 * A volume change in progress is <emphasis>not</emphasis> paused, and may
683 * proceed to completion, generating a notification if appropriate.
686 glk_schannel_pause(schanid_t chan)
688 VALID_SCHANNEL(chan, return);
691 return; /* Silently do nothing */
693 /* Mark the channel as paused even if there is no sound playing yet */
696 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
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"));
702 if(state != GST_STATE_PLAYING)
703 return; /* Silently do nothing if no sound is playing */
705 if(!gst_element_set_state(chan->pipeline, GST_STATE_PAUSED)) {
706 WARNING_S(_("Could not set GstElement state to"), "PAUSED");
709 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
713 * glk_schannel_unpause:
714 * @chan: Channel to unpause.
716 * Unpause the channel. Any paused sounds begin playing where they left off. If
717 * the channel is not already paused, this does nothing.
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.
727 glk_schannel_unpause(schanid_t chan)
729 VALID_SCHANNEL(chan, return);
732 return; /* Silently do nothing */
734 /* Mark the channel as not paused in any case */
735 chan->paused = FALSE;
737 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
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"));
743 if(state != GST_STATE_PAUSED)
744 return; /* Silently do nothing */
746 if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
747 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
750 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
754 * glk_schannel_set_volume:
755 * @chan: Channel to set the volume of.
756 * @vol: Integer representing the volume; 0x10000 is 100%.
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.
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.
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
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.
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>.
778 * <note><title>Chimara</title>
779 * <para>Chimara supports volumes from 0 to 1000%, that is, values of
780 * @vol up to 0xA0000.</para>
784 glk_schannel_set_volume(schanid_t chan, glui32 vol)
786 glk_schannel_set_volume_ext(chan, vol, 0, 0);
790 volume_glk_to_gstreamer(glui32 volume_glk)
792 return CLAMP(((double)volume_glk / 0x10000), 0.0, 10.0);
795 #if defined(GSTREAMER_0_10_SOUND) || defined(GSTREAMER_1_0_SOUND)
797 volume_change_timeout(schanid_t chan)
800 g_get_current_time(&now);
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);
806 if(chan->volume_notify)
807 event_throw(chan->glk, evtype_VolumeNotify, NULL, 0, chan->volume_notify);
809 chan->volume_timer_id = 0;
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", ¤t_volume, NULL);
820 double volume_step = (chan->target_volume - current_volume) / steps_left;
822 g_object_set(chan->filter, "volume", current_volume + volume_step, NULL);
826 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */
829 * glk_schannel_set_volume_ext:
830 * @chan: Channel to set the volume of.
831 * @vol: Integer representing the volume; 0x10000 is 100%.
832 * @duration: Length of volume change in milliseconds, or 0 for immediate.
833 * @notify: If nonzero, requests a notification when the volume change finishes.
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.
839 * If the @duration is zero, the change is immediate. Otherwise, the change
840 * begins immediately, and occurs smoothly over the next @duration milliseconds.
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.
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
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).
857 * Not all libraries support these functions. You should test the appropriate
858 * gestalt selectors before you rely on them; see "Testing for Sound
862 glk_schannel_set_volume_ext(schanid_t chan, glui32 vol, glui32 duration, glui32 notify)
864 VALID_SCHANNEL(chan, return);
865 /* Silently ignore out-of-range volume values */
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);
872 double target_volume = volume_glk_to_gstreamer(vol);
875 g_object_set(chan->filter, "volume", target_volume, NULL);
878 event_throw(chan->glk, evtype_VolumeNotify, NULL, 0, notify);
883 GTimeVal target_time;
884 g_get_current_time(&target_time);
885 g_time_val_add(&target_time, (long)duration * 1000);
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;
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 */
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.
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.
911 glk_sound_load_hint(glui32 snd, glui32 flag)
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;
918 /* Sound load hints only work for Blorb resource maps */
919 if(!glk_data->resource_map)
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) );
931 /* Get the Blorb chunk number by loading the resource with
932 method_DontLoad, then unload that chunk - has no effect if the chunk
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) );
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) );
945 #endif /* GSTREAMER_0_10_SOUND || GSTREAMER_1_0_SOUND */