From 70965e3e30960129535f84ceda0fd8bc75dc51f7 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Sat, 28 Jun 2008 13:43:35 +0000 Subject: [PATCH] added support for exporting WAVE-files with 32-bit-float format git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1186 0778d3d1-df1d-0410-868b-ea421aaaa00d --- ChangeLog | 12 +++++ include/audio_file_device.h | 9 ++++ include/audio_file_ogg.h | 4 +- include/audio_file_wave.h | 15 ++++++- include/project_renderer.h | 15 ++++++- src/core/audio/audio_file_device.cpp | 4 +- src/core/audio/audio_file_ogg.cpp | 12 ++--- src/core/audio/audio_file_wave.cpp | 66 +++++++++++++++++++++++----- src/core/main.cpp | 3 +- src/core/project_renderer.cpp | 1 + src/gui/export_project_dialog.cpp | 8 ++-- 11 files changed, 124 insertions(+), 25 deletions(-) diff --git a/ChangeLog b/ChangeLog index 81245bd07..3018b2317 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,17 @@ 2008-06-28 Tobias Doerffel + * include/audio_file_device.h: + * include/audio_file_ogg.h: + * include/audio_file_wave.h: + * include/project_renderer.h: + * src/core/audio/audio_file_device.cpp: + * src/core/audio/audio_file_ogg.cpp: + * src/core/audio/audio_file_wave.cpp: + * src/core/main.cpp: + * src/core/project_renderer.cpp: + * src/gui/export_project_dialog.cpp: + added support for exporting WAVE-files with 32-bit-float format + * include/automation_editor.h: fixed node-name diff --git a/include/audio_file_device.h b/include/audio_file_device.h index 0ff3d03bf..7595d99dc 100644 --- a/include/audio_file_device.h +++ b/include/audio_file_device.h @@ -42,6 +42,7 @@ public: const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ); virtual ~audioFileDevice(); @@ -75,6 +76,11 @@ protected: return( m_maxBitrate ); } + inline int depth( void ) const + { + return( m_depth ); + } + inline bool outputFileOpened( void ) const { return( m_outputFile.isOpen() ); @@ -90,6 +96,8 @@ private: bitrate_t m_minBitrate; bitrate_t m_maxBitrate; + int m_depth; + } ; @@ -102,6 +110,7 @@ typedef audioFileDevice * ( * audioFileDeviceInstantiaton ) const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ); diff --git a/include/audio_file_ogg.h b/include/audio_file_ogg.h index 9d0c1f9bc..52aa3ef46 100644 --- a/include/audio_file_ogg.h +++ b/include/audio_file_ogg.h @@ -48,6 +48,7 @@ public: const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ); virtual ~audioFileOgg(); @@ -59,12 +60,13 @@ public: const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ) { return( new audioFileOgg( _sample_rate, _channels, _success_ful, _file, _use_vbr, _nom_bitrate, _min_bitrate, _max_bitrate, - _mixer ) ); + _depth, _mixer ) ); } diff --git a/include/audio_file_wave.h b/include/audio_file_wave.h index 948b82a35..20aab818b 100644 --- a/include/audio_file_wave.h +++ b/include/audio_file_wave.h @@ -2,7 +2,7 @@ * audio_file_wave.h - Audio-device which encodes wave-stream and writes it * into an WAVE-file. This is used for song-export. * - * Copyright (c) 2004-2006 Tobias Doerffel + * Copyright (c) 2004-2008 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * @@ -28,8 +28,12 @@ #define _AUDIO_FILE_WAVE_H +#include "lmmsconfig.h" #include "audio_file_device.h" +#ifdef LMMS_HAVE_SNDFILE_H +#include +#endif class audioFileWave : public audioFileDevice @@ -43,6 +47,7 @@ public: const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ); virtual ~audioFileWave(); @@ -54,12 +59,13 @@ public: const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ) { return( new audioFileWave( _sample_rate, _channels, _success_ful, _file, _use_vbr, _nom_bitrate, _min_bitrate, - _max_bitrate, + _max_bitrate, _depth, _mixer ) ); } @@ -73,6 +79,10 @@ private: void finishEncoding( void ); +#if LMMS_HAVE_SNDFILE_H + SF_INFO m_si; + SNDFILE * m_sf; +#else int m_bytesWritten; struct waveFileHeader @@ -91,6 +101,7 @@ private: char data_chunk_id[4]; // "data" Uint32 data_bytes; // total size of sample-data } m_waveFileHeader; +#endif } ; diff --git a/include/project_renderer.h b/include/project_renderer.h index 13cf0f8a3..648b392d8 100644 --- a/include/project_renderer.h +++ b/include/project_renderer.h @@ -42,15 +42,26 @@ public: NumFileFormats } ; + enum Depths + { + Depth_16Bit, + Depth_32Bit, + NumDepths + } ; + struct outputSettings { sample_rate_t samplerate; bool vbr; int bitrate; - outputSettings( sample_rate_t _sr, bool _vbr, int _bitrate ) : + Depths depth; + + outputSettings( sample_rate_t _sr, bool _vbr, int _bitrate, + Depths _d ) : samplerate( _sr ), vbr( _vbr ), - bitrate( _bitrate ) + bitrate( _bitrate ), + depth( _d ) { } } ; diff --git a/src/core/audio/audio_file_device.cpp b/src/core/audio/audio_file_device.cpp index 9c6448da7..452c9adfe 100644 --- a/src/core/audio/audio_file_device.cpp +++ b/src/core/audio/audio_file_device.cpp @@ -39,13 +39,15 @@ audioFileDevice::audioFileDevice( const sample_rate_t _sample_rate, const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ) : audioDevice( _channels, _mixer ), m_outputFile( _file ), m_useVbr( _use_vbr ), m_nomBitrate( _nom_bitrate ), m_minBitrate( _min_bitrate ), - m_maxBitrate( _max_bitrate ) + m_maxBitrate( _max_bitrate ), + m_depth( _depth ) { setSampleRate( _sample_rate ); diff --git a/src/core/audio/audio_file_ogg.cpp b/src/core/audio/audio_file_ogg.cpp index 0fb5dbcfe..4a30ddd37 100644 --- a/src/core/audio/audio_file_ogg.cpp +++ b/src/core/audio/audio_file_ogg.cpp @@ -49,9 +49,11 @@ audioFileOgg::audioFileOgg( const sample_rate_t _sample_rate, const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ) : audioFileDevice( _sample_rate, _channels, _file, _use_vbr, - _nom_bitrate, _min_bitrate, _max_bitrate, _mixer ) + _nom_bitrate, _min_bitrate, _max_bitrate, + _depth, _mixer ) { _success_ful = startEncoding(); } @@ -95,13 +97,13 @@ bool audioFileOgg::startEncoding( void ) // vbr enabled? if( useVBR() == 0 ) { - m_minBitrate = nominalBitrate(); // min for vbr - m_maxBitrate = nominalBitrate(); // max for vbr + m_minBitrate = nominalBitrate(); // min for vbr + m_maxBitrate = nominalBitrate(); // max for vbr } else { - m_minBitrate = minBitrate(); // min for vbr - m_maxBitrate = maxBitrate(); // max for vbr + m_minBitrate = minBitrate(); // min for vbr + m_maxBitrate = maxBitrate(); // max for vbr } m_rate = sampleRate(); // default-samplerate diff --git a/src/core/audio/audio_file_wave.cpp b/src/core/audio/audio_file_wave.cpp index f1f8f0f3b..d012033b7 100644 --- a/src/core/audio/audio_file_wave.cpp +++ b/src/core/audio/audio_file_wave.cpp @@ -40,9 +40,11 @@ audioFileWave::audioFileWave( const sample_rate_t _sample_rate, const bitrate_t _nom_bitrate, const bitrate_t _min_bitrate, const bitrate_t _max_bitrate, + const int _depth, mixer * _mixer ) : audioFileDevice( _sample_rate, _channels, _file, _use_vbr, - _nom_bitrate, _min_bitrate, _max_bitrate, _mixer ) + _nom_bitrate, _min_bitrate, _max_bitrate, + _depth, _mixer ) { _success_ful = startEncoding(); } @@ -60,6 +62,21 @@ audioFileWave::~audioFileWave() bool audioFileWave::startEncoding( void ) { +#if LMMS_HAVE_SNDFILE_H + m_si.samplerate = sampleRate(); + m_si.channels = channels(); + m_si.frames = getMixer()->framesPerPeriod(); + m_si.sections = 1; + m_si.seekable = 0; + + switch( depth() ) + { + case 32: m_si.format = SF_FORMAT_WAV | SF_FORMAT_FLOAT; break; + case 16: + default: m_si.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16; break; + } + m_sf = sf_open( outputFile().toUtf8().constData(), SFM_WRITE, &m_si ); +#else if( outputFileOpened() == FALSE ) { return( FALSE ); @@ -68,11 +85,10 @@ bool audioFileWave::startEncoding( void ) m_bytesWritten = 0; memcpy( m_waveFileHeader.riff_id, "RIFF", 4 ); - m_waveFileHeader.total_bytes = swap32IfBE( 0 ); + m_waveFileHeader.total_bytes = 0; memcpy( m_waveFileHeader.wave_fmt_str, "WAVEfmt ", 8 ); - m_waveFileHeader.bitrate_1 = - m_waveFileHeader.bitrate_2 = - swap16IfBE( BYTES_PER_INT_SAMPLE * 8 ); + m_waveFileHeader.bitrate_1 = m_waveFileHeader.bitrate_2 = + BYTES_PER_INT_SAMPLE * 8; m_waveFileHeader.uncompressed = swap16IfBE( 1 ); m_waveFileHeader.channels = swap16IfBE( channels() ); m_waveFileHeader.sample_rate = swap32IfBE( sampleRate() ); @@ -81,10 +97,10 @@ bool audioFileWave::startEncoding( void ) m_waveFileHeader.block_alignment = swap16IfBE( BYTES_PER_INT_SAMPLE * channels() ); memcpy ( m_waveFileHeader.data_chunk_id, "data", 4 ); - m_waveFileHeader.data_bytes = swap32IfBE( 0 ); + m_waveFileHeader.data_bytes = 0; writeData( &m_waveFileHeader, sizeof( m_waveFileHeader ) ); - +#endif return( TRUE ); } @@ -95,14 +111,40 @@ void audioFileWave::writeBuffer( const surroundSampleFrame * _ab, const fpp_t _frames, const float _master_gain ) { +#if LMMS_HAVE_SNDFILE_H + if( depth() == 32 ) + { + float * buf = new float[_frames*channels()]; + for( fpp_t frame = 0; frame < _frames; ++frame ) + { + for( ch_cnt_t chnl = 0; chnl < channels(); ++chnl ) + { + buf[frame*channels()+chnl] = _ab[frame][chnl] * + _master_gain; + } + } + sf_writef_float( m_sf, buf, _frames ); + delete[] buf; + } + else + { + int_sample_t * buf = new int_sample_t[_frames * channels()]; + convertToS16( _ab, _frames, _master_gain, buf, + !isLittleEndian() ); + + sf_writef_short( m_sf, buf, _frames ); + delete[] buf; + } +#else + int bytes = 0; int_sample_t * outbuf = new int_sample_t[_frames * channels()]; Uint32 bytes = convertToS16( _ab, _frames, _master_gain, outbuf, !isLittleEndian() ); writeData( outbuf, bytes ); - delete[] outbuf; m_bytesWritten += bytes; +#endif } @@ -110,14 +152,18 @@ void audioFileWave::writeBuffer( const surroundSampleFrame * _ab, void audioFileWave::finishEncoding( void ) { +#if LMMS_HAVE_SNDFILE_H + sf_close( m_sf ); +#else seekToBegin(); - m_waveFileHeader.total_bytes = m_bytesWritten+36; - m_waveFileHeader.data_bytes = m_bytesWritten; + m_waveFileHeader.total_bytes = swap32IfBE( m_bytesWritten+36 ); + m_waveFileHeader.data_bytes = swap32IfBE( m_bytesWritten ); // write header again, because total-bytes-field and data-bytes-field // have to be updated... writeData( &m_waveFileHeader, sizeof( m_waveFileHeader ) ); +#endif } diff --git a/src/core/main.cpp b/src/core/main.cpp index 9013c9c42..5b8693e2f 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -102,7 +102,8 @@ int main( int argc, char * * argv ) QString file_to_load, render_out; mixer::qualitySettings qs( mixer::qualitySettings::Mode_HighQuality ); - projectRenderer::outputSettings os( 44100, FALSE, 160 ); + projectRenderer::outputSettings os( 44100, FALSE, 160, + projectRenderer::Depth_16Bit ); projectRenderer::ExportFileFormats eff = projectRenderer::WaveFile; diff --git a/src/core/project_renderer.cpp b/src/core/project_renderer.cpp index f329715b0..6b2cfa02d 100644 --- a/src/core/project_renderer.cpp +++ b/src/core/project_renderer.cpp @@ -79,6 +79,7 @@ projectRenderer::projectRenderer( const mixer::qualitySettings & _qs, _os.samplerate, DEFAULT_CHANNELS, success_ful, _out_file, _os.vbr, _os.bitrate, _os.bitrate - 64, _os.bitrate + 64, + _os.depth == Depth_32Bit ? 32 : 16, engine::getMixer() ); if( success_ful == FALSE ) { diff --git a/src/gui/export_project_dialog.cpp b/src/gui/export_project_dialog.cpp index f46dc77fe..aad90f69a 100644 --- a/src/gui/export_project_dialog.cpp +++ b/src/gui/export_project_dialog.cpp @@ -137,9 +137,11 @@ void exportProjectDialog::startBtnClicked( void ) aliasFreeOscillatorsCB->isChecked() ); projectRenderer::outputSettings os = projectRenderer::outputSettings( - samplerateCB->currentText().section( " ", 0, 0 ).toUInt(), - FALSE, - bitrateCB->currentText().section( " ", 0, 0 ).toUInt() ); + samplerateCB->currentText().section( " ", 0, 0 ).toUInt(), + FALSE, + bitrateCB->currentText().section( " ", 0, 0 ).toUInt(), + static_cast( + depthCB->currentIndex() ) ); m_renderer = new projectRenderer( qs, os, ft, m_fileName ); connect( m_renderer, SIGNAL( progressChanged( int ) ),