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