smol stack synthesizer for embedded systems, single-header, based on work by viznut et al.
$ make music
birb programs are arrays of values between 0x00 and 0x1F (5 bits), terminated with 0xFF (BIRB_END).
Programs operate on a stack, making the programs very RPN-like. Popping a value from an empty stack yields 0, and pushing a value onto a full stack overwrites the top value. The default maximum stack size is 64 but can be customized with BIRB_STACK_SIZE.
When evaluating programs, there is a special T value set which can be referenced from the birb program. This is intended to be used to produce time-varying output for synthesizing sounds.
After hitting the end of the program, top value on the stack is popped and returned from birb_eval (or 0 if the stack was empty).
Most opcodes pop 2 values from the stack (x then y) and push a single value back:
BIRB_SHR:y >> xBIRB_SHL:y << xBIRB_AND:y & xBIRB_OR:y | xBIRB_XOR:y ^ xBIRB_ADD:y + xBIRB_SUB:y - xBIRB_MUL:y * xBIRB_DIV:y / x, or0ifx == 0BIRB_MOD:y % x, or0ifx == 0BIRB_DIG:(x << 4) | y. Useful for constructing larger constants, e.g.4 3 2 1 DIG DIG DIGyields0x1234
Other opcodes that do not follow the above pattern:
0x0-0xF: Push the given opcode onto the stackBIRB_T: Push the special variableTonto the stackBIRB_U: Push the special variableUonto the stackBIRB_SWP: Swap the position of the top two values on the stackBIRB_DUP: Duplicate the top value on the stackBIRB_RND: Popxfrom the stack, then pushhash(x)back