Controllers can be thought of as the supporting layer of the BitMasher application. It communicates with the platform components and drives the scenes that run on top of it. As mentioned in the Architecture section, BitMasher runs on a state machine and each state is assigned its own controller.

Most controllers run in an infinite loop, reading audio/UI update messages from the queue and processing them accordingly. Each controller has their own way of processing these messages.

Active Controller

This is the main controller (BM_Controller_Active) that drives the playable scenes. The controller maintains a list of playable scenes in the _scenes array and tracks the current scene through _sceneIndex.

When a BM_SERVICE_UI message is read from the queue, the active controller will perform the following tasks:

  1. Call BM_UserIO_update( ) to detect rising/falling edge transisitons (see UserIO).
  2. Check to see if the Menu button has been pressed (via handleUserIO( ))
  3. Call the current scene's handleUserIO, update and draw callback functions
  4. Draw the frame buffer to the display

When a BM_SERVICE_AUDIO message is read, the controller first converts the input buffer of int16_t samples to float32_t before passing the buffer of floating point values to the scene's processAudio callback function.

Reading Messages from the Queue


Scene Callback Functions

A scene must implement the below callback funtions in order to work with the active controller


More information about creating custom scenes can be found in Making Your Own Scenes.

Entering the Menu

When the Menu button is pressed, the BM_Controller_Active_enterMenu function is executed. The menu system needs information about the list of available scenes and the current scene. This information is packaged in an instance of a BM_Controller_Menu_Entry_Packet struct and passed into the menu controller.

When exiting from the Menu, the active controller checks to see if the newScene member in BM_Controller_Menu_Entry_Packet is a non-negative number. If it is, then this means that the scene has changed and _sceneIndex is updated to reflect the new current scene.

Menu Entry Process


The menu controller works nearly identically to the active controller. The menu controller also runs scenes but in this context, the scenes are the menu pages instead of the playable scenes that the active controller runs. The menu controller also handles BM_SERVICE_AUDIO messages but does not process the input buffers.

There are two main menu scenes that the Menu Controller runs. The first is the Main Menu Scene. This is where the user can select a new scene to run as well as enter the settings menu and put the system to sleep. The second is the Settings Scene, where the user can make changes to the audio settings and the time.

The Menu Controller manages scenes through a stack data structure. At the bottom of the stack is the Main Menu Scene. If the user enters the Settings Menu, the first settings scene/page is pushed onto the stack. More scenes are pushed onto the stack as the user moves deeper into the settings options.

When the user exits a settings scene, it is popped off the stack.


The stack is implemented as an array with predetermined size to avoid the need for dynamic memory allocation.

Sleep Controller

The sleep controller works quite differently from the Active and Menu Controllers. The controller still runs an infinite loop but does not process queue messages. This is because the audio and render engines are stopped before entering the sleep controller so there are no messages in the queue to process.

The sleep controller updates the clock (displayed on the LCD) and shuts down the CPU (and other peripherals). Every minute, the CPU wakes up, updates the display and goes back to sleep.