Playmodes 2 v0.01 PDF Print E-mail
Written by arturo castro   
Wednesday, 31 December 2008 15:41

Here's the first public release of the new Playmodes architecture. Playmodes is developed with openframeworks.

The new Playmodes is based in the openframeworks event system, that is still under development and for what this software is being also a test platform as it has diferent kinds of events and is also threaded.

You will need a modified openframeworks version that you can find in the links at the end of this post. This openframeworks version is not oficial and will be changed for sure for 0.06 so don't rely on it for any development. Also expect some changes in the event system in Playmodes. Apart from that, the public interface should remain more or less unchanged in the future.

Here's a brief explanation of the object model and it's use... + download

 

 

pipeline: in this directory you will find the base classes from what most of the Playmodes classes inherit. There's an object model for video and another one for audio but the syntax is really similar between them.
  • Source:The classes that inherit from AudioSource or VideoSource are meant to be data providers for other objects in the pipeline. A source can provide data through event notification to other objects or through method call from other object that needs data with it's own timing.
event notification: a buffer receives frames from a grabber whenever there's a new frame available so the grabber notifies a new frame and the buffer stores it.
To add and remove listeners to an specific source you can call:
    void addListener(VideoFrameListener * listener)
    void removeListener(VideoFrameListener * listener)
Also most of the objects that receive data in Playmodes have can be created by passing a source in it's constructor, so most of the times you won't need this methods.
Sources have an event named newFrameEvent so if you implement you're own sources you will need to notify this method whenever new data is available:
    newFrameEvent.notify(this, frame);

This way every listener attached to a source will receive the new data.

method call: a renderer needs a new frame in the draw call so it calls it source getNextFrame method to get the frame and draw it.
   VideoFrame * getNextVideoFrame()

Depending on the kind of source this method will return a frame. Most of the times, it returns the last frame available, except for headers were it provide the frame at the position the header is pointing.

    int getFps()

This method returns the frames per second the source is using. Theoretically different sources can have different fps but in the current implementation every object in the pipeline gets its fps from the previous one so the fps for all of them is that of the grabber and the physical device providing the data.

 

  • Sink : The classes inheriting from this one, are that ones that receive data for storing it as buffers, processing it as filters or rendering it as renderers. Every sink is a frame listener so it's able to be notified of a newFrame event so the only method that a sink needs to implement is:
                  void newVideoFrame(VideoFrame & frame)

Although it's not mandatory, most of the sinks also implement a constructor like:

    VideoSink(VideoSource* source)

So it can register as a listener for the newFrame event from it's creation and it can receive new frames when they are available.

 

 

grabbers : in this directory you can find the audio and video grabbers. This classes are responsible for grabbing data from a physical device or file and provide that data to other objects. In the current implementation these are the only classes that are only sources. Most of the rest of the classes are both sources and sinks (buffers, headers and filters) or just sins (renderers).
  • VideoGrabber: this class extends ofxAdvVideo to be able to provide video frames from both physical devices or video files. ofxAdvVideo is just a wrapper over VideoGrabber and VideoPlayer, in the case of linux also Pierre Proske's ofxDVGrabber. Depending on the initialization call it will work as one of them. This class will be substituted at some point when openframeworks provide some object hierarchy for video classes
  • AudioGrabber: this class inherits from openframeworks' ofAudioListener and registers to ofEvents::audioReceived to be able to receive audio data and provide it to the rest of the objecs in Playmodes in a more playmodish way. Audio is divided in audio frames (you can call them grains). By now the audio frame size is that of the soundcard buffer used in the openframeworks audio initialization with the ofSoundStreamSetup call. This will change in the future to a continous data stream so audiograin size can be adjusted when recovering frames from a buffer.

buffers: audio and video buffers store a limited amount of data. Whenever a buffer is full, it begins to remove the first frames to be able to add the new ones. The removal of a frame is delayed till it's not used anymore so there's no problem with threaded applications. Take a look to the frame description to know how it works, but related to buffers the only thing you need to know is they automatically retain frames when you ask for them, so if you recover a frame directly from a buffer you just need to release it when you finish using it.
Buffers are prepared to work in threaded environments. Apart from the delayed removal of frames they also provide lock and unlock methods. Most of the times you won't need to use them, they're used from headers so position calculations based on buffer size are thread safe.
To recover data from a buffer you can use the following methods:
    VideoFrame * getVideoFrame(int position);          // frame number in the buffer
    VideoFrame * getVideoFrame(ofTimeDiff time);   // frame at n microseconds from the end of the buffer
    VideoFrame * getVideoFrame(float pct);             // % of the buffer
    VideoFrame * getNextVideoFrame();                  // the last video frame in the buffer

ofTimeDiff is indeed poco's TimeDiff, and you can just use an integer number to call that version of getFrame.

Most of the times you won't need this methods. instead you will create a header, that can be set to provide frames from a buffer at diferent speeds, delays, loop modes...

