Skip to content

Instantly share code, notes, and snippets.

@pjakuszew
Created October 30, 2023 13:50
Show Gist options
  • Select an option

  • Save pjakuszew/9080e5b4fa20be82496dfe8e9511a3d5 to your computer and use it in GitHub Desktop.

Select an option

Save pjakuszew/9080e5b4fa20be82496dfe8e9511a3d5 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# Usage: soco_alarm_sound_dumper.py <alarm_spi_flash_dump_file>
#
# This script reads the sounds from specified Super Soco alarm module
# memory dump and exports them as playable Wave files.
#
# There seems to be a "table of contents" (ToC) at the beginning of
# SPI flash that defines the location and length of each sound.
# Each ToC entry is 8 bytes long, where 4 bytes are the sound offset and
# and next 4 bytes are denoting the length. After the last ToC entry
# there is a value which I couldn't figure out, that's what causes the
# error about current sound offset being greater that last sound's offset.
#
# Sounds I have found in memory dumps from two alarm modules:
# (listed in no particular order)
# - Lock/unlock/startup sound made out of iPhone "Popcorn" alarm tone
# - Lock/unlock/startup featuring the infamous piano jingle startup that
# pissed me so much I started working on this script
# - Lock/unlock/startup bleeps which I presumed to be some old stuff
# that was supposed to be played on a piezoelectric speaker, but then
# I met someone with a '19 TC Max which used these
# - Error tone which can be heard when you keep pressing Start button
# repeatedly
# - Rather long alarm siren that takes a lot of memory space
# - Short alarm siren
# - Shutdown sound
#
# Sound format: Raw PCM frames, 11025Hz framerate, mono.
# On two different alarm modules these sounds were stored on
# a BoyaMicro 25D16ASTIG SPI flash chip that I was able to read using CH341
# and flashrom utility on Linux.
#
# I'm sharing this because I don't have time nor will to play with this anymore.
import sys
import struct
import wave
sounds = []
with open(sys.argv[1], 'rb') as f:
for _ in range(16):
chunk = f.read(8)
if len(chunk) < 8:
break
offset, length = struct.unpack('>ii', chunk)
sounds.append((offset, length))
for i, sound in enumerate(sounds):
f.seek(sound[0])
if i+1 < len(sounds):
length = sounds[i+1][0] - sound[0]
if length < 0:
print(f"Error: sound{i} offset is greater than sound{i+1} offset")
continue
data = f.read(length)
else:
data = f.read()
with open(f'sound{i}.pcm', 'wb') as raw_file:
raw_file.write(data)
with wave.open(f'sound{i}.wav', 'wb') as sound_file:
sound_file.setnchannels(1)
sound_file.setsampwidth(4)
sound_file.setframerate(11025.0)
sound_file.writeframesraw(data)
for i, sound in enumerate(sounds):
print(f'[sound{i}] Offset: {sound[0]:x}, Length: {sound[1]:x}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment