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 ++++++ release/scripts/startup/bl_ui/properties_scene.py | 2 + source/blender/editors/sound/CMakeLists.txt | 8 + source/blender/editors/sound/sound_ops.c | 287 ++++++++++++++++++- 20 files changed, 1241 insertions(+), 43 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 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 diff --git a/release/scripts/startup/bl_ui/properties_scene.py b/release/scripts/startup/bl_ui/properties_scene.py index a9310fcc532..c3784b9f692 100644 --- a/release/scripts/startup/bl_ui/properties_scene.py +++ b/release/scripts/startup/bl_ui/properties_scene.py @@ -70,6 +70,8 @@ class SCENE_PT_audio(SceneButtonsPanel, bpy.types.Panel): col.prop(rd, "ffmpeg_audio_channels", text="") col.prop(rd, "ffmpeg_audio_mixrate", text="Rate") + layout.operator("sound.mixdown") + class SCENE_PT_unit(SceneButtonsPanel, bpy.types.Panel): bl_label = "Units" diff --git a/source/blender/editors/sound/CMakeLists.txt b/source/blender/editors/sound/CMakeLists.txt index f66288812ad..11da4165ec8 100644 --- a/source/blender/editors/sound/CMakeLists.txt +++ b/source/blender/editors/sound/CMakeLists.txt @@ -47,4 +47,12 @@ if(WITH_AUDASPACE) add_definitions(-DWITH_AUDASPACE) endif() +if(WITH_CODEC_FFMPEG) + add_definitions(-DWITH_FFMPEG) +endif() + +if(WITH_CODEC_SNDFILE) + add_definitions(-DWITH_SNDFILE) +endif() + blender_add_lib(bf_editor_sound "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 8744ec5dfd6..c6a3663d512 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -82,7 +82,7 @@ static void open_init(bContext *C, wmOperator *op) { PropertyPointerRNA *pprop; - + op->customdata= pprop= MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA"); uiIDContextProperty(C, &pprop->ptr, &pprop->prop); } @@ -101,7 +101,7 @@ static int open_exec(bContext *C, wmOperator *op) if(!op->customdata) open_init(C, op); - + if (sound==NULL || sound->playback_handle == NULL) { if(op->customdata) MEM_freeN(op->customdata); BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); @@ -120,15 +120,15 @@ static int open_exec(bContext *C, wmOperator *op) if (RNA_boolean_get(op->ptr, "cache")) { sound_cache(sound, 0); } - + /* hook into UI */ pprop= op->customdata; - + if(pprop->prop) { /* when creating new ID blocks, use is already 1, but RNA * pointer se also increases user, so this compensates it */ sound->id.us--; - + RNA_id_pointer_create(&sound->id, &idptr); RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr); RNA_property_update(C, &pprop->ptr, pprop->prop); @@ -153,12 +153,12 @@ static int open_invoke(bContext *C, wmOperator *op, wmEvent *event) { if(!RNA_property_is_set(op->ptr, "relative_path")) RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS); - + if(RNA_property_is_set(op->ptr, "filepath")) return open_exec(C, op); - + open_init(C, op); - + return WM_operator_filesel(C, op, event); } @@ -181,6 +181,276 @@ void SOUND_OT_open(wmOperatorType *ot) RNA_def_boolean(ot->srna, "cache", FALSE, "Cache", "Cache the sound in memory."); } +/******************** mixdown operator ********************/ + +static int mixdown_exec(bContext *C, wmOperator *op) +{ + char path[FILE_MAX]; + char filename[FILE_MAX]; + Scene *scene; + Main *bmain; + + int bitrate, accuracy; + AUD_DeviceSpecs specs; + AUD_Container container; + AUD_Codec codec; + const char* result; + + RNA_string_get(op->ptr, "filepath", path); + bitrate = RNA_int_get(op->ptr, "bitrate") * 1000; + accuracy = RNA_int_get(op->ptr, "accuracy"); + specs.format = RNA_enum_get(op->ptr, "format"); + container = RNA_enum_get(op->ptr, "container"); + codec = RNA_enum_get(op->ptr, "codec"); + scene = CTX_data_scene(C); + bmain = CTX_data_main(C); + specs.channels = scene->r.ffcodecdata.audio_channels; + specs.rate = scene->r.ffcodecdata.audio_mixrate; + + BLI_strncpy(filename, path, sizeof(filename)); + BLI_path_abs(filename, bmain->name); + + result = AUD_mixdown(scene->sound_scene, SFRA * specs.rate / FPS, (EFRA - SFRA) * specs.rate / FPS, + accuracy, filename, specs, container, codec, bitrate); + + if(result) + { + BKE_report(op->reports, RPT_ERROR, result); + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +static int mixdown_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + if(!RNA_property_is_set(op->ptr, "relative_path")) + RNA_boolean_set(op->ptr, "relative_path", U.flag & USER_RELPATHS); + + if(RNA_property_is_set(op->ptr, "filepath")) + return mixdown_exec(C, op); + + return WM_operator_filesel(C, op, event); +} + +static int mixdown_draw_check_prop(PropertyRNA *prop) +{ + const char *prop_id= RNA_property_identifier(prop); + return !( strcmp(prop_id, "filepath") == 0 || + strcmp(prop_id, "directory") == 0 || + strcmp(prop_id, "filename") == 0 + ); +} + +static void mixdown_draw(bContext *C, wmOperator *op) +{ + static EnumPropertyItem pcm_format_items[] = { + {AUD_FORMAT_U8, "U8", 0, "U8", "8 bit unsigned"}, + {AUD_FORMAT_S16, "S16", 0, "S16", "16 bit signed"}, +#ifdef WITH_SNDFILE + {AUD_FORMAT_S24, "S24", 0, "S24", "24 bit signed"}, +#endif + {AUD_FORMAT_S32, "S32", 0, "S32", "32 bit signed"}, + {AUD_FORMAT_FLOAT32, "F32", 0, "F32", "32 bit floating point"}, + {AUD_FORMAT_FLOAT64, "F64", 0, "F64", "64 bit floating point"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem mp3_format_items[] = { + {AUD_FORMAT_S16, "S16", 0, "S16", "16 bit signed"}, + {AUD_FORMAT_S32, "S32", 0, "S32", "32 bit signed"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem ac3_format_items[] = { + {AUD_FORMAT_S16, "S16", 0, "S16", "16 bit signed"}, + {AUD_FORMAT_FLOAT32, "F32", 0, "F32", "32 bit floating point"}, + {0, NULL, 0, NULL, NULL}}; + +#ifdef WITH_SNDFILE + static EnumPropertyItem flac_format_items[] = { + {AUD_FORMAT_S16, "S16", 0, "S16", "16 bit signed"}, + {AUD_FORMAT_S24, "S24", 0, "S24", "24 bit signed"}, + {0, NULL, 0, NULL, NULL}}; +#endif + + static EnumPropertyItem all_codec_items[] = { + {AUD_CODEC_AAC, "AAC", 0, "AAC", "Advanced Audio Coding"}, + {AUD_CODEC_AC3, "AC3", 0, "AC3", "Dolby Digital ATRAC 3"}, + {AUD_CODEC_FLAC, "FLAC", 0, "FLAC", "Free Lossless Audio Codec"}, + {AUD_CODEC_MP2, "MP2", 0, "MP2", "MPEG-1 Audio Layer II"}, + {AUD_CODEC_MP3, "MP3", 0, "MP3", "MPEG-2 Audio Layer III"}, + {AUD_CODEC_PCM, "PCM", 0, "PCM", "Pulse Code Modulation (RAW)"}, + {AUD_CODEC_VORBIS, "VORBIS", 0, "Vorbis", "Xiph.Org Vorbis Codec"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem ogg_codec_items[] = { + {AUD_CODEC_FLAC, "FLAC", 0, "FLAC", "Free Lossless Audio Codec"}, + {AUD_CODEC_VORBIS, "VORBIS", 0, "Vorbis", "Xiph.Org Vorbis Codec"}, + {0, NULL, 0, NULL, NULL}}; + + uiLayout *layout = op->layout; + wmWindowManager *wm= CTX_wm_manager(C); + PointerRNA ptr; + PropertyRNA *prop_format; + PropertyRNA *prop_codec; + PropertyRNA *prop_bitrate; + + AUD_Container container = RNA_enum_get(op->ptr, "container"); + AUD_Codec codec = RNA_enum_get(op->ptr, "codec"); + + prop_format = RNA_struct_find_property(op->ptr, "format"); + prop_codec = RNA_struct_find_property(op->ptr, "codec"); + prop_bitrate = RNA_struct_find_property(op->ptr, "bitrate"); + + RNA_def_property_clear_flag(prop_bitrate, PROP_HIDDEN); + RNA_def_property_flag(prop_codec, PROP_HIDDEN); + RNA_def_property_flag(prop_format, PROP_HIDDEN); + + switch(container) + { + case AUD_CONTAINER_AC3: + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + RNA_def_property_enum_items(prop_format, ac3_format_items); + RNA_def_property_enum_items(prop_codec, all_codec_items); + RNA_enum_set(op->ptr, "codec", AUD_CODEC_AC3); + break; + case AUD_CONTAINER_FLAC: + RNA_def_property_flag(prop_bitrate, PROP_HIDDEN); + RNA_def_property_enum_items(prop_codec, all_codec_items); + RNA_enum_set(op->ptr, "codec", AUD_CODEC_FLAC); +#ifdef WITH_SNDFILE + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + RNA_def_property_enum_items(prop_format, flac_format_items); +#else + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); +#endif + break; + case AUD_CONTAINER_MATROSKA: + RNA_def_property_clear_flag(prop_codec, PROP_HIDDEN); + RNA_def_property_enum_items(prop_codec, all_codec_items); + + switch(codec) + { + case AUD_CODEC_AAC: + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); + break; + case AUD_CODEC_AC3: + RNA_def_property_enum_items(prop_format, ac3_format_items); + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + break; + case AUD_CODEC_FLAC: + RNA_def_property_flag(prop_bitrate, PROP_HIDDEN); + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); + break; + case AUD_CODEC_MP2: + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); + break; + case AUD_CODEC_MP3: + RNA_def_property_enum_items(prop_format, mp3_format_items); + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + break; + case AUD_CODEC_PCM: + RNA_def_property_flag(prop_bitrate, PROP_HIDDEN); + RNA_def_property_enum_items(prop_format, pcm_format_items); + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + break; + case AUD_CODEC_VORBIS: + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); + break; + } + + break; + case AUD_CONTAINER_MP2: + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); + RNA_enum_set(op->ptr, "codec", AUD_CODEC_MP2); + RNA_def_property_enum_items(prop_codec, all_codec_items); + break; + case AUD_CONTAINER_MP3: + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + RNA_def_property_enum_items(prop_format, mp3_format_items); + RNA_def_property_enum_items(prop_codec, all_codec_items); + RNA_enum_set(op->ptr, "codec", AUD_CODEC_MP3); + break; + case AUD_CONTAINER_OGG: + RNA_def_property_clear_flag(prop_codec, PROP_HIDDEN); + RNA_def_property_enum_items(prop_codec, ogg_codec_items); + RNA_enum_set(op->ptr, "format", AUD_FORMAT_S16); + break; + case AUD_CONTAINER_WAV: + RNA_def_property_flag(prop_bitrate, PROP_HIDDEN); + RNA_def_property_clear_flag(prop_format, PROP_HIDDEN); + RNA_def_property_enum_items(prop_format, pcm_format_items); + RNA_def_property_enum_items(prop_codec, all_codec_items); + RNA_enum_set(op->ptr, "codec", AUD_CODEC_PCM); + break; + } + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* main draw call */ + uiDefAutoButsRNA(layout, &ptr, mixdown_draw_check_prop, '\0'); +} + +void SOUND_OT_mixdown(wmOperatorType *ot) +{ + static EnumPropertyItem format_items[] = { + {AUD_FORMAT_U8, "U8", 0, "U8", "8 bit unsigned"}, + {AUD_FORMAT_S16, "S16", 0, "S16", "16 bit signed"}, + {AUD_FORMAT_S24, "S24", 0, "S24", "24 bit signed"}, + {AUD_FORMAT_S32, "S32", 0, "S32", "32 bit signed"}, + {AUD_FORMAT_FLOAT32, "F32", 0, "F32", "32 bit floating point"}, + {AUD_FORMAT_FLOAT64, "F64", 0, "F64", "64 bit floating point"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem container_items[] = { +#ifdef WITH_FFMPEG + {AUD_CONTAINER_AC3, "AC3", 0, "ac3", "Dolby Digital ATRAC 3"}, +#endif + {AUD_CONTAINER_FLAC, "FLAC", 0, "flac", "Free Lossless Audio Codec"}, +#ifdef WITH_FFMPEG + {AUD_CONTAINER_MATROSKA, "MATROSKA", 0, "mkv", "Matroska"}, + {AUD_CONTAINER_MP2, "MP2", 0, "mp2", "MPEG-1 Audio Layer II"}, + {AUD_CONTAINER_MP3, "MP3", 0, "mp3", "MPEG-2 Audio Layer III"}, +#endif + {AUD_CONTAINER_OGG, "OGG", 0, "ogg", "Xiph.Org Ogg Container"}, + {AUD_CONTAINER_WAV, "WAV", 0, "wav", "Waveform Audio File Format"}, + {0, NULL, 0, NULL, NULL}}; + + static EnumPropertyItem codec_items[] = { +#ifdef WITH_FFMPEG + {AUD_CODEC_AAC, "AAC", 0, "AAC", "Advanced Audio Coding"}, + {AUD_CODEC_AC3, "AC3", 0, "AC3", "Dolby Digital ATRAC 3"}, +#endif + {AUD_CODEC_FLAC, "FLAC", 0, "FLAC", "Free Lossless Audio Codec"}, +#ifdef WITH_FFMPEG + {AUD_CODEC_MP2, "MP2", 0, "MP2", "MPEG-1 Audio Layer II"}, + {AUD_CODEC_MP3, "MP3", 0, "MP3", "MPEG-2 Audio Layer III"}, +#endif + {AUD_CODEC_PCM, "PCM", 0, "PCM", "Pulse Code Modulation (RAW)"}, + {AUD_CODEC_VORBIS, "VORBIS", 0, "Vorbis", "Xiph.Org Vorbis Codec"}, + {0, NULL, 0, NULL, NULL}}; + + /* identifiers */ + ot->name= "Mixdown"; + ot->description= "Mixes the scene's audio to a sound file"; + ot->idname= "SOUND_OT_mixdown"; + + /* api callbacks */ + ot->exec= mixdown_exec; + ot->invoke= mixdown_invoke; + ot->ui= mixdown_draw; + + /* flags */ + ot->flag= OPTYPE_REGISTER; + + /* properties */ + WM_operator_properties_filesel(ot, FOLDERFILE|SOUNDFILE, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH); + RNA_def_int(ot->srna, "accuracy", 1024, 1, 16777216, "Accuracy", "Sample accuracy. Important for animation data. The lower the value, the more accurate.", 1, 16777216); + RNA_def_enum(ot->srna, "container", container_items, AUD_CONTAINER_FLAC, "Container", "File format"); + RNA_def_enum(ot->srna, "codec", codec_items, AUD_CODEC_FLAC, "Codec", "Audio Codec"); + RNA_def_enum(ot->srna, "format", format_items, AUD_FORMAT_S16, "Format", "Sample format"); + RNA_def_int(ot->srna, "bitrate", 192, 32, 512, "Bitrate", "Bitrate in kbit/s", 32, 512); +} + /* ******************************************************* */ static int sound_poll(bContext *C) @@ -393,6 +663,7 @@ void SOUND_OT_bake_animation(wmOperatorType *ot) void ED_operatortypes_sound(void) { WM_operatortype_append(SOUND_OT_open); + WM_operatortype_append(SOUND_OT_mixdown); WM_operatortype_append(SOUND_OT_pack); WM_operatortype_append(SOUND_OT_unpack); WM_operatortype_append(SOUND_OT_update_animation_flags); -- cgit v1.2.3