| // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // Templatized, thread-safe RingBuffer. Acts like a FIFO, but using a |
| // fixed-size buffer that wraps around and optimized for operations that |
| // read or write many elements at a time. Commonly used to buffer audio |
| // samples that need to be passed from one thread to another. |
| // |
| // Supports a flag "finished" so the writing thread can notify the reading |
| // thread that there's no more data. |
| // |
| // The implementation is all contained within this .h file because |
| // it's templatized. |
| |
| #ifndef SPEECH_CLIENT_SYNTHESIS_SERVICE_RINGBUFFER_H_ |
| #define SPEECH_CLIENT_SYNTHESIS_SERVICE_RINGBUFFER_H_ |
| |
| #include "threading.h" |
| |
| namespace speech_synthesis { |
| |
| template<typename T> class RingBuffer { |
| public: |
| // Construct a RingBuffer using a given Threading object (used for |
| // locking - may not be NULL) of a given capacity (cannot be increased |
| // later). |
| RingBuffer(Threading* theading, int capacity); |
| |
| // Destructor. |
| ~RingBuffer(); |
| |
| // |
| // Methods for either thread |
| // |
| |
| int GetCapacity() { return capacity_; } |
| |
| // |
| // Methods for the writer thread |
| // |
| |
| // Reset to the initial state: the ring buffer is empty and marked as |
| // unfinished. |
| void Reset(); |
| |
| // Get the number of items of type T that are available to be written. |
| // This will be a number between 0 and capacity, inclusive. |
| int WriteAvail(); |
| |
| // Write *len* elements to the end of the ring buffer. Returns true on |
| // success. If all *len* elements cannot be written without blocking, |
| // returns false and writes nothing. |
| bool Write(const T* data, int len); |
| |
| // Mark the buffer as finished. Future write operations will fail. |
| // Read operations will succeed until the buffer is empty, but |
| // IsFinished() will return true immediately. |
| void MarkFinished(); |
| |
| // |
| // Methods for the reader thread |
| // |
| |
| // Get the number of items of type T that are available to be read. |
| // This will be a number between 0 and capacity, inclusive. |
| int ReadAvail(); |
| |
| // Read *len* elements from the front of the ring buffer. Returns true on |
| // success. If all *len* elements cannot be read without blocking, |
| // returns false and reads nothing. |
| bool Read(T* data, int len); |
| |
| // Returns true if the buffer has been marked as finished by a call to |
| // MarkFinished, whether the buffer is empty or not. |
| bool IsFinished(); |
| |
| private: |
| Mutex* mutex_; |
| T* buffer_; |
| bool finished_; |
| int capacity_; |
| volatile int read_pos_; |
| volatile int write_pos_; |
| }; |
| |
| // |
| // Implementation notes: |
| // |
| // We use a simple mutex to protect all of the fields. |
| // |
| // A special value of -1 is used for read_pos_ to indicate that the buffer |
| // is empty, otherwise there would be no way to distinguish between an empty |
| // buffer and a full buffer when read_pos_ == write_pos_. |
| // |
| |
| template<typename T> RingBuffer<T>::RingBuffer( |
| Threading* threading, int capacity) { |
| capacity_ = capacity; |
| mutex_ = threading->CreateMutex(); |
| buffer_ = new T[capacity_]; |
| Reset(); |
| } |
| |
| template<typename T> RingBuffer<T>::~RingBuffer() { |
| delete mutex_; |
| delete[] buffer_; |
| } |
| |
| template<typename T> void RingBuffer<T>::Reset() { |
| mutex_->Lock(); |
| finished_ = false; |
| read_pos_ = -1; |
| write_pos_ = 0; |
| mutex_->Unlock(); |
| }; |
| |
| template<typename T> int RingBuffer<T>::WriteAvail() { |
| int avail = capacity_; |
| mutex_->Lock(); |
| if (read_pos_ != -1) { |
| avail = read_pos_ - write_pos_; |
| } |
| mutex_->Unlock(); |
| if (avail < 0) { |
| avail += capacity_; |
| } |
| return avail; |
| } |
| |
| template<typename T> bool RingBuffer<T>::Write(const T* data, int len) { |
| mutex_->Lock(); |
| if (finished_) { |
| mutex_->Unlock(); |
| return false; |
| } |
| int avail = capacity_; |
| if (read_pos_ != -1) { |
| avail = read_pos_ - write_pos_; |
| } |
| if (avail < 0) { |
| avail += capacity_; |
| } |
| if (len > avail) { |
| mutex_->Unlock(); |
| return false; |
| } |
| |
| if (read_pos_ == -1) { |
| read_pos_ = write_pos_; |
| } |
| for (int i = 0; i < len; i++) { |
| buffer_[write_pos_] = data[i]; |
| write_pos_ = (write_pos_ + 1) % capacity_; |
| } |
| mutex_->Unlock(); |
| |
| return true; |
| } |
| |
| template<typename T> void RingBuffer<T>::MarkFinished() { |
| mutex_->Lock(); |
| finished_ = true; |
| mutex_->Unlock(); |
| } |
| |
| template<typename T> int RingBuffer<T>::ReadAvail() { |
| mutex_->Lock(); |
| if (read_pos_ == -1) { |
| mutex_->Unlock(); |
| return 0; |
| } |
| int avail = write_pos_ - read_pos_; |
| mutex_->Unlock(); |
| if (avail <= 0) { |
| avail += capacity_; |
| } |
| return avail; |
| } |
| |
| template<typename T> bool RingBuffer<T>::Read(T* data, int len) { |
| mutex_->Lock(); |
| int avail; |
| if (read_pos_ == -1) { |
| avail = 0; |
| } else { |
| avail = write_pos_ - read_pos_; |
| if (avail <= 0) { |
| avail += capacity_; |
| } |
| } |
| if (len > avail) { |
| mutex_->Unlock(); |
| return false; |
| } |
| |
| for (int i = 0; i < len; i++) { |
| data[i] = buffer_[read_pos_]; |
| read_pos_ = (read_pos_ + 1) % capacity_; |
| } |
| |
| if (read_pos_ == write_pos_) { |
| read_pos_ = -1; |
| } |
| |
| mutex_->Unlock(); |
| |
| return true; |
| } |
| |
| template<typename T> bool RingBuffer<T>::IsFinished() { |
| mutex_->Lock(); |
| bool is_finished = finished_; |
| mutex_->Unlock(); |
| return is_finished; |
| } |
| |
| } // namespace speech_synthesis |
| |
| #endif // SPEECH_CLIENT_SYNTHESIS_SERVICE |
| |