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 @ 1:b5bcad8e7803

History | View | Annotate | Download (9.73 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
inline void zeroJackPort( jack_port_t *port, jack_nframes_t nframes )
30
{
31
  // memset(port, 0, sizeof(jack_default_audio_sample_t) * nframes);
32
}
33
34
inline void copyToJackPort(jack_port_t *port, float *source, jack_nframes_t nframes )
35
{
36
    // dest, source, n
37
    jack_default_audio_sample_t *out;
38
    out = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
39
40
    memcpy( out, source, sizeof(jack_default_audio_sample_t) * nframes ) ;
41
}
42
43
inline void copyFromJackPort(jack_port_t *port, float *dest, jack_nframes_t nframes )
44
{
45
    // dest, source, n
46
    jack_default_audio_sample_t *in;
47
    in = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
48
49
    memcpy( dest, in, sizeof(jack_default_audio_sample_t) * nframes ) ;
50
}
51
52
53
int OutputDeviceNodeJack::jackCallback(jack_nframes_t nframes, void* userData)
54
{
55
        RenderData *renderData = static_cast<RenderData *>( userData );
56
57
        OutputDeviceNodeJack *outputDeviceNode = static_cast<OutputDeviceNodeJack *>( renderData->outputNode );
58
59
        auto ctx = renderData->outputNode->getContext();
60
        if( ! ctx ) {
61
        for( size_t chan = 0; chan < 2; chan++)
62
            // FIXME segfault at shutdown
63
                    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
64
65
                return 0;
66
        }
67
68
        std::lock_guard<std::mutex> lock( ctx->getMutex() );
69
70
        // verify associated context still exists, which may not be true if we blocked in ~Context() and were then deallocated.
71
        ctx = renderData->outputNode->getContext();
72
        if( ! ctx ) {
73
74
        for( size_t chan = 0; chan < 2; chan++)
75
                    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
76
77
                return 0;
78
        }
79
80
81
        Buffer *internalBuffer = outputDeviceNode->getInternalBuffer();
82
        internalBuffer->zero();
83
84
        ctx->preProcess();
85
        outputDeviceNode->pullInputs( internalBuffer );
86
87
        // if clip detection is enabled and buffer clipped, silence it
88
        if( false && outputDeviceNode->checkNotClipping() ){
89
        for( size_t chan = 0; chan < 2; chan++)
90
                    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
91
        }
92
    else {
93
        for( size_t chan = 0; chan < 2; chan++)
94
            copyToJackPort( outputDeviceNode->mOutputPorts[chan], internalBuffer->getChannel( chan ), nframes  );
95
    }
96
97
        ctx->postProcess();
98
99
    return 0;
100
}
101
102
inline void OutputDeviceNodeJack::setInput( InputDeviceNodeRef inputDeviceNode)
103
{
104
    mInputDeviceNode = std::static_pointer_cast<InputDeviceNodeJack>(inputDeviceNode);
105
}
106
107
ContextJack::ContextJack()
108
{
109
110
}
111
112
ContextJack::~ContextJack()
113
{
114
115
}
116
117
118
OutputDeviceNodeRef        ContextJack::createOutputDeviceNode( const DeviceRef &device, const Node::Format &format )
119
{
120
121
    if( mOutputDeviceNode  == nullptr ) {
122
        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
123
124
        mOutputDeviceNode = makeNode( new OutputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
125
126
        if( mInputDeviceNode != nullptr){
127
            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
128
            castedOutputDeviceNode->setInput( mInputDeviceNode );
129
        }
130
    }
131
132
        return mOutputDeviceNode;
133
}
134
135
InputDeviceNodeRef ContextJack::createInputDeviceNode( const DeviceRef &device, const Node::Format &format  )
136
{
137
    if( mInputDeviceNode  == nullptr ) {
138
        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
139
140
        mInputDeviceNode = makeNode( new InputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
141
142
        if( mOutputDeviceNode != nullptr){
143
            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
144
            castedOutputDeviceNode->setInput( mInputDeviceNode );
145
        }
146
    }
147
148
        return mInputDeviceNode;
149
}
150
151
152
// OutputDeviceNodeJack
153
154
OutputDeviceNodeJack::OutputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
155
    OutputDeviceNode( device, format),
156
    mCinderContext( context )
157
{
158
}
159
160
void OutputDeviceNodeJack::initialize()
161
{
162
163
    const char *client_name = "Collidoscope";
164
    const char *server_name = NULL;
165
    jack_options_t options = JackNullOption;
166
    jack_status_t status;
167
168
    // connect to JAck server
169
    mClient = jack_client_open (client_name, options, &status, server_name);
170
    if( mClient == NULL){
171
172
        std::string msg = "jack_client_open() failed. ";
173
        if(status & JackServerFailed)
174
            msg += "Unable to connect to Jack server";
175
176
        throw cinder::audio::AudioContextExc(msg);
177
    }
178
179
180
    mRenderData.outputNode = this;
181
    mRenderData.inputNode = mInputDeviceNode.get();
182
    CI_ASSERT(mInputDeviceNode != nullptr);
183
    mRenderData.context = static_cast<ContextJack *>( getContext().get() );
184
185
    // install callback
186
    jack_set_process_callback (mClient, jackCallback, &mRenderData );
187
188
    // jack shutdown ?
189
190
191
    // setup output ports
192
    mOutputPorts[0] = jack_port_register (mClient, "output1",
193
                       JACK_DEFAULT_AUDIO_TYPE,
194
                       JackPortIsOutput, 0);
195
196
    mOutputPorts[1] = jack_port_register (mClient, "output2",
197
                       JACK_DEFAULT_AUDIO_TYPE,
198
                       JackPortIsOutput, 0);
199
200
     if ((mOutputPorts[0] == NULL) || (mOutputPorts[0] == NULL)) {
201
        throw cinder::audio::AudioContextExc("no more JACK ports available");
202
     }
203
204
    // setup input ports
205
    mInputDeviceNode->mInputPorts[0] = jack_port_register (mClient, "input1",
206
                       JACK_DEFAULT_AUDIO_TYPE,
207
                       JackPortIsInput, 0);
208
209
    mInputDeviceNode->mInputPorts[1] = jack_port_register (mClient, "input2",
210
                       JACK_DEFAULT_AUDIO_TYPE,
211
                       JackPortIsInput, 0);
212
213
214
    /* Tell the JACK server that we are ready to roll.  Our callback will start running now. */
215
    if (jack_activate (mClient)) {
216
        throw cinder::audio::AudioContextExc("cannot activate client");
217
    }
218
219
    // connect input ports to physical device (microphones)
220
    const char **mikePorts = jack_get_ports (mClient, NULL, NULL,
221
                JackPortIsPhysical|JackPortIsOutput);
222
223
        if (mikePorts == NULL) {
224
        throw cinder::audio::AudioContextExc("no physical input ports available");
225
        }
226
227
        if (jack_connect (mClient,  mikePorts[0], jack_port_name (mInputDeviceNode->mInputPorts[0]))) {
228
        throw cinder::audio::AudioContextExc("cannot connect input port 0");
229
        }
230
231
        if (jack_connect (mClient, mikePorts[1], jack_port_name( mInputDeviceNode->mInputPorts[1]) )) {
232
        throw cinder::audio::AudioContextExc("cannot connect input port 1");
233
        }
234
235
    // connect output ports to physical device (audio out )
236
    const char **speakerPorts = jack_get_ports (mClient, NULL, NULL,
237
                                JackPortIsPhysical|JackPortIsInput);
238
239
        if (speakerPorts == NULL) {
240
        throw cinder::audio::AudioContextExc("no physical output ports available");
241
        }
242
243
        if (jack_connect (mClient, jack_port_name (mOutputPorts[0]), speakerPorts[0])) {
244
        throw cinder::audio::AudioContextExc("cannot connect output port 0");
245
        }
246
247
        if (jack_connect (mClient, jack_port_name (mOutputPorts[1]), speakerPorts[1])) {
248
        throw cinder::audio::AudioContextExc("cannot connect output port 1");
249
        }
250
251
        jack_free( mikePorts );
252
        jack_free( speakerPorts );
253
}
254
255
256
void OutputDeviceNodeJack::uninitialize()
257
{
258
    jack_client_close( mClient );
259
}
260
261
void OutputDeviceNodeJack::enableProcessing()
262
{
263
}
264
265
void OutputDeviceNodeJack::disableProcessing()
266
{
267
}
268
269
270
//-------------------------- InputDeviceNodeJack -------------------------------
271
272
273
InputDeviceNodeJack::InputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
274
    InputDeviceNode( device, format)
275
{
276
}
277
278
void InputDeviceNodeJack::initialize()
279
{
280
}
281
282
void InputDeviceNodeJack::uninitialize()
283
{
284
}
285
286
void InputDeviceNodeJack::enableProcessing()
287
{
288
}
289
290
void InputDeviceNodeJack::disableProcessing()
291
{
292
}
293
294
void InputDeviceNodeJack::process( Buffer *buffer )
295
{
296
    for( size_t chan = 0; chan < 2; chan++){
297
       copyFromJackPort(mInputPorts[chan], buffer->getChannel( chan ), buffer->getNumFrames() );
298
    }
299
}
300
301
} } } // namespace cinder::audio::linux