Drag and drop sorting lists of records in an Ember 2 application, using JQuery UI’s sortable plugin! Working example up on GitHub.

I’ve been rebuilding Three D Radio‘s internal software using Ember JS. One aspect is to allow announcers to create playlists to log what they play on air. I wanted announcers to be able to reorder tracks using simple drag-n-drop. In this post I’ll explain how to do it.

Firstly, this post is based on the work by Benjamin Rhodes. However, I found that his solution didn’t work out of the box. Whether that is API changes from Ember 1.11 to Ember 2.x I’m not sure. So what I’m going to do here is bring his technique up to date for 2016 and Ember 2.6.

Starting an Ember Project

I’ll build this from scratch so we have a complete example. You shouldn’t have problems integrating this into an existing project though. So we’ll create a new Ember CLI project called sortable, and install JQuery UI:

ember new sortable-demo
cd sortable-demo
bower install --save jqueryui

We need to add JQuery UI to our build as well.

// ember-cli-build.js
var EmberApp = require("ember-cli/lib/broccoli/ember-app")

module.exports = function (defaults) {
  var app = new EmberApp(defaults, {
    // Add options here
  })

  app.import("bower_components/jquery-ui/jquery-ui.js")

  return app.toTree()
}

Models

We are going to need a model for the data we are going to sort. Here’s something simple

ember generate model note

Inside the note model we’ll have two attributes, the content of the note, and an index for the sorted order:

// app/models/note.js
import Model from "ember-data/model"
import attr from "ember-data/attr"

export default Model.extend({
  index: attr("number"),
  content: attr("string"),
})

Fake data with Mirage

For the sake of this example, we’ll use Mirage to pretend we have a server providing data. Skip this bit if you have your REST API done.

ember install ember-cli-mirage

And provide some mock data:

// app/mirage/config.js
export default function () {
  this.get("/notes", function () {
    return {
      data: [
        {
          type: "notes",
          id: 1,
          attributes: {
            index: "1",
            content: "Remember to feed dog",
          },
        },
        {
          type: "notes",
          id: 2,
          attributes: {
            index: "2",
            content: "You will add tests for all this, right?",
          },
        },
        {
          type: "notes",
          id: 3,
          attributes: {
            index: "3",
            content: "Learn React Native at some point",
          },
        },
      ],
    }
  })
}

A Route

We will need a route for viewing the list of notes, and a template. Here’s something simple that will do for now:

ember generate route list

And in here we will simply return all the notes:

// app/routes/list.js
import Ember from "ember"

export default Ember.Route.extend({
  model() {
    return this.store.findAll("note")
  },
})

A template? No, a component!

We are going to display our notes in a table, but the Sortable plugin also works on lists if that’s what you’d like to do.

You may be tempted to just put your entire table into the list template that Ember created for you. However, you won’t be able to activate the Sortable plugin if you try this way. This is because we need to call sortable after the table has been inserted into the DOM, and a simple route won’t give you this hook. So, we will instead create a component for our table!

ember generate component sortable-table

We will get to the logic in a moment, but first let’s render the table:

