To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Tag: | Revision:

root / JackDevice / ContextJack.cpp @ 5:75b744078d66

History | View | Annotate | Download (12.2 KB)

1 1:b5bcad8e7803 f
/*
2

3 5:75b744078d66 f
 Copyright (C) 2016  Queen Mary University of London
4
 Author: Fiore Martin
5 1:b5bcad8e7803 f

6 5:75b744078d66 f
 This file is part of Collidoscope.
7

8
 Collidoscope is free software: you can redistribute it and/or modify
9
 it under the terms of the GNU General Public License as published by
10
 the Free Software Foundation, either version 3 of the License, or
11
 (at your option) any later version.
12

13
 This program is distributed in the hope that it will be useful,
14
 but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
 GNU General Public License for more details.
17

18
 You should have received a copy of the GNU General Public License
19
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
20

21
 This file incorporates work covered by the following copyright and permission notice:
22

23
    Copyright (c) 2014, The Cinder Project
24

25
    This code is intended to be used with the Cinder C++ library, http://libcinder.org
26

27
    Redistribution and use in source and binary forms, with or without modification, are permitted provided that
28
    the following conditions are met:
29 1:b5bcad8e7803 f

30
    * Redistributions of source code must retain the above copyright notice, this list of conditions and
31 5:75b744078d66 f
    the following disclaimer.
32 1:b5bcad8e7803 f
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
33 5:75b744078d66 f
    the following disclaimer in the documentation and/or other materials provided with the distribution.
34 1:b5bcad8e7803 f

35 5:75b744078d66 f
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
36
    WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
37
    PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
38
    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
39
    TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40
    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
42
    POSSIBILITY OF SUCH DAMAGE.
43

44 1:b5bcad8e7803 f
*/
45
46 5:75b744078d66 f
47 1:b5bcad8e7803 f
#include "cinder/audio/linux/ContextJack.h"
48
#include "cinder/audio/Exception.h"
49
50
namespace cinder { namespace audio { namespace linux {
51
52 4:ab6db404403a f
53
54
//--------------------------------------static utilities------------------------------------------
55
56 1:b5bcad8e7803 f
inline void zeroJackPort( jack_port_t *port, jack_nframes_t nframes )
57
{
58 4:ab6db404403a f
  // FIXME seg fault at shutdown
59 1:b5bcad8e7803 f
  // memset(port, 0, sizeof(jack_default_audio_sample_t) * nframes);
60
}
61
62 4:ab6db404403a f
// copy audio from node buffer to jack port
63 1:b5bcad8e7803 f
inline void copyToJackPort(jack_port_t *port, float *source, jack_nframes_t nframes )
64
{
65
    jack_default_audio_sample_t *out;
66
    out = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
67
68
    memcpy( out, source, sizeof(jack_default_audio_sample_t) * nframes ) ;
69
}
70
71 4:ab6db404403a f
// copy audio from jack port to  node buffer
72 1:b5bcad8e7803 f
inline void copyFromJackPort(jack_port_t *port, float *dest, jack_nframes_t nframes )
73
{
74
    jack_default_audio_sample_t *in;
75
    in = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
76
77
    memcpy( dest, in, sizeof(jack_default_audio_sample_t) * nframes ) ;
78
}
79
80
81 4:ab6db404403a f
// -------------------------------OutputDeviceNodeJack-------------------------------------------
82
83 1:b5bcad8e7803 f
int OutputDeviceNodeJack::jackCallback(jack_nframes_t nframes, void* userData)
84
{
85 4:ab6db404403a f
    // retrieve user data
86 5:75b744078d66 f
    RenderData *renderData = static_cast<RenderData *>( userData );
87 1:b5bcad8e7803 f
88 5:75b744078d66 f
    OutputDeviceNodeJack *outputDeviceNode = static_cast<OutputDeviceNodeJack *>( renderData->outputNode );
89 1:b5bcad8e7803 f
90 5:75b744078d66 f
    auto ctx = renderData->outputNode->getContext();
91
    if( ! ctx ) {
92 4:ab6db404403a f
        // this is from some cinder library code but it should not happen in Collidoscope as the context is set
93 1:b5bcad8e7803 f
        for( size_t chan = 0; chan < 2; chan++)
94 5:75b744078d66 f
            zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
95 1:b5bcad8e7803 f
96 5:75b744078d66 f
        return 0;
97
    }
98 1:b5bcad8e7803 f
99 5:75b744078d66 f
    std::lock_guard<std::mutex> lock( ctx->getMutex() );
100 1:b5bcad8e7803 f
101 5:75b744078d66 f
    // verify associated context still exists, which may not be true if we blocked in ~Context() and were then deallocated.
102
    ctx = renderData->outputNode->getContext();
103
    if( ! ctx ) {
104 1:b5bcad8e7803 f
105
        for( size_t chan = 0; chan < 2; chan++)
106 5:75b744078d66 f
            zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
107 1:b5bcad8e7803 f
108 5:75b744078d66 f
        return 0;
109
    }
110 1:b5bcad8e7803 f
111
112 5:75b744078d66 f
    Buffer *internalBuffer = outputDeviceNode->getInternalBuffer();
113
    internalBuffer->zero();
114 1:b5bcad8e7803 f
115 5:75b744078d66 f
    ctx->preProcess();
116 4:ab6db404403a f
    // process the whole audio graph using by recursively pulling the input all the way to the top of the graph
117 5:75b744078d66 f
    outputDeviceNode->pullInputs( internalBuffer );
118 1:b5bcad8e7803 f
119 5:75b744078d66 f
    // if clip detection is enabled and buffer clipped, silence it
120
    //if( outputDeviceNode->checkNotClipping() ){
121 4:ab6db404403a f
        //for( size_t chan = 0; chan < 2; chan++)
122 5:75b744078d66 f
        //    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
123
    //}
124 4:ab6db404403a f
    //else {
125 1:b5bcad8e7803 f
        for( size_t chan = 0; chan < 2; chan++)
126
            copyToJackPort( outputDeviceNode->mOutputPorts[chan], internalBuffer->getChannel( chan ), nframes  );
127 4:ab6db404403a f
    //}
128 1:b5bcad8e7803 f
129 5:75b744078d66 f
    ctx->postProcess();
130 1:b5bcad8e7803 f
131
    return 0;
132
}
133
134
inline void OutputDeviceNodeJack::setInput( InputDeviceNodeRef inputDeviceNode)
135
{
136
    mInputDeviceNode = std::static_pointer_cast<InputDeviceNodeJack>(inputDeviceNode);
137
}
138
139
OutputDeviceNodeJack::OutputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
140
    OutputDeviceNode( device, format),
141
    mCinderContext( context )
142
{
143
}
144
145
void OutputDeviceNodeJack::initialize()
146
{
147
148
    const char *client_name = "Collidoscope";
149
    const char *server_name = NULL;
150
    jack_options_t options = JackNullOption;
151
    jack_status_t status;
152
153 4:ab6db404403a f
    // connect to Jack server
154 1:b5bcad8e7803 f
    mClient = jack_client_open (client_name, options, &status, server_name);
155
    if( mClient == NULL){
156
157
        std::string msg = "jack_client_open() failed. ";
158
        if(status & JackServerFailed)
159
            msg += "Unable to connect to Jack server";
160
161
        throw cinder::audio::AudioContextExc(msg);
162
    }
163
164
165 4:ab6db404403a f
    // prepare user data for callback
166 1:b5bcad8e7803 f
    mRenderData.outputNode = this;
167
    mRenderData.inputNode = mInputDeviceNode.get();
168
    CI_ASSERT(mInputDeviceNode != nullptr);
169
    mRenderData.context = static_cast<ContextJack *>( getContext().get() );
170
171
    // install callback
172
    jack_set_process_callback (mClient, jackCallback, &mRenderData );
173
174
    // jack shutdown ?
175
176
177
    // setup output ports
178
    mOutputPorts[0] = jack_port_register (mClient, "output1",
179
                       JACK_DEFAULT_AUDIO_TYPE,
180
                       JackPortIsOutput, 0);
181
182
    mOutputPorts[1] = jack_port_register (mClient, "output2",
183
                       JACK_DEFAULT_AUDIO_TYPE,
184
                       JackPortIsOutput, 0);
185
186
     if ((mOutputPorts[0] == NULL) || (mOutputPorts[0] == NULL)) {
187
        throw cinder::audio::AudioContextExc("no more JACK ports available");
188
     }
189
190 4:ab6db404403a f
    // setup input ports. Note that the reference to the input node is used.
191 1:b5bcad8e7803 f
    mInputDeviceNode->mInputPorts[0] = jack_port_register (mClient, "input1",
192
                       JACK_DEFAULT_AUDIO_TYPE,
193
                       JackPortIsInput, 0);
194
195
    mInputDeviceNode->mInputPorts[1] = jack_port_register (mClient, "input2",
196
                       JACK_DEFAULT_AUDIO_TYPE,
197
                       JackPortIsInput, 0);
198
199
200 4:ab6db404403a f
    /* Tell the Jack server that we are ready to roll.  Our callback will start running now. */
201 1:b5bcad8e7803 f
    if (jack_activate (mClient)) {
202
        throw cinder::audio::AudioContextExc("cannot activate client");
203
    }
204
205
    // connect input ports to physical device (microphones)
206
    const char **mikePorts = jack_get_ports (mClient, NULL, NULL,
207 5:75b744078d66 f
        JackPortIsPhysical|JackPortIsOutput);
208 1:b5bcad8e7803 f
209 5:75b744078d66 f
    if (mikePorts == NULL) {
210 1:b5bcad8e7803 f
        throw cinder::audio::AudioContextExc("no physical input ports available");
211 5:75b744078d66 f
    }
212 1:b5bcad8e7803 f
213 5:75b744078d66 f
    if (jack_connect (mClient,  mikePorts[0], jack_port_name (mInputDeviceNode->mInputPorts[0]))) {
214 1:b5bcad8e7803 f
        throw cinder::audio::AudioContextExc("cannot connect input port 0");
215 5:75b744078d66 f
    }
216 1:b5bcad8e7803 f
217 5:75b744078d66 f
    if (jack_connect (mClient, mikePorts[1], jack_port_name( mInputDeviceNode->mInputPorts[1]) )) {
218 1:b5bcad8e7803 f
        throw cinder::audio::AudioContextExc("cannot connect input port 1");
219 5:75b744078d66 f
    }
220 1:b5bcad8e7803 f
221
    // connect output ports to physical device (audio out )
222
    const char **speakerPorts = jack_get_ports (mClient, NULL, NULL,
223 5:75b744078d66 f
                JackPortIsPhysical|JackPortIsInput);
224 1:b5bcad8e7803 f
225 5:75b744078d66 f
    if (speakerPorts == NULL) {
226 1:b5bcad8e7803 f
        throw cinder::audio::AudioContextExc("no physical output ports available");
227 5:75b744078d66 f
    }
228 1:b5bcad8e7803 f
229 5:75b744078d66 f
    if (jack_connect (mClient, jack_port_name (mOutputPorts[0]), speakerPorts[0])) {
230 1:b5bcad8e7803 f
        throw cinder::audio::AudioContextExc("cannot connect output port 0");
231 5:75b744078d66 f
    }
232 1:b5bcad8e7803 f
233 5:75b744078d66 f
    if (jack_connect (mClient, jack_port_name (mOutputPorts[1]), speakerPorts[1])) {
234 1:b5bcad8e7803 f
        throw cinder::audio::AudioContextExc("cannot connect output port 1");
235 5:75b744078d66 f
    }
236 1:b5bcad8e7803 f
237 5:75b744078d66 f
    jack_free( mikePorts );
238
    jack_free( speakerPorts );
239 1:b5bcad8e7803 f
}
240
241
242
void OutputDeviceNodeJack::uninitialize()
243
{
244
    jack_client_close( mClient );
245
}
246
247
void OutputDeviceNodeJack::enableProcessing()
248
{
249
}
250
251
void OutputDeviceNodeJack::disableProcessing()
252
{
253
}
254
255
256 4:ab6db404403a f
//----------------------------------------- InputDeviceNodeJack ---------------------------------------------------
257 1:b5bcad8e7803 f
258
259
InputDeviceNodeJack::InputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
260
    InputDeviceNode( device, format)
261
{
262
}
263
264
void InputDeviceNodeJack::initialize()
265
{
266
}
267
268
void InputDeviceNodeJack::uninitialize()
269
{
270
}
271
272
void InputDeviceNodeJack::enableProcessing()
273
{
274
}
275
276
void InputDeviceNodeJack::disableProcessing()
277
{
278
}
279
280 4:ab6db404403a f
// This is called when the output node pull all the inputs in the jack callback.
281
// Takes audio interface input from the jack port and copies it in the node buffer
282 1:b5bcad8e7803 f
void InputDeviceNodeJack::process( Buffer *buffer )
283
{
284
    for( size_t chan = 0; chan < 2; chan++){
285
       copyFromJackPort(mInputPorts[chan], buffer->getChannel( chan ), buffer->getNumFrames() );
286
    }
287
}
288
289 4:ab6db404403a f
290
//-------------------------------------------ContextJack-----------------------------------------------------------
291
292 5:75b744078d66 f
OutputDeviceNodeRef ContextJack::createOutputDeviceNode( const DeviceRef &device, const Node::Format &format )
293 4:ab6db404403a f
{
294 5:75b744078d66 f
295 4:ab6db404403a f
    if( mOutputDeviceNode  == nullptr ) {
296
        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
297
298
        mOutputDeviceNode = makeNode( new OutputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
299
300
        // the output device node must have a reference to input device node. In OutputDeviceNodeJack::initialize()
301
        // the input node is passed the jack input ports that it will use to fetch incoming audio from the audio interface
302
        // Whichever node (input or ouput) gets initialized after the other, executes the following block:
303
        if( mInputDeviceNode != nullptr){
304
            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
305
            castedOutputDeviceNode->setInput( mInputDeviceNode );
306
        }
307
    }
308
309 5:75b744078d66 f
    return mOutputDeviceNode;
310 4:ab6db404403a f
}
311
312
InputDeviceNodeRef ContextJack::createInputDeviceNode( const DeviceRef &device, const Node::Format &format  )
313
{
314
    if( mInputDeviceNode  == nullptr ) {
315
        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
316
317
        mInputDeviceNode = makeNode( new InputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
318
319
        // the output device node must have a reference to input device node. In OutputDeviceNodeJack::initialize()
320
        // the input node is passed the jack input ports that it will use to fetch incoming audio from the audio interface
321
        // Whichever node (input or ouput) gets initialized after the other, executes the following block:
322
        if( mOutputDeviceNode != nullptr){
323
            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
324
            castedOutputDeviceNode->setInput( mInputDeviceNode );
325
        }
326
    }
327
328 5:75b744078d66 f
    return mInputDeviceNode;
329 4:ab6db404403a f
}
330
331
332
333
334
335 1:b5bcad8e7803 f
} } } // namespace cinder::audio::linux