root/branches/jackMidi/libs/hydrogen/src/IO/jack_client.cpp @ 401

Revision 401, 7.4 KB (checked in by gabriel, 5 years ago)

Merge rev 373:374 from trunk.

Conflicts:

features.pri
libs/hydrogen/src/hydrogen.cpp

Line 
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 *
5 * http://www.hydrogen-music.org
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY, without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 *
21 */
22/* JackClient.cpp
23 * Copyright(c) 2008 by Gabriel M. Beddingfield <gabriel@teuton.org>
24 */
25
26#include "JackClient.h"
27#include <jack/jack.h>
28#include <hydrogen/Object.h>
29#include <cassert>
30
31#ifdef JACK_SUPPORT
32
33#ifdef LASH_SUPPORT
34#include <hydrogen/Preferences.h>
35#include <hydrogen/LashClient.h>
36#endif
37
38using namespace std;
39namespace H2Core
40{
41
42JackClient* JackClient::instance = NULL;
43
44JackClient* JackClient::get_instance(bool init_jack)
45{
46        if (instance == NULL) {
47                instance = new JackClient;
48        }
49        if (init_jack && (instance->m_client == NULL)) {
50                instance->open();
51        }
52        return instance;
53}
54
55jack_client_t* JackClient::ref(void)
56{
57        return m_client;
58}
59
60JackClient::JackClient()
61        : Object( "JackClient" ),
62          m_client(0),
63          m_audio_process(0),
64          m_nonaudio_process(0)
65{
66        INFOLOG( "INIT" );
67        open();
68}
69
70#define CLIENT_FAILURE(msg) {                                           \
71                ERRORLOG("Could not connect to JACK server (" msg ")"); \
72                if (m_client) {                                         \
73                        ERRORLOG("...but JACK returned a non-null pointer?"); \
74                        (m_client) = 0;                                 \
75                }                                                       \
76                if (tries) ERRORLOG("...trying again.");                \
77        }
78
79
80#define CLIENT_SUCCESS(msg) {                           \
81                assert(m_client);                       \
82                INFOLOG(msg);                           \
83                tries = 0;                              \
84        }
85
86void JackClient::open(void)
87{
88
89        QString sClientName = "Hydrogen";
90        jack_status_t status;
91        int tries = 2;  // Sometimes jackd doesn't stop and start fast enough.
92        while ( tries > 0 ) {
93                --tries;
94                m_client = jack_client_open(
95                        sClientName.toAscii(),
96                        JackNullOption,
97                        &status);
98                switch(status) {
99                case JackFailure:
100                        CLIENT_FAILURE("unknown error");
101                        break;
102                case JackInvalidOption:
103                        CLIENT_FAILURE("invalid option");
104                        break;
105                case JackNameNotUnique:
106                        if (m_client) {
107                                sClientName = jack_get_client_name(m_client);
108                                CLIENT_SUCCESS(QString("Jack assigned the client name '%1'")
109                                               .arg(sClientName));
110                        } else {
111                                CLIENT_FAILURE("name not unique");
112                        }
113                        break;
114                case JackServerStarted:
115                        CLIENT_SUCCESS("JACK Server started for Hydrogen.");
116                        break;
117                case JackServerFailed:
118                        CLIENT_FAILURE("unable to connect");
119                        break;
120                case JackServerError:
121                        CLIENT_FAILURE("communication error");
122                        break;
123                case JackNoSuchClient:
124                        CLIENT_FAILURE("unknown client type");
125                        break;
126                case JackLoadFailure:
127                        CLIENT_FAILURE("can't load internal client");
128                        break;
129                case JackInitFailure:
130                        CLIENT_FAILURE("can't initialize client");
131                        break;
132                case JackShmFailure:
133                        CLIENT_FAILURE("unable to access shared memory");
134                        break;
135                case JackVersionError:
136                        CLIENT_FAILURE("client/server protocol version mismatch");
137                default:
138                        if (status) {
139                                ERRORLOG("Unknown status with JACK server.");
140                                if (m_client) {
141                                        CLIENT_SUCCESS("Client pointer is *not* null..."
142                                                       " assuming we're OK");
143                                }
144                        } else {
145                                CLIENT_SUCCESS("Connected to JACK server");
146                        }                               
147                }
148        }
149
150        // Here, m_client should either be valid, or NULL.     
151
152#ifdef LASH_SUPPORT
153        if ( Preferences::getInstance()->useLash() ) {
154                LashClient* lashClient = LashClient::getInstance();
155                if (lashClient && lashClient->isConnected())
156                {
157                        lashClient->setJackClientName(sClientName.toStdString());
158                        lashClient->sendJackClientName();
159                }
160        }
161#endif
162}
163
164JackClient::~JackClient()
165{
166        INFOLOG( "DESTROY" );
167        close();
168}
169
170void JackClient::close(void)
171{
172        int rv;
173        if(m_client) {
174                rv = jack_deactivate(m_client); // return value ignored
175                if (rv) WARNINGLOG("jack_deactive(m_client) reported an error");
176                jack_client_close(m_client);  // Ignore return value
177                if (rv) WARNINGLOG("jack_client_close(m_client) reported an error");
178                m_client = 0;
179        }   
180}
181
182int JackClient::setAudioProcessCallback(JackProcessCallback process)
183{
184        if (jack_deactivate(m_client)) {
185                ERRORLOG("Could not deactivate JACK client.");
186        }
187        int rv = jack_set_process_callback(m_client, process, 0);
188        if (!rv) {
189                INFOLOG("JACK Callback changed.");
190                m_audio_process = process;
191        }
192        if (jack_activate(m_client)) {
193                ERRORLOG("Could not activate JACK client");
194        }
195        return rv;
196}
197
198int JackClient::setNonAudioProcessCallback(JackProcessCallback process)
199{
200        if (jack_deactivate(m_client)) {
201                ERRORLOG("Could not deactivate JACK client.");
202        }
203        int rv = 0;
204        if (!m_audio_process) {
205                INFOLOG("No current audio process callback... setting the non-audio one.");
206                rv = jack_set_process_callback(m_client, process, 0);
207        }
208        if (!rv) {
209                INFOLOG("Non-audio process callback changed.");
210                m_nonaudio_process = process;
211        } else {
212                ERRORLOG("Could not set the non-audio process callback.");
213        }
214        if (jack_activate(m_client)) {
215                ERRORLOG("Could not activate JACK client");
216        }
217        return rv;
218}
219
220int JackClient::clearAudioProcessCallback(void)
221{
222        int rv = 0;
223        if (!m_audio_process) {
224                return rv;
225        }
226        if (jack_deactivate(m_client)) {
227                ERRORLOG("Could not deactivate JACK client");
228        }
229        // make sure the process cycle is over before killing anything
230        if (m_nonaudio_process) {
231                INFOLOG("Switching to non-audio process");
232                rv = jack_set_process_callback(m_client, m_nonaudio_process, 0);
233        }
234        if (m_nonaudio_process && rv) {
235                ERRORLOG("Could not switch to non-audio process");
236                rv = jack_set_process_callback(m_client, 0, 0);
237                m_nonaudio_process = 0;
238                if (rv) ERRORLOG("JACK returned an error when clearing the process callback.");
239        }
240        if (jack_activate(m_client)) {
241                ERRORLOG("Could not activate JACK client.");
242        }               
243        m_audio_process = 0;
244        return rv;
245}
246
247int JackClient::clearNonAudioProcessCallback(void)
248{
249        int rv = 0;
250        if (!m_audio_process) {
251                if (jack_deactivate(m_client)) {
252                        ERRORLOG("Could not deactivate JACK client.");
253                }
254                rv = jack_set_process_callback(m_client, 0, 0);
255                if (rv) {
256                        ERRORLOG("JACK returned an error when clearing out the process callback.");
257                }
258        }
259        m_nonaudio_process = 0;
260        return rv;
261}
262
263void JackClient::subscribe(void* child_obj)
264{
265        m_children.insert(child_obj);
266        INFOLOG(QString("JackClient subscribers: %1").arg(m_children.size()));
267}
268
269void JackClient::unsubscribe(void* child_obj)
270{
271        INFOLOG(QString("JackClient subscribers (before): %1").arg(m_children.size()));
272        if (m_children.size() == 0)
273                return;
274        std::set<void*>::iterator pos = m_children.find(child_obj);
275        if (pos != m_children.end()) {
276                m_children.erase(pos);
277        }
278        INFOLOG(QString("JackClient subscribers (after): %1").arg(m_children.size()));
279        if (m_children.size() == 0) {
280                INFOLOG("JackClient is closing.");
281                close();
282        }
283}
284
285std::vector<QString> JackClient::getMidiOutputPortList(void)
286{
287        vector<QString> ports;
288        const char **port_names = 0;
289        port_names = jack_get_ports(m_client,
290                                    0,
291                                    JACK_DEFAULT_MIDI_TYPE,
292                                    JackPortIsOutput);
293        if (!port_names) return ports;
294        int k = 0;
295        while (port_names[k]) {
296                ports.push_back(QString(port_names[k]));
297                ++k;
298        }
299        free((void*)port_names);
300        return ports;
301}
302
303} // namespace H2Core
304
305#endif // JACK_SUPPORT
Note: See TracBrowser for help on using the browser.