Skip to content

Commit

Permalink
Updated README. Added set-color!. Exposed tune? and sound?
Browse files Browse the repository at this point in the history
  • Loading branch information
massung committed Jan 25, 2020
1 parent 6a11bc7 commit 4652554
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 3 deletions.
225 changes: 225 additions & 0 deletions README.md
Expand Up @@ -62,6 +62,230 @@ For some more complicated code samples, check out the [examples folder in GitHub
* Voice instruments and envelopes
* Generating 8-bit waveforms and playing sounds

## Documentation

This section is a brief introduction of all the functions available.

### Main Game Loop

(**run** _loop width height #:scale 3 #:fps 60 #:title "R-cade"_)

Creates a new game window, video memory, and enters the main game loop. The _loop_ parameter is a function you provide, which will be called once per frame and should take no arguments. The _width_ and _height_ parameters define the size of video memory (not the size of the window!).

(**quit**)

Closes the window, which will terminate the main game loop.

(**wait** _[until btn-any]_)

Hard stops the game loop and waits until either the window is closed or the _until_ function returns true. While waiting, events are still processed.

(**sync**)

Called once per frame automatically by the main game loop. You shouldn't need to call this yourself unless you are creating your own game loop. It processes all events, renders video memory, and ensures the framerate is locked.

(**frame**)

Returns the current frame: 1, 2, 3, ....

(**width**)

Returns the width of video memory in pixels.

(**height**)

Returns the height of video memory in pixels.

(**flip**)

Renders video memory to the window. This is done automatically when `sync` is called (which is also automaticall called). You should never need to call this yourself.

### Inputs

All `btn-*` functions return `#f` if their respective button is not pressed, otherwise they return the count of how many frames the button has been pressed for. For exmaple `1` would indicate the button was _just_ pressed. This allows for easy testing of pressed, just pressed, or against a repeat rate.

(**btn-start**)

Returns the button state of the ENTER key.

(**btn-select**)

Returns the button state of the SPACEBAR key.

(**btn-quit**)

Returns the button state of the ESCAPE key.

(**btn-z**)

Returns the button state of the Z key.

(**btn-x**)

Returns the button state of the X key.

(**btn-up**)

Returns the button state of the UP arrow key.

(**btn-down**)

Returns the button state of the DOWN arrow key.

(**btn-right**)

Returns the button state of the RIGHT arrow key.

(**btn-left**)

Returns the button state of the LEFT arrow key.

(**btn-mouse**)

Returns the button state of the LEFT mouse button.

(**mouse-x**)

Returns the X coordinate (in pixels) of the current mouse location. 0 is the far left edge.

(**mouse-y**)

Returns the Y coordinate (in pixels) of the current mouse location. 0 is the top edge.

### Actions

Sometimes you want to be able to bind buttons to specific, named actions so your code is easier to read (and modify if you want to change your button mapping). To do this, use the `define-action` macro:

(**define-action** _name btn [rate]_)

Some examples:

```racket
; the (move-left) function will return #t if the left arrow key is pressed
(define-action move-left btn-left)
; the (jump) function will return #t if Z is key was just pressed
(define-action jump btn-z #t)
; the (fire) function will return #t while X is pressed at a rate of 5 times per second
(define-action fire btn-x 5)
```

### Drawing

(**cls**)

Clears video memory.

(**color** n)

Changes the active color to n (0-15). The default color palette is the same as the [PICO-8][pico-8]:

![](https://www.lexaloffle.com/gfx/pico8_pal_017.png)

(**set-color!** _n r g b_)

Change a color in the palette at index _n_ [0, 15] to the RGB byte values.

(**draw** _x y bytes_)

Uses the current color to render a 1-bit sprite composed of _bytes_ to video memory at (_x_,_y_). For example:

```racket
(draw 10 12 '(#b010 #b111 #b010))
```

The above would draw a 3x3 sprite that looks like + sign to the pixels at (10,12) -> (12,14). Any bit set will set the pixel in video memory to the current color. A clear bit is considered to be transparent and will not alter video memory.

(**text** _x y s_)

Draw the string _s_ (note: under the hood `(~a _s)` is used, so _s_ can be of any type) at (_x_,_y_). The default font is a fixed-width ASCII font with each character being 3x6 pixels in size.

(**line** _x1 y1 x2 y2_)

Draws a 1-pixel width line from (_x1_,_y1_) to (_x2_,_y2_) using the current color.

(**rect** _x y w h #:fill #f_)

Draw a rectangle starting at (_x_,_y_) with a size of (_w_,_h_). If _fill_ is `#t` then it will be a solid rectangle, otherwise just the outline.

(**circle** _x y r #:fill #f_)

Draw a circle starting at (_x_,_y_) with a radius of _r_ pixels If _fill_ is `#t` then it will be a solid circle, otherwise just the outline.

### Sounds

All audio is played by composing 8-bit PCM WAV data.

(**waveform** _curve seconds #:instrument sin #:envelope (const 1)_)

All sounds are made using the `waveform` function. The _curve_ argument is a function that is given a single value in the range of [0.0, 1.0] and should return a frequency to play at that time; 0.0 is the beginning of the waveform and 1.0 is the end. The _seconds_ parameter is how long the sound should play for.

The _instrument_ is the wave function to use and defaults to a simple sine wave. Other - built in - wave functions include `sawtooth-wave`, `square-wave`, `triangle-wave`, and `noise-wave`. But the user can define their own function and use it as well.

The _envelope_ is an amplitude multiplier. It is a function which - like _curve_ - is given a single value in the range [0.0, 1.0] indicating where in the sound it is. It should return a value in the range [0.0, 1.0], where 0.0 indicates a null amplitude and 1.0 indicates full amplitude. The default is to simply play the entire sound at full amplitude. Three built-in envelopes include `z-envelope` and `s-envelope`. There is also an `envelope` function that helps with the creation of your own envelopes.

(**envelope** _y . ys_)

The `envelope` function returns a function that can be used as the _envelope_ paramater to any of the sound functions. It builds a simple, evenly spaced, linearly interpolated plot of amplitude envelopes. For example, the `z-envelope` is defined as:

```
(define z-envelope (envelope 1 1 0 0))
```

This means that in the time range of [0.0, 0.33] the sound will play at full amplitude. From [0.33, 0.66] the envelope will decay the amplitude linearly from 1.0 down to 0.0. Finally, from [0.66, 1.0] the amplitude of the sound will be forced to 0.0.

(**tone** _freq seconds #:instrument sin #:envelope (const 1)_)

This is a simple helper function for generating waveforms of a constant frequency.

(**sweep** _start end seconds #:instrument sin #:envelope (const 1)_)

A helper for creating a waveform that linearly interpolates over a duration of _seconds_ from _start_ Hz to _end_ Hz.

(**sound?** _sound_)

Returns `#t` if _sound_ is a valid waveform object.

(**play-sound** _waveform #:volume 100.0 #:pitch 1.0 #:loop #f_)

Uses one of 4 sound channels to play the waveform. If no channels are available then the sound will not be played. Returns the sound channel the waveform is playing on or `#f`.

(**stop-sound** _channel_)

Stops the waveform currently playing on the _channel_ returned with `play-sound`.

### Music

Music is created by parsing notes and creating an individual waveform for each note, then combining them together into a single waveform to be played on a dedicated music channel. Only one "tune" can be playing at a time.

(**make-tune** _notes #:bpm 160 #:instrument sin_)

Parses the _notes_ string and builds a waveform for each note. Notes are in the format `<key>[<octave>][<hold>]`. Examples:

* `"C#3--"` is a C-sharp in 3rd octave and held for a total of 3 quarter-notes time;
* `"Bb"` is a B-flat held for a single quarter-note and uses the octave of whatever note preceeded it;

How long each note is held for (in seconds) is determined by the _bpm_ (beats per minute) parameter. A single beat is assumed to be a single quarter-note. So, with a little math, the `C#--` at a rate of 160 BPM would play for 1.125 seconds (3 beats * 60 s/m ÷ 160 bpm). It is not possible to specify 1/8th and 1/16th notes. In order to achieve them, increase the _bpm_ appropriately.

All note waveforms are played with an ADSR (attack, decay, sustain, release) envelope. This is not allowed to be overridden.

(**tune?** _tune_)

Returns `#t` if _tune_ is a valid music object.

(**play-music** _tune #:volume 100.0 #:pitch 1.0 #:loop #f_)

Stops any music currently playing and switches it to _tune_.

(**stop-music**)

Stops any music currently playing.

(**pause-music** _[pause #t]_)

If _pause_ is `#t` then the currently playing music is paused, otherwise it is resumed. If the music was not paused already and resumed, the music will restart from the beginning.

## Goals

As stated earlier, the three pillars `r-cade` is built on are:
Expand Down Expand Up @@ -110,3 +334,4 @@ With drawing, I wanted every pixel to be a single bit and sprites to be as simpl
[twinkle]: https://raw.github.com/massung/r-cade/master/screenshots/twinkle.gif
[breakout]: https://raw.github.com/massung/r-cade/master/screenshots/breakout.gif
[tetris]: https://raw.github.com/massung/r-cade/master/screenshots/tetris.gif
[pico-8]: https://www.lexaloffle.com/pico-8.php
8 changes: 5 additions & 3 deletions main.rkt
Expand Up @@ -17,9 +17,11 @@ All rights reserved.
(require "video.rkt")
(require "game.rkt")
(require "input.rkt")
(require "palette.rkt")
(require "draw.rkt")
(require "voice.rkt")
(require "sound.rkt")
(require "music.rkt")
(require "audio.rkt")

;; ----------------------------------------------------
Expand Down Expand Up @@ -57,6 +59,7 @@ All rights reserved.
; draw
cls
color
set-color!
draw
text
line
Expand All @@ -77,11 +80,10 @@ All rights reserved.
waveform
tone
sweep

; music
; TODO: ??

; audio
sound?
tune?
play-sound
stop-sound
make-tune
Expand Down
9 changes: 9 additions & 0 deletions palette.rkt
Expand Up @@ -34,3 +34,12 @@ All rights reserved.
(sfColor_fromRGBA #x83 #x76 #x9c #xff)
(sfColor_fromRGBA #xff #x77 #xab #xff)
(sfColor_fromRGBA #xff #xcc #xaa #xff)))

;; ----------------------------------------------------

(define (set-color! index red green blue)
(let ([n (bitwise-and index #x0f)]
[r (bitwise-and index #xff)]
[g (bitwise-and index #xff)]
[b (bitwise-and index #xff)])
(vector-set! palette n (sfColor_fromRGBA r g b #xff))))

0 comments on commit 4652554

Please sign in to comment.