Added SpeedCalc and MelodyBuilder. Evaluation Pending
This commit is contained in:
215
SecondaryApps/MelodyBuilders/convert_to_bin.py
Normal file
215
SecondaryApps/MelodyBuilders/convert_to_bin.py
Normal file
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Bell Melody Converter
|
||||
Converts human-readable bell notation to binary .bsm files for ESP32
|
||||
|
||||
Format: MELODY_NAME: 1,2,3+4,0,5+6+7
|
||||
Output: MELODY_NAME.bsm (binary file, uint16_t big-endian)
|
||||
"""
|
||||
|
||||
import sys
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
|
||||
|
||||
def parse_bell_notation(notation: str) -> int:
|
||||
"""
|
||||
Convert human-readable bell notation to bit flag value
|
||||
|
||||
Examples:
|
||||
"2+8" → bells 2,8 → bits 1,7 → 0x0082 (130)
|
||||
"4" → bell 4 → bit 3 → 0x0008 (8)
|
||||
"1+2+3" → bells 1,2,3 → bits 0,1,2 → 0x0007 (7)
|
||||
"0" → no bells → 0x0000 (0)
|
||||
|
||||
Formula: Bell #N → Bit (N-1) → Value = 1 << (N-1)
|
||||
"""
|
||||
notation = notation.strip()
|
||||
|
||||
# Handle zero/silence
|
||||
if notation == '0' or not notation:
|
||||
return 0
|
||||
|
||||
# Split by + to get individual bell numbers
|
||||
bell_numbers = notation.split('+')
|
||||
|
||||
value = 0
|
||||
for bell_str in bell_numbers:
|
||||
try:
|
||||
bell_num = int(bell_str.strip())
|
||||
|
||||
if bell_num == 0:
|
||||
continue # Bell 0 means silence, contributes nothing
|
||||
|
||||
if bell_num < 1 or bell_num > 16:
|
||||
print(f"Warning: Bell number {bell_num} out of range (1-16), skipping")
|
||||
continue
|
||||
|
||||
# Convert bell number to bit position (1-indexed to 0-indexed)
|
||||
bit_position = bell_num - 1
|
||||
bit_value = 1 << bit_position
|
||||
value |= bit_value
|
||||
|
||||
except ValueError:
|
||||
print(f"Warning: Invalid bell number '{bell_str}', skipping")
|
||||
continue
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def parse_melody_line(line: str) -> Tuple[str, List[int]]:
|
||||
"""
|
||||
Parse a melody line in format: MELODY_NAME: step,step,step
|
||||
|
||||
Returns:
|
||||
(melody_name, list of uint16_t values)
|
||||
"""
|
||||
line = line.strip()
|
||||
|
||||
if not line or line.startswith('#'):
|
||||
return None, []
|
||||
|
||||
# Split by colon
|
||||
if ':' not in line:
|
||||
print(f"Warning: Invalid format (missing ':'): {line}")
|
||||
return None, []
|
||||
|
||||
parts = line.split(':', 1)
|
||||
melody_name = parts[0].strip()
|
||||
steps_str = parts[1].strip()
|
||||
|
||||
if not melody_name:
|
||||
print(f"Warning: Empty melody name in line: {line}")
|
||||
return None, []
|
||||
|
||||
# Parse steps (comma-separated)
|
||||
step_strings = steps_str.split(',')
|
||||
values = []
|
||||
|
||||
for i, step_str in enumerate(step_strings):
|
||||
value = parse_bell_notation(step_str)
|
||||
values.append(value)
|
||||
|
||||
return melody_name, values
|
||||
|
||||
|
||||
def write_binary_melody(filepath: str, values: List[int]):
|
||||
"""
|
||||
Write melody values as binary file (uint16_t, big-endian)
|
||||
|
||||
Args:
|
||||
filepath: Output file path
|
||||
values: List of uint16_t values (0-65535)
|
||||
"""
|
||||
with open(filepath, 'wb') as f:
|
||||
for value in values:
|
||||
# Ensure value fits in uint16_t
|
||||
if value > 0xFFFF:
|
||||
print(f"Warning: Value {value} exceeds uint16_t range, truncating")
|
||||
value = value & 0xFFFF
|
||||
|
||||
# Write as 2 bytes, big-endian (MSB first)
|
||||
f.write(value.to_bytes(2, byteorder='big'))
|
||||
|
||||
|
||||
def convert_melodies_file(input_path: str, output_dir: str = '.'):
|
||||
"""
|
||||
Convert multi-melody file to individual .bsm binary files
|
||||
|
||||
Args:
|
||||
input_path: Path to input text file
|
||||
output_dir: Directory for output .bsm files
|
||||
"""
|
||||
output_path = Path(output_dir)
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
melodies_created = 0
|
||||
total_steps = 0
|
||||
|
||||
try:
|
||||
with open(input_path, 'r', encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
print(f"Reading from: {input_path}")
|
||||
print(f"Output directory: {output_path.absolute()}\n")
|
||||
|
||||
for line_num, line in enumerate(lines, 1):
|
||||
melody_name, values = parse_melody_line(line)
|
||||
|
||||
if melody_name and values:
|
||||
# Create output filename
|
||||
output_file = output_path / f"{melody_name}.bsm"
|
||||
|
||||
# Write binary file
|
||||
write_binary_melody(str(output_file), values)
|
||||
|
||||
# Calculate file size
|
||||
file_size = len(values) * 2 # 2 bytes per uint16_t
|
||||
|
||||
# Show what bells are used
|
||||
all_bells = set()
|
||||
for value in values:
|
||||
for bit in range(16):
|
||||
if value & (1 << bit):
|
||||
all_bells.add(bit + 1) # Convert back to 1-indexed
|
||||
|
||||
bells_str = ','.join(map(str, sorted(all_bells))) if all_bells else 'none'
|
||||
|
||||
print(f"✓ {melody_name}.bsm")
|
||||
print(f" Steps: {len(values)}")
|
||||
print(f" Size: {file_size} bytes")
|
||||
print(f" Bells used: {bells_str}")
|
||||
print()
|
||||
|
||||
melodies_created += 1
|
||||
total_steps += len(values)
|
||||
|
||||
print(f"{'='*50}")
|
||||
print(f"✓ Successfully created {melodies_created} melody files")
|
||||
print(f" Total steps: {total_steps}")
|
||||
print(f" Total size: {total_steps * 2} bytes")
|
||||
|
||||
return True
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"Error: Input file '{input_path}' not found")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point"""
|
||||
print("=== Bell Melody Converter ===")
|
||||
print("Creates binary .bsm files for ESP32\n")
|
||||
|
||||
# Default input file
|
||||
input_file = "all_melodies.txt"
|
||||
output_dir = "."
|
||||
|
||||
# Check if file exists
|
||||
if not Path(input_file).exists():
|
||||
print(f"Error: '{input_file}' not found in current directory!")
|
||||
print("\nPlease create 'all_melodies.txt' with format:")
|
||||
print(" MELODY_NAME: step,step,step,...")
|
||||
print("\nStep notation:")
|
||||
print(" 0 - Silence")
|
||||
print(" 4 - Bell #4 only")
|
||||
print(" 2+8 - Bells #2 and #8 together")
|
||||
print(" 1+2+3 - Bells #1, #2, and #3 together")
|
||||
print("\nExample:")
|
||||
print(" JINGLE_BELLS: 4,4,4,0,4,4,4,0,4,8,1,2,4")
|
||||
print(" ALARM: 2+8,0,2+8,0,2+8,0")
|
||||
print(" HAPPY_BIRTHDAY: 1,1,2,1,4,3,0,1,1,2,1,8,4")
|
||||
sys.exit(1)
|
||||
|
||||
success = convert_melodies_file(input_file, output_dir)
|
||||
sys.exit(0 if success else 1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user