AudioFileMp3: provide support for mp3 export

Add MP3 as one of the options to export the project as. Currently
the lib path is hard-coded, need to add that to settings. It loads
lame dynamically.
This commit is contained in:
Andrew Kelley
2009-07-15 04:56:41 -07:00
parent a626763671
commit e53ea570b0
7 changed files with 1726 additions and 6 deletions

6
TODO
View File

@@ -52,6 +52,12 @@
- add FLAC as export-format?
Andrew Kelley's todo:
* when you press down or up while browsing the resource browser, it should
play the sample
* select the output file format based on what you selected in file->save as
* segfault when you export an mp3 and press cancel
- automation recording:
* when you record and there is already an auto clip, it repeats it
* it freezes when you try to do it with the Volume or Panning slider

182
include/audio_file_mp3.h Normal file
View File

@@ -0,0 +1,182 @@
/*
* audio_file_mp3.h - Audio-device which encodes mp3-stream and writes it
* into an mp3-file. This is used for song-export.
*
* Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* 2009 Andrew Kelley <superjoe30@gmail.com>
*
* 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_MP3_H
#define _AUDIO_FILE_MP3_H
#include <QFile>
#include <QLibrary>
#include "lmmsconfig.h"
#include "audio_file_device.h"
#include "lame.h"
#include <sndfile.h>
class AudioFileMp3 : public audioFileDevice
{
public:
AudioFileMp3( 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 ~AudioFileMp3();
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 AudioFileMp3( _sample_rate, _channels,
_success_ful, _file, _use_vbr,
_nom_bitrate, _min_bitrate,
_max_bitrate, _depth,
_mixer ) );
}
private:
// functions we'll be importing from lame
typedef lame_global_flags *lame_init_t(void);
typedef int lame_init_params_t(lame_global_flags*);
typedef const char* get_lame_version_t(void);
typedef int lame_encode_buffer_t (
lame_global_flags* gf,
const short int buffer_l [],
const short int buffer_r [],
const int nsamples,
unsigned char * mp3buf,
const int mp3buf_size );
typedef int lame_encode_buffer_interleaved_t(
lame_global_flags* gf,
short int pcm[],
int num_samples, /* per channel */
unsigned char* mp3buf,
int mp3buf_size );
typedef int lame_encode_flush_t(
lame_global_flags *gf,
unsigned char* mp3buf,
int size );
typedef int lame_close_t(lame_global_flags*);
typedef int lame_set_in_samplerate_t(lame_global_flags*, int);
typedef int lame_set_out_samplerate_t(lame_global_flags*, int);
typedef int lame_set_num_channels_t(lame_global_flags*, int );
typedef int lame_set_quality_t(lame_global_flags*, int);
typedef int lame_set_brate_t(lame_global_flags*, int);
typedef int lame_set_VBR_t(lame_global_flags *, vbr_mode);
typedef int lame_set_VBR_q_t(lame_global_flags *, int);
typedef int lame_set_VBR_min_bitrate_kbps_t(lame_global_flags *, int);
typedef int lame_set_mode_t(lame_global_flags *, MPEG_mode);
typedef int lame_set_preset_t(lame_global_flags *, int);
typedef int lame_set_error_protection_t(lame_global_flags *, int);
typedef int lame_set_disable_reservoir_t(lame_global_flags *, int);
typedef int lame_set_padding_type_t(lame_global_flags *, Padding_type);
typedef int lame_set_bWriteVbrTag_t(lame_global_flags *, int);
typedef size_t lame_get_lametag_frame_t(const lame_global_flags *, unsigned char* buffer, size_t size);
typedef void lame_mp3_tags_fid_t(lame_global_flags *, FILE *);
typedef int lame_set_findReplayGain_t(lame_global_flags *, int);
typedef int lame_set_VBR_quality_t(lame_global_flags *, float);
typedef int lame_set_VBR_mean_bitrate_kbps_t(lame_global_flags *, int);
typedef int lame_set_VBR_max_bitrate_kbps_t(lame_global_flags *, int);
bool initLame(QString libpath);
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 );
bool m_ok; // true if we need to close the handle in finishEncoding
// handle to lame
lame_global_flags *m_lgf;
QLibrary * m_lame; // lame .so file
QFile * m_outfile;
bool m_hq_mode; // true if we want really high quality
/* function pointers to the symbols we get from the library */
lame_init_t* lame_init;
lame_init_params_t* lame_init_params;
lame_encode_buffer_t* lame_encode_buffer;
lame_encode_buffer_interleaved_t* lame_encode_buffer_interleaved;
lame_encode_flush_t* lame_encode_flush;
lame_close_t* lame_close;
get_lame_version_t* get_lame_version;
lame_set_in_samplerate_t* lame_set_in_samplerate;
lame_set_out_samplerate_t* lame_set_out_samplerate;
lame_set_num_channels_t* lame_set_num_channels;
lame_set_quality_t* lame_set_quality;
lame_set_brate_t* lame_set_brate;
lame_set_VBR_t* lame_set_VBR;
lame_set_VBR_q_t* lame_set_VBR_q;
lame_set_VBR_min_bitrate_kbps_t* lame_set_VBR_min_bitrate_kbps;
lame_set_mode_t* lame_set_mode;
lame_set_preset_t* lame_set_preset;
lame_set_error_protection_t* lame_set_error_protection;
lame_set_disable_reservoir_t *lame_set_disable_reservoir;
lame_set_padding_type_t *lame_set_padding_type;
lame_set_bWriteVbrTag_t *lame_set_bWriteVbrTag;
lame_get_lametag_frame_t *lame_get_lametag_frame;
lame_mp3_tags_fid_t *lame_mp3_tags_fid;
lame_set_findReplayGain_t *lame_set_findReplayGain;
lame_set_VBR_quality_t *lame_set_VBR_quality;
lame_set_VBR_mean_bitrate_kbps_t *lame_set_VBR_mean_bitrate_kbps;
lame_set_VBR_max_bitrate_kbps_t *lame_set_VBR_max_bitrate_kbps;
} ;
#endif

1212
include/lame.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -37,9 +37,12 @@ public:
{
WaveFile,
OggFile,
Mp3File,
NumFileFormats
} ;
static const char * EFF_ext[];
enum Depths
{
Depth_16Bit,

View File

@@ -0,0 +1,312 @@
/*
* audo_file_mp3.cpp - audio-device which encodes mp3-stream and writes it
* into an mp3-file. This is used for song-export.
*
* Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
* 2009 Andrew Kelley <superjoe30@gmail.com>
*
* 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 "lame.h"
#include "audio_file_mp3.h"
#include <cstring>
#include <limits>
using namespace std;
AudioFileMp3::AudioFileMp3( 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 ),
m_lgf( NULL ),
m_lame( NULL ),
m_outfile( NULL ),
m_hq_mode( false )
{
// connect to lame
m_ok = initLame("/usr/lib/libmp3lame.so.0");
m_ok = _success_ful = m_ok && startEncoding();
}
bool AudioFileMp3::initLame(QString libpath)
{
// dynamically load the .so file
m_lame = new QLibrary(libpath);
if( ! m_lame->load() ) return false;
// grab the functions and stuff we need
lame_init = (lame_init_t *)
m_lame->resolve("lame_init");
get_lame_version = (get_lame_version_t *)
m_lame->resolve("get_lame_version");
lame_init_params = (lame_init_params_t *)
m_lame->resolve("lame_init_params");
lame_encode_buffer = (lame_encode_buffer_t *)
m_lame->resolve("lame_encode_buffer");
lame_encode_buffer_interleaved = (lame_encode_buffer_interleaved_t *)
m_lame->resolve("lame_encode_buffer_interleaved");
lame_encode_flush = (lame_encode_flush_t *)
m_lame->resolve("lame_encode_flush");
lame_close = (lame_close_t *)
m_lame->resolve("lame_close");
lame_set_in_samplerate = (lame_set_in_samplerate_t *)
m_lame->resolve("lame_set_in_samplerate");
lame_set_out_samplerate = (lame_set_out_samplerate_t *)
m_lame->resolve("lame_set_out_samplerate");
lame_set_num_channels = (lame_set_num_channels_t *)
m_lame->resolve("lame_set_num_channels");
lame_set_quality = (lame_set_quality_t *)
m_lame->resolve("lame_set_quality");
lame_set_brate = (lame_set_brate_t *)
m_lame->resolve("lame_set_brate");
lame_set_VBR = (lame_set_VBR_t *)
m_lame->resolve("lame_set_VBR");
lame_set_VBR_q = (lame_set_VBR_q_t *)
m_lame->resolve("lame_set_VBR_q");
lame_set_VBR_min_bitrate_kbps = (lame_set_VBR_min_bitrate_kbps_t *)
m_lame->resolve("lame_set_VBR_min_bitrate_kbps");
lame_set_mode = (lame_set_mode_t *)
m_lame->resolve("lame_set_mode");
lame_set_preset = (lame_set_preset_t *)
m_lame->resolve("lame_set_preset");
lame_set_error_protection = (lame_set_error_protection_t *)
m_lame->resolve("lame_set_error_protection");
lame_set_disable_reservoir = (lame_set_disable_reservoir_t *)
m_lame->resolve("lame_set_disable_reservoir");
lame_set_padding_type = (lame_set_padding_type_t *)
m_lame->resolve("lame_set_padding_type");
lame_set_bWriteVbrTag = (lame_set_bWriteVbrTag_t *)
m_lame->resolve("lame_set_bWriteVbrTag");
lame_set_findReplayGain = (lame_set_findReplayGain_t *)
m_lame->resolve("lame_set_findReplayGain");
lame_set_VBR_quality = (lame_set_VBR_quality_t *)
m_lame->resolve("lame_set_VBR_quality");
lame_set_VBR_mean_bitrate_kbps = (lame_set_VBR_mean_bitrate_kbps_t *)
m_lame->resolve("lame_set_VBR_mean_bitrate_kbps");
lame_set_VBR_max_bitrate_kbps = (lame_set_VBR_max_bitrate_kbps_t *)
m_lame->resolve("lame_set_VBR_max_bitrate_kbps");
// These are optional
lame_get_lametag_frame = (lame_get_lametag_frame_t *)
m_lame->resolve("lame_get_lametag_frame");
lame_mp3_tags_fid = (lame_mp3_tags_fid_t *)
m_lame->resolve("lame_mp3_tags_fid");
if (!lame_init ||
!get_lame_version ||
!lame_init_params ||
!lame_encode_buffer ||
!lame_encode_buffer_interleaved ||
!lame_encode_flush ||
!lame_close ||
!lame_set_in_samplerate ||
!lame_set_out_samplerate ||
!lame_set_num_channels ||
!lame_set_quality ||
!lame_set_brate ||
!lame_set_VBR ||
!lame_set_VBR_q ||
!lame_set_mode ||
!lame_set_preset ||
!lame_set_error_protection ||
!lame_set_disable_reservoir ||
!lame_set_padding_type ||
!lame_set_bWriteVbrTag)
{
// some symbols are missing
printf("AudioFileMp3: some symbols are missing from the lame library\n");
m_lame->unload();
return false;
}
return true;
}
AudioFileMp3::~AudioFileMp3()
{
finishEncoding();
if( m_lame )
{
if( m_lame->isLoaded() )
m_lame->unload();
delete m_lame;
m_lame = NULL;
}
if( m_outfile )
{
if( m_outfile->isOpen() )
m_outfile->close();
delete m_outfile;
}
}
void AudioFileMp3::finishEncoding( void )
{
if( m_lgf )
{
// flush
int bufSize = 7200;
unsigned char * out = new unsigned char[bufSize];
int rc = lame_encode_flush(m_lgf, out, bufSize);
if( m_outfile && m_outfile->isOpen() ){
m_outfile->write( (const char *) out, rc );
m_outfile->close();
delete m_outfile;
m_outfile = NULL;
}
// cleanup
delete[] out;
// close any open handles we may have
lame_close(m_lgf);
m_lgf = NULL;
}
}
bool AudioFileMp3::startEncoding( void )
{
// open any handles, files, etc
m_lgf = lame_init();
if( m_lgf == NULL ){
printf("AudioFileMp3: Unable to initialize lame\n");
return false;
}
if( channels() > 2 )
printf("I don't think lame can do more than 2 channels\n");
lame_set_in_samplerate(m_lgf, sampleRate() );
lame_set_num_channels(m_lgf, channels() );
if( m_hq_mode )
lame_set_quality(m_lgf, 0); // best, very slow
else
lame_set_quality(m_lgf, 2); // near-best, not too slow
lame_set_mode(m_lgf, STEREO);
lame_set_findReplayGain(m_lgf, 1); // perform ReplayGain analysis
if( useVBR() == 0 )
{
lame_set_VBR(m_lgf, vbr_off);
lame_set_brate(m_lgf, nominalBitrate() );
}
else
{
lame_set_VBR(m_lgf, vbr_abr);
lame_set_VBR_quality(m_lgf, 2);
lame_set_VBR_mean_bitrate_kbps(m_lgf, nominalBitrate() );
lame_set_VBR_min_bitrate_kbps(m_lgf, minBitrate() );
lame_set_VBR_max_bitrate_kbps(m_lgf, maxBitrate() );
}
if( lame_init_params( m_lgf ) < 0 )
return false;
// open the file
m_outfile = new QFile( outputFile() );
if( ! m_outfile->open( QIODevice::WriteOnly ) )
{
printf("AudioFileMp3: unable to open file for output\n");
return false;
}
// write the headers and such
return true;
}
short int AudioFileMp3::rescale(float sample) {
return (sample / 1) * std::numeric_limits<short int>::max();
}
// encode data and write to file
void AudioFileMp3::writeBuffer( const surroundSampleFrame * _ab,
const fpp_t _frames, const float _master_gain )
{
// encode with lame
int bufSize = 1.25 * _frames + 7200;
short int * in = new short int[_frames*2];
unsigned char * out = new unsigned char[bufSize];
// scale to short int instead of float
for(int i=0; i < _frames; ++i)
{
in[i*2] = rescale( _ab[i][0] );
in[i*2+1] = rescale( _ab[i][1] );
}
int rc = lame_encode_buffer_interleaved( m_lgf, in, _frames, out, bufSize);
switch(rc){
case -1:
printf("AudioFileMp3: encode error: buffer too small.\n");
return;
case -2:
printf("AudioFileMp3: encode error: out of memory\n");
return;
case -3:
printf("AudioFileMp3: encode error: lame_init_params not called\n");
return;
case -4:
printf("AudioFileMp3: encode error: psycho acoustic problems\n");
return;
}
// write to file
m_outfile->write( (const char *) out, rc );
// clean up
delete[] out;
delete[] in;
}

View File

@@ -162,7 +162,7 @@ int main( int argc, char * * argv )
"-r, --render <project file> render given project file\n"
"-o, --output <file> render into <file>\n"
"-f, --output-format <format> specify format of render-output where\n"
" format is either 'wav' or 'ogg'.\n"
" format is either 'wav', 'ogg', or 'mp3'.\n"
"-s, --samplerate <samplerate> specify output samplerate in Hz\n"
" range: 44100 (default) to 192000\n"
"-b, --bitrate <bitrate> specify output bitrate in kHz\n"
@@ -227,6 +227,10 @@ int main( int argc, char * * argv )
eff = projectRenderer::OggFile;
}
#endif
else if( ext == "mp3" )
{
eff = projectRenderer::Mp3File;
}
else
{
printf( "\nInvalid output format %s.\n\n"
@@ -491,10 +495,7 @@ int main( int argc, char * * argv )
{
// create renderer
projectRenderer * r = new projectRenderer( qs, os, eff,
render_out +
QString( ( eff ==
projectRenderer::WaveFile ) ?
"wav" : "ogg" ) );
render_out + QString(projectRenderer::EFF_ext[eff]));
QCoreApplication::instance()->connect( r,
SIGNAL( finished() ), SLOT( quit() ) );

View File

@@ -31,6 +31,7 @@
#include "audio_file_wave.h"
#include "audio_file_ogg.h"
#include "audio_file_mp3.h"
#ifdef LMMS_HAVE_PTHREAD_H
#include <pthread.h>
@@ -53,6 +54,9 @@ fileEncodeDevice __fileEncodeDevices[] =
NULL
#endif
},
{ projectRenderer::Mp3File,
QT_TRANSLATE_NOOP( "projectRenderer", "MP3 File (*.mp3)" ),
".mp3", &AudioFileMp3::getInst },
// ... insert your own file-encoder-infos here... may be one day the
// user can add own encoders inside the program...
@@ -60,7 +64,7 @@ fileEncodeDevice __fileEncodeDevices[] =
} ;
const char * projectRenderer::EFF_ext[] = {"wav", "ogg", "mp3"};
projectRenderer::projectRenderer( const mixer::qualitySettings & _qs,