conform to the buffer-frames props entry -- much nicer now...
[platform/upstream/gst-plugins-good.git] / ext / jack / gstjackbin.c
1 /* -*- Mode: C; c-basic-offset: 4 -*- */
2 /*
3     Copyright (C) 2002, 2003 Andy Wingo <wingo@pobox.com>
4
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU General Public
7     License as published by the Free Software Foundation; either
8     version 2 of the License, or (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     General Public License for more details.
14
15     You should have received a copy of the GNU General Public
16     License along with this library; if not, write to the Free
17     Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <signal.h>
23 #include "gstjack.h"
24
25
26 static GstBinClass *parent_class = NULL;
27
28 static void gst_jack_bin_init(GstJackBin *this);
29 static void gst_jack_bin_class_init(GstJackBinClass *klass);
30
31 static GstElementStateReturn gst_jack_bin_change_state(GstElement *element);
32
33 /* jack callbacks */
34 static int process (jack_nframes_t nframes, void *arg);
35 static int sample_rate (jack_nframes_t nframes, void *arg);
36 static int buffer_size (jack_nframes_t nframes, void *arg);
37 static void shutdown (void *arg);
38
39 static void sighup_handler (int sig);
40 static GstJackBin *_jackbin = NULL;
41 static gboolean watchdog_please_set_the_jackbin_to_ready = FALSE;
42
43 /* fixme: we need a watchdog thread to see if we have received a SIGHUP, and if
44  * so set the state of the bin to READY. */
45
46 GType
47 gst_jack_bin_get_type (void) 
48 {
49     static GType jack_bin_type = 0;
50 if (!jack_bin_type) {
51         static const GTypeInfo jack_bin_info = {
52             sizeof(GstJackBinClass),
53             NULL,
54             NULL,
55             (GClassInitFunc)gst_jack_bin_class_init,
56             NULL,
57             NULL,
58             sizeof(GstJackBin),
59             0,
60             (GInstanceInitFunc)gst_jack_bin_init,
61         };
62         jack_bin_type = g_type_register_static (GST_TYPE_BIN, "GstJackBin", &jack_bin_info, 0);
63     }
64     return jack_bin_type;
65 }
66
67 static void
68 gst_jack_bin_class_init(GstJackBinClass *klass)
69 {
70     GObjectClass *object_class;
71     GstElementClass *element_class;
72     
73     object_class = (GObjectClass *)klass;
74     element_class = (GstElementClass *)klass;
75     
76     parent_class = g_type_class_ref(GST_TYPE_BIN);
77
78     element_class->change_state = gst_jack_bin_change_state;
79 }
80
81 static void
82 gst_jack_bin_init(GstJackBin *this)
83 {
84     GST_DEBUG ("initializing jack bin");
85     
86     /* jack bins are managing bins and iterate themselves */
87     GST_FLAG_SET (this, GST_BIN_FLAG_MANAGER);
88     GST_FLAG_SET (this, GST_BIN_SELF_SCHEDULABLE);
89     
90     /* make a new scheduler and associate it with the bin */
91     gst_scheduler_factory_make (NULL, GST_ELEMENT (this));
92 }
93
94 static GstElementStateReturn
95 gst_jack_bin_change_state (GstElement *element)
96 {
97     GstJackBin *this;
98     GList *l = NULL;
99     GstJackPad *pad;
100     
101     g_return_val_if_fail (element != NULL, FALSE);
102     this = GST_JACK_BIN (element);
103     
104     switch (GST_STATE_PENDING (element)) {
105     case GST_STATE_NULL:
106         JACK_DEBUG ("jackbin: NULL state");
107         if (this->client) {
108             JACK_DEBUG ("jackbin: closing client");
109             jack_client_close (this->client);
110             this->client = NULL;
111         }
112         
113         if (_jackbin)
114             signal (SIGHUP, SIG_DFL);
115         _jackbin = NULL;
116
117         if (GST_ELEMENT_CLASS (parent_class)->change_state)
118             return GST_ELEMENT_CLASS (parent_class)->change_state (element);
119         break;
120         
121     case GST_STATE_READY:
122         JACK_DEBUG ("jackbin: READY");
123
124         _jackbin = this;
125         signal (SIGHUP, sighup_handler);
126
127         if (!this->client) {
128           if (!(this->client = jack_client_new ("gst-jack"))) {
129             g_warning ("jack server not running?");
130             return GST_STATE_FAILURE;
131           }
132                 
133           gst_scheduler_setup (GST_ELEMENT_SCHED (this));
134
135           jack_set_process_callback (this->client, process, this);
136           jack_set_sample_rate_callback (this->client, sample_rate, this);
137           jack_set_buffer_size_callback (this->client, buffer_size, this);
138           this->nframes = jack_get_buffer_size (this->client);
139           jack_on_shutdown (this->client, shutdown, this);
140         }
141         
142         if (GST_FLAG_IS_SET (GST_OBJECT (this), GST_JACK_OPEN)) {
143             l = this->src_pads;
144             while (l) {
145                 JACK_DEBUG ("jackbin: unregistering pad %s:%s", GST_JACK_PAD (l)->name, GST_JACK_PAD (l)->peer_name);
146                 jack_port_unregister (this->client, GST_JACK_PAD (l)->port);
147                 l = g_list_next (l);
148             }
149             l = this->sink_pads;
150             while (l) {
151                 JACK_DEBUG ("jackbin: unregistering pad %s:%s", GST_JACK_PAD (l)->name, GST_JACK_PAD (l)->peer_name);
152                 jack_port_unregister (this->client, GST_JACK_PAD (l)->port);
153                 l = g_list_next (l);
154             }
155             GST_FLAG_UNSET (GST_OBJECT (this), GST_JACK_OPEN);
156
157             if (GST_FLAG_IS_SET (GST_OBJECT (this), GST_JACK_ACTIVE)) {
158                 JACK_DEBUG ("jackbin: deactivating client");
159                 jack_deactivate (this->client);
160                 GST_FLAG_UNSET (GST_OBJECT (this), GST_JACK_ACTIVE);
161             }
162         }
163             
164         if (GST_ELEMENT_CLASS (parent_class)->change_state)
165             return GST_ELEMENT_CLASS (parent_class)->change_state (element);
166         break;
167         
168     case GST_STATE_PAUSED:
169         JACK_DEBUG ("jackbin: PAUSED");
170         
171         if (!GST_FLAG_IS_SET (GST_OBJECT (this), GST_JACK_OPEN)) {
172             l = this->src_pads;
173             while (l) {
174                 pad = GST_JACK_PAD (l);
175                 JACK_DEBUG ("jackbin: registering input port %s (peer %s)", pad->name, pad->peer_name);
176                 pad->port = jack_port_register (this->client, pad->name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput|JackPortIsTerminal, 0);
177                 l = g_list_next (l);
178             }
179             l = this->sink_pads;
180             while (l) {
181                 pad = GST_JACK_PAD (l);
182                 JACK_DEBUG ("jackbin: registering output port %s (peer %s)", pad->name, pad->peer_name);
183                 pad->port = jack_port_register (this->client, pad->name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput|JackPortIsTerminal, 0);
184                 l = g_list_next (l);
185             }
186
187             /* must activate before connecting */
188             if (!GST_FLAG_IS_SET (GST_OBJECT (this), GST_JACK_ACTIVE)) {
189                 JACK_DEBUG ("jackbin: activating client");
190                 jack_activate (this->client);
191                 GST_FLAG_SET (GST_OBJECT (this), GST_JACK_ACTIVE);
192             }
193
194             l = this->src_pads;
195             while (l) {
196                 pad = GST_JACK_PAD (l);
197                 JACK_DEBUG ("connecting jack port %s to gst jack port %s", pad->peer_name, jack_port_name (pad->port));
198                 if (jack_connect (this->client, pad->peer_name, jack_port_name (pad->port))) {
199                     g_warning ("jackbin: could not connect %s and %s", pad->peer_name, jack_port_name (pad->port));
200                     return GST_STATE_FAILURE;
201                 }
202                 l = g_list_next (l);
203             }
204             l = this->sink_pads;
205             while (l) {
206                 pad = GST_JACK_PAD (l);
207                 JACK_DEBUG ("connecting gst jack port %s to jack port %s", jack_port_name (pad->port), pad->peer_name);
208                 if (jack_connect (this->client, jack_port_name (pad->port), pad->peer_name)) {
209                     g_warning ("jackbin: could not connect %s and %s", pad->peer_name, jack_port_name (pad->port));
210                     return GST_STATE_FAILURE;
211                 }
212                 l = g_list_next (l);
213             }
214
215             JACK_DEBUG ("jackbin: setting OPEN flag");
216             GST_FLAG_SET (GST_OBJECT (this), GST_JACK_OPEN);
217
218             if (GST_ELEMENT_CLASS (parent_class)->change_state)
219                 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
220         } else {
221             if (GST_ELEMENT_CLASS (parent_class)->change_state)
222                 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
223         }
224
225         break;
226     case GST_STATE_PLAYING:
227         JACK_DEBUG ("jackbin: PLAYING");
228
229         if (GST_ELEMENT_CLASS (parent_class)->change_state)
230             return GST_ELEMENT_CLASS (parent_class)->change_state (element);
231         break;
232     }
233     
234     JACK_DEBUG ("jackbin: state change finished");
235     
236     return GST_STATE_SUCCESS;
237 }
238
239 /* jack callbacks */
240
241 /* keep in mind that these run in another thread, mm-kay? */
242
243 static int
244 process (jack_nframes_t nframes, void *arg)
245 {
246     GstJackBin *bin = (GstJackBin*) arg;
247     GstJackPad *pad;
248     GList *l;
249     
250     g_assert (bin);
251     
252     JACK_DEBUG ("jackbin: process()");
253
254     if (GST_STATE (bin) != GST_STATE_PLAYING) {
255         JACK_DEBUG ("jackbin: bin is not PLAYING yet, returning");
256         return 0;
257     } else {
258         JACK_DEBUG ("jackbin: we are PLAYING, let's process()");
259     }
260
261     l = bin->src_pads;
262     while (l) {
263         pad = GST_JACK_PAD (l);
264         pad->data = jack_port_get_buffer (pad->port, nframes);
265         l = g_list_next (l);
266     }
267     
268     l = bin->sink_pads;
269     while (l) {
270         pad = GST_JACK_PAD (l);
271         pad->data = jack_port_get_buffer (pad->port, nframes);
272         l = g_list_next (l);
273     }
274     
275     bin->nframes = nframes;
276     
277     JACK_DEBUG ("jackbin: iterating to process %ld frames of audio...", nframes);
278     if (!gst_bin_iterate (GST_BIN_CAST (bin))) {
279         g_warning ("bin failed to iterate");
280         return -1;
281     }
282     
283     /* that's all folks */
284     
285     return 0;      
286 }
287
288 static int
289 sample_rate (jack_nframes_t nframes, void *arg)
290 {
291     GstJackBin *bin = (GstJackBin*) arg;
292     JACK_DEBUG ("the sample rate is now %lu/sec\n", nframes);
293     bin->rate = nframes;
294     return 0;
295 }
296
297 static int
298 buffer_size (jack_nframes_t nframes, void *arg)
299 {
300     GstJackBin *bin = (GstJackBin*) arg;
301     JACK_DEBUG ("the buffer size is now %lu\n", nframes);
302     bin->nframes = nframes;
303     return 0;
304 }
305
306 static void
307 shutdown (void *arg)
308 {
309 /*    GstJackClient *client = (GstJackClient*) arg; */
310     printf ("shutdown %p\n", arg);
311 /*    gst_element_set_state (GST_ELEMENT (client->manager), GST_STATE_READY); */
312 }
313
314 static void sighup_handler (int sig)
315 {
316     g_message ("got sighup, setting state to READY");
317     watchdog_please_set_the_jackbin_to_ready = TRUE;
318 }