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