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