Audaspace:
[blender-staging.git] / intern / audaspace / intern / AUD_LinearResampleReader.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * Copyright 2009-2011 Jörg Hermann Müller
5  *
6  * This file is part of AudaSpace.
7  *
8  * Audaspace is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * AudaSpace is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Audaspace; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file audaspace/intern/AUD_LinearResampleReader.cpp
26  *  \ingroup audaspaceintern
27  */
28
29
30 #include "AUD_LinearResampleReader.h"
31
32 #include <cmath>
33 #include <cstring>
34
35 #define CC m_channels + channel
36
37 AUD_LinearResampleReader::AUD_LinearResampleReader(boost::shared_ptr<AUD_IReader> reader,
38                                                                                                    AUD_Specs specs) :
39         AUD_ResampleReader(reader, specs.rate),
40         m_channels(reader->getSpecs().channels),
41         m_cache_pos(0),
42         m_cache_ok(false)
43 {
44         specs.channels = m_channels;
45         m_cache.resize(2 * AUD_SAMPLE_SIZE(specs));
46 }
47
48 void AUD_LinearResampleReader::seek(int position)
49 {
50         position = floor(position * double(m_reader->getSpecs().rate) / double(m_rate));
51         m_reader->seek(position);
52         m_cache_ok = false;
53         m_cache_pos = 0;
54 }
55
56 int AUD_LinearResampleReader::getLength() const
57 {
58         return floor(m_reader->getLength() * double(m_rate) / double(m_reader->getSpecs().rate));
59 }
60
61 int AUD_LinearResampleReader::getPosition() const
62 {
63         return floor((m_reader->getPosition() + (m_cache_ok ? m_cache_pos - 1 : 0))
64                                  * m_rate / m_reader->getSpecs().rate);
65 }
66
67 AUD_Specs AUD_LinearResampleReader::getSpecs() const
68 {
69         AUD_Specs specs = m_reader->getSpecs();
70         specs.rate = m_rate;
71         return specs;
72 }
73
74 void AUD_LinearResampleReader::read(int& length, bool& eos, sample_t* buffer)
75 {
76         if(length == 0)
77                 return;
78
79         AUD_Specs specs = m_reader->getSpecs();
80
81         int samplesize = AUD_SAMPLE_SIZE(specs);
82         int size = length;
83         float factor = m_rate / m_reader->getSpecs().rate;
84         float spos;
85         sample_t low, high;
86         eos = false;
87
88         // check for channels changed
89
90         if(specs.channels != m_channels)
91         {
92                 m_cache.resize(2 * samplesize);
93                 m_channels = specs.channels;
94                 m_cache_ok = false;
95         }
96
97         if(factor == 1 && (!m_cache_ok || m_cache_pos == 1))
98         {
99                 // can read directly!
100                 m_reader->read(length, eos, buffer);
101
102                 if(length > 0)
103                 {
104                         memcpy(m_cache.getBuffer() + m_channels, buffer + m_channels * (length - 1), samplesize);
105                         m_cache_pos = 1;
106                         m_cache_ok = true;
107                 }
108
109                 return;
110         }
111
112         int len;
113         sample_t* buf;
114
115         if(m_cache_ok)
116         {
117                 int need = ceil(length / factor + m_cache_pos) - 1;
118
119                 len = need;
120
121                 m_buffer.assureSize((len + 2) * samplesize);
122                 buf = m_buffer.getBuffer();
123
124                 memcpy(buf, m_cache.getBuffer(), 2 * samplesize);
125                 m_reader->read(len, eos, buf + 2 * m_channels);
126
127                 if(len < need)
128                         length = floor((len + 1 - m_cache_pos) * factor);
129         }
130         else
131         {
132                 m_cache_pos = 1 - 1 / factor;
133
134                 int need = ceil(length / factor + m_cache_pos);
135
136                 len = need;
137
138                 m_buffer.assureSize((len + 1) * samplesize);
139                 buf = m_buffer.getBuffer();
140
141                 memset(buf, 0, samplesize);
142                 m_reader->read(len, eos, buf + m_channels);
143
144                 if(len == 0)
145                 {
146                         length = 0;
147                         return;
148                 }
149
150                 if(len < need)
151                 {
152                         length = floor((len - m_cache_pos) * factor);
153                 }
154
155                 m_cache_ok = true;
156         }
157
158         if(length == 0)
159                 return;
160
161         for(int channel = 0; channel < m_channels; channel++)
162         {
163                 for(int i = 0; i < length; i++)
164                 {
165                         spos = (i + 1) / factor + m_cache_pos;
166
167                         low = buf[(int)floor(spos) * CC];
168                         high = buf[(int)ceil(spos) * CC];
169
170                         buffer[i * CC] = low + (spos - floor(spos)) * (high - low);
171                 }
172         }
173
174         if(floor(spos) == spos)
175         {
176                 memcpy(m_cache.getBuffer() + m_channels, buf + int(floor(spos)) * m_channels, samplesize);
177                 m_cache_pos = 1;
178         }
179         else
180         {
181                 memcpy(m_cache.getBuffer(), buf + int(floor(spos)) * m_channels, 2 * samplesize);
182                 m_cache_pos = spos - floor(spos);
183         }
184
185         eos &= length < size;
186 }