Buffers are 'position safe'  so if  you try to access a position that doesn't exists you will get the nearest frame (the beginning or the end of the buffer)

Buffers extend Source and Sink. They provide a constructor that receives a Source and register to it's newFrame event so they receive the data as soon as it's avalable.

You can freeze a buffer by calling it's stop method so although it's asociated source continues generating data, the buffer won't store it. When you want to continue storing data, just call resume:

    void stop();                                    // stop receiving new frames
    void resume();                              // continue receiving new frames

The size of a buffer is determined from a constant defined in it's header, so it's hardcoded andby now cannot be changed at runtime. This has been a design decision as we think when you're running Playmodes you will adjust this variable to the maimum ram you have available in your computer. At some point this will be changed to an xml config file, but this won't affect your current code.

 

AVHeaders: This classes are usually the prefered way of accessing a buffer. They have methods for setting their speed, delay from the end of the buffer, loop and in/out position. The position can be also setted from another class, in the example app that ships with the current version, this is used to set it with osc messages from other applications.
This is one of the key concepts in the new architecture, separating the position control from the previous core object we gain in flexibility in the whole system as from here you can connect all kind of filters, renderers and in general any sink with different timing over the same buffer.
By now headers doesn't notify the new frame event as in most implementations, from here the timing will be controlled by the opengl draw cycle or the audioRequested event. This can be limiting if you need to connect certain kind of sinks to a header like filters or another buffer. In those cases renderers will be responsible for calling that sinks before drawing or sending audio data to the sound card. 
This is the most important methods in the public interface of a video header (the audio one is exactly the same):
    VideoHeader(VideoBuffer *buffer)

The constructor, although most sinks in Playmodes have a constructor to receive data from any kind of source, headers are meant to be connected only to a buffer.

    void draw()

This method doesn't draw the frame pointed by the header but the state of it, it's position in the buffer, the in and out position and in the case of the audio header the density of the returned frame.

    VideoFrame * getNextVideoFrame()

Returns the frame in the buffer at the current position of the header.

    int getNextPosition()

Although this method is mainly used inside of the same header to know what frame it has to return, you can call it to know what frame of the buffer the header is pointing at.

    float speed;
    int delay;
    float in, out;
    bool loopMode; 

This variables are used to set the different parameters of the header, they determine what frame will be returned when you call the getNextFrame method.

    void setPct(float & pct)
    void incrPct(float & incr)

This methods are used to set the position of the buffer in percentage(0 to 1) of the current buffer size. In the future we will provide similar methods to set the position in ms. In the example application provided with the current version this are used to set the position from external applications with osc messages.

 

 

renderers: this classes are ment to draw video frames or send audio frames to the soundcard. Currently there's only renderers for video as the current audio implementation is really simple so we are just using the main app (or any other audioRequested listener) to 'render' audio.
  • VideoRenderer: There's a simple renderer that receives a video source in it's constructor and renders the current frame from that source on the opengl draw cycle. It has also some methods and properties to set the draw position in 3D, this allows to set all this variables by using different methods (like the perlin noise generator used in the example app) and allows for different emergent behaivours:
    void draw()

Draws the current frame affected by the following properties:

    float scale
    float rotationZ, rotationX, rotationY
    float x, y, z
    float anchorX, anchorY      // to set the rotation origin of the renderer
    int alpha
    int tintR, tintG, tintB

    bool minmaxBlend // to set the blending method to min or max instead of add

    void setDraw(bool isDrawing) // allows to set the renderer not to draw
// when it's draw method is called
 
  • MultixRenderer: This class implements the previous Multix mode with some additional 3D position and time controls. It receives a buffer and an initial number of headers and create that number of headers and the same number of renderers. You can access all that headers and renderers from outside the class to modify they're properties further than the multix renderer controls allow.

The controls provide the same properties as a simple renderer, when you set some of those properties, in the next update cycle, the first renderer is set with that property and the rest of the properties copy the property of the next from the last one giving a kind of trailing effect.
It also provides some offsets in the rotation, delay, speed.. from the first renderer to the next.
 
  • RecLoopRenderer: This class implements the previous RecLoop mode. Combining it with a header and a simple renderer you'll get all the new 3D and timing effects provided by them.
You need to pass a buffered video source (usually a header) and a live input (usually a grabber) and it will modify the original frames in the buffer adding to them the live input using a max or min blend equation depending on how you set the mimaxBlend property.

 
filters : This folder contains the basic VideoFilter that you'll need to extend to provide video filter effects. A filter is both a source and a sink, it receives frames from any video source, process them and notifies the next element in the pipeline passing the modified frame. A basic video filter has this public interface:
    VideoFrame * getNextVideoFrame()

Returns the last processed videoFrame, if you rely on event notification in your filter, you can just store as a property the last processed frame and return it on this method call.

    void newVideoFrame(VideoFrame & frame)
