A voice recorder that can't lose a recording

2 min read

TapedeckiOSAudio

Most recorders write the file when you press stop. Tapedeck writes it all the time, so a crash or a dead battery costs a few seconds at most.

The worst reviews of voice recorder apps are not about missing features. They are about recordings that were there, and then weren't. In the reviews I read before building Tapedeck, people had lost the recording of a child's birth and evidence they needed for a court case.

It sounds like a bug in those apps, but most of the time it is the file format.

The format decides

An .m4a file keeps its index, the part that tells a player where the audio is, at the end of the file. It is written when you press stop. If the app never gets to stop, because it crashed or the phone switched off, the audio data may be on disk but there is no index, and nothing will play it.

So Tapedeck doesn't record into M4A. It records into CAF, Apple's Core Audio Format. CAF is built from chunks and is designed so that a file which was never closed is still readable up to the last byte that was written. A cut-off CAF is a shorter recording, not a broken one.

The loop

While recording, audio goes from AVAudioEngine straight into the CAF file. Every autosave interval, which you can set to 1, 3 or 10 seconds, the app forces the data to disk with fsync and rewrites a small session file next to it: when the recording started, the markers, and when it was last saved. That session file is written to a temporary name and then renamed, so it is never half written either.

When you press stop, Tapedeck converts the CAF into a normal M4A, opens the result to check that it plays and that its length matches, and only then deletes the CAF.

Recovery

On every launch the app looks for sessions that never reached the end of that process. Anything it finds is a recording that survived something: it is converted, added to the library, and a banner says so.

The library database is only an index, written afterwards. Recovery trusts what is on disk, never the database.

I built this loop before any screen. Its test is simple: kill the app in the middle of a recording, and what comes back has to play, to within one autosave interval of where it stopped. Everything else in Tapedeck, the transcripts, the search, the themes, sits on top of that one promise.