mirror of
https://github.com/mikecarper/MeshCore.git
synced 2026-10-22 12:32:28 +00:00
67 lines
2.8 KiB
Python
67 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Encode the fixed button-confirmation WAV catalog as independent IMA clips."""
|
|
import argparse
|
|
from pathlib import Path
|
|
|
|
from generate_gps_voice import encode, read
|
|
|
|
PHRASES = (
|
|
('ready', 'Ready'),
|
|
('notificationCleared', 'Notification cleared'),
|
|
('advertQueued', 'Advert queued'),
|
|
('advertFailed', 'Advert failed'),
|
|
('soundOn', 'Sound on'),
|
|
('soundOff', 'Sound off'),
|
|
('actionFailed', 'Action failed'),
|
|
('usbSetup', 'USB setup'),
|
|
('shuttingDown', 'Shutting down'),
|
|
('restarting', 'Restarting'),
|
|
('alertsSoundVibration', 'Sound and vibration'),
|
|
('alertsSoundOnly', 'Sound only'),
|
|
('alertsVibrationOnly', 'Vibration only'),
|
|
('alertsSilent', 'Silent'),
|
|
)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--directory', type=Path, required=True)
|
|
parser.add_argument('--output', type=Path, required=True)
|
|
parser.add_argument('--voice', help='Speech voice used to synthesize the source WAVs')
|
|
args = parser.parse_args()
|
|
if args.voice is not None and (not args.voice.strip() or '\n' in args.voice or '\r' in args.voice):
|
|
parser.error('--voice must be a nonempty single-line label')
|
|
lines = [
|
|
'// Generated by scripts/generate_button_voice.py. 8 kHz IMA ADPCM.',
|
|
'// Fixed spoken confirmations; no runtime speech engine.',
|
|
'#pragma once', '#include <helpers/ui/GpsVoice.h>',
|
|
'namespace mesh { namespace audio {',
|
|
]
|
|
if args.voice is not None:
|
|
lines.insert(2, f'// Voice: {args.voice}')
|
|
total = 0
|
|
for name, phrase in PHRASES:
|
|
samples = read(args.directory / (name + '.wav'))
|
|
# Shutdown drains audio nonblockingly for at most five seconds.
|
|
if not samples or len(samples) > 32000:
|
|
raise ValueError(f'{phrase}: expected a nonempty clip under four seconds')
|
|
encoded = encode(samples)
|
|
total += len(encoded)
|
|
if name == 'alertsSoundVibration':
|
|
# Only buzzer+vibration devices need the additional mode catalog.
|
|
lines.append('#ifdef HAS_DRV2605')
|
|
lines.extend([f'// "{phrase}"', f'static const uint8_t {name}Data[] = {{'])
|
|
for offset in range(0, len(encoded), 20):
|
|
lines.append(' ' + ','.join(f'0x{b:02x}' for b in encoded[offset:offset + 20]) + ',')
|
|
lines.extend(['};', f'static constexpr VoiceClip {name}Clip = {{',
|
|
f' {name}Data, sizeof({name}Data), {len(samples)}, 8000', '};'])
|
|
print(f'{phrase}: {len(samples) / 8000:.3f}s, {len(encoded)} bytes')
|
|
lines.append('#endif // HAS_DRV2605')
|
|
lines.append('}} // namespace mesh::audio')
|
|
args.output.write_text('\n'.join(lines) + '\n', encoding='utf-8')
|
|
print(f'Button confirmations: {total} audio bytes')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|