Revision 4:ab6db404403a
| CollidoscopeApp/include/ParticleController.h | ||
|---|---|---|
| 10 | 10 |
|
| 11 | 11 |
struct Particle {
|
| 12 | 12 |
|
| 13 |
ci::vec2 mCloudCenter; |
|
| 14 |
ci::vec2 mVel; |
|
| 15 |
float mCloudSize; |
|
| 13 |
ci::vec2 mCloudCenter; // initial positin of the particle |
|
| 14 |
ci::vec2 mVel; // velocity |
|
| 15 |
float mCloudSize; // how big is the area where particle float around. When a particle hits the |
|
| 16 |
// border of the area it gets deflected |
|
| 16 | 17 |
|
| 17 |
int mAge; |
|
| 18 |
int mLifespan; |
|
| 19 |
bool mFlyOver; |
|
| 18 |
int mAge; // when mAge == mLifeSpan the particle is disposed
|
|
| 19 |
int mLifespan; // how long a particle lives
|
|
| 20 |
bool mFlyOver; // some particles last longer and fly over the screen and reach the other user
|
|
| 20 | 21 |
|
| 21 | 22 |
}; |
| 22 | 23 |
|
| ... | ... | |
| 28 | 29 |
// current number of active particles |
| 29 | 30 |
size_t mNumParticles; |
| 30 | 31 |
|
| 31 |
ci::gl::VboRef mParticleVbo; |
|
| 32 |
ci::gl::VboRef mParticleVbo; // virtual buffer object
|
|
| 32 | 33 |
ci::gl::BatchRef mParticleBatch; |
| 33 | 34 |
|
| 34 | 35 |
public: |
| CollidoscopeApp/src/ParticleController.cpp | ||
|---|---|---|
| 10 | 10 |
mNumParticles( 0 ) |
| 11 | 11 |
|
| 12 | 12 |
{
|
| 13 |
// uses Cinder (and OpenGL) virtual buffer object based drawing |
|
| 14 |
// see ParticleSphereCPU example in Cinder library |
|
| 15 |
|
|
| 13 | 16 |
mParticles.assign( kMaxParticles, Particle() ); |
| 14 | 17 |
mParticlePositions.assign( kMaxParticles, vec2( -1, -1 ) ); |
| 15 | 18 |
|
| ... | ... | |
| 20 | 23 |
|
| 21 | 24 |
auto mesh = gl::VboMesh::create( mParticlePositions.size(), GL_POINTS, { { particleLayout, mParticleVbo } } );
|
| 22 | 25 |
|
| 26 |
// creates glsl program to run the batch with |
|
| 23 | 27 |
#if ! defined( CINDER_GL_ES ) |
| 24 | 28 |
auto glsl = gl::GlslProg::create( gl::GlslProg::Format() |
| 25 | 29 |
.vertex( CI_GLSL( 150, |
| ... | ... | |
| 70 | 74 |
|
| 71 | 75 |
void ParticleController::updateParticles() |
| 72 | 76 |
{
|
| 77 |
// update the positions of the particles and dispose them if they're reached their timespan |
|
| 73 | 78 |
for ( size_t i = 0; i < mNumParticles; i++ ){
|
| 74 | 79 |
|
| 75 | 80 |
Particle &particle = mParticles[i]; |
| ... | ... | |
| 95 | 100 |
} |
| 96 | 101 |
} |
| 97 | 102 |
|
| 103 |
// Copy particle data onto the GPU. |
|
| 104 |
// Map the GPU memory and write over it. |
|
| 98 | 105 |
void *gpuMem = mParticleVbo->mapReplace(); |
| 99 | 106 |
memcpy( gpuMem, mParticlePositions.data(), mParticlePositions.size() * sizeof( vec2 ) ); |
| 100 | 107 |
mParticleVbo->unmap(); |
| ... | ... | |
| 102 | 109 |
|
| 103 | 110 |
void ParticleController::addParticles(int amount, const vec2 &initialLocation, const float cloudSize) |
| 104 | 111 |
{
|
| 112 |
// reduce the particles liearly to the total number of particles already present |
|
| 113 |
// the more particles aleary present the less particle are added |
|
| 105 | 114 |
int reduction = ci::lmap<int>(mNumParticles, 0, kMaxParticles, 0, kMaxParticleAdd); |
| 106 | 115 |
amount -= reduction; |
| 107 | 116 |
|
| 108 | 117 |
if ( mNumParticles + amount > kMaxParticles ){
|
| 109 |
//return;
|
|
| 118 |
//a.k.a. return if reached kMaxParticles
|
|
| 110 | 119 |
amount = kMaxParticles - mNumParticles; |
| 111 | 120 |
} |
| 112 | 121 |
|
| CollidoscopeApp/src/Wave.cpp | ||
|---|---|---|
| 16 | 16 |
mChunks.emplace_back( i ); |
| 17 | 17 |
} |
| 18 | 18 |
|
| 19 |
// init cinder batch drawing |
|
| 19 | 20 |
auto lambert = gl::ShaderDef().color(); |
| 20 | 21 |
gl::GlslProgRef shader = gl::getStockShader( lambert ); |
| 21 | 22 |
mChunkBatch = gl::Batch::create( geom::Rect( ci::Rectf( 0, 0, Chunk::kWidth, 1 ) ), shader ); |
| ... | ... | |
| 63 | 64 |
|
| 64 | 65 |
double elapsed = now - itr->second.lastUpdate; |
| 65 | 66 |
|
| 67 |
// A chunk of audio corresponds to a certain time according to sample rate. |
|
| 68 |
// Use elapsed time to advance through chunks so that the cursor is animated |
|
| 69 |
// and goes from start to end of the seleciton in the time span of the grain |
|
| 66 | 70 |
itr->second.pos = mSelection.getStart() + int( elapsed / secondsPerChunk ); |
| 67 | 71 |
|
| 68 |
/* check we don't go too far off */
|
|
| 72 |
// check we don't go too far off
|
|
| 69 | 73 |
if (itr->second.pos > mSelection.getEnd()){
|
| 70 | 74 |
itr->second.pos = Cursor::kNoPosition; |
| 71 | 75 |
} |
| 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