3D Audio GSoC:
authorJoerg Mueller <nexyon@gmail.com>
Sat, 6 Aug 2011 17:57:20 +0000 (17:57 +0000)
committerJoerg Mueller <nexyon@gmail.com>
Sat, 6 Aug 2011 17:57:20 +0000 (17:57 +0000)
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

20 files changed:
intern/audaspace/CMakeLists.txt
intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp
intern/audaspace/ffmpeg/AUD_FFMPEGReader.h
intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp [new file with mode: 0644]
intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h [new file with mode: 0644]
intern/audaspace/intern/AUD_C-API.cpp
intern/audaspace/intern/AUD_C-API.h
intern/audaspace/intern/AUD_ConverterFunctions.cpp
intern/audaspace/intern/AUD_FileWriter.cpp [new file with mode: 0644]
intern/audaspace/intern/AUD_FileWriter.h [new file with mode: 0644]
intern/audaspace/intern/AUD_IReader.h
intern/audaspace/intern/AUD_IWriter.h [new file with mode: 0644]
intern/audaspace/intern/AUD_NULLDevice.cpp
intern/audaspace/intern/AUD_NULLDevice.h
intern/audaspace/intern/AUD_Space.h
intern/audaspace/sndfile/AUD_SndFileWriter.cpp [new file with mode: 0644]
intern/audaspace/sndfile/AUD_SndFileWriter.h [new file with mode: 0644]
release/scripts/startup/bl_ui/properties_scene.py
source/blender/editors/sound/CMakeLists.txt
source/blender/editors/sound/sound_ops.c

index b08c5f62b29338a29184135f87e9fb5d7f60c489..8b7cb1d9e691ab80de64330ddeb101953db5d50c 100644 (file)
@@ -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()
 
index b7690d55383bdaac82f9a07dcb9cbc80a3d1cca5..a7534dbed32be40f7c1652d1262519711e584a4f 100644 (file)
@@ -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<AUD_Buffer> buffer) :
 {
        m_membuf = reinterpret_cast<data_t*>(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<data_t*>(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<AUD_Buffer> 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);
index 06d6fe1e5f699a566e188eabef5aa22999583454..222a3d8581aa0f537470850b96f75a9f21457c1e 100644 (file)
@@ -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 (file)
index 0000000..f2b7acc
--- /dev/null
@@ -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 <libavcodec/avcodec.h>
+#include <libavformat/avformat.h>
+#include <libavformat/avio.h>
+}
+
+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<float>;
+                       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_t*>(data), reinterpret_cast<data_t*>(data), m_input_size * m_specs.channels);
+
+       AVPacket packet;
+       av_init_packet(&packet);
+       packet.size = avcodec_encode_audio(m_codecCtx, reinterpret_cast<uint8_t*>(outbuf), m_output_buffer.getSize(), reinterpret_cast<short*>(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<uint8_t*>(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<data_t*>(buf), reinterpret_cast<data_t*>(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 (file)
index 0000000..618ec94
--- /dev/null
@@ -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 <string>
+
+struct AVCodecContext;
+extern "C" {
+#include <libavformat/avformat.h>
+}
+
+/**
+ * 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
index 23245b56b200e863e6e0eec7c37caa826038efc6..467ee736b7f3f6e8eea4bf1d06bbf720a6e2e398 100644 (file)
@@ -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<AUD_SequencerFactory*>(sound->get());
+
+               f->setSpecs(specs.specs);
+               AUD_Reference<AUD_IReader> reader = f->createReader();
+               reader->seek(start);
+               AUD_Reference<AUD_IWriter> 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;
+       }
+}
index a6ef34280c2ed751ff008a64c9704fe6d8e12302..abcbd21507487832ed7b7780d119f7d39e587f40 100644 (file)
@@ -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);
 
index c45fde72b1b814085b77592f7a90ab4bbbda01a1..f7be2ca805f2182d8f8bfd25ca2b536c61326b84 100644 (file)
 #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 (file)
index 0000000..a5ef592
--- /dev/null
@@ -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_IWriter> 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<AUD_IReader> reader, AUD_Reference<AUD_IWriter> 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 (file)
index 0000000..60aec1b
--- /dev/null
@@ -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 <string>
+
+#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<AUD_IWriter> createWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate);
+       static void writeReader(AUD_Reference<AUD_IReader> reader, AUD_Reference<AUD_IWriter> writer, unsigned int length, unsigned int buffersize);
+};
+
+#endif //AUD_FILEWRITER
index b1a282c9d495351484617cacc0dada7d370bc89a..5fc2cd62fb25ec31f8f4d18d28f5b68f3cc455c9 100644 (file)
@@ -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 (file)
index 0000000..944bce9
--- /dev/null
@@ -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
index c3a8c754cb2ef939852c87bd1b2ca30f0e09d968..78a02d32cdd6986bb74d999067467cc4d1fd945f 100644 (file)
@@ -40,6 +40,10 @@ AUD_NULLDevice::AUD_NULLDevice()
 {
 }
 
+AUD_NULLDevice::~AUD_NULLDevice()
+{
+}
+
 AUD_DeviceSpecs AUD_NULLDevice::getSpecs() const
 {
        AUD_DeviceSpecs specs;
index 69ca5b74f3b52fe86fe62094b78c9fe90e6b757f..04458575f1cff7061a7461ecd4eedd7f902e0be5 100644 (file)
@@ -46,6 +46,8 @@ public:
         */
        AUD_NULLDevice();
 
+       virtual ~AUD_NULLDevice();
+
        virtual AUD_DeviceSpecs getSpecs() const;
        virtual AUD_Reference<AUD_IHandle> play(AUD_Reference<AUD_IReader> reader, bool keep = false);
        virtual AUD_Reference<AUD_IHandle> play(AUD_Reference<AUD_IFactory> factory, bool keep = false);
index 117c37b56ba7ad006ff1b16f35b12ea314a535f6..4d0a06e37b280175d255167b2d632f1ef82d7ce9 100644 (file)
@@ -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 (file)
index 0000000..ba59cd3
--- /dev/null
@@ -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 <cstring>
+
+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 (file)
index 0000000..63f9e9e
--- /dev/null
@@ -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 <string>
+#include <sndfile.h>
+
+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
index a9310fcc532f57a1778297c85b6081b270d366b3..c3784b9f6922e9dbefd9926ffea82b7ed22c322b 100644 (file)
@@ -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"
index f66288812ad72c91cf7e23f78134d9c83e19a399..11da4165ec845927f9f4b3a7589979ce74155918 100644 (file)
@@ -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}")
index 8744ec5dfd64c336fed03a8cfff0f9743b4ba751..c6a3663d512d352f8f0146cb28f3a814e2edce72 100644 (file)
@@ -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);