/* Copyright (C) 2013, 2014 Matthias P. Braendli, matthias.braendli@mpb.li An implementation for a threadsafe queue using boost thread library for audio samples. */ #ifndef _SAMPLE_QUEUE_H_ #define _SAMPLE_QUEUE_H_ #define DEBUG_SAMPLE_QUEUE 0 #include #include #include /* This queue is meant to be used by two threads. One producer * that pushes elements into the queue, and one consumer that * retrieves the elements. * * This queue should contain audio sample data, interleaved L/R * form, 2bytes per sample. Therefore, the push and pop functions * should always place or retrieve data in multiples of * bytes_per_sample * number_of_channels * * The queue has a maximum size. If this size is reached, push() * ignores new data. * * If pop() is called but there is not enough data in the queue, * the missing samples are replaced by zeros. pop() will always * write the requested length. */ /* The template is actually not really tested for anything else * than uint8_t */ template class SampleQueue { public: SampleQueue(unsigned int bytes_per_sample, unsigned int channels, size_t max_size) : m_bytes_per_sample(bytes_per_sample), m_channels(channels), m_max_size(max_size), m_overruns(0) {} /* Push a bunch of samples into the buffer */ size_t push(const T *val, size_t len) { boost::mutex::scoped_lock lock(m_mutex); assert(len % (m_channels * m_bytes_per_sample) == 0); #if DEBUG_SAMPLE_QUEUE fprintf(stdout, "######## push %s %zu, %zu >= %zu\n", (m_queue.size() >= m_max_size) ? "overrun" : "ok", len / 4, m_queue.size() / 4, m_max_size / 4); #endif if (m_queue.size() >= m_max_size) { m_overruns++; return 0; } for (size_t i = 0; i < len; i++) { m_queue.push_back(val[i]); } size_t new_size = m_queue.size(); return new_size; } size_t size() const { boost::mutex::scoped_lock lock(m_mutex); return m_queue.size(); } /* Get len elements, place them into the buf array * Returns the number of elements it was able to take * from the queue */ size_t pop(T* buf, size_t len) { size_t ovr; return pop(buf, len, ovr); } /* Get len elements, place them into the buf array. * Also update the overrun variable with the information * of how many overruns we saw since the last pop. * Returns the number of elements it was able to take * from the queue */ size_t pop(T* buf, size_t len, size_t* overruns) { boost::mutex::scoped_lock lock(m_mutex); assert(len % (m_channels * m_bytes_per_sample) == 0); #if DEBUG_SAMPLE_QUEUE fprintf(stdout, "######## pop %zu (%zu), %zu overruns: ", len / 4, m_queue.size() / 4, m_overruns); #endif *overruns = m_overruns; m_overruns = 0; size_t ret = 0; if (m_queue.size() < len) { /* Not enough data in queue, fill with zeros */ size_t i; for (i = 0; i < m_queue.size(); i++) { buf[i] = m_queue[i]; } ret = i; for (; i < len; i++) { buf[i] = 0; } m_queue.resize(0); #if DEBUG_SAMPLE_QUEUE fprintf(stdout, "after short pop %zu (%zu)\n", len / 4, m_queue.size() / 4); #endif } else { /* Queue contains enough data */ for (size_t i = 0; i < len; i++) { buf[i] = m_queue[i]; } ret = len; m_queue.erase(m_queue.begin(), m_queue.begin() + len); #if DEBUG_SAMPLE_QUEUE fprintf(stdout, "after ok pop %zu (%zu)\n", len / 4, m_queue.size() / 4); #endif } return ret; } private: std::deque m_queue; mutable boost::mutex m_mutex; unsigned int m_channels; unsigned int m_bytes_per_sample; size_t m_max_size; /* Counter to keep track of number of overruns between calls * to pop() */ size_t m_overruns; }; #endif