| 26 |
26 |
|
| 27 |
27 |
namespace cinder { namespace audio { namespace linux {
|
| 28 |
28 |
|
|
29 |
|
|
30 |
|
|
31 |
//--------------------------------------static utilities------------------------------------------
|
|
32 |
|
| 29 |
33 |
inline void zeroJackPort( jack_port_t *port, jack_nframes_t nframes )
|
| 30 |
34 |
{
|
|
35 |
// FIXME seg fault at shutdown
|
| 31 |
36 |
// memset(port, 0, sizeof(jack_default_audio_sample_t) * nframes);
|
| 32 |
37 |
}
|
| 33 |
38 |
|
|
39 |
// copy audio from node buffer to jack port
|
| 34 |
40 |
inline void copyToJackPort(jack_port_t *port, float *source, jack_nframes_t nframes )
|
| 35 |
41 |
{
|
| 36 |
|
// dest, source, n
|
| 37 |
42 |
jack_default_audio_sample_t *out;
|
| 38 |
43 |
out = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
|
| 39 |
44 |
|
| 40 |
45 |
memcpy( out, source, sizeof(jack_default_audio_sample_t) * nframes ) ;
|
| 41 |
46 |
}
|
| 42 |
47 |
|
|
48 |
// copy audio from jack port to node buffer
|
| 43 |
49 |
inline void copyFromJackPort(jack_port_t *port, float *dest, jack_nframes_t nframes )
|
| 44 |
50 |
{
|
| 45 |
|
// dest, source, n
|
| 46 |
51 |
jack_default_audio_sample_t *in;
|
| 47 |
52 |
in = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
|
| 48 |
53 |
|
| ... | ... | |
| 50 |
55 |
}
|
| 51 |
56 |
|
| 52 |
57 |
|
|
58 |
// -------------------------------OutputDeviceNodeJack-------------------------------------------
|
|
59 |
|
| 53 |
60 |
int OutputDeviceNodeJack::jackCallback(jack_nframes_t nframes, void* userData)
|
| 54 |
61 |
{
|
|
62 |
// retrieve user data
|
| 55 |
63 |
RenderData *renderData = static_cast<RenderData *>( userData );
|
| 56 |
64 |
|
| 57 |
65 |
OutputDeviceNodeJack *outputDeviceNode = static_cast<OutputDeviceNodeJack *>( renderData->outputNode );
|
| 58 |
66 |
|
| 59 |
67 |
auto ctx = renderData->outputNode->getContext();
|
| 60 |
68 |
if( ! ctx ) {
|
|
69 |
// this is from some cinder library code but it should not happen in Collidoscope as the context is set
|
| 61 |
70 |
for( size_t chan = 0; chan < 2; chan++)
|
| 62 |
|
// FIXME segfault at shutdown
|
| 63 |
71 |
zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
|
| 64 |
72 |
|
| 65 |
73 |
return 0;
|
| ... | ... | |
| 82 |
90 |
internalBuffer->zero();
|
| 83 |
91 |
|
| 84 |
92 |
ctx->preProcess();
|
|
93 |
// process the whole audio graph using by recursively pulling the input all the way to the top of the graph
|
| 85 |
94 |
outputDeviceNode->pullInputs( internalBuffer );
|
| 86 |
95 |
|
| 87 |
96 |
// 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 {
|
|
97 |
//if( outputDeviceNode->checkNotClipping() ){
|
|
98 |
//for( size_t chan = 0; chan < 2; chan++)
|
|
99 |
// zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
|
|
100 |
//}
|
|
101 |
//else {
|
| 93 |
102 |
for( size_t chan = 0; chan < 2; chan++)
|
| 94 |
103 |
copyToJackPort( outputDeviceNode->mOutputPorts[chan], internalBuffer->getChannel( chan ), nframes );
|
| 95 |
|
}
|
|
104 |
//}
|
| 96 |
105 |
|
| 97 |
106 |
ctx->postProcess();
|
| 98 |
107 |
|
| ... | ... | |
| 104 |
113 |
mInputDeviceNode = std::static_pointer_cast<InputDeviceNodeJack>(inputDeviceNode);
|
| 105 |
114 |
}
|
| 106 |
115 |
|
| 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 |
116 |
OutputDeviceNodeJack::OutputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
|
| 155 |
117 |
OutputDeviceNode( device, format),
|
| 156 |
118 |
mCinderContext( context )
|
| ... | ... | |
| 165 |
127 |
jack_options_t options = JackNullOption;
|
| 166 |
128 |
jack_status_t status;
|
| 167 |
129 |
|
| 168 |
|
// connect to JAck server
|
|
130 |
// connect to Jack server
|
| 169 |
131 |
mClient = jack_client_open (client_name, options, &status, server_name);
|
| 170 |
132 |
if( mClient == NULL){
|
| 171 |
133 |
|
| ... | ... | |
| 177 |
139 |
}
|
| 178 |
140 |
|
| 179 |
141 |
|
|
142 |
// prepare user data for callback
|
| 180 |
143 |
mRenderData.outputNode = this;
|
| 181 |
144 |
mRenderData.inputNode = mInputDeviceNode.get();
|
| 182 |
145 |
CI_ASSERT(mInputDeviceNode != nullptr);
|
| ... | ... | |
| 201 |
164 |
throw cinder::audio::AudioContextExc("no more JACK ports available");
|
| 202 |
165 |
}
|
| 203 |
166 |
|
| 204 |
|
// setup input ports
|
|
167 |
// setup input ports. Note that the reference to the input node is used.
|
| 205 |
168 |
mInputDeviceNode->mInputPorts[0] = jack_port_register (mClient, "input1",
|
| 206 |
169 |
JACK_DEFAULT_AUDIO_TYPE,
|
| 207 |
170 |
JackPortIsInput, 0);
|
| ... | ... | |
| 211 |
174 |
JackPortIsInput, 0);
|
| 212 |
175 |
|
| 213 |
176 |
|
| 214 |
|
/* Tell the JACK server that we are ready to roll. Our callback will start running now. */
|
|
177 |
/* Tell the Jack server that we are ready to roll. Our callback will start running now. */
|
| 215 |
178 |
if (jack_activate (mClient)) {
|
| 216 |
179 |
throw cinder::audio::AudioContextExc("cannot activate client");
|
| 217 |
180 |
}
|
| ... | ... | |
| 267 |
230 |
}
|
| 268 |
231 |
|
| 269 |
232 |
|
| 270 |
|
//-------------------------- InputDeviceNodeJack -------------------------------
|
|
233 |
//----------------------------------------- InputDeviceNodeJack ---------------------------------------------------
|
| 271 |
234 |
|
| 272 |
235 |
|
| 273 |
236 |
InputDeviceNodeJack::InputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
|
| ... | ... | |
| 291 |
254 |
{
|
| 292 |
255 |
}
|
| 293 |
256 |
|
|
257 |
// 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
|
| 294 |
259 |
void InputDeviceNodeJack::process( Buffer *buffer )
|
| 295 |
260 |
{
|
| 296 |
261 |
for( size_t chan = 0; chan < 2; chan++){
|
| ... | ... | |
| 298 |
263 |
}
|
| 299 |
264 |
}
|
| 300 |
265 |
|
|
266 |
|
|
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 |
|
| 301 |
312 |
} } } // namespace cinder::audio::linux
|