changeset 4:ab6db404403a

commented JackDevice
author Fiore Martin <f.martin@qmul.ac.uk>
date Wed, 13 Jul 2016 12:31:37 +0200
parents 7fb593d53361
children 75b744078d66
files CollidoscopeApp/include/ParticleController.h CollidoscopeApp/src/ParticleController.cpp CollidoscopeApp/src/Wave.cpp JackDevice/ContextJack.cpp JackDevice/ContextJack.h JackDevice/DeviceManagerJack.cpp JackDevice/DeviceManagerJack.h
diffstat 7 files changed, 119 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/CollidoscopeApp/include/ParticleController.h	Tue Jul 12 18:29:38 2016 +0200
+++ b/CollidoscopeApp/include/ParticleController.h	Wed Jul 13 12:31:37 2016 +0200
@@ -10,13 +10,14 @@
 
     struct Particle {
 
-        ci::vec2	mCloudCenter;
-        ci::vec2	mVel;
-        float       mCloudSize;
+        ci::vec2	mCloudCenter; // initial positin of the particle 
+        ci::vec2	mVel;         // velocity 
+        float       mCloudSize;   // how big is the area where particle float around. When a particle hits the 
+                                  //   border of the area it gets deflected 
 
-        int			mAge;
-        int			mLifespan;
-        bool        mFlyOver;
+        int			mAge;      // when mAge == mLifeSpan the particle is disposed 
+        int			mLifespan; // how long a particle lives
+        bool        mFlyOver;  // some particles last longer and fly over the screen and reach the other user
 
     };
 
@@ -28,7 +29,7 @@
     // current number of active particles
     size_t mNumParticles;
 
-    ci::gl::VboRef			mParticleVbo;
+    ci::gl::VboRef			mParticleVbo;    // virtual buffer object 
     ci::gl::BatchRef		mParticleBatch;
 
  public:
