Mercurial > hg > opencollidoscope
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: