o
odinpkg.dev
packages / library / binaural

binaural

e3fc552library

Miniaudio binaural sound player in Odin.

No license · updated 8 months ago

Binaural Beats

What "binaural" means

The word binaural literally means "two ears." In audio, it refers to sounds that are designed to be different in each ear so that your brain perceives something new when combining them.

For binaural beats, you feed each ear a pure tone (sine wave) of slightly different frequency. Your ears send those signals to the brain, and the brainstem processes them together.

How binaural beats are made

Let’s say you want a 7 Hz (alpha, relaxed hearing) binaural beat:

  • Left ear: 220 Hz sine wave
  • Right ear: 227 Hz sine wave

You never directly hear "7 Hz" - the brain perceives the difference between the two tones as a sort of rhythmic pulsing. This happens because:

  • Each ear sends its tone's signal to the auditory cortex.
  • The brain compares the two, and neural activity synchronizes to the frequency difference (here, 7 Hz).

This only works with headphones

If you play the two tones from speakers into open air, they just physically mix, creating an acoustic beat - a real amplitude fluctuation in the air. That’s a different thing.

With headphones, the signals stay separated until they’re inside your head, so the "beating" is a brain perception effect rather than an acoustic interference effect.

Brainwave entrainment idea

The "beat" frequency can be chosen to align with certain brainwave ranges:

Brainwave type Frequency range Associated mental state
Delta 0.5–4 Hz Deep sleep, unconsciousness
Theta 4–8 Hz Meditation, creativity, light sleep
Alpha 8–13 Hz Relaxed, calm, learning
Beta 13–30 Hz Alertness, problem-solving
Gamma 30–50 Hz High-level processing

Some people use binaural beats to try to encourage the brain into certain states (research on effectiveness is mixed).

How to calculate

If we have:

  • Carrier frequency f_c (base tone, same order of magnitude in each ear)
  • Beat frequency f_b (difference between ears)

Then:

  • The left ear frequency is f_c; the signal is L(t)=sin(2*π*f_c*t)
  • The right ear frequency is f_c + f_b; the signal is L(t)=sin(2*π*(f_c + f_b)*t)

The brain detects f_b as the binaural beat.

Difference to “monaural beats”

Monaural beat: Two tones are mixed before reaching the ears. The interference creates a real amplitude modulation in the waveform.

Binaural beat: Two tones are kept separate to each ear, and the interference is processed by the brain.

Why you sometimes add noise (pink/white noise)

A steady sine tone can sound sterile.

Adding low-level pink noise makes it more natural and masks high-frequency artifacts. Some claim it also helps immerse the listener in the soundscape.

Some common carrier frequencies

Frequencies for a C major scale, based on A4 = 440 Hz

Note Frequency (Hz)
C4 261.63
D4 293.66
E4 329.63
F4 349.23
G4 392.00
A4 440.00
B4 493.88
C5 523.25

Using the binaural.Engine

main :: proc() {
    sr : u32 = 48_000
    
    eng: binaural.Engine
    if !binaural.engine_start(&eng, sr) {
        fmt.eprintln("Failed to start audio device")
        return
    }
    defer binaural.engine_stop(&eng)
    
    // let's play an "A Mixolydian" chord (A, B, C#, D, E, F#, G)
    beat :: proc(min:f64, max:f64) -> f64 {
        return rand.float64_range(min, max)
    }
    // stimulate "Delta" brain waves, try not to fall asleep
    min := 0.5
    max := 4.0
    
    _ = binaural.play_binaural(&eng, 0, binaural.new_opts(440.00, beat(min, max)))
    _ = binaural.play_binaural(&eng, 1, binaural.new_opts(493.88, beat(min, max)))
    _ = binaural.play_binaural(&eng, 2, binaural.new_opts(554.37, beat(min, max)))
    _ = binaural.play_binaural(&eng, 3, binaural.new_opts(587.33, beat(min, max)))
    _ = binaural.play_binaural(&eng, 4, binaural.new_opts(659.25, beat(min, max)))
    _ = binaural.play_binaural(&eng, 5, binaural.new_opts(739.99, beat(min, max)))
    _ = binaural.play_binaural(&eng, 6, binaural.new_opts(783.99, beat(min, max)))
    
    current_time := time.now()
    for time.duration_seconds(time.since(current_time)) < 10.0 {
        fmt.print("Tick ")
        time.sleep(1000 * time.Millisecond)
        fmt.println("- tock...")
        time.sleep(1000 * time.Millisecond)
    }

}