From fb733051bd63f9c8ccce3df7860250b2b0694503 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 12 Aug 2009 07:18:16 -0700 Subject: [PATCH] added FLAC as an export format --- CMakeLists.txt | 18 ++++- cmake/modules/FindFLAC.cmake | 31 +++++++ include/audio_file_flac.h | 89 ++++++++++++++++++++ include/project_renderer.h | 1 + lmmsconfig.h.in | 1 + src/core/audio/audio_file_flac.cpp | 125 +++++++++++++++++++++++++++++ src/core/main.cpp | 8 +- src/core/project_renderer.cpp | 12 ++- 8 files changed, 281 insertions(+), 4 deletions(-) create mode 100644 cmake/modules/FindFLAC.cmake create mode 100644 include/audio_file_flac.h create mode 100644 src/core/audio/audio_file_flac.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b072a3804..4fbfa733b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -68,6 +68,7 @@ OPTION(WANT_VST "Include VST support" ON) OPTION(WANT_VST_NOWINE "Include partial VST support (without wine)" OFF) OPTION(WANT_WINMM "Include WinMM MIDI support" OFF) OPTION(WANT_ZIP "Include zip support" ON) +OPTION(WANT_FLAC "Include flac support" ON) IF(LMMS_BUILD_WIN32) SET(WANT_ALSA OFF) @@ -199,6 +200,18 @@ IF(WANT_ZIP) ENDIF(ZIP_FOUND) ENDIF(WANT_ZIP) +# check for libflac++ +IF(WANT_FLAC) + FIND_PACKAGE(FLAC) + IF(FLAC_FOUND) + SET(LMMS_HAVE_FLAC TRUE) + SET(STATUS_FLAC "OK") + ELSE(FLAC_FOUND) + SET(STATUS_FLAC "not found, install libflac++-dev (or similar) " + "if you want flac as an export format") + ENDIF(FLAC_FOUND) +ENDIF(WANT_FLAC) + # check for Stk IF(WANT_STK) FIND_PACKAGE(STK) @@ -528,9 +541,9 @@ ADD_SUBDIRECTORY(data) # ADD_DEFINITIONS(-D'LIB_DIR="${CMAKE_INSTALL_PREFIX}/${LIB_DIR}/"' -D'PLUGIN_DIR="${CMAKE_INSTALL_PREFIX}/${LIB_DIR}/lmms/"' ${PULSEAUDIO_DEFINITIONS} ${PORTAUDIO_DEFINITIONS}) -INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include ${SDL_INCLUDE_DIR} ${PORTAUDIO_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIR} ${JACK_INCLUDE_DIRS} ${OGGVORBIS_INCLUDE_DIR} ${SAMPLERATE_INCLUDE_DIRS} ${SNDFILE_INCLUDE_DIRS} ${ZIP_INCLUDE_DIR}) +INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/include ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/include ${SDL_INCLUDE_DIR} ${PORTAUDIO_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIR} ${JACK_INCLUDE_DIRS} ${OGGVORBIS_INCLUDE_DIR} ${SAMPLERATE_INCLUDE_DIRS} ${SNDFILE_INCLUDE_DIRS} ${ZIP_INCLUDE_DIR} ${FLAC_INCLUDE_DIR}) LINK_DIRECTORIES(${CMAKE_INSTALL_PREFIX}/lib ${ASOUND_LIBRARY_DIR} ${JACK_LIBRARY_DIRS} ${SAMPLERATE_LIBRARY_DIRS} ${SNDFILE_LIBRARY_DIRS}) -LINK_LIBRARIES(${QT_LIBRARIES} ${ASOUND_LIBRARY} ${SDL_LIBRARY} ${PORTAUDIO_LIBRARIES} ${PULSEAUDIO_LIBRARIES} ${JACK_LIBRARIES} ${OGGVORBIS_LIBRARIES} ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${ZIP_LIBRARIES} ${SLV2_LIBRARY}) +LINK_LIBRARIES(${QT_LIBRARIES} ${ASOUND_LIBRARY} ${SDL_LIBRARY} ${PORTAUDIO_LIBRARIES} ${PULSEAUDIO_LIBRARIES} ${JACK_LIBRARIES} ${OGGVORBIS_LIBRARIES} ${SAMPLERATE_LIBRARIES} ${SNDFILE_LIBRARIES} ${ZIP_LIBRARIES} ${FLAC_LIBRARIES} ${SLV2_LIBRARY}) ADD_CUSTOM_COMMAND(OUTPUT ${CMAKE_BINARY_DIR}/lmms.1.gz COMMAND gzip -c ${CMAKE_SOURCE_DIR}/lmms.1 > ${CMAKE_BINARY_DIR}/lmms.1.gz DEPENDS ${CMAKE_SOURCE_DIR}/lmms.1 COMMENT "Generating lmms.1.gz") @@ -698,6 +711,7 @@ MESSAGE( "-----------------------------------------\n" "* WAVE : OK\n" "* OGG/VORBIS : ${STATUS_OGGVORBIS}\n" +"* FLAC : ${STATUS_FLAC}\n" ) MESSAGE( diff --git a/cmake/modules/FindFLAC.cmake b/cmake/modules/FindFLAC.cmake new file mode 100644 index 000000000..4eec8bba9 --- /dev/null +++ b/cmake/modules/FindFLAC.cmake @@ -0,0 +1,31 @@ +# Find libflac++ +# find the native libflac++ includes and library +# +# +# FLAC_INCLUDE_DIRS - where to find the .h files +# FLAC_LIBRARIES - list of libraries when using libflac++ +# FLAC_FOUND - True if libflac++ found. + +FIND_PATH(FLAC_INCLUDE_DIRS all.h /usr/include/FLAC++ /local/usr/include/FLAC++) +FIND_PATH(FLAC_INCLUDE_DIRS decoder.h /usr/include/FLAC++ /local/usr/include/FLAC++) +FIND_PATH(FLAC_INCLUDE_DIRS encoder.h /usr/include/FLAC++ /local/usr/include/FLAC++) +FIND_PATH(FLAC_INCLUDE_DIRS export.h /usr/include/FLAC++ /local/usr/include/FLAC++) +FIND_PATH(FLAC_INCLUDE_DIRS metadata.h /usr/include/FLAC++ /local/usr/include/FLAC++) +FIND_LIBRARY(FLAC_LIBRARIES NAMES FLAC++ PATH /usr/lib /usr/local/lib) + +IF(FLAC_INCLUDE_DIRS AND FLAC_LIBRARIES) + SET(FLAC_FOUND TRUE) +ENDIF(FLAC_INCLUDE_DIRS AND FLAC_LIBRARIES) + +IF(FLAC_FOUND) + IF(NOT FLAC_FIND_QUIETLY) + MESSAGE(STATUS "Found libflac++: ${FLAC_LIBRARIES}") + ENDIF(NOT FLAC_FIND_QUIETLY) +ELSE(FLAC_FOUND) + SET(FLAC_LIBRARIES "") + SET(FLAC_INCLUDE_DIRS "") + MESSAGE(STATUS "Could not find libflac++") +ENDIF(FLAC_FOUND) + +MARK_AS_ADVANCED( FLAC_LIBRARIES FLAC_INCLUDE_DIRS ) + diff --git a/include/audio_file_flac.h b/include/audio_file_flac.h new file mode 100644 index 000000000..9d1af7213 --- /dev/null +++ b/include/audio_file_flac.h @@ -0,0 +1,89 @@ +/* + * audio_file_flac.h - Audio-device which encodes a flac stream and writes it + * into a flac file. This is used for song-export. + * + * Copyright (c) 2009 Andrew Kelley + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program 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. + * + * This program 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 this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef _AUDIO_FILE_FLAC_H_ +#define _AUDIO_FILE_FLAC_H_ + +#include +#include "audio_file_device.h" + +#include "FLAC++/metadata.h" +#include "FLAC++/encoder.h" + +class AudioFileFlac : public audioFileDevice +{ +public: + AudioFileFlac( const sample_rate_t _sample_rate, + const ch_cnt_t _channels, + bool & _success_ful, + const QString & _file, + const bool _use_vbr, + const bitrate_t _nom_bitrate, + const bitrate_t _min_bitrate, + const bitrate_t _max_bitrate, + const int _depth, + mixer * _mixer ); + virtual ~AudioFileFlac(); + + static audioFileDevice * getInst( const sample_rate_t _sample_rate, + const ch_cnt_t _channels, + bool & _success_ful, + const QString & _file, + const bool _use_vbr, + const bitrate_t _nom_bitrate, + const bitrate_t _min_bitrate, + const bitrate_t _max_bitrate, + const int _depth, + mixer * _mixer ) + { + return( new AudioFileFlac( _sample_rate, _channels, + _success_ful, _file, _use_vbr, + _nom_bitrate, _min_bitrate, + _max_bitrate, _depth, + _mixer ) ); + } + + +private: + short int rescale(float sample); // convert float flame to short int frame + + // overloaded functions + virtual void writeBuffer( const surroundSampleFrame * _ab, + const fpp_t _frames, + float _master_gain ); + + bool startEncoding( void ); + void finishEncoding( void ); + + + FLAC::Encoder::File m_encoder; + +} ; + + +#endif //_AUDIO_FILE_FLAC_H_ + +/* vim: set tw=0 noexpandtab: */ diff --git a/include/project_renderer.h b/include/project_renderer.h index f4629c57e..c3f3f9578 100644 --- a/include/project_renderer.h +++ b/include/project_renderer.h @@ -39,6 +39,7 @@ public: WaveFile, OggFile, Mp3File, + FlacFile, NumFileFormats } ; diff --git a/lmmsconfig.h.in b/lmmsconfig.h.in index 4cb296cb7..34864a701 100644 --- a/lmmsconfig.h.in +++ b/lmmsconfig.h.in @@ -19,6 +19,7 @@ #cmakedefine LMMS_HAVE_STK #cmakedefine LMMS_HAVE_VST #cmakedefine LMMS_HAVE_ZIP +#cmakedefine LMMS_HAVE_FLAC #cmakedefine LMMS_HAVE_STDINT_H #cmakedefine LMMS_HAVE_STDBOOL_H diff --git a/src/core/audio/audio_file_flac.cpp b/src/core/audio/audio_file_flac.cpp new file mode 100644 index 000000000..eeb7a8c01 --- /dev/null +++ b/src/core/audio/audio_file_flac.cpp @@ -0,0 +1,125 @@ +/* + * audio_file_flac.cpp - Audio-device which encodes a flac stream and writes it + * into a flac file. This is used for song-export. + * + * Copyright (c) 2009 Andrew Kelley + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * This program 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. + * + * This program 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 this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "audio_file_flac.h" + +#include + +#include "lmms_basics.h" + + + +AudioFileFlac::AudioFileFlac( const sample_rate_t _sample_rate, + const ch_cnt_t _channels, bool & _success_ful, const QString & _file, + const bool _use_vbr, 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, _depth, _mixer ) +{ + _success_ful = startEncoding(); +} + + +bool AudioFileFlac::startEncoding( void ) +{ + // check the encoder + if( ! m_encoder ) + { + qWarning() << "AudioFileFlac: unable to allocate encoder"; + return false; + } + + bool ok = true; + + ok &= m_encoder.set_verify(true); + ok &= m_encoder.set_compression_level(5); + ok &= m_encoder.set_channels(channels()); + ok &= m_encoder.set_bits_per_sample(16); + ok &= m_encoder.set_sample_rate(sampleRate()); + //ok &= encoder.set_total_samples_estimate(??); + + if( ! ok ) return false; + + // TODO: set metadata: http://flac.cvs.sourceforge.net/viewvc/flac/flac/examples/cpp/encode/file/main.cpp?view=markup + + + // initialize encoder + FLAC__StreamEncoderInitStatus init_status = m_encoder.init( + outputFile().toAscii()); + if( init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK ) + { + qWarning() << "AudioFileFlac: unable to initialize encoder:" << + FLAC__StreamEncoderInitStatusString[init_status]; + return false; + } + + return true; + +} + + +Sint16 AudioFileFlac::rescale(float sample) { + return (qMax(qMin(sample, 1), -1) / 1) + * std::numeric_limits::max(); +} + + +// encode data and write to file +void AudioFileFlac::writeBuffer( const surroundSampleFrame * _ab, + const fpp_t _frames, const float _master_gain ) +{ + // scale to short int instead of float + FLAC__int32 * in = new FLAC__int32[_frames*channels()]; + + for(int i=0; i < _frames; ++i) + { + for(int c=0; c < channels(); ++c) + { + in[i*channels()+c] = (FLAC__int32) rescale( _ab[i][c] + * _master_gain ); + } + } + + // feed to the encoder + m_encoder.process_interleaved(in, _frames); + + // clean up + delete[] in; +} + + +void AudioFileFlac::finishEncoding( void ) +{ + m_encoder.finish(); +} + + +AudioFileFlac::~AudioFileFlac() +{ + finishEncoding(); +} + +/* vim: set tw=0 noexpandtab: */ diff --git a/src/core/main.cpp b/src/core/main.cpp index 50c85b506..f62607045 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -160,7 +160,7 @@ int main( int argc, char * * argv ) "-r, --render render given project file\n" "-o, --output render into \n" "-f, --output-format specify format of render-output where\n" - " format is either 'wav', 'ogg', or 'mp3'.\n" + " format is either 'wav', 'ogg', 'mp3', or 'flac'.\n" "-s, --samplerate specify output samplerate in Hz\n" " range: 44100 (default) to 192000\n" "-b, --bitrate specify output bitrate in kHz\n" @@ -229,6 +229,12 @@ int main( int argc, char * * argv ) { eff = projectRenderer::Mp3File; } +#ifdef LMMS_HAVE_FLAC + else if( ext == "flac" ) + { + eff = projectRenderer::FlacFile; + } +#endif else { printf( "\nInvalid output format %s.\n\n" diff --git a/src/core/project_renderer.cpp b/src/core/project_renderer.cpp index a5211c24d..feb6167f9 100644 --- a/src/core/project_renderer.cpp +++ b/src/core/project_renderer.cpp @@ -33,6 +33,7 @@ #include "audio_file_wave.h" #include "audio_file_ogg.h" #include "audio_file_mp3.h" +#include "audio_file_flac.h" #ifdef LMMS_HAVE_PTHREAD_H #include @@ -58,6 +59,15 @@ fileEncodeDevice __fileEncodeDevices[] = { projectRenderer::Mp3File, QT_TRANSLATE_NOOP( "projectRenderer", "MP3 File (*.mp3)" ), ".mp3", &AudioFileMp3::getInst }, + { projectRenderer::FlacFile, + QT_TRANSLATE_NOOP( "projectRenderer", "FLAC File (*.flac)" ), + ".flac", +#ifdef LMMS_HAVE_FLAC + &AudioFileFlac::getInst +#else + NULL +#endif + }, // ... insert your own file-encoder-infos here... may be one day the // user can add own encoders inside the program... @@ -65,7 +75,7 @@ fileEncodeDevice __fileEncodeDevices[] = } ; -const char * projectRenderer::EFF_ext[] = {"wav", "ogg", "mp3"}; +const char * projectRenderer::EFF_ext[] = {"wav", "ogg", "mp3", "flac"}; projectRenderer::projectRenderer( const mixer::qualitySettings & _qs,