1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "ring_buffer.h"
18
19#include "integral_types.h"
20
21namespace video_editing {
22
23void RingBuffer::Init(int size, int num_channels, int num_readers) {
24  size_ = size;
25  num_channels_ = num_channels;
26  num_readers_ = num_readers;
27  temp_read_buffer_size_ = 1024;
28  initialized_ = true;
29  Reset();
30}
31
32RingBuffer::RingBuffer()
33    : initialized_(false), samples_(NULL),
34      num_readers_(0), temp_read_buffer_(NULL) {
35}
36
37RingBuffer::~RingBuffer() {
38  delete[] samples_;
39  delete[] temp_read_buffer_;
40}
41
42void RingBuffer::Reset() {
43  delete[] samples_;
44  samples_ = new float[size_ * num_channels_];
45  memset(samples_, 0,
46         size_ * num_channels_ * sizeof(samples_[0]));
47
48  temp_read_buffer_size_ = 1024;
49  delete[] temp_read_buffer_;
50  temp_read_buffer_ = new float[temp_read_buffer_size_ * num_channels_];
51  memset(temp_read_buffer_, 0,
52         temp_read_buffer_size_ * num_channels_ * sizeof(samples_[0]));
53  readers_.clear();
54  for (int i = 0; i < num_readers_; ++i) {
55    readers_.push_back(0LL);
56  }
57  head_logical_ = 0LL;
58  head_ = 0;
59}
60
61int RingBuffer::available(int reader) const {
62  return head_logical_ - readers_[reader];
63}
64
65int RingBuffer::overhead() const {
66  int64 tail = GetTail();
67  return tail + size_ - head_logical_;
68}
69
70int64 RingBuffer::GetTail() const {
71  return *min_element(readers_.begin(), readers_.end());
72}
73
74int64 RingBuffer::Tell(int reader) const {
75  return readers_[reader];
76}
77
78void RingBuffer::Seek(int reader, int64 position) {
79  readers_[reader] = position;
80}
81
82void RingBuffer::Write(const float* samples, int num_frames) {
83  if (!num_frames) {
84    return;
85  }
86  if (head_ + num_frames <= size_) {
87    memcpy(samples_ + head_ * num_channels_, samples,
88           num_frames * num_channels_ * sizeof(samples[0]));
89    head_ += num_frames;
90  } else {
91    int overhead = size_ - head_;
92    memcpy(samples_ + head_ * num_channels_, samples,
93           num_channels_ * overhead * sizeof(samples[0]));
94    head_ = num_frames - overhead;
95    memcpy(samples_, samples + overhead * num_channels_,
96           num_channels_ * head_ * sizeof(samples[0]));
97  }
98  head_logical_ += num_frames;
99}
100
101void RingBuffer::Copy(int reader, float* destination, int num_frames) const {
102  int pos = Tell(reader) % size_;
103  if (pos + num_frames <= size_) {
104    memcpy(destination, samples_ + pos * num_channels_,
105           num_channels_ * num_frames * sizeof(destination[0]));
106  } else {
107    int wrapped = size_ - pos;
108    memcpy(destination, samples_ + pos * num_channels_,
109           num_channels_ * wrapped * sizeof(destination[0]));
110    int remaining = num_frames - wrapped;
111    memcpy(destination + wrapped * num_channels_, samples_,
112           num_channels_ * remaining * sizeof(destination[0]));
113  }
114}
115
116float* RingBuffer::GetPointer(int reader, int num_frames) {
117  int pos = Tell(reader) % size_;
118  if (pos + num_frames <= size_) {
119    return samples_ + pos * num_channels_;
120  } else {
121    if (num_frames > temp_read_buffer_size_) {
122      temp_read_buffer_size_ = num_frames;
123      delete[] temp_read_buffer_;
124      temp_read_buffer_ =
125          new float[temp_read_buffer_size_ * num_channels_];  // NOLINT
126    }
127    Copy(reader, temp_read_buffer_, num_frames);
128    return temp_read_buffer_;
129  }
130}
131
132void RingBuffer::MergeBack(int reader, const float* source, int num_frames) {
133  // If the source pointer is not the temporary buffer,
134  // data updates were performed in place, so there is nothing to do.
135  // Otherwise, copy samples from the temp buffer back to the ring buffer.
136  if (source == temp_read_buffer_) {
137    int pos = Tell(reader) % size_;
138    if (pos + num_frames <= size_) {
139      memcpy(samples_ + (pos * num_channels_), source,
140             num_channels_ * num_frames * sizeof(source[0]));
141    } else {
142      int wrapped = size_ - pos;
143      memcpy(samples_ + (pos * num_channels_), source,
144             num_channels_ * wrapped * sizeof(source[0]));
145      int remaining = num_frames - wrapped;
146      memcpy(samples_, source + (wrapped * num_channels_),
147             num_channels_ * remaining * sizeof(source[0]));
148    }
149  }
150}
151
152}  // namespace video_editing
153