NOMNOM 2: The Video Machine – The Programming Behind the Project

Credit: This project was developed together with Mint. Thank you :))

For my ICM final, I worked on an improved version of my mid-term pcomp project.

This time the computational challenges were even greater.
Here is the outcome after long weeks of intensive coding –

NomNom: The Video Machine

NOMNOM’s github repository can be found here – https://github.com/dodiku/the_video_machine_v2

Synching the videos

As a conclusion from the mid-term project, we wanted to give users that ability to play cohesive music. In order to that, we knew that we have to find a way to make sure that all the videos are being played in sync (automatically).

There are many ways to make sure the media is being played synchronously, but none of them deal with videos. To workaround that, we repurposed 2 functions from the p5.js sound library — Phrase and Part.
We used these functions to handle our playback as a loop that includes bars. We can call any callback function at any point on the loop, and therefore, we can actually use them to time our play and stop functions (and many others), based on the user action.


/*********************************************
SETUP FUNCTION (P5.JS)
*********************************************/
function setup() {
  noCanvas();

  // setting up serial communication
  serial = new p5.SerialPort();
  serial.on('connected', serverConnected);
  serial.on('open', portOpen);
  serial.on('data', serialEvent);
  serial.on('error', serialError);
  serial.list();
  serial.open(portName);

  // creating a new 'part' object (http://p5js.org/reference/#/p5.Part)
  allVideosPart = new p5.Part();
  allVideosPart.setBPM(56.5);

  // adding general phrase (http://p5js.org/reference/#/p5.Phrase) to the 'part'
  var generalSequence = [1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0, 1,0,0,0, 0,0,0,0];
  generalPhrase = new p5.Phrase('general', countSteps, generalSequence);
  allVideosPart.addPhrase(generalPhrase);

  for (var i = 0; i<16; i++){
    allVideosPart.addPhrase(new p5.Phrase(i, videoSteps, [0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0]));
  }

  // console.log(allVideosPart);
  allVideosPart.loop();

}

We initiate the Part, a Phrase per video, and a general Phrase that will be used as a clock, on the setup function.

The ‘countSteps’ callback function is being used to store the current step on a global variable, and the ‘videoSteps’ callback function is being used to play and stop video at the right time.

First success with the beat-sync feature – 

Improving the UI

We really wanted to make it easier for users to understand what is going on on the screen, and to provide a better sense of control on the videos.

In order to achieve that, we used the NexusUI JS library and added 4 graphical elements, each of which indicates a different property of the video (number of repetitions, volume, speed, and trim), on every video.

The graphical elements are shown to the user only when the video is being played.

Also, we add a grayscale CSS filter on videos that are not being played. This way, it is easier for the user to focus on the videos that are being played and making sounds.

Built to perform

While designing the technical architecture for the project, I faced many limitations, mostly because of the slow nature of the ASCII serial communication protocol. Therefore, I had to develop a very efficient internal communication protocol to compensate for the delay we had when pressing the buttons on the box. That was the only way to achieve fast responding controller, that will change the video states on the screen immediately.

This was the first time I was required to write efficient code (and not just for the fun of it). After 2 weeks of re-writing the code, and reducing few milliseconds every time, I came up with the following lines:

Reading data from controller (Arduino side) –


trellis.readSwitches();
for (uint8_t n = 0; n < numKeys; n++) {
  if (trellis.justPressed(n)) {
   LEDstatus[n] = 3; 

   continue; 
   }
    
    if (LEDstatus[n] == 3) {
        buttonPress[n]++;
        if (blinkTime >= 4) {
          if (trellis.isLED(n)) {
            trellis.clrLED(n);
            trellis.writeDisplay();
            } else {
              trellis.setLED(n);
              trellis.writeDisplay();
            }
        }
      }

    if (trellis.justReleased(n)) {
      if (buttonPress[n] > 8) {
        LEDstatus[n] = 1;
        oldStatus[n] = 1;
        buttonPress[n] = 0;
        trellis.setLED(n);
        trellis.writeDisplay();
      } else {
        buttonPress[n] = 0;
        if (oldStatus[n] == 1) {
          LEDstatus[n] = 0;
          oldStatus[n] = 0;
          trellis.clrLED(n);
          trellis.writeDisplay();
        } else {
          LEDstatus[n] = 1;
          oldStatus[n] = 1;
          trellis.setLED(n);
          trellis.writeDisplay();
        }
      }
    }

Parsing the data on the browser (JavaScript side) – 


/*********************************************
PARSER: PARSE DATA THAT ARRIVES FROM
ARDUINO, AND APPLY CHANGES IF NEEDED
*********************************************/
function parseData(data){

  // parsing the data by ','
  var newStatus = data.split(",");

  // turning strings into integers
  for (var x=0; x CONTINUE
    if ((newStatus[i] !== 3) && (newStatus[i] === videos[i].status)){
      var vidID = i+1;
      vidID = "#video" + vidID;
      $(vidID).css('border-color', "rgba(177,15,46,0)");
      continue;
    }
    else {

      // getting the relevant phrase
      var phraseIndex = i;
      var updatedPhrase = allVideosPart.getPhrase(phraseIndex);

      if (newStatus[i] === 3){

        if (videos[i].originStep === null) {
          videos[i].originStep = currentStep;
        }

        changeColor(i, 1);
        showKnobs(i);

        videos[i].volume = vol;
        videos[i].cut = cut;
        videos[i].speed = speed;
        videos[i].steps = newStatus[16];
        changeKnobs(i);

        // making the video border blink
        var vidID = i+1;
        vidID = "#video" + vidID;
        if (newStatus[20] === 2) {
          if (($(vidID).css('border-color')) === "rgba(177, 15, 46, 0)"){
            $(vidID).css('border-color', "rgba(255,255,255,0.9)");
          }
          else {
            $(vidID).css('border-color', "rgba(177, 15, 46, 0)");
          }
        }


        // clearing the sequence
        for (var n=0; n<32; n++){
          updatedPhrase.sequence[n] = 0;
        }

        // applying steps changes, if any
        var stepNum = videos[i].originStep;
        for (var m=0; m 31) {
            stepNum = stepNum - 32;
          }
        }

      }

      else if (newStatus[i] === 1) {
        videos[i].status = 1;
        changeColor(i, videos[i].status);
        var vidID = i+1;
        vidID = "#video" + vidID;
        $(vidID).css('border-color', "rgba(177,15,46,0)");
      }

      else if (newStatus[i] === 0) {
        videos[i].status = 0;
        hideKnobs(i);
        changeColor(i, videos[i].status);
        var vidID = i+1;
        vidID = "#video" + vidID;
        $(vidID).css('border-color', "rgba(177,15,46,0)");

        // clearing the sequence
        for (var n=0; n<32; n++){
          updatedPhrase.sequence[n] = 0;
        }

        videos[i].originStep = null;

      }
    }
  }
  serial.write(1);
}


