tree: 8bbd1c80e3c45ea1f9c57b06fc9a58e7b4d603ba [path history] [tgz]
  4. wav_writer.h

WavWriter library

This library enables clients to save audio stream(s) to WAV audio files. This is intended for use during development only: it allows developers to analyze and improve audio fidelity in their components (including Fuchsia OS itself).

This functionality can be toggled by the bool template parameter on the WavWriter object itself. When false, it results in zero additional code added (assuming a reasonable optimizing compiler).

In Fuchsia, this library is used in various locations, including media examples FX and wav_recorder, as well as the system audio mixer itself. What follows are details on how to use the WavWriter library, followed by instructions on using the WavWriter support built into the system mixer to inspect the final mixed audio output streams -- useful when debugging audio-related code.

Using WavWriter

See wav_recorder in ‘examples/media’ for a focused, easy-to-follow example of using this library.

After creating a WavWriter object (with template parameter ‘true’ or blank), the Initialize call creates a RIFF-based WAV audio file, leaving it open and ready for writing the audio data into the file. The file is of non-zero size because there is a file header describing the contents, but it contains no audio data yet. The Reset call, incidently, returns the file to exactly this state.

Subsequently, the Write call accepts a buffer of audio data, as well as the number of bytes in that buffer, and it transfers those bytes directly into the file. Note that for performance reasons, the file headers are not updated upon every single Write call. Because the headers contain fields that denote the amount of audio data, they must be updated eventually, but this could be done only occasionally, or even just once at the end of the recording process. The UpdateHeaders call does exactly this, writing the two fields that change as the file grows in size, and returning the write cursor to the end of the open file. Clients can call UpdateHeader as frequently as they wish (after every Write, or every few seconds, or only once).

The Close call finalizes (closes) the file handle and returns the library to an uninitialized state, preventing it from being written further. Before it does so, Close first conveniently calls UpdateHeader. At any time, Delete will remove the WAV file that has been created, leaving the file system as it was before. Note that Delete can be called at any time after Initialize has been called, even after Close has been called.

As mentioned above, by simply switching the template parameter to , all library code will convert to inlined stubs that are eliminated by a modern optimizing compiler -- so you can instrument your code without worrying about any impact on performance when this tracing is disenabled.

Inspecting final output from the system mixer

In development builds of the Fuchsia OS, the WavWriter can be used to examine the audio streams emitted by the system mixer (of course, this functionality is removed from official production builds where this might pose a media security or overall resource consumption risk).

To enable the WavWriter support in the system audio mixer, change the bool (kWavWriterEnabled) found toward the top of to ‘true’. The Fuchsia system mixer produces a final mix output stream (potentially multi-channel) for every audio output device found. Thus, enabling the WavWriter will cause an audio file to be created for each output device.

These files are created on the target (Fuchsia) device at location ‘/tmp/wav_writer_N.wav’, where N is a unique integer for each output. One can copy these files back to the host with:

  fx scp <ip of fuchsia device>:tmp/wav_writer_*.wav ~/Desktop/

At this time, once audio playback begins on any device, the system audio mixer produces audio for ALL audio output devices (even if no client is playing audio to that device). The wave files for these devices will, naturally, contain silence.

Additionally, at this time Fuchsia continues mixing (once it has started) to output devices indefinitely, even after all clients have closed. This will change in the future. Until then, however, the most effective way to use this tracing feature is to killall audio_core on the target, once playback is complete. (The audio_core process restarts automatically when needed, so this is benign.) The mixer calls UpdateHeader to update the ‘length’ parameter in both RIFF Chunk and WAV header after every successive file write, so the files should be complete and usable even if you kill audio_core during audio playback (which means that Close is never called).