Revision 4:ab6db404403a JackDevice

View differences:

JackDevice/ContextJack.cpp
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
JackDevice/ContextJack.h
31 31
class ContextJack;
32 32
class InputDeviceNodeJack;
33 33

  
34
/**
35
 * OutputNode (as in the cinder::audio::OutputNode) that sends audio to the sound card using the jack audio callback method. 
36
 */ 
34 37
class OutputDeviceNodeJack : public OutputDeviceNode {
35 38
  public:
36 39
	OutputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context );
37 40

  
41
    /** Gives this output node a reference to the JackInputNode. 
42
     *  In initialize() the reference is used to give the input node access to jack input ports 
43
     */
38 44
    void setInput(InputDeviceNodeRef inputDeviceNode);
39 45

  
40 46
  protected:
......
45 51
	bool supportsProcessInPlace() const	override	{ return false; }
46 52

  
47 53
  private:
54
    // this is called by jack in the audio thread at every tick of the sound card 
48 55
    static int jackCallback(jack_nframes_t nframes, void* userData);
49 56

  
50 57

  
51 58
	void renderToBufferFromInputs();
52 59

  
60
    /**
61
     * RenderData is passed as user_data to jack when the jack process callback is installed
62
     */ 
53 63
    struct RenderData{
54 64
        RenderData() : inputNode(nullptr), outputNode(nullptr), context(nullptr){}
55 65
        ~RenderData() { inputNode = nullptr; outputNode = nullptr;  context = nullptr; }
......
67 77
    std::shared_ptr<InputDeviceNodeJack> mInputDeviceNode;
68 78
};
69 79

  
80
/**
81
 * InputNode (as in the cinder::audio::OutputNode) that reads audio from the sound card using the jack audio callback method. 
82
 */ 
70 83
class InputDeviceNodeJack : public InputDeviceNode {
71 84
  friend OutputDeviceNodeJack;
72 85

  
......
86 99

  
87 100
class ContextJack : public Context {
88 101
  public:
89
	ContextJack();
90
	virtual ~ContextJack();
102
	ContextJack() {}
103
	virtual ~ContextJack() {}
91 104
    
92 105

  
93 106
	OutputDeviceNodeRef	createOutputDeviceNode( const DeviceRef &device, const Node::Format &format = Node::Format() ) override;
......
101 114
};	
102 115

  
103 116
} } } // namespace cinder::audio::linux
117

  
JackDevice/DeviceManagerJack.cpp
32 32
DeviceManagerJack::DeviceManagerJack() 
33 33
{
34 34

  
35
    // hardcoded devices. They are always JackIn and JackOut 
35 36
    mDevices.push_back( addDevice("JackIn") );
36 37
    mDevices.push_back( addDevice("JackOut") );
37 38

  
38 39
    jack_status_t status;
39 40
    
41
    // open a jack client, get info and close
40 42
	jack_client_t *client = jack_client_open ("device info", JackNullOption, &status, NULL);
41 43
    if( client == NULL){
42 44

  
......
73 75
    return mDevices[0];
74 76
}
75 77

  
78
//hardcoded name same as key 
76 79
std::string DeviceManagerJack::getName( const DeviceRef &device )
77 80
{
78 81
    return device->getKey();
JackDevice/DeviceManagerJack.h
27 27

  
28 28
namespace cinder { namespace audio { namespace linux {
29 29

  
30
/**
31
 * DeviceManager ( as in cinder::audio::DeviceManager ) that handle the hardware device through the jack library.
32
 * Note that this is not suitable for general purpose use. Most of the functionalities are indeed hard coded
33
 * just to suit Collidoscope needs. In particular only two input and two output ports are assumed. 
34
 *
35
 */ 
30 36
class DeviceManagerJack : public DeviceManager {
31 37
  public:
32 38

  

Also available in: Unified diff