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

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