This method is not meant to be directly called, it's just the method called on the newFrame notification from the source that sends data to the filter. Whenever a new frame arrives, the filter process it and notifies its own newFrameEvent passing the modifed frame.
The current implementation has the problem that if you don't connect the filter to a sink buffer, you won't be able to use the event system as the renderers needs to use they're own timing because of the thread unsafeness of the opengl system. A posible solution to this should be implementing a new kind of buffer that only stores frames till they're required, this way a filter can notify the newFrame event whenever a new processed frame is available, this temporary buffer will store that frame till a renderer (or any other sink) asks for it by calling the getNextVideoFrame metho. This buffer can be implemented in the VideoFilter base class although having it in a separate class should be the way to go.

  • FreeFrameFilter: As an example filter implementation you will find a Playmodes wrapper for FreeFrame 1.0. This filter receives a video source in it's constructor and register to it's new frame event in it's creation. Then you can call it's loadPlugin and setParameter methods to configure it's behaivour. You can use any FF 1.0 plugin, that requires only one input.
 
frames : The classes in this directory are the most basic data store in Playmodes. We've used the same paradigm and naming both from video and audio so the syntax is more uniform.
  • VideoFrame: It receives a byte array with the video frame data and the height and width in it's constructor. It stores that data in memory and also in an opengl texture. It's prepared to avoid the problems with opengl unsafeness by uploading the data to the texture only in the openframeworks update event. Whenever a new VideoFrame is created, it registers itself to the update event and only then upload the data to an ofTexture.
It's also prepared to be deleted only when no object is using it anymore. For that it extends ofxObjCPointer. This super useful openframeworks addon from Memo Atken provides a simple way of controlling when a class is not used anymore and only then delete it. For that it provides retain and release method. In Playmodes, buffers automatically retain frames when you ask for them so if you directly read them from a buffer or a header you don't need to retain them, only call they're release method when you don't use them anymore.
Also to be thread safe, the VideoFrame has to delete it's texture on deletion so instead of deleting the object just when the use counter arrives to 0, it registers to the openframeworks update event and only then it deletes itself.
Frames has an static counter that is updated on every frame creation and deletion for debugging purposes. This allows to know how many frames exists currently in memory.
  • AudioFrame: The audioFrame works exactly the same as the video one. The only specifics in the audio version, is it doesn't need to be thread safe (in the sense it doesn't contains an opengl texture). Also on creation it calculates the average value of the data it contains for wave visualization purposes.
  • Envelopes: The Playmodes audio system is based on a granular sampling paradigm. Although currently it's pretty basic we think this way it will allow for some nice effects at the audio level. Currently it's only used for time stretching in sync with the video speed so whenever you send data to the soundcard you need to apply an envelope to the frame in order to reduce the noise in case the reproduction speed is not the original one. You can see how this works in the testApp audioRequested method in the example app provided with this release. The only envelope provided by now is a raised cosine bell.

oscControl: in this directory you can find a class that allows to map simple osc messages to variable or setters, so whenever a new message arrives, it automatically modifies the value of the mapped variable or call a setter with the arriving value. To map a variable to a message you need to call:
        oscInterface->mapMessage("speed",&speed);
This way when a new  message with the format /setspeed arrives, the first parameter of the message is assigned to the speed variable. The type of the variable and the message parameter is verified so the message doesn't crash the application.
To map a message to a setter you need to use a poco delegate, the syntax is a bit complex and perhaps will change in the future as the last poco version provides a simpler way to do this:
        oscInterface.mapMessage("speed",
new Poco::Delegate<VideoHeader,float>
(videoHeader,&VideoHeader::setSpeed));

With this when a message with the format /setspeed arrives, the first paramenter of the message is passed to the setSpeed method of the videoHeader object.

This class uses a modified version of the ofxOsc addon by Damian Frey that is able to cast events whenever a new message arrives. The modified addon is also in the package, you will need to substitute your osc addon in the openframeworks addons folder with this one. It won't break any previous code.

 

Example application: The current version ships with an example app that provides some gui controls with Todd Vanderlin's ofxSimpleGui. As it is in the download, it shows a MultixRenderer with it's parameters controllable through the gui. There's also a perlin noise generator class that affects the x rotation of the renderer as an example of how to use an external class to modify the diferent position (or even timing) parameters.
Comented in the testApp setup code, you can find how to setup a recloop renderer instead of the multix one.
In future implementations we'll separate the gui from the rendering screen by sending osc messages.
 

You can download Playmodes 2 v0.01 from here (it includes the preRelease 0.6 of Openframeworks, not final version!!). If you're under Linux rename the libsLinux folder as libs !!

http://www.playmodes.com/pm/of_preRelease_Playmodes_0.06.rar

For any question regarding this experimental (work in progress)  version of Playmodes, send us an This e-mail address is being protected from spambots. You need JavaScript enabled to view it .

Enjoy it and have a happy 2009!!
Last Updated ( Thursday, 09 April 2009 21:23 )