From c334bf69a7282254bb80bb2896bd8716930a4adf Mon Sep 17 00:00:00 2001 From: Joerg Mueller Date: Sat, 6 Aug 2011 17:57:20 +0000 Subject: 3D Audio GSoC: Mixdown functionality. * Mixdown possible via libsndfile and ffmpeg! * Fixed some ffmpeg deprecation warnings * Mixdown UI only shows working Container, Codec and Format combinations! * Minor bugs and warnings fixed --- intern/audaspace/CMakeLists.txt | 7 + intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp | 31 +-- intern/audaspace/ffmpeg/AUD_FFMPEGReader.h | 4 +- intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp | 303 +++++++++++++++++++++ intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h | 114 ++++++++ intern/audaspace/intern/AUD_C-API.cpp | 21 ++ intern/audaspace/intern/AUD_C-API.h | 4 +- intern/audaspace/intern/AUD_ConverterFunctions.cpp | 6 +- intern/audaspace/intern/AUD_FileWriter.cpp | 97 +++++++ intern/audaspace/intern/AUD_FileWriter.h | 58 ++++ intern/audaspace/intern/AUD_IReader.h | 15 +- intern/audaspace/intern/AUD_IWriter.h | 69 +++++ intern/audaspace/intern/AUD_NULLDevice.cpp | 4 + intern/audaspace/intern/AUD_NULLDevice.h | 2 + intern/audaspace/intern/AUD_Space.h | 24 ++ intern/audaspace/sndfile/AUD_SndFileWriter.cpp | 141 ++++++++++ intern/audaspace/sndfile/AUD_SndFileWriter.h | 87 ++++++ 17 files changed, 952 insertions(+), 35 deletions(-) create mode 100644 intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp create mode 100644 intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h create mode 100644 intern/audaspace/intern/AUD_FileWriter.cpp create mode 100644 intern/audaspace/intern/AUD_FileWriter.h create mode 100644 intern/audaspace/intern/AUD_IWriter.h create mode 100644 intern/audaspace/sndfile/AUD_SndFileWriter.cpp create mode 100644 intern/audaspace/sndfile/AUD_SndFileWriter.h (limited to 'intern/audaspace') diff --git a/intern/audaspace/CMakeLists.txt b/intern/audaspace/CMakeLists.txt index b08c5f62b29..8b7cb1d9e69 100644 --- a/intern/audaspace/CMakeLists.txt +++ b/intern/audaspace/CMakeLists.txt @@ -88,12 +88,15 @@ set(SRC intern/AUD_ConverterReader.h intern/AUD_FileFactory.cpp intern/AUD_FileFactory.h + intern/AUD_FileWriter.cpp + intern/AUD_FileWriter.h intern/AUD_I3DDevice.h intern/AUD_I3DHandle.h intern/AUD_IDevice.h intern/AUD_IFactory.h intern/AUD_IHandle.h intern/AUD_IReader.h + intern/AUD_IWriter.h intern/AUD_JOSResampleFactory.cpp intern/AUD_JOSResampleFactory.h intern/AUD_JOSResampleReader.cpp @@ -184,9 +187,11 @@ if(WITH_CODEC_FFMPEG) list(APPEND SRC ffmpeg/AUD_FFMPEGFactory.cpp ffmpeg/AUD_FFMPEGReader.cpp + ffmpeg/AUD_FFMPEGWriter.cpp ffmpeg/AUD_FFMPEGFactory.h ffmpeg/AUD_FFMPEGReader.h + ffmpeg/AUD_FFMPEGWriter.h ) endif() @@ -246,9 +251,11 @@ if(WITH_CODEC_SNDFILE) list(APPEND SRC sndfile/AUD_SndFileFactory.cpp sndfile/AUD_SndFileReader.cpp + sndfile/AUD_SndFileWriter.cpp sndfile/AUD_SndFileFactory.h sndfile/AUD_SndFileReader.h + sndfile/AUD_SndFileWriter.h ) endif() diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp index b7690d55383..a7534dbed32 100644 --- a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp @@ -176,11 +176,11 @@ static const char* fileopen_error = "AUD_FFMPEGReader: File couldn't be " AUD_FFMPEGReader::AUD_FFMPEGReader(std::string filename) : m_pkgbuf(AVCODEC_MAX_AUDIO_FRAME_SIZE<<1), - m_byteiocontext(NULL), + m_aviocontext(NULL), m_membuf(NULL) { // open file - if(av_open_input_file(&m_formatCtx, filename.c_str(), NULL, 0, NULL)!=0) + if(avformat_open_input(&m_formatCtx, filename.c_str(), NULL, NULL)!=0) AUD_THROW(AUD_ERROR_FILE, fileopen_error); try @@ -204,25 +204,20 @@ AUD_FFMPEGReader::AUD_FFMPEGReader(AUD_Reference buffer) : { m_membuf = reinterpret_cast(av_malloc(FF_MIN_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE)); - m_byteiocontext = av_alloc_put_byte(m_membuf, FF_MIN_BUFFER_SIZE, 0, this, - read_packet, NULL, seek_packet); + m_aviocontext = avio_alloc_context(m_membuf, FF_MIN_BUFFER_SIZE, 0, this, + read_packet, NULL, seek_packet); - if(!m_byteiocontext) + if(!m_aviocontext) { - av_free(m_byteiocontext); + av_free(m_aviocontext); AUD_THROW(AUD_ERROR_FILE, fileopen_error); } - AVProbeData probe_data; - probe_data.filename = ""; - probe_data.buf = reinterpret_cast(buffer.get()->getBuffer()); - probe_data.buf_size = buffer.get()->getSize(); - AVInputFormat* fmt = av_probe_input_format(&probe_data, 1); - - // open stream - if(av_open_input_stream(&m_formatCtx, m_byteiocontext, "", fmt, NULL)!=0) + m_formatCtx = avformat_alloc_context(); + m_formatCtx->pb = m_aviocontext; + if(avformat_open_input(&m_formatCtx, "", NULL, NULL)!=0) { - av_free(m_byteiocontext); + av_free(m_aviocontext); AUD_THROW(AUD_ERROR_FILE, streamopen_error); } @@ -233,7 +228,7 @@ AUD_FFMPEGReader::AUD_FFMPEGReader(AUD_Reference buffer) : catch(AUD_Exception&) { av_close_input_stream(m_formatCtx); - av_free(m_byteiocontext); + av_free(m_aviocontext); throw; } } @@ -242,10 +237,10 @@ AUD_FFMPEGReader::~AUD_FFMPEGReader() { avcodec_close(m_codecCtx); - if(m_byteiocontext) + if(m_aviocontext) { av_close_input_stream(m_formatCtx); - av_free(m_byteiocontext); + av_free(m_aviocontext); } else av_close_input_file(m_formatCtx); diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h index 06d6fe1e5f6..222a3d8581a 100644 --- a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h @@ -86,9 +86,9 @@ private: AVCodecContext* m_codecCtx; /** - * The ByteIOContext to read the data from. + * The AVIOContext to read the data from. */ - ByteIOContext* m_byteiocontext; + AVIOContext* m_aviocontext; /** * The stream ID in the file. diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp new file mode 100644 index 00000000000..f2b7acc5ea2 --- /dev/null +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp @@ -0,0 +1,303 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/ffmpeg/AUD_FFMPEGWriter.cpp + * \ingroup audffmpeg + */ + + +// needed for INT64_C +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include "AUD_FFMPEGWriter.h" + +extern "C" { +#include +#include +#include +} + +static const char* context_error = "AUD_FFMPEGWriter: Couldn't allocate context."; +static const char* codec_error = "AUD_FFMPEGWriter: Invalid codec or codec not found."; +static const char* stream_error = "AUD_FFMPEGWriter: Couldn't allocate stream."; +static const char* format_error = "AUD_FFMPEGWriter: Unsupported sample format."; +static const char* file_error = "AUD_FFMPEGWriter: File couldn't be written."; +static const char* write_error = "AUD_FFMPEGWriter: Error writing packet."; + +AUD_FFMPEGWriter::AUD_FFMPEGWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate) : + m_position(0), + m_specs(specs), + m_input_samples(0) +{ + static const char* formats[] = { NULL, "ac3", "flac", "matroska", "mp2", "mp3", "ogg", "wav" }; + + if(avformat_alloc_output_context2(&m_formatCtx, NULL, formats[format], filename.c_str())) + AUD_THROW(AUD_ERROR_FFMPEG, context_error); + + m_outputFmt = m_formatCtx->oformat; + + switch(codec) + { + case AUD_CODEC_AAC: + m_outputFmt->audio_codec = CODEC_ID_AAC; + break; + case AUD_CODEC_AC3: + m_outputFmt->audio_codec = CODEC_ID_AC3; + break; + case AUD_CODEC_FLAC: + m_outputFmt->audio_codec = CODEC_ID_FLAC; + break; + case AUD_CODEC_MP2: + m_outputFmt->audio_codec = CODEC_ID_MP2; + break; + case AUD_CODEC_MP3: + m_outputFmt->audio_codec = CODEC_ID_MP3; + break; + case AUD_CODEC_PCM: + switch(specs.format) + { + case AUD_FORMAT_U8: + m_outputFmt->audio_codec = CODEC_ID_PCM_U8; + break; + case AUD_FORMAT_S16: + m_outputFmt->audio_codec = CODEC_ID_PCM_S16LE; + break; + case AUD_FORMAT_S24: + m_outputFmt->audio_codec = CODEC_ID_PCM_S24LE; + break; + case AUD_FORMAT_S32: + m_outputFmt->audio_codec = CODEC_ID_PCM_S32LE; + break; + case AUD_FORMAT_FLOAT32: + m_outputFmt->audio_codec = CODEC_ID_PCM_F32LE; + break; + case AUD_FORMAT_FLOAT64: + m_outputFmt->audio_codec = CODEC_ID_PCM_F64LE; + break; + default: + m_outputFmt->audio_codec = CODEC_ID_NONE; + break; + } + break; + case AUD_CODEC_VORBIS: + m_outputFmt->audio_codec = CODEC_ID_VORBIS; + break; + default: + m_outputFmt->audio_codec = CODEC_ID_NONE; + break; + } + + try + { + if(m_outputFmt->audio_codec == CODEC_ID_NONE) + AUD_THROW(AUD_ERROR_SPECS, codec_error); + + m_stream = av_new_stream(m_formatCtx, 0); + if(!m_stream) + AUD_THROW(AUD_ERROR_FFMPEG, stream_error); + + m_codecCtx = m_stream->codec; + m_codecCtx->codec_id = m_outputFmt->audio_codec; + m_codecCtx->codec_type = AVMEDIA_TYPE_AUDIO; + m_codecCtx->bit_rate = bitrate; + m_codecCtx->sample_rate = int(m_specs.rate); + m_codecCtx->channels = m_specs.channels; + m_codecCtx->time_base = (AVRational){1, m_codecCtx->sample_rate}; + + switch(m_specs.format) + { + case AUD_FORMAT_U8: + m_convert = AUD_convert_float_u8; + m_codecCtx->sample_fmt = SAMPLE_FMT_U8; + break; + case AUD_FORMAT_S16: + m_convert = AUD_convert_float_s16; + m_codecCtx->sample_fmt = SAMPLE_FMT_S16; + break; + case AUD_FORMAT_S32: + m_convert = AUD_convert_float_s32; + m_codecCtx->sample_fmt = SAMPLE_FMT_S32; + break; + case AUD_FORMAT_FLOAT32: + m_convert = AUD_convert_copy; + m_codecCtx->sample_fmt = SAMPLE_FMT_FLT; + break; + case AUD_FORMAT_FLOAT64: + m_convert = AUD_convert_float_double; + m_codecCtx->sample_fmt = SAMPLE_FMT_DBL; + break; + default: + AUD_THROW(AUD_ERROR_FFMPEG, format_error); + } + + try + { + if(m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER) + m_codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + AVCodec* codec = avcodec_find_encoder(m_codecCtx->codec_id); + if(!codec) + AUD_THROW(AUD_ERROR_FFMPEG, codec_error); + + if(avcodec_open(m_codecCtx, codec)) + AUD_THROW(AUD_ERROR_FFMPEG, codec_error); + + m_output_buffer.resize(FF_MIN_BUFFER_SIZE); + int samplesize = AUD_MAX(AUD_SAMPLE_SIZE(m_specs), AUD_DEVICE_SAMPLE_SIZE(m_specs)); + + if(m_codecCtx->frame_size <= 1) + m_input_size = 0; + else + { + m_input_buffer.resize(m_codecCtx->frame_size * samplesize); + m_input_size = m_codecCtx->frame_size; + } + + try + { + if(avio_open(&m_formatCtx->pb, filename.c_str(), AVIO_WRONLY)) + AUD_THROW(AUD_ERROR_FILE, file_error); + + avformat_write_header(m_formatCtx, NULL); + } + catch(AUD_Exception&) + { + avcodec_close(m_codecCtx); + av_freep(&m_formatCtx->streams[0]->codec); + throw; + } + } + catch(AUD_Exception&) + { + av_freep(&m_formatCtx->streams[0]); + throw; + } + } + catch(AUD_Exception&) + { + av_free(m_formatCtx); + throw; + } +} + +AUD_FFMPEGWriter::~AUD_FFMPEGWriter() +{ + // writte missing data + if(m_input_samples) + { + sample_t* buf = m_input_buffer.getBuffer(); + memset(buf + m_specs.channels * m_input_samples, 0, + (m_input_size - m_input_samples) * AUD_DEVICE_SAMPLE_SIZE(m_specs)); + + encode(buf); + } + + av_write_trailer(m_formatCtx); + + avcodec_close(m_codecCtx); + + av_freep(&m_formatCtx->streams[0]->codec); + av_freep(&m_formatCtx->streams[0]); + + avio_close(m_formatCtx->pb); + av_free(m_formatCtx); +} + +int AUD_FFMPEGWriter::getPosition() const +{ + return m_position; +} + +AUD_DeviceSpecs AUD_FFMPEGWriter::getSpecs() const +{ + return m_specs; +} + +void AUD_FFMPEGWriter::encode(sample_t* data) +{ + sample_t* outbuf = m_output_buffer.getBuffer(); + + // convert first + if(m_input_size) + m_convert(reinterpret_cast(data), reinterpret_cast(data), m_input_size * m_specs.channels); + + AVPacket packet; + av_init_packet(&packet); + packet.size = avcodec_encode_audio(m_codecCtx, reinterpret_cast(outbuf), m_output_buffer.getSize(), reinterpret_cast(data)); + if(m_codecCtx->coded_frame && m_codecCtx->coded_frame->pts != AV_NOPTS_VALUE) + packet.pts = av_rescale_q(m_codecCtx->coded_frame->pts, m_codecCtx->time_base, m_stream->time_base); + packet.flags |= AV_PKT_FLAG_KEY; + packet.stream_index = m_stream->index; + packet.data = reinterpret_cast(outbuf); + + if(av_interleaved_write_frame(m_formatCtx, &packet)) + AUD_THROW(AUD_ERROR_FFMPEG, write_error); +} + +void AUD_FFMPEGWriter::write(unsigned int length, sample_t* buffer) +{ + unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs); + + if(m_input_size) + { + sample_t* inbuf = m_input_buffer.getBuffer(); + + while(length) + { + unsigned int len = AUD_MIN(m_input_size - m_input_samples, length); + + memcpy(inbuf + m_input_samples * m_specs.channels, buffer, len * samplesize); + + buffer += len * m_specs.channels; + m_input_samples += len; + m_position += len; + length -= len; + + if(m_input_samples == m_input_size) + { + encode(inbuf); + + m_input_samples = 0; + } + } + } + else // PCM data, can write directly! + { + int samplesize = AUD_SAMPLE_SIZE(m_specs); + if(m_output_buffer.getSize() != length * m_specs.channels * m_codecCtx->bits_per_coded_sample / 8) + m_output_buffer.resize(length * m_specs.channels * m_codecCtx->bits_per_coded_sample / 8); + m_input_buffer.assureSize(length * AUD_MAX(AUD_DEVICE_SAMPLE_SIZE(m_specs), samplesize)); + + sample_t* buf = m_input_buffer.getBuffer(); + m_convert(reinterpret_cast(buf), reinterpret_cast(buffer), length * m_specs.channels); + + encode(buf); + + m_position += length; + } +} diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h new file mode 100644 index 00000000000..618ec9402ce --- /dev/null +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h @@ -0,0 +1,114 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/ffmpeg/AUD_FFMPEGWriter.h + * \ingroup audffmpeg + */ + + +#ifndef AUD_FFMPEGWRITER +#define AUD_FFMPEGWRITER + +#include "AUD_ConverterFunctions.h" +#include "AUD_Buffer.h" +#include "AUD_IWriter.h" + +#include + +struct AVCodecContext; +extern "C" { +#include +} + +/** + * This class writes a sound file via ffmpeg. + */ +class AUD_FFMPEGWriter : public AUD_IWriter +{ +private: + /** + * The current position in samples. + */ + int m_position; + + /** + * The specification of the audio data. + */ + AUD_DeviceSpecs m_specs; + + /** + * The AVFormatContext structure for using ffmpeg. + */ + AVFormatContext* m_formatCtx; + + /** + * The AVCodecContext structure for using ffmpeg. + */ + AVCodecContext* m_codecCtx; + + AVOutputFormat* m_outputFmt; + + AVStream* m_stream; + + AUD_Buffer m_input_buffer; + + AUD_Buffer m_output_buffer; + + unsigned int m_input_samples; + + unsigned int m_input_size; + + /** + * Converter function. + */ + AUD_convert_f m_convert; + + // hide copy constructor and operator= + AUD_FFMPEGWriter(const AUD_FFMPEGWriter&); + AUD_FFMPEGWriter& operator=(const AUD_FFMPEGWriter&); + + void encode(sample_t* data); + +public: + /** + * Creates a new writer. + * \param filename The path to the file to be read. + * \exception AUD_Exception Thrown if the file specified does not exist or + * cannot be read with ffmpeg. + */ + AUD_FFMPEGWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate); + + /** + * Destroys the writer and closes the file. + */ + virtual ~AUD_FFMPEGWriter(); + + virtual int getPosition() const; + virtual AUD_DeviceSpecs getSpecs() const; + virtual void write(unsigned int length, sample_t* buffer); +}; + +#endif //AUD_FFMPEGWRITER diff --git a/intern/audaspace/intern/AUD_C-API.cpp b/intern/audaspace/intern/AUD_C-API.cpp index 23245b56b20..467ee736b7f 100644 --- a/intern/audaspace/intern/AUD_C-API.cpp +++ b/intern/audaspace/intern/AUD_C-API.cpp @@ -48,6 +48,7 @@ #include "AUD_I3DDevice.h" #include "AUD_I3DHandle.h" #include "AUD_FileFactory.h" +#include "AUD_FileWriter.h" #include "AUD_StreamBufferFactory.h" #include "AUD_DelayFactory.h" #include "AUD_LimiterFactory.h" @@ -1153,3 +1154,23 @@ void* AUD_getSet(void* set) return NULL; } + +const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate) +{ + try + { + AUD_SequencerFactory* f = dynamic_cast(sound->get()); + + f->setSpecs(specs.specs); + AUD_Reference reader = f->createReader(); + reader->seek(start); + AUD_Reference writer = AUD_FileWriter::createWriter(filename, specs, format, codec, bitrate); + AUD_FileWriter::writeReader(reader, writer, length, buffersize); + + return NULL; + } + catch(AUD_Exception& e) + { + return e.str; + } +} diff --git a/intern/audaspace/intern/AUD_C-API.h b/intern/audaspace/intern/AUD_C-API.h index a6ef34280c2..abcbd215074 100644 --- a/intern/audaspace/intern/AUD_C-API.h +++ b/intern/audaspace/intern/AUD_C-API.h @@ -508,7 +508,7 @@ extern AUD_Sound* AUD_copy(AUD_Sound* sound); extern void AUD_freeHandle(AUD_Handle* channel); -extern void* AUD_createSet(); +extern void* AUD_createSet(void); extern void AUD_destroySet(void* set); @@ -518,6 +518,8 @@ extern void AUD_addSet(void* set, void* entry); extern void* AUD_getSet(void* set); +extern const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate); + #ifdef WITH_PYTHON extern PyObject* AUD_getPythonFactory(AUD_Sound* sound); diff --git a/intern/audaspace/intern/AUD_ConverterFunctions.cpp b/intern/audaspace/intern/AUD_ConverterFunctions.cpp index c45fde72b1b..f7be2ca805f 100644 --- a/intern/audaspace/intern/AUD_ConverterFunctions.cpp +++ b/intern/audaspace/intern/AUD_ConverterFunctions.cpp @@ -35,10 +35,10 @@ #define AUD_U8_0 0x80 #define AUD_S16_MAX 0x7FFF #define AUD_S16_MIN 0x8000 -#define AUD_S16_FLT 32768.0f +#define AUD_S16_FLT 32767.0f #define AUD_S32_MAX 0x7FFFFFFF #define AUD_S32_MIN 0x80000000 -#define AUD_S32_FLT 2147483648.0f +#define AUD_S32_FLT 2147483647.0f #define AUD_FLT_MAX 1.0f #define AUD_FLT_MIN -1.0f @@ -379,7 +379,7 @@ void AUD_convert_float_double(data_t* target, data_t* source, int length) { float* s = (float*) source; double* t = (double*) target; - for(int i = length - 1; i >= 0; i++) + for(int i = length - 1; i >= 0; i--) t[i] = s[i]; } diff --git a/intern/audaspace/intern/AUD_FileWriter.cpp b/intern/audaspace/intern/AUD_FileWriter.cpp new file mode 100644 index 00000000000..a5ef592ea17 --- /dev/null +++ b/intern/audaspace/intern/AUD_FileWriter.cpp @@ -0,0 +1,97 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/intern/AUD_FileWriter.cpp + * \ingroup audaspaceintern + */ + +#ifdef WITH_FFMPEG +// needed for INT64_C +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif +#include "AUD_FFMPEGWriter.h" +#endif + +#ifdef WITH_SNDFILE +#include "AUD_SndFileWriter.h" +#endif + +#include "AUD_FileWriter.h" +#include "AUD_Buffer.h" + +static const char* write_error = "AUD_FileWriter: File couldn't be written."; + +AUD_Reference AUD_FileWriter::createWriter(std::string filename,AUD_DeviceSpecs specs, + AUD_Container format, AUD_Codec codec, unsigned int bitrate) +{ +#ifdef WITH_SNDFILE + try + { + return new AUD_SndFileWriter(filename, specs, format, codec, bitrate); + } + catch(AUD_Exception&) {} +#endif + +#ifdef WITH_FFMPEG + try + { + return new AUD_FFMPEGWriter(filename, specs, format, codec, bitrate); + } + catch(AUD_Exception&) {} +#endif + + AUD_THROW(AUD_ERROR_SPECS, write_error); +} + +void AUD_FileWriter::writeReader(AUD_Reference reader, AUD_Reference writer, unsigned int length, unsigned int buffersize) +{ + AUD_Buffer buffer(buffersize * AUD_SAMPLE_SIZE(writer->getSpecs())); + sample_t* buf = buffer.getBuffer(); + + int len; + bool eos = false; + int channels = writer->getSpecs().channels; + + for(unsigned int pos = 0; ((pos < length) || (length <= 0)) && !eos; pos += len) + { + len = buffersize; + if((len > length - pos) && (length > 0)) + len = length - pos; + reader->read(len, eos, buf); + + for(int i = 0; i < len * channels; i++) + { + // clamping! + if(buf[i] > 1) + buf[i] = 1; + else if(buf[i] < -1) + buf[i] = -1; + } + + writer->write(len, buf); + } +} diff --git a/intern/audaspace/intern/AUD_FileWriter.h b/intern/audaspace/intern/AUD_FileWriter.h new file mode 100644 index 00000000000..60aec1b5927 --- /dev/null +++ b/intern/audaspace/intern/AUD_FileWriter.h @@ -0,0 +1,58 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/intern/AUD_FileWriter.h + * \ingroup audaspaceintern + */ + + +#ifndef AUD_FILEWRITER +#define AUD_FILEWRITER + +#include + +#include "AUD_Reference.h" + +#include "AUD_IWriter.h" +#include "AUD_IReader.h" + +/** + * This factory tries to read a sound file via all available file readers. + */ +class AUD_FileWriter +{ +private: + // hide default constructor, copy constructor and operator= + AUD_FileWriter(); + AUD_FileWriter(const AUD_FileWriter&); + AUD_FileWriter& operator=(const AUD_FileWriter&); + +public: + static AUD_Reference createWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate); + static void writeReader(AUD_Reference reader, AUD_Reference writer, unsigned int length, unsigned int buffersize); +}; + +#endif //AUD_FILEWRITER diff --git a/intern/audaspace/intern/AUD_IReader.h b/intern/audaspace/intern/AUD_IReader.h index b1a282c9d49..5fc2cd62fb2 100644 --- a/intern/audaspace/intern/AUD_IReader.h +++ b/intern/audaspace/intern/AUD_IReader.h @@ -49,27 +49,22 @@ public: /** * Tells whether the source provides seeking functionality or not. * \warning This doesn't mean that the seeking always has to succeed. - * \return Always returns true for readers of the buffer type. - * \see getType + * \return Always returns true for readers of buffering types. */ virtual bool isSeekable() const=0; /** * Seeks to a specific position in the source. - * This function must work for buffer type readers. * \param position The position to seek for measured in samples. To get * from a given time to the samples you simply have to multiply the * time value in seconds with the sample rate of the reader. * \warning This may work or not, depending on the actual reader. - * \see getType */ virtual void seek(int position)=0; /** * Returns an approximated length of the source in samples. - * For readers of the type buffer this has to return a correct value! * \return The length as sample count. May be negative if unknown. - * \see getType */ virtual int getLength() const=0; @@ -77,10 +72,8 @@ public: * Returns the position of the source as a sample count value. * \return The current position in the source. A negative value indicates * that the position is unknown. - * \warning The value returned doesn't always have to be correct for readers - * of the stream type, especially after seeking, it must though for - * the buffer ones. - * \see getType + * \warning The value returned doesn't always have to be correct for readers, + * especially after seeking. */ virtual int getPosition() const=0; @@ -98,7 +91,7 @@ public: * there were only fewer samples available. * A smaller value also indicates the end of the reader. * \param[out] eos End of stream, whether the end is reached or not. - * \param[int] buffer The pointer to the buffer to read into. + * \param[in] buffer The pointer to the buffer to read into. */ virtual void read(int& length, bool& eos, sample_t* buffer)=0; }; diff --git a/intern/audaspace/intern/AUD_IWriter.h b/intern/audaspace/intern/AUD_IWriter.h new file mode 100644 index 00000000000..944bce961c3 --- /dev/null +++ b/intern/audaspace/intern/AUD_IWriter.h @@ -0,0 +1,69 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/intern/AUD_IWriter.h + * \ingroup audaspaceintern + */ + + +#ifndef AUD_IWRITER +#define AUD_IWRITER + +#include "AUD_Space.h" + +/** + * This class represents a sound sink where audio data can be written to. + */ +class AUD_IWriter +{ +public: + /** + * Destroys the writer. + */ + virtual ~AUD_IWriter(){} + + /** + * Returns how many samples have been written so far. + * \return The writing position as sample count. May be negative if unknown. + */ + virtual int getPosition() const=0; + + /** + * Returns the specification of the audio data being written into the sink. + * \return The AUD_DeviceSpecs structure. + * \note Regardless of the format the input still has to be float! + */ + virtual AUD_DeviceSpecs getSpecs() const=0; + + /** + * Request to write the next length samples out into the sink. + * \param length The count of samples to write. + * \param buffer The pointer to the buffer containing the data. + */ + virtual void write(unsigned int length, sample_t* buffer)=0; +}; + +#endif //AUD_IWRITER diff --git a/intern/audaspace/intern/AUD_NULLDevice.cpp b/intern/audaspace/intern/AUD_NULLDevice.cpp index c3a8c754cb2..78a02d32cdd 100644 --- a/intern/audaspace/intern/AUD_NULLDevice.cpp +++ b/intern/audaspace/intern/AUD_NULLDevice.cpp @@ -40,6 +40,10 @@ AUD_NULLDevice::AUD_NULLDevice() { } +AUD_NULLDevice::~AUD_NULLDevice() +{ +} + AUD_DeviceSpecs AUD_NULLDevice::getSpecs() const { AUD_DeviceSpecs specs; diff --git a/intern/audaspace/intern/AUD_NULLDevice.h b/intern/audaspace/intern/AUD_NULLDevice.h index 69ca5b74f3b..04458575f1c 100644 --- a/intern/audaspace/intern/AUD_NULLDevice.h +++ b/intern/audaspace/intern/AUD_NULLDevice.h @@ -46,6 +46,8 @@ public: */ AUD_NULLDevice(); + virtual ~AUD_NULLDevice(); + virtual AUD_DeviceSpecs getSpecs() const; virtual AUD_Reference play(AUD_Reference reader, bool keep = false); virtual AUD_Reference play(AUD_Reference factory, bool keep = false); diff --git a/intern/audaspace/intern/AUD_Space.h b/intern/audaspace/intern/AUD_Space.h index 117c37b56ba..4d0a06e37b2 100644 --- a/intern/audaspace/intern/AUD_Space.h +++ b/intern/audaspace/intern/AUD_Space.h @@ -169,6 +169,30 @@ typedef enum AUD_AP_ORIENTATION } AUD_AnimateablePropertyType; +typedef enum +{ + AUD_CONTAINER_INVALID = 0, + AUD_CONTAINER_AC3, + AUD_CONTAINER_FLAC, + AUD_CONTAINER_MATROSKA, + AUD_CONTAINER_MP2, + AUD_CONTAINER_MP3, + AUD_CONTAINER_OGG, + AUD_CONTAINER_WAV +} AUD_Container; + +typedef enum +{ + AUD_CODEC_INVALID = 0, + AUD_CODEC_AAC, + AUD_CODEC_AC3, + AUD_CODEC_FLAC, + AUD_CODEC_MP2, + AUD_CODEC_MP3, + AUD_CODEC_PCM, + AUD_CODEC_VORBIS +} AUD_Codec; + /// Sample type.(float samples) typedef float sample_t; diff --git a/intern/audaspace/sndfile/AUD_SndFileWriter.cpp b/intern/audaspace/sndfile/AUD_SndFileWriter.cpp new file mode 100644 index 00000000000..ba59cd3d9d7 --- /dev/null +++ b/intern/audaspace/sndfile/AUD_SndFileWriter.cpp @@ -0,0 +1,141 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/sndfile/AUD_SndFileWriter.cpp + * \ingroup audsndfile + */ + + +#include "AUD_SndFileWriter.h" + +#include + +static const char* fileopen_error = "AUD_SndFileWriter: File couldn't be written."; +static const char* format_error = "AUD_SndFileWriter: Unsupported format."; + +AUD_SndFileWriter::AUD_SndFileWriter(std::string filename, AUD_DeviceSpecs specs, + AUD_Container format, AUD_Codec codec, unsigned int bitrate) : + m_specs(specs) +{ + SF_INFO sfinfo; + + sfinfo.channels = specs.channels; + sfinfo.samplerate = int(specs.rate); + + switch(format) + { + case AUD_CONTAINER_FLAC: + sfinfo.format = SF_FORMAT_FLAC; + switch(specs.format) + { + case AUD_FORMAT_S16: + sfinfo.format |= SF_FORMAT_PCM_16; + break; + case AUD_FORMAT_S24: + sfinfo.format |= SF_FORMAT_PCM_24; + break; + case AUD_FORMAT_S32: + sfinfo.format |= SF_FORMAT_PCM_32; + break; + case AUD_FORMAT_FLOAT32: + sfinfo.format |= SF_FORMAT_FLOAT; + break; + case AUD_FORMAT_FLOAT64: + sfinfo.format |= SF_FORMAT_DOUBLE; + break; + default: + sfinfo.format = 0; + break; + } + break; + case AUD_CONTAINER_OGG: + if(codec == AUD_CODEC_VORBIS) + sfinfo.format = SF_FORMAT_OGG | SF_FORMAT_VORBIS; + else + sfinfo.format = 0; + break; + case AUD_CONTAINER_WAV: + sfinfo.format = SF_FORMAT_WAV; + switch(specs.format) + { + case AUD_FORMAT_U8: + sfinfo.format |= SF_FORMAT_PCM_U8; + break; + case AUD_FORMAT_S16: + sfinfo.format |= SF_FORMAT_PCM_16; + break; + case AUD_FORMAT_S24: + sfinfo.format |= SF_FORMAT_PCM_24; + break; + case AUD_FORMAT_S32: + sfinfo.format |= SF_FORMAT_PCM_32; + break; + case AUD_FORMAT_FLOAT32: + sfinfo.format |= SF_FORMAT_FLOAT; + break; + case AUD_FORMAT_FLOAT64: + sfinfo.format |= SF_FORMAT_DOUBLE; + break; + default: + sfinfo.format = 0; + break; + } + break; + default: + sfinfo.format = 0; + break; + } + + if(sfinfo.format == 0) + AUD_THROW(AUD_ERROR_SPECS, format_error); + + m_sndfile = sf_open(filename.c_str(), SFM_WRITE, &sfinfo); + + if(!m_sndfile) + AUD_THROW(AUD_ERROR_FILE, fileopen_error); +} + +AUD_SndFileWriter::~AUD_SndFileWriter() +{ + sf_close(m_sndfile); +} + +int AUD_SndFileWriter::getPosition() const +{ + return m_position; +} + +AUD_DeviceSpecs AUD_SndFileWriter::getSpecs() const +{ + return m_specs; +} + +void AUD_SndFileWriter::write(unsigned int length, sample_t* buffer) +{ + length = sf_writef_float(m_sndfile, buffer, length); + + m_position += length; +} diff --git a/intern/audaspace/sndfile/AUD_SndFileWriter.h b/intern/audaspace/sndfile/AUD_SndFileWriter.h new file mode 100644 index 00000000000..63f9e9e1371 --- /dev/null +++ b/intern/audaspace/sndfile/AUD_SndFileWriter.h @@ -0,0 +1,87 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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 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 General Public License for more details. + * + * 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 GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/sndfile/AUD_SndFileWriter.h + * \ingroup audsndfile + */ + + +#ifndef AUD_SNDFILEWRITER +#define AUD_SNDFILEWRITER + +#include "AUD_IWriter.h" +//#include "AUD_Buffer.h" + +#include +#include + +typedef sf_count_t (*sf_read_f)(SNDFILE *sndfile, void *ptr, sf_count_t frames); + +/** + * This class writes a sound file via libsndfile. + */ +class AUD_SndFileWriter : public AUD_IWriter +{ +private: + /** + * The current position in samples. + */ + int m_position; + + /** + * The specification of the audio data. + */ + AUD_DeviceSpecs m_specs; + + /** + * The sndfile. + */ + SNDFILE* m_sndfile; + + // hide copy constructor and operator= + AUD_SndFileWriter(const AUD_SndFileWriter&); + AUD_SndFileWriter& operator=(const AUD_SndFileWriter&); + +public: + /** + * Creates a new writer. + * \param filename The path to the file to be read. + * \exception AUD_Exception Thrown if the file specified cannot be written + * with libsndfile. + */ + AUD_SndFileWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate); + + /** + * Destroys the writer and closes the file. + */ + virtual ~AUD_SndFileWriter(); + + virtual int getPosition() const; + virtual AUD_DeviceSpecs getSpecs() const; + virtual void write(unsigned int length, sample_t* buffer); +}; + +#endif //AUD_SNDFILEWRITER -- cgit v1.2.3