Files
project-vesper/vesper/src/BuiltInMelodies

Built-In Melodies System

Overview

The built-in melodies system allows you to bake melodies directly into the firmware, eliminating the need for SD card downloads. Melodies are stored in PROGMEM (Flash memory), so they don't consume precious RAM.

How It Works

  1. Check: When a melody is requested, the Player first checks if the UID starts with builtin_
  2. Load: If it's built-in, the melody is loaded from Flash memory (PROGMEM)
  3. Fallback: If not built-in, it loads from SD card as usual

Adding New Melodies

Step 1: Create Your Melody Data

Each melody step is a 2-byte (uint16_t) bitmask representing which bells to activate:

// Example: Simple pattern
const uint16_t PROGMEM melody_my_tune[] = {
    0x0001,  // Bell 0
    0x0002,  // Bell 1
    0x0004,  // Bell 2
    0x0008,  // Bell 3
    0x0003,  // Bells 0+1 together
    0x000F   // Bells 0+1+2+3 together
};

Bitmask Reference:

  • 0x0001 = Bell 0 (bit 0)
  • 0x0002 = Bell 1 (bit 1)
  • 0x0004 = Bell 2 (bit 2)
  • 0x0008 = Bell 3 (bit 3)
  • 0x0010 = Bell 4 (bit 4)
  • 0x0020 = Bell 5 (bit 5)
  • 0x0040 = Bell 6 (bit 6)
  • 0x0080 = Bell 7 (bit 7)
  • 0x0100 = Bell 8 (bit 8)
  • ... up to 0x8000 = Bell 15 (bit 15)
  • 0x0000 = Silence/rest

Combining Bells:

  • 0x0003 = Bells 0+1 (0x0001 | 0x0002)
  • 0x0005 = Bells 0+2 (0x0001 | 0x0004)
  • 0x000F = Bells 0+1+2+3
  • 0xFFFF = All 16 bells

Step 2: Add to BuiltInMelodies.hpp

Open src/BuiltInMelodies/BuiltInMelodies.hpp and:

  1. Add your melody array:
// Your new melody
const uint16_t PROGMEM melody_my_awesome_tune[] = {
    0x0001, 0x0002, 0x0004, 0x0008,
    0x0010, 0x0020, 0x0040, 0x0080,
    // ... up to 200 steps
};
  1. Add to MELODY_LIBRARY array:
const MelodyInfo MELODY_LIBRARY[] = {
    // ... existing melodies ...

    // Your new melody
    {
        "My Awesome Tune",           // Display name
        "builtin_my_awesome_tune",   // UID (must start with "builtin_")
        melody_my_awesome_tune,      // Data array
        sizeof(melody_my_awesome_tune) / sizeof(uint16_t),  // Step count
        200  // Default speed in milliseconds per beat
    }
};

Step 3: Use Your Melody

Send a play command with the built-in melody UID:

MQTT:

{
  "group": "playback",
  "action": "play",
  "uid": "builtin_my_awesome_tune",
  "name": "My Awesome Tune",
  "speed": 200
}

WebSocket/HTTP:

{
  "group": "playback",
  "action": "play",
  "uid": "builtin_my_awesome_tune",
  "name": "My Awesome Tune",
  "speed": 200
}

Pre-Loaded Melodies

The following melodies are already built-in:

UID Name Steps Default Speed
builtin_scale Simple Scale 8 200ms
builtin_happy_birthday Happy Birthday 23 250ms
builtin_jingle_bells Jingle Bells 32 180ms
builtin_westminster Westminster Chimes 16 400ms
builtin_alarm Alarm 16 150ms
builtin_doorbell Doorbell 4 300ms
builtin_single_bell Single Bell Test 1 100ms

Memory Usage

Flash Memory (PROGMEM)

  • Small melody (~30 steps): 60 bytes
  • Large melody (~200 steps): 400 bytes
  • 40 melodies average: ~6-10KB (stored in Flash, not RAM!)

RAM Usage

Only the currently playing melody is loaded into RAM. Built-in melodies are copied from Flash when needed.

Tips

  1. Always use builtin_ prefix for UIDs to identify them as built-in
  2. Test with small melodies first before adding large ones
  3. Use hex calculator for complex bell combinations: 0x0001 | 0x0004 = 0x0005
  4. Add rests with 0x0000 for silence between notes
  5. Keep it simple - most melodies work great with 30-50 steps

Converting Binary Files to Code

If you have existing binary melody files and want to convert them to built-in format:

# Python script to convert binary file to C++ array
with open('melody.bin', 'rb') as f:
    data = f.read()

print("const uint16_t PROGMEM melody_name[] = {")
for i in range(0, len(data), 2):
    if i % 16 == 0:
        print("    ", end="")
    high = data[i]
    low = data[i+1]
    value = (high << 8) | low
    print(f"0x{value:04X}", end="")
    if i < len(data) - 2:
        print(", ", end="")
    if (i + 2) % 16 == 0:
        print()
print("\n};")

Example: Creating a Custom Melody

Let's create "Mary Had a Little Lamb":

// Mary Had a Little Lamb
// Notes: E D C D E E E, D D D, E G G
// Mapping: E=0, D=1, C=2, G=3
const uint16_t PROGMEM melody_mary_lamb[] = {
    0x0001, 0x0002, 0x0004, 0x0002,  // E D C D
    0x0001, 0x0001, 0x0001, 0x0000,  // E E E (rest)
    0x0002, 0x0002, 0x0002, 0x0000,  // D D D (rest)
    0x0001, 0x0008, 0x0008           // E G G
};

// Add to MELODY_LIBRARY:
{
    "Mary Had a Little Lamb",
    "builtin_mary_lamb",
    melody_mary_lamb,
    sizeof(melody_mary_lamb) / sizeof(uint16_t),
    300  // 300ms per beat
}

Now you can play it with UID builtin_mary_lamb!