--- a/CollidoscopeApp/src/ParticleController.cpp	Tue Jul 12 18:29:38 2016 +0200
+++ b/CollidoscopeApp/src/ParticleController.cpp	Wed Jul 13 12:31:37 2016 +0200
@@ -10,6 +10,9 @@
 mNumParticles( 0 )
 
 {
+    // uses Cinder (and OpenGL) virtual buffer object based drawing
+    // see ParticleSphereCPU example in Cinder library 
+     
     mParticles.assign( kMaxParticles, Particle() );
     mParticlePositions.assign( kMaxParticles, vec2( -1, -1 ) );
 
@@ -20,6 +23,7 @@
 
     auto mesh = gl::VboMesh::create( mParticlePositions.size(), GL_POINTS, { { particleLayout, mParticleVbo } } );
 
+    // creates glsl program to run the batch with 
 #if ! defined( CINDER_GL_ES )
     auto glsl = gl::GlslProg::create( gl::GlslProg::Format()
         .vertex( CI_GLSL( 150,
@@ -70,6 +74,7 @@
 
 void ParticleController::updateParticles()
 {
+    // update the positions of the particles and dispose them if they're reached their timespan
     for ( size_t i = 0; i < mNumParticles; i++ ){
 
         Particle &particle = mParticles[i];
@@ -95,6 +100,8 @@
         }
     }
 
+    // Copy particle data onto the GPU.
+	// Map the GPU memory and write over it.
     void *gpuMem = mParticleVbo->mapReplace();
     memcpy( gpuMem, mParticlePositions.data(), mParticlePositions.size() * sizeof( vec2 ) );
     mParticleVbo->unmap();
@@ -102,11 +109,13 @@
 
 void ParticleController::addParticles(int amount, const vec2 &initialLocation, const float cloudSize)
 {
+    // reduce the particles liearly to the total number of particles already present 
+    // the more particles aleary present the less particle are added
     int reduction = ci::lmap<int>(mNumParticles, 0, kMaxParticles, 0, kMaxParticleAdd);
     amount -= reduction;
 
     if ( mNumParticles + amount > kMaxParticles ){
-		//return;
+		//a.k.a. return if reached kMaxParticles 
         amount = kMaxParticles - mNumParticles;
 	}
 
--- a/CollidoscopeApp/src/Wave.cpp	Tue Jul 12 18:29:38 2016 +0200
+++ b/CollidoscopeApp/src/Wave.cpp	Wed Jul 13 12:31:37 2016 +0200
@@ -16,6 +16,7 @@
 		mChunks.emplace_back( i );
 	}
 
+    // init cinder batch drawing
     auto lambert = gl::ShaderDef().color();
     gl::GlslProgRef shader = gl::getStockShader( lambert );
     mChunkBatch = gl::Batch::create( geom::Rect( ci::Rectf( 0, 0, Chunk::kWidth, 1 ) ), shader );
@@ -63,9 +64,12 @@
 
         double elapsed = now - itr->second.lastUpdate;
 
+        // A chunk of audio corresponds to a certain time according to sample rate.
+        // Use elapsed time to advance through chunks so that the cursor is animated 
+        // and goes from start to end of the seleciton in the time span of the grain 
         itr->second.pos = mSelection.getStart() + int( elapsed / secondsPerChunk );
 
-        /* check we don't go too far off */
+        // check we don't go too far off 
         if (itr->second.pos > mSelection.getEnd()){
             itr->second.pos = Cursor::kNoPosition;
         }
--- a/JackDevice/ContextJack.cpp	Tue Jul 12 18:29:38 2016 +0200
+++ b/JackDevice/ContextJack.cpp	Wed Jul 13 12:31:37 2016 +0200
@@ -26,23 +26,28 @@
 
 namespace cinder { namespace audio { namespace linux {
 
+
+
+//--------------------------------------static utilities------------------------------------------
+
 inline void zeroJackPort( jack_port_t *port, jack_nframes_t nframes )
 {   
+  // FIXME seg fault at shutdown 
   // memset(port, 0, sizeof(jack_default_audio_sample_t) * nframes); 
 }
 
+// copy audio from node buffer to jack port 
 inline void copyToJackPort(jack_port_t *port, float *source, jack_nframes_t nframes )
 {
-    // dest, source, n 
     jack_default_audio_sample_t *out;
     out = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
 
     memcpy( out, source, sizeof(jack_default_audio_sample_t) * nframes ) ;
 }
 
+// copy audio from jack port to  node buffer
 inline void copyFromJackPort(jack_port_t *port, float *dest, jack_nframes_t nframes )
 {
-    // dest, source, n 
     jack_default_audio_sample_t *in;
     in = (jack_default_audio_sample_t *) jack_port_get_buffer( port, nframes );
 
@@ -50,16 +55,19 @@
 }
 
 
+// -------------------------------OutputDeviceNodeJack-------------------------------------------
+
 int OutputDeviceNodeJack::jackCallback(jack_nframes_t nframes, void* userData)
 {
+    // retrieve user data 
 	RenderData *renderData = static_cast<RenderData *>( userData );
 
 	OutputDeviceNodeJack *outputDeviceNode = static_cast<OutputDeviceNodeJack *>( renderData->outputNode );
 
 	auto ctx = renderData->outputNode->getContext();
 	if( ! ctx ) {
+        // this is from some cinder library code but it should not happen in Collidoscope as the context is set
         for( size_t chan = 0; chan < 2; chan++)
-            // FIXME segfault at shutdown 
 		    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
 
 		return 0;
@@ -82,17 +90,18 @@
 	internalBuffer->zero();
 
 	ctx->preProcess();
+    // process the whole audio graph using by recursively pulling the input all the way to the top of the graph 
 	outputDeviceNode->pullInputs( internalBuffer );
     
 	// if clip detection is enabled and buffer clipped, silence it
-	if( false && outputDeviceNode->checkNotClipping() ){
-        for( size_t chan = 0; chan < 2; chan++)
-		    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
-	} 
-    else {
+	//if( outputDeviceNode->checkNotClipping() ){
+        //for( size_t chan = 0; chan < 2; chan++)
+		//    zeroJackPort( outputDeviceNode->mOutputPorts[chan], nframes );
+	//} 
+    //else {
         for( size_t chan = 0; chan < 2; chan++)
             copyToJackPort( outputDeviceNode->mOutputPorts[chan], internalBuffer->getChannel( chan ), nframes  );
-    }
+    //}
 
 	ctx->postProcess();
 
@@ -104,53 +113,6 @@
     mInputDeviceNode = std::static_pointer_cast<InputDeviceNodeJack>(inputDeviceNode);
 }
 
-ContextJack::ContextJack()
-{
-
-}
-
-ContextJack::~ContextJack()
-{
-
-}
-
-
-OutputDeviceNodeRef	ContextJack::createOutputDeviceNode( const DeviceRef &device, const Node::Format &format )
-{
-	
-    if( mOutputDeviceNode  == nullptr ) {
-        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
-
-        mOutputDeviceNode = makeNode( new OutputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
-
-        if( mInputDeviceNode != nullptr){
-            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
-            castedOutputDeviceNode->setInput( mInputDeviceNode );   
-        }
-    }
-
-	return mOutputDeviceNode;
-}
-
-InputDeviceNodeRef ContextJack::createInputDeviceNode( const DeviceRef &device, const Node::Format &format  )
-{
-    if( mInputDeviceNode  == nullptr ) {
-        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
-
-        mInputDeviceNode = makeNode( new InputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
-
-        if( mOutputDeviceNode != nullptr){
-            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
-            castedOutputDeviceNode->setInput( mInputDeviceNode );   
-        }
-    }
-
-	return mInputDeviceNode;
-}
-
-
-// OutputDeviceNodeJack 
-
 OutputDeviceNodeJack::OutputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
     OutputDeviceNode( device, format),
     mCinderContext( context )
@@ -165,7 +127,7 @@
     jack_options_t options = JackNullOption;
     jack_status_t status;
 
-    // connect to JAck server 
+    // connect to Jack server 
     mClient = jack_client_open (client_name, options, &status, server_name);
     if( mClient == NULL){
 
@@ -177,6 +139,7 @@
     }
 
     
+    // prepare user data for callback 
     mRenderData.outputNode = this;
     mRenderData.inputNode = mInputDeviceNode.get();
     CI_ASSERT(mInputDeviceNode != nullptr);
@@ -201,7 +164,7 @@
         throw cinder::audio::AudioContextExc("no more JACK ports available");
      }
 
-    // setup input ports 
+    // setup input ports. Note that the reference to the input node is used. 
     mInputDeviceNode->mInputPorts[0] = jack_port_register (mClient, "input1",
                        JACK_DEFAULT_AUDIO_TYPE,
                        JackPortIsInput, 0);
@@ -211,7 +174,7 @@
                        JackPortIsInput, 0);
 
 
-    /* Tell the JACK server that we are ready to roll.  Our callback will start running now. */
+    /* Tell the Jack server that we are ready to roll.  Our callback will start running now. */
     if (jack_activate (mClient)) {
         throw cinder::audio::AudioContextExc("cannot activate client");
     }
@@ -267,7 +230,7 @@
 }
 
 
-//-------------------------- InputDeviceNodeJack -------------------------------
+//----------------------------------------- InputDeviceNodeJack ---------------------------------------------------
 
 
 InputDeviceNodeJack::InputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context ):
@@ -291,6 +254,8 @@
 {
 }
 
+// This is called when the output node pull all the inputs in the jack callback. 
+// Takes audio interface input from the jack port and copies it in the node buffer
 void InputDeviceNodeJack::process( Buffer *buffer )
 {
     for( size_t chan = 0; chan < 2; chan++){
@@ -298,4 +263,50 @@
     }
 }
 
+
+//-------------------------------------------ContextJack-----------------------------------------------------------
+
+OutputDeviceNodeRef	ContextJack::createOutputDeviceNode( const DeviceRef &device, const Node::Format &format )
+{
+	
+    if( mOutputDeviceNode  == nullptr ) {
+        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
+
+        mOutputDeviceNode = makeNode( new OutputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
+
+        // the output device node must have a reference to input device node. In OutputDeviceNodeJack::initialize() 
+        // the input node is passed the jack input ports that it will use to fetch incoming audio from the audio interface
+        // Whichever node (input or ouput) gets initialized after the other, executes the following block:
+        if( mInputDeviceNode != nullptr){
+            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
+            castedOutputDeviceNode->setInput( mInputDeviceNode );   
+        }
+    }
+
+	return mOutputDeviceNode;
+}
+
+InputDeviceNodeRef ContextJack::createInputDeviceNode( const DeviceRef &device, const Node::Format &format  )
+{
+    if( mInputDeviceNode  == nullptr ) {
+        auto thisRef = std::static_pointer_cast<ContextJack>( shared_from_this() );
+
+        mInputDeviceNode = makeNode( new InputDeviceNodeJack( device, Node::Format().channels(2), thisRef ) ) ;
+
+        // the output device node must have a reference to input device node. In OutputDeviceNodeJack::initialize() 
+        // the input node is passed the jack input ports that it will use to fetch incoming audio from the audio interface
+        // Whichever node (input or ouput) gets initialized after the other, executes the following block:
+        if( mOutputDeviceNode != nullptr){
+            auto castedOutputDeviceNode = std::static_pointer_cast<OutputDeviceNodeJack>( mOutputDeviceNode );
+            castedOutputDeviceNode->setInput( mInputDeviceNode );   
+        }
+    }
+
+	return mInputDeviceNode;
+}
+
+
+
+
+
 } } } // namespace cinder::audio::linux
--- a/JackDevice/ContextJack.h	Tue Jul 12 18:29:38 2016 +0200
+++ b/JackDevice/ContextJack.h	Wed Jul 13 12:31:37 2016 +0200
@@ -31,10 +31,16 @@
 class ContextJack;
 class InputDeviceNodeJack;
 
+/**
+ * OutputNode (as in the cinder::audio::OutputNode) that sends audio to the sound card using the jack audio callback method. 
+ */ 
 class OutputDeviceNodeJack : public OutputDeviceNode {
   public:
 	OutputDeviceNodeJack( const DeviceRef &device, const Format &format, const std::shared_ptr<ContextJack> &context );
 
+    /** Gives this output node a reference to the JackInputNode. 
+     *  In initialize() the reference is used to give the input node access to jack input ports 
+     */
     void setInput(InputDeviceNodeRef inputDeviceNode);
 
   protected:
@@ -45,11 +51,15 @@
 	bool supportsProcessInPlace() const	override	{ return false; }
 
   private:
+    // this is called by jack in the audio thread at every tick of the sound card 
     static int jackCallback(jack_nframes_t nframes, void* userData);
 
 
 	void renderToBufferFromInputs();
 
+    /**
+     * RenderData is passed as user_data to jack when the jack process callback is installed
+     */ 
     struct RenderData{
         RenderData() : inputNode(nullptr), outputNode(nullptr), context(nullptr){}
         ~RenderData() { inputNode = nullptr; outputNode = nullptr;  context = nullptr; }
@@ -67,6 +77,9 @@
     std::shared_ptr<InputDeviceNodeJack> mInputDeviceNode;
 };
 
+/**
+ * InputNode (as in the cinder::audio::OutputNode) that reads audio from the sound card using the jack audio callback method. 
+ */ 
 class InputDeviceNodeJack : public InputDeviceNode {
   friend OutputDeviceNodeJack;
 
@@ -86,8 +99,8 @@
 
 class ContextJack : public Context {
   public:
-	ContextJack();
-	virtual ~ContextJack();
+	ContextJack() {}
+	virtual ~ContextJack() {}
     
 
 	OutputDeviceNodeRef	createOutputDeviceNode( const DeviceRef &device, const Node::Format &format = Node::Format() ) override;
@@ -101,3 +114,4 @@
 };	
 
 } } } // namespace cinder::audio::linux
+
--- a/JackDevice/DeviceManagerJack.cpp	Tue Jul 12 18:29:38 2016 +0200
+++ b/JackDevice/DeviceManagerJack.cpp	Wed Jul 13 12:31:37 2016 +0200
@@ -32,11 +32,13 @@
 DeviceManagerJack::DeviceManagerJack() 
 {
 
+    // hardcoded devices. They are always JackIn and JackOut 
     mDevices.push_back( addDevice("JackIn") );
     mDevices.push_back( addDevice("JackOut") );
 
     jack_status_t status;
     
+    // open a jack client, get info and close
 	jack_client_t *client = jack_client_open ("device info", JackNullOption, &status, NULL);
     if( client == NULL){
 
@@ -73,6 +75,7 @@
     return mDevices[0];
 }
 
+//hardcoded name same as key 
 std::string DeviceManagerJack::getName( const DeviceRef &device )
 {
     return device->getKey();
--- a/JackDevice/DeviceManagerJack.h	Tue Jul 12 18:29:38 2016 +0200
+++ b/JackDevice/DeviceManagerJack.h	Wed Jul 13 12:31:37 2016 +0200
@@ -27,6 +27,12 @@
 
 namespace cinder { namespace audio { namespace linux {
 
+/**
+ * DeviceManager ( as in cinder::audio::DeviceManager ) that handle the hardware device through the jack library.
+ * Note that this is not suitable for general purpose use. Most of the functionalities are indeed hard coded
+ * just to suit Collidoscope needs. In particular only two input and two output ports are assumed. 
+ *
+ */ 
 class DeviceManagerJack : public DeviceManager {
   public: