Merging with trunk up to r38631.
[blender.git] / intern / audaspace / intern / AUD_LinearResampleReader.cpp
index dcd5310575dd9c9a421141feb2183f1bea9457bb..db050b5a9b7ff4a4f0a6410c66c795e8a4b93e66 100644 (file)
 /*
  * $Id$
  *
- * ***** BEGIN LGPL LICENSE BLOCK *****
+ * ***** BEGIN GPL LICENSE BLOCK *****
  *
- * Copyright 2009 Jörg Hermann Müller
+ * Copyright 2009-2011 Jörg Hermann Müller
  *
  * This file is part of AudaSpace.
  *
- * AudaSpace is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
+ * Audaspace is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
  *
  * AudaSpace is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License for more details.
+ * GNU General Public License for more details.
  *
- * You should have received a copy of the GNU Lesser General Public License
- * along with AudaSpace.  If not, see <http://www.gnu.org/licenses/>.
+ * You should have received a copy of the GNU General Public License
+ * along with Audaspace; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
- * ***** END LGPL LICENSE BLOCK *****
+ * ***** END GPL LICENSE BLOCK *****
  */
 
+/** \file audaspace/intern/AUD_LinearResampleReader.cpp
+ *  \ingroup audaspaceintern
+ */
+
+
 #include "AUD_LinearResampleReader.h"
-#include "AUD_Buffer.h"
 
 #include <cmath>
 #include <cstring>
 
-#define CC channels + channel
+#define CC m_channels + channel
 
-AUD_LinearResampleReader::AUD_LinearResampleReader(AUD_IReader* reader,
+AUD_LinearResampleReader::AUD_LinearResampleReader(AUD_Reference<AUD_IReader> reader,
                                                                                                   AUD_Specs specs) :
-               AUD_EffectReader(reader)
-{
-       m_sspecs = reader->getSpecs();
-
-       m_tspecs = specs;
-       m_tspecs.channels = m_sspecs.channels;
-       m_factor = (float)m_tspecs.rate / (float)m_sspecs.rate;
-
-       m_position = 0;
-       m_sposition = 0;
-
-       m_buffer = new AUD_Buffer(); AUD_NEW("buffer")
-       m_cache = new AUD_Buffer(2 * AUD_SAMPLE_SIZE(specs)); AUD_NEW("buffer")
-}
-
-AUD_LinearResampleReader::~AUD_LinearResampleReader()
+       AUD_ResampleReader(reader, specs.rate),
+       m_channels(reader->getSpecs().channels),
+       m_position(0),
+       m_cache_pos(0),
+       m_cache_ok(false)
 {
-       delete m_buffer; AUD_DELETE("buffer")
-       delete m_cache; AUD_DELETE("buffer")
+       specs.channels = m_channels;
+       m_cache.resize(2 * AUD_SAMPLE_SIZE(specs));
 }
 
 void AUD_LinearResampleReader::seek(int position)
 {
-       m_position = position;
-       m_sposition = floor(position / m_factor);
-       m_reader->seek(m_sposition);
+       position = floor(position * double(m_reader->getSpecs().rate) / double(m_rate));
+       m_reader->seek(position);
+       m_cache_ok = false;
+       m_cache_pos = 0;
 }
 
-int AUD_LinearResampleReader::getLength()
+int AUD_LinearResampleReader::getLength() const
 {
-       return m_reader->getLength() * m_factor;
+       return floor(m_reader->getLength() * double(m_rate) / double(m_reader->getSpecs().rate));
 }
 
-int AUD_LinearResampleReader::getPosition()
+int AUD_LinearResampleReader::getPosition() const
 {
-       return m_position;
+       return floor((m_reader->getPosition() + (m_cache_ok ? m_cache_pos - 1 : 0))
+                                * m_rate / m_reader->getSpecs().rate);
 }
 
-AUD_Specs AUD_LinearResampleReader::getSpecs()
+AUD_Specs AUD_LinearResampleReader::getSpecs() const
 {
-       return m_tspecs;
+       AUD_Specs specs = m_reader->getSpecs();
+       specs.rate = m_rate;
+       return specs;
 }
 
-void AUD_LinearResampleReader::read(int & length, sample_t* & buffer)
+void AUD_LinearResampleReader::read(int& length, bool& eos, sample_t* buffer)
 {
-       int samplesize = AUD_SAMPLE_SIZE(m_tspecs);
-       int size = length * samplesize;
+       if(length == 0)
+               return;
+
+       AUD_Specs specs = m_reader->getSpecs();
+
+       int samplesize = AUD_SAMPLE_SIZE(specs);
+       int size = length;
+       float factor = float(m_rate) / float(m_reader->getSpecs().rate);
+       float spos;
+       sample_t low, high;
+       eos = false;
+
+       // check for channels changed
+
+       if(specs.channels != m_channels)
+       {
+               m_cache.resize(2 * samplesize);
+               m_channels = specs.channels;
+               m_cache_ok = false;
+       }
+
+       if(factor == 1 && (!m_cache_ok || m_cache_pos == 1))
+       {
+               // can read directly!
+               m_reader->read(length, eos, buffer);
+
+               if(length > 0)
+               {
+                       memcpy(m_cache.getBuffer() + m_channels, buffer + m_channels * (length - 1), samplesize);
+                       m_cache_pos = 1;
+                       m_cache_ok = true;
+               }
 
-       if(m_buffer->getSize() < size)
-               m_buffer->resize(size);
+               return;
+       }
 
-       int need = ceil((m_position + length) / m_factor) + 1 - m_sposition;
-       int len = need;
+       int len;
        sample_t* buf;
-       buffer = m_buffer->getBuffer();
 
-       m_reader->read(len, buf);
+       if(m_cache_ok)
+       {
+               int need = ceil(length / factor + m_cache_pos) - 1;
+
+               len = need;
 
-       if(len < need)
-               length = floor((m_sposition + len - 1) * m_factor) - m_position;
+               m_buffer.assureSize((len + 2) * samplesize);
+               buf = m_buffer.getBuffer();
 
-       float spos;
-       sample_t low, high;
-       int channels = m_sspecs.channels;
+               memcpy(buf, m_cache.getBuffer(), 2 * samplesize);
+               m_reader->read(len, eos, buf + 2 * m_channels);
+
+               if(len < need)
+                       length = floor((len + 1 - m_cache_pos) * factor);
+       }
+       else
+       {
+               m_cache_pos = 1 - 1 / factor;
+
+               int need = ceil(length / factor + m_cache_pos);
+
+               len = need;
+
+               m_buffer.assureSize((len + 1) * samplesize);
+               buf = m_buffer.getBuffer();
+
+               memset(buf, 0, samplesize);
+               m_reader->read(len, eos, buf + m_channels);
+
+               if(len == 0)
+               {
+                       length = 0;
+                       return;
+               }
+
+               if(len < need)
+               {
+                       length = floor((len - m_cache_pos) * factor);
+               }
+
+               m_cache_ok = true;
+       }
 
-       for(int channel = 0; channel < channels; channel++)
+       for(int channel = 0; channel < m_channels; channel++)
        {
                for(int i = 0; i < length; i++)
                {
-                       spos = (m_position + i) / m_factor - m_sposition;
-
-                       if(floor(spos) < 0)
-                       {
-                               low = m_cache->getBuffer()[(int)(floor(spos) + 2) * CC];
-                               if(ceil(spos) < 0)
-                                       high = m_cache->getBuffer()[(int)(ceil(spos) + 2) * CC];
-                               else
-                                       high = buf[(int)ceil(spos) * CC];
-                       }
-                       else
-                       {
-                                       low = buf[(int)floor(spos) * CC];
-                                       high = buf[(int)ceil(spos) * CC];
-                       }
+                       spos = (i + 1) / factor + m_cache_pos;
+
+                       low = buf[(int)floor(spos) * CC];
+                       high = buf[(int)ceil(spos) * CC];
+
                        buffer[i * CC] = low + (spos - floor(spos)) * (high - low);
                }
        }
 
-       if(len > 1)
-               memcpy(m_cache->getBuffer(),
-                          buf + (len - 2) * channels,
-                          2 * samplesize);
-       else if(len == 1)
-               memcpy(m_cache->getBuffer() + 1 * channels, buf, samplesize);
+       if(floor(spos) == spos)
+       {
+               memcpy(m_cache.getBuffer() + m_channels, buf + int(floor(spos)) * m_channels, samplesize);
+               m_cache_pos = 1;
+       }
+       else
+       {
+               memcpy(m_cache.getBuffer(), buf + int(floor(spos)) * m_channels, 2 * samplesize);
+               m_cache_pos = spos - floor(spos);
+       }
 
-       m_sposition += len;
-       m_position += length;
+       eos &= length < size;
 }