Wiki

Clone wiki

Core / SoundsPlus

Back to Codea's Example Projects


ProjectSounds-Small.png Sounds Plus by Dylan Sale and Simeon

Introduction


Sounds Plus is one of the example projects supplied with Codea. It provides a utility for exploring simple and advanced sounds. A user can use the integrated sound picker by tapping on the highlighted sound function.

The example also illustrates the implementation of button and menu elements, as well as the use of a subclass SoundButton inheriting from a superclass Button.

Tabs in the project


In addition to Main the project comprises the following tabs:

TabComment
BasicMenuEstablishes the class BasicMenu.
AdvancedMenuEstablishes the class AdvancedMenu.
RoundRectContains the function roundRect().
ButtonEstablishes the class Button.
SoundButtonEstablishes the class SoundButton, a subclass of Button.

Each of the BasicMenu and AdvancedMenu classes implements the following functions: myMenu:onEnter(), myMenu:draw() and myMenu:touched(touch).

Main tab


The Main tab implements three callback functions (which are explained further later in this section):

setup()        -- called once
draw()         -- called up to 60 times a second
touched(touch) -- called whenever a touch on screen begins, ends or moves

The Main tab also implements the following functions:

toggleMenu() -- Toggles between menus
drawButton() -- Draws the button referred to by 'button'

setup()

This creates one instance of the BasicMenu class (in basicMenu and one instance of the AdvancedMenu class (in advancedMenu. Global variable menu is set to refer to the same instance as basicMenu. The onEnter() function of menu is then called.

Global variable button is set to refer to an instance of the Button class (initialised with string "Go To Advanced"). The action field of button is set to a function that toggles between menus:

    button = Button("Go To Advanced")
    button.action = function () toggleMenu() end -- Set to refer to a function

toggleMenu()

The toggleMenu() function toggles the global variable menu referring to the instance referred to by basicMenu or the instance referred to by advancedMenu. In each case, the displayName field of button is set to refer to the other menu option:

if menu == basicMenu then
    menu = advancedMenu
    button.displayName = "Go To Basic"
else
    menu = basicMenu
    button.displayName = "Go To Advanced"
end

Once menu has been toggled, the onEnter() function of menu is then called.

draw()

The draw() function is called by Codea up to 60 times a second and does three things:

function draw()
    background(63, 63, 88, 255) -- Colour is a dark bluish grey
    menu:draw()                 -- Draw 'menu'
    drawButton()                -- Draw 'button' (see below)
end

drawButton()

The drawButton() function sets the pos field of button to be to the middle of the right of the viewer (as a vec2 value) and then calls the draw() function of button.

touched(touch)

This function is called when the viewer is touched. It passes the touch on to first menu and then button to handle:

function touched(touch)
    menu:touched(touch)
    button:touched(touch)
end

BasicMenu tab


The BasicMenu tab implements BasicMenu as a class, defining the following functions:

BasicMenu:init()
BasicMenu:onEnter()
BasicMenu:draw()
BasicMenu:drawButtons()
BasicMenu:touched(touch)

AdvancedMenu tab


The AdvancedMenu tab implements AdvancedMenu as a class, defining the following functions:

AdvancedMenu:init()
AdvancedMenu:onEnter()
AdvancedMenu:draw()
AdvancedMenu:touched(touch)

It also implements a global function: playSound().

RoundRect tab


The RoundRect tab holds the code for a global function: roundRect(x, y, w, h, r). As its name suggests, this function draws a rectangle with rounded corners (of radius r). The bottom left-hand corner of the rectangle is at x, y and it has width w and height h. That is, the rounded rectangle is drawn as if rectMode(CORNER) has been called.

The method used is to use the built-in rect() function to draw an 'inner', inset, rectangle:

insetPos  = vec2(x + r, y + r)         -- Bottom-left is one 'radius' in
insetSize = vec2(w - 2 * r, h - 2 * r) -- Width and height are two 'radii' shorter   
...
noSmooth()
rectMode(CORNER)
rect(insetPos.x, insetPos.y, insetSize.x, insetSize.y)

and to use the built-in line function with lineCapMode(ROUND) to draw a boundary around it:

-- Copy fill into stroke
local red, green, blue, a = fill()
stroke(red, green, blue, a)
...    
if r > 0 then                                 -- Check that the radius is positive
    smooth()
    lineCapMode(ROUND)                        -- Set the line caps to rounded
    strokeWidth(r * 2)                        -- Set width to the diameter
    line(insetPos.x, insetPos.y, 
        insetPos.x + insetSize.x, insetPos.y)               -- Line along the bottom
    line(insetPos.x, insetPos.y,
        insetPos.x, insetPos.y + insetSize.y)               -- Line along left
    line(insetPos.x, insetPos.y + insetSize.y,
        insetPos.x + insetSize.x, insetPos.y + insetSize.y) -- Line along the top
    line(insetPos.x + insetSize.x, insetPos.y,
        insetPos.x + insetSize.x, insetPos.y + insetSize.y) -- Line along right            

In order to protect the existing style, all the drawing takes place between a pushStyle() and matching popStyle():

function roundRect(x,y,w,h,r)
    pushStyle() -- Preserve the style on the stack
    ...         -- Drawing
    popStyle()  -- Retrieve the style from the stack
end

Button tab


The Button tab implements Button as a class, defining the following functions:

Button:init(displayName)
Button:draw()
Button:hit(p)
Button:touched(touch)

The Button class acts as the superclass for the SoundButton class.

Button:hit(p)

This function returns true if point p falls within the boundary of the button (determined by fields pos and size), and false otherwise.

Button:touched(touch)

This function checks whether the touch.state is ENDED and its location is within the boundary of the button (using self:touched(touch). If it is, the function referenced by the field action is called (if it exists):

function Button:touched(touch)
    if touch.state == ENDED and
       self:hit(vec2(touch.x, touch.y)) then
        if self.action then                  -- Does 'action' exist?
            self.action()                    -- Call 'action'
        end
    end
end

SoundButton tab


The SoundButton tab implements SoundButton as a subclass of Button, defining the following functions:

SoundButton:init(displayName)
SoundButton:draw()

Updated