2.5 audio cleanup:
[blender.git] / intern / audaspace / SDL / AUD_SDLMixerReader.cpp
1 /*
2  * $Id$
3  *
4  * ***** BEGIN LGPL LICENSE BLOCK *****
5  *
6  * Copyright 2009 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 Lesser General Public License as published by
12  * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with AudaSpace.  If not, see <http://www.gnu.org/licenses/>.
22  *
23  * ***** END LGPL LICENSE BLOCK *****
24  */
25
26 #include "AUD_SDLMixerReader.h"
27 #include "AUD_Buffer.h"
28
29 #include <cstring>
30
31 inline Uint16 AUD_TO_SDL(AUD_SampleFormat format)
32 {
33         // SDL only supports 8 and 16 bit audio
34         switch(format)
35         {
36         case AUD_FORMAT_U8:
37                 return AUDIO_U8;
38         case AUD_FORMAT_S16:
39                 return AUDIO_S16SYS;
40         default:
41                 AUD_THROW(AUD_ERROR_SDL);
42         }
43 }
44
45 // greatest common divisor
46 inline int gcd(int a, int b)
47 {
48         int c;
49
50         // make sure a is the bigger
51         if(b > a)
52         {
53                 c = b;
54                 b = a;
55                 a = c;
56         }
57
58         // greetings from Euclides
59         while(b != 0)
60         {
61                 c = a % b;
62                 a = b;
63                 b = c;
64         }
65         return a;
66 }
67
68 AUD_SDLMixerReader::AUD_SDLMixerReader(AUD_IReader* reader,
69                                                                                          AUD_Specs specs)
70 {
71         if(reader == NULL)
72                 AUD_THROW(AUD_ERROR_READER);
73
74         m_reader = reader;
75         m_tspecs = specs;
76         m_sspecs = reader->getSpecs();
77
78         try
79         {
80                 // SDL only supports 8 and 16 bit sample formats
81                 if(SDL_BuildAudioCVT(&m_cvt,
82                                                          AUD_TO_SDL(m_sspecs.format),
83                                                          m_sspecs.channels,
84                                                          m_sspecs.rate,
85                                                          AUD_TO_SDL(specs.format),
86                                                          specs.channels,
87                                                          specs.rate) == -1)
88                         AUD_THROW(AUD_ERROR_SDL);
89         }
90         catch(AUD_Exception e)
91         {
92                 delete m_reader; AUD_DELETE("reader")
93                 throw;
94         }
95
96         m_eor = false;
97         m_rsposition = 0;
98         m_rssize = 0;
99         m_ssize = m_sspecs.rate / gcd(specs.rate, m_sspecs.rate);
100         m_tsize = m_tspecs.rate * m_ssize / m_sspecs.rate;
101
102         m_buffer = new AUD_Buffer(); AUD_NEW("buffer")
103         m_rsbuffer = new AUD_Buffer(); AUD_NEW("buffer")
104 }
105
106 AUD_SDLMixerReader::~AUD_SDLMixerReader()
107 {
108         delete m_reader; AUD_DELETE("reader")
109         delete m_buffer; AUD_DELETE("buffer")
110         delete m_rsbuffer; AUD_DELETE("buffer")
111 }
112
113 bool AUD_SDLMixerReader::isSeekable()
114 {
115         return m_reader->isSeekable();
116 }
117
118 void AUD_SDLMixerReader::seek(int position)
119 {
120         m_reader->seek(position * m_ssize / m_tsize);
121         m_eor = false;
122 }
123
124 int AUD_SDLMixerReader::getLength()
125 {
126         return m_reader->getLength() * m_tsize / m_ssize;
127 }
128
129 int AUD_SDLMixerReader::getPosition()
130 {
131         return m_reader->getPosition() * m_tsize / m_ssize;
132 }
133
134 AUD_Specs AUD_SDLMixerReader::getSpecs()
135 {
136         return m_tspecs;
137 }
138
139 AUD_ReaderType AUD_SDLMixerReader::getType()
140 {
141         return m_reader->getType();
142 }
143
144 bool AUD_SDLMixerReader::notify(AUD_Message &message)
145 {
146         return m_reader->notify(message);
147 }
148
149 void AUD_SDLMixerReader::read(int & length, sample_t* & buffer)
150 {
151         // sample count for the target buffer without getting a shift
152         int tns = length + m_tsize - length % m_tsize;
153         // sample count for the source buffer without getting a shift
154         int sns = tns * m_ssize / m_tsize;
155         // target sample size
156         int tss = AUD_SAMPLE_SIZE(m_tspecs);
157         // source sample size
158         int sss = AUD_SAMPLE_SIZE(m_sspecs);
159
160         // input is output buffer
161         int buf_size = AUD_MAX(tns*tss, sns*sss);
162
163         // resize if necessary
164         if(m_rsbuffer->getSize() < buf_size)
165                 m_rsbuffer->resize(buf_size, true);
166
167         if(m_buffer->getSize() < length*tss)
168                 m_buffer->resize(length*tss);
169
170         buffer = m_buffer->getBuffer();
171         int size;
172         int index = 0;
173         sample_t* buf;
174
175         while(index < length)
176         {
177                 if(m_rsposition == m_rssize)
178                 {
179                         // no more data
180                         if(m_eor)
181                                 length = index;
182                         // mix
183                         else
184                         {
185                                 // read from source
186                                 size = sns;
187                                 m_reader->read(size, buf);
188
189                                 // prepare
190                                 m_cvt.buf = m_rsbuffer->getBuffer();
191                                 m_cvt.len = size*sss;
192                                 memcpy(m_cvt.buf, buf, size*sss);
193
194                                 // convert
195                                 SDL_ConvertAudio(&m_cvt);
196
197                                 // end of reader
198                                 if(size < sns)
199                                         m_eor = true;
200
201                                 m_rsposition = 0;
202                                 m_rssize = size * m_tsize / m_ssize;
203                         }
204                 }
205
206                 // size to copy
207                 size = AUD_MIN(m_rssize-m_rsposition, length-index);
208
209                 // copy
210                 memcpy(m_buffer->getBuffer() + index * tss,
211                            m_rsbuffer->getBuffer() + m_rsposition * tss,
212                            size*tss);
213                 m_rsposition += size;
214                 index += size;
215         }
216 }