Code Monkey home page Code Monkey logo

lebac's Introduction

le bac

badge audio composer: qguv's music tracker for the rvasec 2018 conference badge created by hackrva

About

The 2017 badge had a speaker connected to a pin of the pic32 MCU. By setting the pin high, we could push the speaker to its limit in one direction; setting the pin low would let the speaker relax and eventually return to its neutral position. This was fairly quiet, but good enough for simple, constant-frequency beeps.

For the 2018 badge, I begged for some way to drive the speaker in more than a single direction. Paul eventually caved and added in an H-bridge, a component that's usually used for driving motors, and connected it to two MCU pins. This lets us push the speaker in one direction (by setting the first pin high and the second low); pull the speaker (by setting the first pin low and the second high); quickly neutralize the speaker back to its natural resting position (by setting both pins high); or let the speaker return to its resting position on its own (by setting both pins low).

The addition of the H-bridge turns one-bit audio (speaker is either neutral or pushed forward) into "one-and-a-half-bit" audio (speaker is either pulled backward, neutral, or pushed forward). This has several nice consequences:

  • potential for higher-resolution audio
  • much louder sound output if driven at a high duty cycle
  • potential for more interesting waveforms: variable shape, duty cycle, etc.

The ability to create more than just square waves opens the door to something that really doesn't belong on a speaker like this one: two channel audio. The holy grail for this project was to have two sounds being synthesized in software and mixed in such a way that both sounds are clearly audible in a final signal that's free of distortions and dissonant harmonics.

To actually accomplish this, we had to overcome several challenges:

  • an extremely CPU-limited audio interrupt handler (on the order of 50 cycles per interrupt) to avoid timing out or hogging the CPU from running badge apps
  • virtually no memory
  • a proprietary compiler with a personal vendetta against me

The CPU limitations of the interrupt handler can be overcome by doing much of the necessary computation before any code is transferred to the badge. We found that small wavetables can greatly reduce the number of ALU operations (especially divisions) needed to synthesize and mix audio. This is the primary purpose of lebac: to do as much computation as possible up front and compile the results into tables and lists of sound instructions that the interrupt handler and other badge apps can understand.

Making a TUI tracker was fun, but two real signal processing challenges had to be addressed:

  1. what wave shapes can best be combined to minimize distortion when mixed together and crunched into one-and-a-half-bit space?
  2. what is the best way to crunch an arbitrary waveform (the sum of the two oscillators) into one-and-a-half-bit space?

For me, the answers were low-duty asymmetrical neutralizing pulse waves and summing and setting threshholds. You can read the audio(...) function in src/main.c for a detailed description of precisely how these are done, but for now, I'll give a general overview.

Now that there's the option to neutralize the speaker (pull it to its resting state), the number of waves that can be made is much greater. Where the 2017 badge could produce square waves of various pulse widths, the 2018 badge can use its H-bridge to approximate several interesting wave shapes:

  • sin/triangle waves: pull, neutral, push, neutral; pull, neutral, push, neutral; ...
  • saw/invsaw waves: pull, neutral push; pull, neutral, push; ...
  • full-cycle square/pulse waves: pull, push; pull, push; ...
  • half-cycle square/pulse waves: pull, neutral; pull, neutral; ... or push, neutral; push neutral; ...
  • noise

All the shapes listed above are symmetrical, but we're not limited to symmetrical shapes. We can also have:

  • asymmetrical neutralizing pulse: pull, neutral, neutral, neutral, push, neutral; ...
  • asymmetrical saw waves: pull, pull, pull, neutral, neutral, push; ...
  • arbitrarily complex waves: e.g. pull, neutral, pull, neutral, neutral, push, push, neutral; ...

As it turns out, using the first of these with a very low duty cycle produces waves that are non-neutral infrequently enough not to interfere with each other too often while also producing enough variance when summing oscillators that any chaotic section of the combined wave doesn't repeat frequently enough to be perceptibly dissonant. This means our waves look like:

__,                           ,__,                           ,__,
  |___________________,  ,____|  |___________________,  ,____|  |______....
                      |__|                           |__|
                                   |
                                 --+--
                                   |

_,              ,_,              ,_,              ,_,              ,_,
 |_________, ,__| |_________, ,__| |_________, ,__| |_________, ,__| |_...
           |_|              |_|              |_|              |_|

                                 ____
                                 ____

__,             ,_,           ,____,              ,_,        ,_,   ,_,
  |________, ,__| |___,  ,__, |    |_________, ,__| |,  ,____| |___| |_....
           |_|        |__|  |_|              |_|     |__|

By transferring simple wavetables onto the badge, the computation needed to generate two such waveforms and sum them is minimal.

Building

out123 (from mpg123) or aplay (from alsa-utils) is required for audio output.

The termbox TUI library is statically linked into the generated executable.

Once you have one of those, type make.

Usage

To run:

./build/lebac [filename]

Press '?' for a list of keybindings. Press Ctrl-C twice to exit.

Thanks

lebac's People

Contributors

dfirebaugh avatar qguv avatar smcameron avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.