1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef SERVICES_AUDIO_DELAY_BUFFER_H_ 6 #define SERVICES_AUDIO_DELAY_BUFFER_H_ 7 8 #include <memory> 9 10 #include "base/containers/circular_deque.h" 11 #include "base/macros.h" 12 13 namespace media { 14 class AudioBus; 15 } // namespace media 16 17 namespace audio { 18 19 // Records and maintains a recent history of an audio signal, then allows 20 // read-back starting from part of the recording. While this looks a lot like a 21 // FIFO, it is not because Read() will typically not be reading from one end of 22 // a queue. 23 // 24 // The audio format is the same throughout all operations, as this DelayBuffer 25 // does not resample or remix the audio. Also, for absolute precision, it uses 26 // frame counts to track the timing of the audio recorded and read. 27 // 28 // The typical use case is the loopback audio system: In this scenario, the 29 // service has an audio output stream running for local playback, and the 30 // stream's audio is timed to play back in the near future (usually, 1 ms to 20 31 // ms, depending on the platform). When loopback is active, that audio will be 32 // copied into this DelayBuffer via calls to Write(). Then, the loopback audio 33 // stream implementation will Read() the audio at a time in the recent past 34 // (approximately 20 ms before "now," but this will vary slightly). Because of 35 // clock drift concerns, the loopback implementation will slightly compress/ 36 // stretch the audio signal it pulls out of this buffer, to maintain 37 // synchronization, and this will cause it to vary the number of frames read for 38 // each successive Read() call. 39 class DelayBuffer { 40 public: 41 // Use sample counts as a measure of audio signal position. 42 using FrameTicks = int64_t; 43 44 // Construct a DelayBuffer that keeps at least |history_size| un-read frames 45 // recorded. 46 explicit DelayBuffer(int history_size); 47 48 ~DelayBuffer(); 49 50 // Inserts a copy of the given audio into the buffer. |position| must be 51 // monotonically increasing, and the audio must not overlap any 52 // previously-written audio. The length of the |input_bus| may vary, but the 53 // channel layout may not. The given |volume| will be used to scale the audio 54 // during the copy to the internal buffer. 55 void Write(FrameTicks position, 56 const media::AudioBus& input_bus, 57 double volume); 58 59 // Reads audio from the buffer, starting at the given |from| position, which 60 // must not overlap any previously-read audio. |frames_to_read| is the number 61 // of frames to read, which may be any amount less than or equal to the size 62 // of the |output_bus|. No part of the |output_bus| beyond the first 63 // |frames_to_read| will be modified. If there are gaps (i.e., missing pieces) 64 // of the recording, zeros will be filled in the output. 65 void Read(FrameTicks from, int frames_to_read, media::AudioBus* output_bus); 66 67 // Returns the current buffered range of the recording, ala the usual C++ 68 // [begin,end) semantics. 69 FrameTicks GetBeginPosition() const; 70 FrameTicks GetEndPosition() const; 71 72 private: 73 struct InputChunk { 74 // The position of the first frame in this chunk. 75 FrameTicks position; 76 77 // The storage for the audio frames. 78 std::unique_ptr<media::AudioBus> bus; 79 80 // Constructor for an InputChunk with data. 81 InputChunk(FrameTicks p, std::unique_ptr<media::AudioBus> b); 82 83 // Move constructor/assignment. 84 InputChunk(InputChunk&& other); 85 InputChunk& operator=(InputChunk&& other); 86 87 ~InputChunk(); 88 89 // Returns the position just after the last frame's position. 90 FrameTicks GetEndPosition() const; 91 92 private: 93 DISALLOW_COPY_AND_ASSIGN(InputChunk); 94 }; 95 96 // The minimum number of un-read frames that must be kept. 97 const int history_size_; 98 99 // A queue storing each chunk of recorded audio. The elements in the queue are 100 // always in-order, chronologically increasing by InputChunk::position, and do 101 // not overlap. 102 base::circular_deque<InputChunk> chunks_; 103 104 DISALLOW_COPY_AND_ASSIGN(DelayBuffer); 105 }; 106 107 } // namespace audio 108 109 #endif // SERVICES_AUDIO_DELAY_BUFFER_H_ 110