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