// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef SERVICES_AUDIO_DELAY_BUFFER_H_ #define SERVICES_AUDIO_DELAY_BUFFER_H_ #include #include "base/containers/circular_deque.h" #include "base/macros.h" namespace media { class AudioBus; } // namespace media namespace audio { // Records and maintains a recent history of an audio signal, then allows // read-back starting from part of the recording. While this looks a lot like a // FIFO, it is not because Read() will typically not be reading from one end of // a queue. // // The audio format is the same throughout all operations, as this DelayBuffer // does not resample or remix the audio. Also, for absolute precision, it uses // frame counts to track the timing of the audio recorded and read. // // The typical use case is the loopback audio system: In this scenario, the // service has an audio output stream running for local playback, and the // stream's audio is timed to play back in the near future (usually, 1 ms to 20 // ms, depending on the platform). When loopback is active, that audio will be // copied into this DelayBuffer via calls to Write(). Then, the loopback audio // stream implementation will Read() the audio at a time in the recent past // (approximately 20 ms before "now," but this will vary slightly). Because of // clock drift concerns, the loopback implementation will slightly compress/ // stretch the audio signal it pulls out of this buffer, to maintain // synchronization, and this will cause it to vary the number of frames read for // each successive Read() call. class DelayBuffer { public: // Use sample counts as a measure of audio signal position. using FrameTicks = int64_t; // Construct a DelayBuffer that keeps at least |history_size| un-read frames // recorded. explicit DelayBuffer(int history_size); ~DelayBuffer(); // Inserts a copy of the given audio into the buffer. |position| must be // monotonically increasing, and the audio must not overlap any // previously-written audio. The length of the |input_bus| may vary, but the // channel layout may not. The given |volume| will be used to scale the audio // during the copy to the internal buffer. void Write(FrameTicks position, const media::AudioBus& input_bus, double volume); // Reads audio from the buffer, starting at the given |from| position, which // must not overlap any previously-read audio. |frames_to_read| is the number // of frames to read, which may be any amount less than or equal to the size // of the |output_bus|. No part of the |output_bus| beyond the first // |frames_to_read| will be modified. If there are gaps (i.e., missing pieces) // of the recording, zeros will be filled in the output. void Read(FrameTicks from, int frames_to_read, media::AudioBus* output_bus); // Returns the current buffered range of the recording, ala the usual C++ // [begin,end) semantics. FrameTicks GetBeginPosition() const; FrameTicks GetEndPosition() const; private: struct InputChunk { // The position of the first frame in this chunk. FrameTicks position; // The storage for the audio frames. std::unique_ptr bus; // Constructor for an InputChunk with data. InputChunk(FrameTicks p, std::unique_ptr b); // Move constructor/assignment. InputChunk(InputChunk&& other); InputChunk& operator=(InputChunk&& other); ~InputChunk(); // Returns the position just after the last frame's position. FrameTicks GetEndPosition() const; private: DISALLOW_COPY_AND_ASSIGN(InputChunk); }; // The minimum number of un-read frames that must be kept. const int history_size_; // A queue storing each chunk of recorded audio. The elements in the queue are // always in-order, chronologically increasing by InputChunk::position, and do // not overlap. base::circular_deque chunks_; DISALLOW_COPY_AND_ASSIGN(DelayBuffer); }; } // namespace audio #endif // SERVICES_AUDIO_DELAY_BUFFER_H_