Lisbit is a programming language designed for creating 1-bit audio. It works by running the program that you write once for every frame of the audio output, passing in the current time as a variable called time
. If the last statement in your program returns on
(Lisbit's version of what other languages might call "true") the audio is turned on for that frame. By alternating which frames end with on
, you can make the audio quickly turn on and off, creating audible sound.
Code in Lisbit consists of nested lists. Each list starts with an (
and ends with a )
. Items in lists are separated by whitespace. Items in lists can be numbers, symbols, or other lists.
Numbers are just normal numbers. They can be positive or negative and they can have a decimal place. You can also put commas where ever you you want in them, which will simply be ignored without changing the meaning of the number. Here are some examples:
500
-30.25
10,000.56
1,00,00.00,00
Symbols are names for things. If Lisbit runs into a symbol when it's trying to read code, it will replace it with whatever value you've bound to it using the define
command. Symols can contain letters, !
, ?
, and -
. They can also contain numbers as long as they aren't the first character. Here are some examples:
this-is-a-symbol
is-this-a-symbol?
symbol3
You can also add comments where ever you want by putting text between /*
and */
. Comment will be ignored by Lisbit and just treated like whitespace, so feel free to use them to leave notes to yourself and anyone else who might be looking at your code.
When Lisbit is trying to read code, it does the following:
There are two types of the commands that can be run: functions and macros. Arguments passed to functions get evaluated as code before the function sees them, but arguments to macros get passed in directly, so the macro can choose whether it wants to evaluate them or not. This lets you build your own custom languages structures, that can be pretty powerful. Here are some examples of functions and macros getting called:
(add 1 1)
(mul (add 1 1) (sub 1 1))
(define foo 5)
(add foo 10) /* This is treated the same as (add 5 10) */
(define double (function (my-number) (add my-number my-number)))
(double 5) /* This is treated the same as (add 5 5) */
While Lisbit gives you all the tools you need to create audio, it doesn't really point you in any particular direction when it comes to actually producing sound. This can be nice, since it lets you experiment with a lot of different techniques, but it can also make it hard to know how to start. Hopefully this section will help you figure out how to make basic notes.
You ears hear sound whenever waves of pressurised air hits your eardrum, causing it so move vibrate and send signals to your brain. If the waves of pressure are short, switching quickly between high pressure and low pressure, you'll hear a high pitched sound. If they're longer, you'll hear a low pitched sound. The larger the difference in pressure, the louder the sound will be.
Lisbit only offers you two options for pressure: on
and off
. This doesn't make it easy to create the complex sounds or the real world, but it can make some pretty interesting sounds and provide a fun constraint for anyone making audio.
You can create alternating waves in Lisbit by writing functions that will return on
or off
depending on what the time is. Here's an example of a function like that:
(define tone (function (time hz)
(eq? (mod (floor (mul time hz)) 2) 0)))
This function will return a value that will switch on and off hz
times per second. If you pass in a high value for hz
you can use it for a hight tone, and if you pass a low value for hz
you can use it for a low tone.
Since the values tone
returns are just regular booleans you can pass them to functions and modify or combine sounds. Here's an example:
(and
(tone time 300)
(tone time 2000))
This example might not sound particularly pleasant, but with some creativity you can use these techniques to create some really cool sounds.
time
- number - the current time in seconds.
on
- boolean - a truthy value.
off
- boolean - a falsey value.
define
Binds a value to a symbol.
symbol - The name of the symbol to bind to.
boolean/number/list/function - The value to bind to it.
(define pi 3.14159)
(define double (function (x) (mul x 2)))
(define no (eq? 5 6))
(define shopping-list (list oranges bread fish))
function
Returns a new function. It also binds the symbol 'recur' to itself so you can make recursive calls.
symbol/list - A symbol that will be bound to the list of parameters, or a list of symbols that will each be bound to one of the parameters.
expression - The code that will be executed whenever the function is called.
(function (x y) (mul x y))
(function numbers (apply mul numbers))
(define factorial (function n (if (eq? n 1) 1 (mul n (recur (sub n 1))))))
macro
Returns a new macro. This works like a function, but it doesn't evaluate its arguments so you can evaluate them yourself.
symbol/list - A symbol that will be bound to the list of parameters, or a list of symbols that will each be bound to one of the parameters.
expression - The code that will be executed whenever the macro is called.
(define unless
(macro (condition if-on if-on)
(if (not (eval condition))
(eval if-off)
(eval if-on))))
(define my-quote (macro (arg) arg))
quote
Return an object without evalutating it.
any - The object to be quoted.
begin
Evaluate pieces of code in sequence
expression - The code to evalute first.
more expressions, etc.
expression - The code to evaluate last. The return value of this expression will be used as the return value of begin
.
(begin
(define a 1)
(define b 2)
(add a b))
begin
Evaluate the first argument. If the result is on
return the result of evaluating the second argument. Otherwise return the result of the third argument if it exists.
expression - Condition to evalute.
expression - The result if it's on
.
expression - The result in all other cases.
(if (eq? 1 1) 1 0)
(if (lt? (add 1 1) (mul 1 1))
(add 100 100)
(mul 100 100))
cond
Return the result of evaluating second item of the first argument who's first item evaluates to on
.
list
expression - Condition to evalute.
expression - The result if it's on
.
more lists, etc.
(cond
((gt? 1 3) 1)
((gt? 2 3) 2)
((gt? 3 3) 3)
((gt? 4 3) 4) /* 4 gets returned */
((gt? 5 3) 5))
and
Evaluates its arguments until it finds one that returns off
, in which it returns off
. If none of them are off
then it returns on
.
expression - First condition to test
more expressions, etc.
(and on on)
(and (lt? 0 1) (gt? 0 -1))
or
Evaluates its arguments until it finds one that returns on
, in which it returns on
. If none of them are on
then it returns off
.
expression - First condition to test
more expressions, etc.
(or on off)
(or on on)
(or (eq? 1 1) (eq? 1 0))
eval
Evaluate a list as code
list - The list to evaluate.
(define code (list add 1 1))
(eval code)
list
Create a list out of the arguments passed in.
any - The first list item.
more of any, etc.
(list 1 2 3)
(list 1 (add 1 1) (add 1 1 1))
apply
Call a function with arguments pulled from a list.
function - The function to call.
list - The arguments to the function.
(apply add (list 1 2 3))
(apply and (list on on))
head
Return the first item in an array.
list - The list to get the item from.
(head (list 1 2 3))
tail
Return a list with every item from a list except the first element.
list - The list to get the items from.
(tail (list 1 2 3))
length
Get the length of a list.
list - The list you want the length of.
(length (list 1 2 3))
(length (list))
index
Get the item in a list that's located at a specific index.
list - The list you want the item from.
number - The index of the item. The first item of the list is at index 0.
(index (list 1 2 3) 0)
(index (list 1 2 3) (add 1 1))
print
Prints whatever values you pass to it to a special console you can use for debugging. It also returns its arguments without modifying them, so you can easily stick print
anywhere you want to know the value of something.
any - The first item to print out.
more of any, etc.
(print 5)
(print 1 2 3)
(print (add 1 1))
(add (print 1) 1)
add
Add numbers together.
number - The first item to add.
more numbers, etc.
(add 1 1)
(add 2 2.5 3 4.5)
sub
Subtract numbers from each other.
number - The starting number.
number - The first number to subtract from it.
more numbers, etc.
(sub 4 2)
(sub 10 5 3 2 1)
mul
Multiply numbers together.
number - The first number to multiply.
more numbers, etc.
(mul 10 10)
(mul 10 20 30 40)
div
Divide numbers by each other.
number - The starting number.
number - The first number to divide it by.
more numbers, etc.
(div 100 10)
(div 10 2 2)
div
Get the remainder of dividing numbers by each other.
number - The starting number.
number - The first number to divide by it to get a remainder.
more numbers, etc.
(mod 5 2)
(mod 13 5 2)
exp
Raise numbers to the power of each other.
number - The starting number.
number - The first power to raise it to.
more numbers, etc.
(exp 10 2)
(exp 10 2 3)
log
Get the logarithm of a number with a specified base.
number - The number your getting a logarithm of.
number - The base of the first logarithm operation.
more numbers, etc.
(log 256 2)
(log 100,000,000 10 2)
min
Get the lowest number from all the arguments.
number - The first number to test.
more numbers, etc.
(min 1 2 5 0)
(min (add 1 1) (sub 1 1) (mul 1 1) (div 1 1))
max
Get the highest number from all the arguments.
number - The first number to test.
more numbers, etc.
(max 100 10000 10)
(max (add 10 10) (sub 10 10) (mul 10 10) (div 10 10))
not
Return the inverse of a boolean argument.
boolean - The boolean to get the inverse of.
(not off)
(not (and off on))
eq?
Returns on
if all of the arguments are equal.
number - The first number to test.
more numbers, etc.
(eq? 1 1)
(eq? (add 1 0) (sub 1 0) (mul 1 1) (div 1 1))
lt?
Returns on
if every argument is less than the next one.
number - The first number to test.
more numbers, etc.
(lt? 1 2 3)
(lt? (mul 1 1) (mul 10 10) (mul 100 1000))
gt?
Returns on
if every argument is greater than the next one.
number - The first number to test.
more numbers, etc.
(gt? 3 2 1)
(gt? (mul 2 2 2 2) (mul 2 2 2) (mul 2 2))
xor
Return on
if an odd number of its arguments are on
.
boolean - The first boolean to test.
more booleans, etc.
(xor on off)
(xor on off on off)
xor
Return on
if an odd number of its arguments are on
.
boolean - The first boolean to test.
more booleans, etc.
(xor on off)
(xor on off on off)
floor
Round down a number.
number - The number to round down.
(floor 3.14)
(floor (div 10 3))
floor
Round up a number.
number - The number to round up.
(ceil 6.28)
(ceil (div 20 7))
rand
Returns a pseudorandom number between 0 and 1 based on a seed value you supply.
number - A seed value that's used to create a pseudorandom number. If you use the same seed multiple times, it will return the same value each time.
(rand 0)
(rand time)
rand-bool
Pseudorandomly returns on
or off
based on a seed value you supply.
number - A seed value that's used to create a pseudorandom result. If you use the same seed multiple times, it will return the same result each time.
(rand-bool 0)
(rand-bool time)