When I review this code now, it all seems so simple (LOL!), but this is one of the pieces of code I'm most proud of.

After looong hours of coding, we are very happy we what we achieved 🙂

Controlling video playback features

Overview

For the first time in my ITP history, I was able to combine home assignment for ICM with home assignment for Pcomp.

I created a video manipulation interface, that could be controlled by a physical controller.
The entire project was build with Mint for our Physical Computing class mid-term.

The Video Machine - Web Interface
The Video Machine – Web Interface

 

The Video Machine – Web Interface from Dror Ayalon on Vimeo.

Functionality

I used the JavaScript video API to do the following manipulations on the video playback:

  • Loop – Playing the video in an infinite loop.
  • Volume – Changing the volume of the sound.
  • Cut – Trimming the length of the video.
  • Speed – Changing the speed of the video playback.
The Video Machine
The Video Machine

Code

Here is the JavaScript code I used for this project –

The Video Machine

Overview

The Video Machine is a video controller, powered by an Arduino, that controls the playback of videos presented on a web browser. By pressing a button on the controller, the correlated video is being played on the screen and heard through the speakers.
Videos are being played in an infinite loop.
Only the videos that are being played, are being heard.

I was lucky enough to work on this project with the super talented Mint for our Physical Computing class mid-term.
Working with Mint not only was a great learning experience, but also a lot of fun! I hope I’ll be able to work with her again on our next project (more on that below).

The Video Machine from Dror Ayalon on Vimeo.

Many thanks for Joe Mango, our beloved resident, who assisted a lot with finding the right technologies for the project, and helped us on one critical moment, when suddenly nothing worked.

The Video Machine – Split Screen from Dror Ayalon on Vimeo.

The building process

The process of building The Video Machine went through the following stages:

  • Prototyping – Once we had a broad idea about what we want to make, we wanted to test how hard would it be to build such interaction, and if the interaction feels ‘right’ to us.
  • Understanding the complications The prototyping stage helped us understand what could be the possible complications of this product, and what might be the limitation. We analysed what could be the limitations of the serial communication between the Arduino board and our laptop computer, and what types of video manipulations could be easily achieved using JavaScript.
    Understanding what’s possible helped us shape our final design, and the different features
  • Designing the architecture – Before we started to build the final product, we talked about the technical design of the product under the hood. These decisions basically defined the way the end product would operate, and the way users would interact with it.
  • Picking up the technologies – To apply our technical design, we needed to find the right tools.
    For the video manipulations, we decided to use vanilla JavaScript, because its easy to use video API. The biggest discussion was around the implantation of the buttons, on which the user needs to press in order to play the videos. After some research, and brainstorming with Joe Mango, we decided to use the Adafruit Trellis. That was probably the most important decision we took, and one that made this project possible to make, given the short amount of time we had at that point (four days).
  • Building, and making changes – We started to assemble the project and the write the needed code. While doing that, we changed our technical design a few times, in order to overcome some limitations we learned about along the way. And then came then moment where everything worked smoothly.
The Video Machine - Final product
The Video Machine – Final product

Some code

The entire code can be viewed on our GitHub repository.

Reactions

The reactions to The Video Machine were amazing. The signals started to arrive on the prototyping stage, when people constantly wanted to check it out.

When we showed the final project to people on the ITP floor, it appeared that everyone wants to put a hand on our box.

The Video Machine
The Video Machine

People were experimenting, listening, looking, clicking, laughing, some of them even lined up to use our product.

The Video Machine
The Video Machine

Further work

I hope that Mint and I will be able to continue to work on this project for our final term.
I cannot wait to see the second version of The Video Machine.
I believe that the goals for the next version would be:

  • To add more functionality, that will allow better and easier video/sound manipulation.
  • To make playing very easy for people with no knowledge of music or playing live music. Beat sync could be a good start. The product should allow anyone to create nice tunes.
  • To find a new way to interact with the content, using the controller. This new interaction needs to be somethings that allows some kind of manipulation to the video or the sound, that is not possible (or less convenient) using the current and typical controller interface.
  • To improve the content so all videos will be very useful for as many types of music as possible.
  • To improve the web interface to make it adjustable for different screen sizes.
The Video Machine - Controller
The Video Machine – Controller