// app/templates/components/sortable-table.hbs
<table>
  <thead>
    <tr><th>Index</th><th>Note</th></tr>
  </thead>
  <tbody class="sortable">

    {{#each model as |note|}}
    <tr>
      <td>{{note.index}}</td>
      <td>{{note.content}}</td>
    </tr>
    {{/each}}

  </tbody>
</table>

The important part here is to make sure your table contains   and   components. We add the class sortable to the tbody, because that’s what we will make sortable. If you were rendering as a list, you add the sortable class to the list element.

Finally, in the template for our route, let’s render the table:

{{sortable-table model=model}}

We should have something that looks like this:

Our table component rendering the notes

Our table component rendering the notes

A quick detour to CSS

Let’s make this slightly less ugly with a quick bit of CSS.

/* app/styles/app.css */

body {
  font-family: sans-serif;
}
table {
  width: 500px;
}
table th {
  text-align: left;
}

tbody tr:nth-child(odd) {
  background: #ddd;
}
tbody tr:nth-child(even) {
  background: #fff;
}

Which gives us a more usable table:

Just make the table a tiny bit less ugly

Just make the table a tiny bit less ugly

Make Them Sortable

Moving over to our component’s Javascript file, we need to activate the sortable plugin. We do this in the didInsertElement hook, which Ember calls for you once the component has been inserted into the DOM. In this method, we will look for elements with the sortable class, and make them sortable!

// app/components/sortable-table.js
export default Ember.Component.extend({
  didInsertElement() {
    Ember.$(".sortable").sortable()
    Ember.$(".sortable").disableSelection()
  },
})

Persisting the records

At this point we have a sortable table where users can drag and drop to re-order elements. However, this is purely cosmetic. You’ll see that when you reorder the table the index column shows the numbers out of order.

We can now reorder notes, but the index fields are not updated

We can now reorder notes, but the index fields are not updated

Open up Ember Inspector and you will see the models’ index is never being updated. We’ll fix this now.

The first step is to store each note’s ID inside the table row that renders it. We will make use of this ID to update the index based on the order in the DOM. So a slight change to our component’s template:

app/templates/components/sortable-table.hbs

<pre class="lang:xhtml decode:true" title="">{{#each model as |note|}}
    <tr data-id="{{note.id}}">
      <td>{{note.index}}</td>
      <td>{{note.content}}</td>
    </tr>
{{/each}}

Next is to give sortable an update function. This gets called whenever a drag-drop is made.

// app/components/sortable-table.js
didInsertElement: function() {

    let component = this;
    Ember.$('.sortable').sortable({
      update: function(e, ui) {
        let indices = {};

        $(this).children().each( (index, item) => {
          indices[$(item).data('id')] = index+1;
        });

        component.updateSortedOrder(indices);
      }
    });

    Ember.$('.sortable').disableSelection();
  }

This function iterates over all the sortable elements in our table. Note that we get them from JQuery in their order in the DOM (ie the new sorted order). So, we create an array, and using the item’s ID store the index for each element. Note that I’m adding 1 to my indices to give values from 1 instead of 0.  Next step is to use this array to update the records themselves:

// app/components/sortable-table.js
updateSortedOrder(indices) {
    this.beginPropertyChanges();
    let tracks = this.get('model').forEach((note) => {
      var index = indices[note.get('id')];
      if (note.get('index') !== index) {
        note.set('index',index);
        note.save();
      }
    });
    this.endPropertyChanges();
  },

We update and save the record only if its index has actually changed. With long lists, this greatly reduces the number of hits to the server. (wish list: A method in ember that will save all dirty records with a single server request!)

Now when we reorder the index fields are updated correctly!

Now when we reorder the index fields are updated correctly!

And we’re done! A sortable list of Ember records, that persist those changes to the server*.

Have a look on GitHub!

(Note: If you’re using Mirage, you’ll get errors about saving records, because we need code to handle patches).

Join the conversation!

Hit me up on Twitter or send me an email.
Posted on June 16, 2016ProgrammingTags: emberjs, javascript, jquery, jqueryui, sortable

I worked for South Australia’s youth circus organisation Cirkidz on their production We The Unseen. Using the same 3D projection mapping technology I developed at UniSA and expanding from the work I did with Half Real, we built several interactive projection based special effects to compliment the performance. Let’s have a look at the Storm.

So what’s going on here? We have two Microsoft Kinects, either side of the stage, tracking the performers in 3D. We can then use that information to make projected effects that respond to the performers movements.

For Storm, I created a particle simulation that would provide a (deliberately abstract) storm effect. We have two particle emitters; one at the top of the triangle at the back of the stage, and another at the front. This gives the illusion that particles are travelling from the sail out onto the floor. Then, we have a couple of forces attached to the performer. The first is a rather strong attractor, which draws the particles to the actor. The next is a vortex, which manipulates the direction.

The result is a particle system that appears to dance with the performer.

We The Unseen’s projected effects were developed over a 6 week period. The first step was to figure out what was actually possible to track with the Kinect. These are circus performers, not people in their living rooms doing over the top gestures!

Tracking multiple performers

Tracking multiple performers

Having multiple actors on stage is fine, even on unicycles:

Skeleton tracking not so much

Skeleton tracking not so much

The main problem was the size of the stage. For this reason we used two Kinect devices, which were connected to separate PCs and sent tracking data using a simple, custom network protocol. Calibration meant the tracking data would be in the same coordinate system. And again, due to the size of the stage, there was almost no interference between the two devices.

In fact, if there was more time, we would have tried four devices.

One of the things thought about for Half Real but never got done was using projectors as dynamic light sources. In We The Unseen, we had a chance:

It mostly works, but you start to see the limits of the Kinect. No matter how precisely you calibrate, depth errors start to cause misalignment of the projection. There’s also a bit of a jump when the tracking switches devices. But overall, works pretty good.

In a smaller space, you could do some very nice lighting effects with projectors and decent tracking data.

Another problem discovered during Half Real was controlling the projection system. The operator was busy dealing with lighting cues, sound cues, and then an entirely separate projection system.

For We The Unseen, I had time to integrate the projection system with QLab, using Open Sound Control. This allowed the operator to program the show exclusively in QLab, and OSC messages told the projection system what to do.

There were additional effects that we built but didn’t make it into the show. For example, we had this idea for some of the acrobatics to create impact effects for when performers landed from great  heights. The problem here was mostly aesthetic. The lighting designer of course wants to light the performers to have the biggest visual impact. For these acrobatic scenes there was simply too much stage lighting for the projectors to cut through. Projectors are getting brighter and brighter, for cheaper, but still can’t compete against stage lighting. So those effects we left unseen.

We The Unseen – pretty amazing youth circus, with a couple of special effects by me where they worked.

Tech runs

Tech runs

Join the conversation!

Hit me up on Twitter or send me an email.
Posted on June 14, 2016Programming, Software ProjectsTags: Augmented Reality, c++, circus, cirkidz, kinect, opengl, particle system, Programming, projection, sar, theatre

Web Audio is an amazingly powerful new Javascript API for building complex audio and music applications in the web browser. I wanted to check it out, so I built Syntho. You can try Syntho out right now!

Syntho is a monophonic synthesizer inspired by the Korg Volca Bass. It features 3 oscillators with sine, saw, triangle, and square wave shapes over 6 octaves. Each oscillator can be detuned independently, giving nice/horrible pulsing as the oscillators go in and out of phase.

A low pass filter with resonance affects the sound of the oscillators. The filter self oscillates if you push the resonance way up.

There is a low frequency oscillator that can be set to affect the pitch of the sound generating oscillators, or the filter cutoff point. The LFO supports triangle and square waveshapes.

Finally, there is an ADSR envelope generator. The ADSR can be set to control the amplitude of the sound, or the filter cutoff point, or both.

Syntho is completely modern Javascript. I use ES6 transpiled with Babel, Handlebars for keeping the HTML sane, and Twitter Bootstrap because I’m lazy with CSS.

The inner workings of Syntho and web-audio will probably be the subject of another series of video tutorials. But for now, the code is on GitHub.

Join the conversation!

Hit me up on Twitter or send me an email.

Three D Radio, a community radio station I help run in Adelaide, had a problem. There are sometimes no announcers available for the late night and extremely early morning timeslots. As all the announcers are volunteers, sometimes things come up and an announcer can’t make it in to do their show. The station switched over to a 5 CD changer and played pre-recorded shows in these situations. There were two glaring problems with this approach:

  1. The station’s volunteers couldn’t create new pre-recorded shows fast enough. This meant that listeners would end up hearing the same shows again, which is lame.
  2. Five CDs isn’t always enough content to make it through the night. This meant that you could listen to a show, go to bed, wake up the next morning and hear the same show. Even worse!

I built the Graveyard Ghoul to replace the CD changer with a never ending assortment of randomised music. Here’s how and why.

Graveyard Ghoul on GitHub!

Existing options

So why build something new? Let’s talk about existing options.

We could just load up an MP3 player with music, switch it to random, and forget about it. However, this would lead to a poor on-air sound, and would probably have the station breaking the law.

Australian community radio law mandates at least 20% of the music broadcast to be Australian. Three D goes further than this and has self imposed quotas of 40% Australian, 20% South Australian, and 25% music containing female artists.

We also need to broadcast messages and station IDs at regular intervals, so listeners know what they’re listening to. An MP3 player wouldn’t do this well.

Most radio stations use some kind of playout software to automate the on air sound. Most of these can be switched into an automatic mode and run the station without anybody present at all. Three D could have taken this approach.

The quotas again would be a problem. Metadata for all the music in Three D’s collection is stored in a Postgresql database that was implemented long before I joined the station. Most of the mp3 files themselves have either no or missing ID3 tags. So we would have to hack/script any playout software to interface with the existing database, or somehow shoehorn all the metadata into tags in the files themselves.

A fullblown playout software solution was also deemed to heavyweight to put into use. Three D is one of the few stations that don’t use one. We needed this solved fast, and trialing, purchasing, deploying, and migrating to new software was going to be too much effort for a station of 130 volunteers.

Requirements

So I decided to try building something new, and if it worked out, suggest the station put it into use. The requirements I had in mind were:

  • Super simple user interface (ideally one button to press play)
  • Play a randomised selection of music from Three D’s music library
  • Meet all of Three D’s music quotas
  • Regularly broadcast station IDs between songs
  • Log the music played to the stations logging system (again, legal requirement)
  • Run on Linux (the on-air computer runs OpenSUSE)
  • Implement in Python. The main reason for this is there are other volunteers at the station who can code in Python, and could look into things if I wasn’t around. Otherwise I would have used Java or QT.

The Solution

After hacking away for a couple of nights after work I had enough to leave running for a week playing music non stop. Obscure bugs would mean the radio station would stop broadcasting, and nobody wants to hear silence on their radio. It now runs every night and whenever an announcer doesn’t show up.

The graveyard ghoul

The graveyard ghoul

Interesting bits

The Ghoul is a fairly simple, albeit important, piece of software. The main interesting bit is the scheduler, which decides what to play next.

def getNextSong(self):
        if self.demoCount / float(self.playCount) < self.demoQuota:
            nextItem = self.music.getRandomDemo()

        elif self.localCount / float(self.playCount) < self.localQuota:
            nextItem = self.music.getRandomLocal()

        elif self.ausCount / float(self.playCount) < self.ausQuota:
            nextItem = self.music.getRandomAustralian()

        elif self.femaleCount / float(self.playCount) < self.femaleQuota:
            nextItem = self.music.getRandomSong(True)

        else:
            nextItem = self.music.getRandomSong(False)


    def getNextItem(self):
        while True: 
            if self.playCount < 5:
                nextItem = self.music.getRandomSong(False)

            # After 5 totally random tracks, we have enough to start working towards quotas...
            else:
                # absolutely must play a sting...
                if self.consecutiveSongs >= self.maxConsecutive:
                    nextItem = self.messages.getRandomSting()

                # Free to choose either a song or a sting
                elif self.consecutiveSongs >= self.minConsecutive:
                    coin = random.randint(0, self.maxConsecutive - self.consecutiveSongs)
                    if coin == 0:
                        nextItem = self.messages.getRandomSting()
                    else:
                        nextItem = self.getNextSong()
                else:
                    nextItem = self.getNextSong()

            # Make sure the mp3 actually exists
            if isinstance(nextItem, Song):
                self.totalRequests += 1
                if self.fileManager.fileExists(nextItem):
                    self.addToPlayCount(nextItem)
                    break;
            else:
                self.consecutiveSongs = 0
                break;
        return nextItem

The config file allows the programming committee to tweak the sound. For example, how many songs should be played before a station ID, or what the quotas are for Australian and South Australian music, music featuring female artists, and demos.

The Ghoul plays 5 totally random tracks when it first starts. This seeds the playlog with enough information to then start working towards meeting the quotas.

Stings/station IDs are inserted into the play queue with a bit of randomness, so it isn’t just a monotonous 4 songs, sting, 4 songs, sting, etc. The randomness can be tweaked through the config file.

Finally, we make sure the MP3 actually exists. This is a problem because some of the music in the catalogue database is from vinyl, or simply hasn’t been ripped to MP3 yet. The database is a bit of a mess, so the Ghoul checks that there is actually a file there to play. This is also why the method sits in a while true loop.

Other than the scheduler, the software makes use of threads to ensure the GUI stays snappy, and Python’s requests library to handle logging to the station’s intranet.

Success!

Join the conversation!

Hit me up on Twitter or send me an email.
Posted on May 24, 2016Software ProjectsTags: gstreamer, gtk, Linux, psycopg2, python, requests