Implemented sound playing with test sound
[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
13 extern GPrivate *glk_data_key;
14
15 /**
16  * glk_schannel_create:
17  * @rock: The rock value to give the new sound channel.
18  *
19  * This creates a sound channel, about as you'd expect.
20  *
21  * Remember that it is possible that the library will be unable to create a new
22  * channel, in which case glk_schannel_create() will return %NULL.
23  *
24  * Returns: A new sound channel, or %NULL.
25  */
26 schanid_t 
27 glk_schannel_create(glui32 rock)
28 {
29 #ifdef GSTREAMER_SOUND
30         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
31
32         schanid_t s = g_new0(struct glk_schannel_struct, 1);
33         s->magic = MAGIC_SCHANNEL;
34         s->rock = rock;
35         if(glk_data->register_obj)
36                 s->disprock = (*glk_data->register_obj)(s, gidisp_Class_Schannel);
37
38         /* Add it to the global sound channel list */
39         glk_data->schannel_list = g_list_prepend(glk_data->schannel_list, s);
40         s->schannel_list = glk_data->schannel_list;
41
42         /* Create a GStreamer pipeline for the sound channel */
43         gchar *pipeline_name = g_strdup_printf("pipeline-%p", s);
44         s->pipeline = gst_pipeline_new(pipeline_name);
45         g_free(pipeline_name);
46
47         /* Create GStreamer elements to put in the pipeline */
48         s->source = gst_element_factory_make("audiotestsrc", NULL);
49         s->filter = gst_element_factory_make("volume", NULL);
50         s->sink = gst_element_factory_make("autoaudiosink", NULL);
51         if(!s->source || !s->filter || !s->sink) {
52                 WARNING("Could not create one or more GStreamer elements");
53                 goto fail;
54         }
55                 
56         gst_bin_add_many(GST_BIN(s->pipeline), s->source, s->filter, s->sink, NULL);
57         if(!gst_element_link_many(s->source, s->filter, s->sink, NULL)) {
58                 WARNING("Could not link GStreamer elements");
59                 goto fail;
60         }
61         
62         return s;
63
64 fail:
65         glk_schannel_destroy(s);
66         return NULL;
67 #else
68         return NULL;
69 #endif /* GSTREAMER_SOUND */
70 }
71
72 /**
73  * glk_schannel_destroy:
74  * @chan: The sound channel to destroy.
75  *
76  * Destroys the channel. If the channel is playing a sound, the sound stops 
77  * immediately (with no notification event).
78  */
79 void 
80 glk_schannel_destroy(schanid_t chan)
81 {
82         VALID_SCHANNEL(chan, return);
83
84 #ifdef GSTREAMER_SOUND
85         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
86
87         if(!gst_element_set_state(chan->pipeline, GST_STATE_NULL))
88                 WARNING_S(_("Could not set GstElement state to"), "NULL");
89         
90         glk_data->schannel_list = g_list_delete_link(glk_data->schannel_list, chan->schannel_list);
91
92         if(glk_data->unregister_obj)
93         {
94                 (*glk_data->unregister_obj)(chan, gidisp_Class_Schannel, chan->disprock);
95                 chan->disprock.ptr = NULL;
96         }
97         
98         if(chan->pipeline)
99                 gst_object_unref(chan->pipeline);
100         
101         chan->magic = MAGIC_FREE;
102         g_free(chan);
103 #endif
104 }
105
106 /**
107  * glk_schannel_iterate:
108  * @chan: A sound channel, or %NULL.
109  * @rockptr: Return location for the next sound channel's rock, or %NULL.
110  *
111  * This function can be used to iterate through the list of all open channels.
112  * See <link linkend="chimara-Iterating-Through-Opaque-Objects">Iterating 
113  * Through Opaque Objects</link>.
114  *
115  * As that section describes, the order in which channels are returned is 
116  * arbitrary.
117  *
118  * Returns: the next sound channel, or %NULL if there are no more.
119  */
120 schanid_t 
121 glk_schannel_iterate(schanid_t chan, glui32 *rockptr)
122 {
123         VALID_SCHANNEL_OR_NULL(chan, return NULL);
124
125 #ifdef GSTREAMER_SOUND
126         ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key);
127         GList *retnode;
128         
129         if(chan == NULL)
130                 retnode = glk_data->schannel_list;
131         else
132                 retnode = chan->schannel_list->next;
133         schanid_t retval = retnode? (schanid_t)retnode->data : NULL;
134                 
135         /* Store the sound channel's rock in rockptr */
136         if(retval && rockptr)
137                 *rockptr = glk_schannel_get_rock(retval);
138                 
139         return retval;
140 #else
141         return NULL;
142 #endif /* GSTREAMER_SOUND */
143 }
144
145 /**
146  * glk_schannel_get_rock:
147  * @chan: A sound channel.
148  * 
149  * Retrieves the channel's rock value. See <link 
150  * linkend="chimara-Rocks">Rocks</link>.
151  *
152  * Returns: A rock value.
153  */
154 glui32 
155 glk_schannel_get_rock(schanid_t chan)
156 {
157         VALID_SCHANNEL(chan, return 0);
158         return chan->rock;
159 }
160
161 /**
162  * glk_schannel_play:
163  * @chan: Channel to play the sound in.
164  * @snd: Resource number of the sound to play.
165  *
166  * Begins playing the given sound on the channel. If the channel was already
167  * playing a sound (even the same one), the old sound is stopped (with no
168  * notification event.
169  *
170  * This returns 1 if the sound actually started playing, and 0 if there was any
171  * problem.
172  * <note><para>
173  *   The most obvious problem is if there is no sound resource with the given
174  *   identifier. But other problems can occur. For example, the MOD-playing 
175  *   facility in a library might be unable to handle two MODs at the same time,
176  *   in which case playing a MOD resource would fail if one was already playing.
177  * </para></note>
178  *
179  * Returns: 1 on success, 0 on failure.
180  */
181 glui32 
182 glk_schannel_play(schanid_t chan, glui32 snd)
183 {
184         return glk_schannel_play_ext(chan, snd, 1, 0);
185 }
186
187 /**
188  * glk_schannel_play_ext:
189  * @chan: Channel to play the sound in.
190  * @snd: Resource number of the sound to play.
191  * @repeats: Number of times to repeat the sound.
192  * @notify: If nonzero, requests a notification when the sound is finished.
193  *
194  * This works the same as glk_schannel_play(), but lets you specify additional 
195  * options. <code>glk_schannel_play(chan, snd)</code> is exactly equivalent to 
196  * <code>glk_schannel_play_ext(chan, snd, 1, 0)</code>.
197  * 
198  * The @repeats value is the number of times the sound should be repeated. A 
199  * repeat value of -1 (or rather 0xFFFFFFFF) means that the sound should repeat 
200  * forever. A repeat value of 0 means that the sound will not be played at all; 
201  * nothing happens. (Although a previous sound on the channel will be stopped, 
202  * and the function will return 1.)
203  * 
204  * The @notify value should be nonzero in order to request a sound notification
205  * event. If you do this, when the sound is completed, you will get an event 
206  * with type %evtype_SoundNotify. The @window will be %NULL, @val1 will be the 
207  * sound's resource id, and @val2 will be the nonzero value you passed as 
208  * @notify.
209  * 
210  * If you request sound notification, and the repeat value is greater than one, 
211  * you will get the event only after the last repetition. If the repeat value is
212  * 0 or -1, you will never get a notification event at all. Similarly, if the 
213  * sound is stopped or interrupted, or if the channel is destroyed while the 
214  * sound is playing, there will be no notification event.
215  *
216  * Not all libraries support sound notification. You should test the
217  * %gestalt_SoundNotify selector before you rely on it; see <link
218  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound 
219  * Capabilities</link>.
220  * 
221  * Returns: 1 on success, 0 on failure.
222  */
223 glui32 
224 glk_schannel_play_ext(schanid_t chan, glui32 snd, glui32 repeats, glui32 notify)
225 {
226         VALID_SCHANNEL(chan, return 0);
227 #ifdef GSTREAMER_SOUND
228         if(!gst_element_set_state(chan->pipeline, GST_STATE_PLAYING)) {
229                 WARNING_S(_("Could not set GstElement state to"), "PLAYING");
230                 return 0;
231         }
232         return 1;
233 #else
234         return 0;
235 #endif
236 }
237
238 /**
239  * glk_schannel_stop:
240  * @chan: Channel to silence.
241  *
242  * Stops any sound playing in the channel. No notification event is generated,
243  * even if you requested one. If no sound is playing, this has no effect.
244  */
245 void 
246 glk_schannel_stop(schanid_t chan)
247 {
248         VALID_SCHANNEL(chan, return);
249 #ifdef GSTREAMER_SOUND
250         if(!gst_element_set_state(chan->pipeline, GST_STATE_READY))
251                 WARNING_S(_("Could not set GstElement state to"), "READY");
252 #endif
253 }
254
255 /**
256  * glk_schannel_set_volume:
257  * @chan: Channel to set the volume of.
258  * @vol: Integer representing the volume; 0x10000 is 100&percnt;.
259  *
260  * Sets the volume in the channel. When you create a channel, it has full 
261  * volume, represented by the value 0x10000. Half volume would be 0x8000, 
262  * three-quarters volume would be 0xC000, and so on. A volume of zero represents
263  * silence, although the sound is still considered to be playing.
264  *
265  * You can call this function between sounds, or while a sound is playing. The 
266  * effect is immediate.
267  * 
268  * You can overdrive the volume of a channel by setting a volume greater than 
269  * 0x10000. However, this is not recommended; the library may be unable to 
270  * increase the volume past full, or the sound may become distorted. You should 
271  * always create sound resources with the maximum volume you will need, and then
272  * call glk_schannel_set_volume() to reduce the volume when appropriate.
273  *
274  * Not all libraries support this function. You should test the
275  * %gestalt_SoundVolume selector before you rely on it; see <link
276  * linkend="chimara-Testing-for-Sound-Capabilities">Testing for Sound
277  * Capabilities</link>.
278  *
279  * <note><title>Chimara</title>
280  *   <para>Chimara supports volumes from 0 to 1000&percnt;, that is, values of
281  *   @vol up to 0xA0000.</para>
282  * </note>
283  */
284 void 
285 glk_schannel_set_volume(schanid_t chan, glui32 vol)
286 {
287         VALID_SCHANNEL(chan, return);
288 #ifdef GSTREAMER_SOUND
289         gdouble volume_gst = (gdouble)vol / 0x10000;
290         g_printerr("Volume set to: %f\n", volume_gst);
291         g_object_set(chan->filter, "volume", CLAMP(volume_gst, 0.0, 10.0), NULL);
292 #endif
293 }
294
295 /**
296  * glk_sound_load_hint:
297  * @snd: Resource number of a sound.
298  * @flag: Nonzero to tell the library to load the sound, zero to tell the
299  * library to unload it.
300  *
301  * This gives the library a hint about whether the given sound should be loaded
302  * or not. If the @flag is nonzero, the library may preload the sound or do
303  * other initialization, so that glk_schannel_play() will be faster. If the
304  * @flag is zero, the library may release memory or other resources associated
305  * with the sound. Calling this function is always optional, and it has no
306  * effect on what the library actually plays.
307  *
308  * <warning><para>This function is not implemented yet.</para></warning>
309  */
310 void 
311 glk_sound_load_hint(glui32 snd, glui32 flag)
312 {
313 }