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 @ 4:ab6db404403a

History | View | Annotate | Download (11.2 KB)

1 1:b5bcad8e7803 f
/*
2
 Copyright (c) 2015, The Cinder Project
3

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

6
 Redistribution and use in source and binary forms, with or without modification, are permitted provided that
7
 the following conditions are met:
8

9
    * Redistributions of source code must retain the above copyright notice, this list of conditions and
10
        the following disclaimer.
11
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
12
        the following disclaimer in the documentation and/or other materials provided with the distribution.
13

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