blender/intern/audaspace/SDL/AUD_SDLMixerReader.cpp
2009-09-06 14:32:02 +00:00

217 lines
4.4 KiB
C++

/*
* $Id$
*
* ***** BEGIN LGPL LICENSE BLOCK *****
*
* Copyright 2009 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
* (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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with AudaSpace. If not, see <http://www.gnu.org/licenses/>.
*
* ***** END LGPL LICENSE BLOCK *****
*/
#include "AUD_SDLMixerReader.h"
#include "AUD_Buffer.h"
#include <cstring>
inline Uint16 AUD_TO_SDL(AUD_SampleFormat format)
{
// SDL only supports 8 and 16 bit audio
switch(format)
{
case AUD_FORMAT_U8:
return AUDIO_U8;
case AUD_FORMAT_S16:
return AUDIO_S16SYS;
default:
AUD_THROW(AUD_ERROR_SDL);
}
}
// greatest common divisor
inline int gcd(int a, int b)
{
int c;
// make sure a is the bigger
if(b > a)
{
c = b;
b = a;
a = c;
}
// greetings from Euclides
while(b != 0)
{
c = a % b;
a = b;
b = c;
}
return a;
}
AUD_SDLMixerReader::AUD_SDLMixerReader(AUD_IReader* reader,
AUD_Specs specs)
{
if(reader == NULL)
AUD_THROW(AUD_ERROR_READER);
m_reader = reader;
m_tspecs = specs;
m_sspecs = reader->getSpecs();
try
{
// SDL only supports 8 and 16 bit sample formats
if(SDL_BuildAudioCVT(&m_cvt,
AUD_TO_SDL(m_sspecs.format),
m_sspecs.channels,
m_sspecs.rate,
AUD_TO_SDL(specs.format),
specs.channels,
specs.rate) == -1)
AUD_THROW(AUD_ERROR_SDL);
}
catch(AUD_Exception)
{
delete m_reader; AUD_DELETE("reader")
throw;
}
m_eor = false;
m_rsposition = 0;
m_rssize = 0;
m_ssize = m_sspecs.rate / gcd(specs.rate, m_sspecs.rate);
m_tsize = m_tspecs.rate * m_ssize / m_sspecs.rate;
m_buffer = new AUD_Buffer(); AUD_NEW("buffer")
m_rsbuffer = new AUD_Buffer(); AUD_NEW("buffer")
}
AUD_SDLMixerReader::~AUD_SDLMixerReader()
{
delete m_reader; AUD_DELETE("reader")
delete m_buffer; AUD_DELETE("buffer")
delete m_rsbuffer; AUD_DELETE("buffer")
}
bool AUD_SDLMixerReader::isSeekable()
{
return m_reader->isSeekable();
}
void AUD_SDLMixerReader::seek(int position)
{
m_reader->seek(position * m_ssize / m_tsize);
m_eor = false;
}
int AUD_SDLMixerReader::getLength()
{
return m_reader->getLength() * m_tsize / m_ssize;
}
int AUD_SDLMixerReader::getPosition()
{
return m_reader->getPosition() * m_tsize / m_ssize;
}
AUD_Specs AUD_SDLMixerReader::getSpecs()
{
return m_tspecs;
}
AUD_ReaderType AUD_SDLMixerReader::getType()
{
return m_reader->getType();
}
bool AUD_SDLMixerReader::notify(AUD_Message &message)
{
return m_reader->notify(message);
}
void AUD_SDLMixerReader::read(int & length, sample_t* & buffer)
{
// sample count for the target buffer without getting a shift
int tns = length + m_tsize - length % m_tsize;
// sample count for the source buffer without getting a shift
int sns = tns * m_ssize / m_tsize;
// target sample size
int tss = AUD_SAMPLE_SIZE(m_tspecs);
// source sample size
int sss = AUD_SAMPLE_SIZE(m_sspecs);
// input is output buffer
int buf_size = AUD_MAX(tns*tss, sns*sss);
// resize if necessary
if(m_rsbuffer->getSize() < buf_size)
m_rsbuffer->resize(buf_size, true);
if(m_buffer->getSize() < length*tss)
m_buffer->resize(length*tss);
buffer = m_buffer->getBuffer();
int size;
int index = 0;
sample_t* buf;
while(index < length)
{
if(m_rsposition == m_rssize)
{
// no more data
if(m_eor)
length = index;
// mix
else
{
// read from source
size = sns;
m_reader->read(size, buf);
// prepare
m_cvt.buf = m_rsbuffer->getBuffer();
m_cvt.len = size*sss;
memcpy(m_cvt.buf, buf, size*sss);
// convert
SDL_ConvertAudio(&m_cvt);
// end of reader
if(size < sns)
m_eor = true;
m_rsposition = 0;
m_rssize = size * m_tsize / m_ssize;
}
}
// size to copy
size = AUD_MIN(m_rssize-m_rsposition, length-index);
// copy
memcpy(m_buffer->getBuffer() + index * tss,
m_rsbuffer->getBuffer() + m_rsposition * tss,
size*tss);
m_rsposition += size;
index += size;
}
}