Recording sleep sounds
Sleep audio recording apps have recently become very popular. I was also curious to hear what I say when I sleep and whether I snore.
If your phone can record audio and your computer can run a simple Python script, you don't need to buy anything or pay for a subscription to find the loud moments during the night.
First, we need to record audio while we sleep. In my case, I just started recording on my iPhone before I went to bed, and the next morning, I had an eight-hour M4A file.
Searching through such a long recording by hand is tedious, so let's extract only the fragments above a chosen volume threshold.
I'll leave the Python environment setup out of this article.
We only need to install pydub,
a high-level Python library for working with audio, and create an empty script such as sleep.py.
The simplest example looks like this:
from pydub import AudioSegment
from pydub.silence import split_on_silence
audio = AudioSegment.from_file("sleep_audio.m4a", format="m4a")
chunks = split_on_silence(audio, silence_thresh=-54)
sum(chunks).export("loud_fragments.mp3", format="mp3")
Here, we load the audio file and use a threshold in
dBFS to separate louder fragments from silence.
Zero is the maximum level, so -54 means 54 decibels below the maximum.
The last line exports the result as an MP3 file.
Now, let's make the processing faster and fine-tune a few parameters to improve the outcome:
...
chunks = split_on_silence(
audio,
keep_silence=10_000,
min_silence_len=10_000,
silence_thresh=-54,
seek_step=1_000)
...
keep_silence=10_000: keeps ten seconds of silence at the beginning and end of each chunk, so the sound isn't cut off abruptly.min_silence_len=10_000: requires at least ten seconds of silence before splitting the audio.silence_thresh=-54: treats anything quieter than-54dBFS as silence.seek_step=1_000: scans the audio in one-second steps. The default is one millisecond, which makes an eight-hour recording take much longer to process.
To distinguish one fragment from another, we can add a "beep" in between,
preceded and followed by a few seconds of silence.
We can also raise the volume of the audio chunks by adding the number of decibels like: chunk + 3.
...
from pydub.generators import Square
...
silence = AudioSegment.silent(duration=500)
beep = Square(1_000).to_audio_segment(duration=250, volume=-54)
beep_with_silence = silence + beep + silence
loud_fragments = sum([(chunk + 3) + beep_with_silence for chunk in chunks])
loud_fragments.export("loud_fragments.mp3", format="mp3")
The final version of my script looks like this:
import sys
from pathlib import Path
from pydub import AudioSegment
from pydub.generators import Square
from pydub.silence import split_on_silence
GAIN = 3
KEEP_SILENCE = 10_000
MIN_SILENCE_LEN = 60_000
SEEK_STEP = 100
SILENCE_THRESH = -54
def main(file_path):
audio = AudioSegment.from_file(file_path, format="m4a")
print(f"Loaded audio: {audio.duration_seconds} seconds")
chunks = [chunk for chunk in split_on_silence(audio,
keep_silence=KEEP_SILENCE,
min_silence_len=MIN_SILENCE_LEN,
seek_step=SEEK_STEP,
silence_thresh=SILENCE_THRESH)
if chunk.duration_seconds > (KEEP_SILENCE * 2 / 1_000)]
print(f"Found {len(chunks)} chunks")
silence = AudioSegment.silent(duration=500)
beep = Square(1_000).to_audio_segment(duration=250, volume=SILENCE_THRESH)
beep_with_silence = silence + beep + silence
loud_audio = sum([(chunk + GAIN) + beep_with_silence for chunk in chunks])
print(f"Combined loud audio: {loud_audio.duration_seconds} seconds")
Path("./data").mkdir(parents=True, exist_ok=True)
loud_audio.export(f"./data/{Path(file_path).stem}_loud.mp3", format="mp3")
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python sleep.py <file_path>")
sys.exit(1)
main(sys.argv[1])
I run it with python sleep.py myaudio.m4a, and it creates a shorter version of the recording.
The result is usually between 8 and 12 minutes long for eight hours of sleep.