Implement MP3 encoding support

Implement MP3 encoding support
This commit is contained in:
Michael Gregorius
2017-06-12 19:57:08 +02:00
committed by Tres Finocchiaro
parent c53dd31064
commit c2f26a76d4
17 changed files with 402 additions and 44 deletions

View File

@@ -75,6 +75,10 @@ IF(NOT ("${OGGVORBIS_INCLUDE_DIR}" STREQUAL ""))
INCLUDE_DIRECTORIES("${OGGVORBIS_INCLUDE_DIR}")
ENDIF()
IF(NOT ("${LAME_INCLUDE_DIRS}" STREQUAL ""))
INCLUDE_DIRECTORIES("${LAME_INCLUDE_DIRS}")
ENDIF()
# Use libraries in non-standard directories (e.g., another version of Qt)
IF(LMMS_BUILD_LINUX)
LINK_LIBRARIES(-Wl,--enable-new-dtags)
@@ -139,6 +143,7 @@ SET(LMMS_REQUIRED_LIBS
${PULSEAUDIO_LIBRARIES}
${JACK_LIBRARIES}
${OGGVORBIS_LIBRARIES}
${LAME_LIBRARIES}
${SAMPLERATE_LIBRARIES}
${SNDFILE_LIBRARIES}
${EXTRA_LIBRARIES}
@@ -194,6 +199,7 @@ IF(LMMS_BUILD_WIN32)
"${MINGW_PREFIX}/bin/libvorbisfile-3.dll"
"${MINGW_PREFIX}/bin/libjpeg-9.dll"
"${MINGW_PREFIX}/bin/libogg-0.dll"
"${MINGW_PREFIX}/bin/libmp3lame-0.dll"
"${MINGW_PREFIX}/bin/libfftw3f-3.dll"
"${MINGW_PREFIX}/bin/libFLAC-8.dll"
"${MINGW_PREFIX}/bin/libpng16-16.dll"

View File

@@ -69,6 +69,7 @@ set(LMMS_SRCS
core/audio/AudioAlsa.cpp
core/audio/AudioDevice.cpp
core/audio/AudioFileDevice.cpp
core/audio/AudioFileMP3.cpp
core/audio/AudioFileOgg.cpp
core/audio/AudioFileWave.cpp
core/audio/AudioJack.cpp

View File

@@ -30,6 +30,7 @@
#include "AudioFileWave.h"
#include "AudioFileOgg.h"
#include "AudioFileMP3.h"
#ifdef LMMS_HAVE_SCHED_H
#include "sched.h"
@@ -48,6 +49,15 @@ const ProjectRenderer::FileEncodeDevice ProjectRenderer::fileEncodeDevices[] =
&AudioFileOgg::getInst
#else
NULL
#endif
},
{ ProjectRenderer::MP3File,
QT_TRANSLATE_NOOP( "ProjectRenderer", "Compressed MP3-File (*.mp3)" ),
".mp3",
#ifdef LMMS_HAVE_MP3LAME
&AudioFileMP3::getInst
#else
NULL
#endif
},
// ... insert your own file-encoder-infos here... may be one day the

View File

@@ -1316,7 +1316,8 @@ void Song::exportProject( bool multiExport )
efd.setFileMode( FileDialog::AnyFile );
int idx = 0;
QStringList types;
while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats )
while( ProjectRenderer::fileEncodeDevices[idx].m_fileFormat != ProjectRenderer::NumFileFormats &&
ProjectRenderer::fileEncodeDevices[idx].isAvailable())
{
types << tr( ProjectRenderer::fileEncodeDevices[idx].m_description );
++idx;

View File

@@ -0,0 +1,133 @@
/*
* AudioFileMP3.cpp - Audio-device which encodes a wave stream into
* an MP3 file. This is used for song export.
*
* Copyright (c) 2017 to present Michael Gregorius <michael.gregorius.git/at/arcor[dot]de>
*
* This file is part of LMMS - https://lmms.io
*
* 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 "AudioFileMP3.h"
#ifdef LMMS_HAVE_MP3LAME
#include "Mixer.h"
#include <cassert>
AudioFileMP3::AudioFileMP3( OutputSettings const & outputSettings,
const ch_cnt_t channels,
bool & successful,
const QString & file,
Mixer* mixer ) :
AudioFileDevice( outputSettings, channels, file, mixer )
{
successful = true;
// For now only accept stereo sources
successful &= channels == 2;
successful &= initEncoder();
successful &= outputFileOpened();
}
AudioFileMP3::~AudioFileMP3()
{
flushRemainingBuffers();
tearDownEncoder();
}
void AudioFileMP3::writeBuffer( const surroundSampleFrame * _buf,
const fpp_t _frames,
const float _master_gain )
{
if (_frames < 1)
{
return;
}
// TODO Why isn't the gain applied by the driver but inside the device?
std::vector<float> interleavedDataBuffer(_frames * 2);
for (fpp_t i = 0; i < _frames; ++i)
{
interleavedDataBuffer[2*i] = _buf[i][0] * _master_gain;
interleavedDataBuffer[2*i + 1] = _buf[i][1] * _master_gain;
}
size_t minimumBufferSize = 1.25 * _frames + 7200;
std::vector<unsigned char> encodingBuffer(minimumBufferSize);
int bytesWritten = lame_encode_buffer_interleaved_ieee_float(m_lame, &interleavedDataBuffer[0], _frames, &encodingBuffer[0], static_cast<int>(encodingBuffer.size()));
assert (bytesWritten >= 0);
writeData(&encodingBuffer[0], bytesWritten);
}
void AudioFileMP3::flushRemainingBuffers()
{
// The documentation states that flush should have at least 7200 bytes. So let's be generous.
std::vector<unsigned char> encodingBuffer(7200 * 4);
int bytesWritten = lame_encode_flush(m_lame, &encodingBuffer[0], static_cast<int>(encodingBuffer.size()));
assert (bytesWritten >= 0);
writeData(&encodingBuffer[0], bytesWritten);
}
MPEG_mode mapToMPEG_mode(OutputSettings::StereoMode stereoMode)
{
switch (stereoMode)
{
case OutputSettings::StereoMode_Stereo:
return STEREO;
case OutputSettings::StereoMode_JointStereo:
return JOINT_STEREO;
case OutputSettings::StereoMode_Mono:
return MONO;
default:
return NOT_SET;
}
}
bool AudioFileMP3::initEncoder()
{
m_lame = lame_init();
// Handle stereo/joint/mono settings
OutputSettings::StereoMode stereoMode = getOutputSettings().getStereoMode();
lame_set_mode(m_lame, mapToMPEG_mode(stereoMode));
// Handle bit rate settings
OutputSettings::BitRateSettings bitRateSettings = getOutputSettings().getBitRateSettings();
int bitRate = static_cast<int>(bitRateSettings.getBitRate());
lame_set_VBR(m_lame, vbr_off);
lame_set_brate(m_lame, bitRate);
// Add a comment
id3tag_set_comment(m_lame, "Created with LMMS");
return lame_init_params(m_lame) != -1;
}
void AudioFileMP3::tearDownEncoder()
{
lame_close(m_lame);
}
#endif

View File

@@ -58,7 +58,7 @@ ExportProjectDialog::ExportProjectDialog( const QString & _file_name,
int cbIndex = 0;
for( int i = 0; i < ProjectRenderer::NumFileFormats; ++i )
{
if( ProjectRenderer::fileEncodeDevices[i].m_getDevInst != NULL )
if( ProjectRenderer::fileEncodeDevices[i].isAvailable() )
{
// get the extension of this format
QString renderExt = ProjectRenderer::fileEncodeDevices[i].m_extension;
@@ -128,7 +128,20 @@ void ExportProjectDialog::closeEvent( QCloseEvent * _ce )
}
OutputSettings::StereoMode mapToStereoMode(int index)
{
switch (index)
{
case 0:
return OutputSettings::StereoMode_Stereo;
case 1:
return OutputSettings::StereoMode_JointStereo;
case 2:
return OutputSettings::StereoMode_Mono;
default:
return OutputSettings::StereoMode_Stereo;
}
}
void ExportProjectDialog::startExport()
{
@@ -146,7 +159,8 @@ void ExportProjectDialog::startExport()
OutputSettings os = OutputSettings(
samplerates[ samplerateCB->currentIndex() ],
bitRateSettings,
static_cast<OutputSettings::BitDepth>( depthCB->currentIndex() ) );
static_cast<OutputSettings::BitDepth>( depthCB->currentIndex() ),
mapToStereoMode(stereoModeComboBox->currentIndex()) );
m_renderManager = new RenderManager( qs, os, m_ft, m_fileName );
@@ -181,6 +195,8 @@ ProjectRenderer::ExportFileFormats convertIndexToExportFileFormat(int index)
return ProjectRenderer::WaveFile;
case 1:
return ProjectRenderer::OggFile;
case 2:
return ProjectRenderer::MP3File;
default:
Q_ASSERT(false);
break;
@@ -195,10 +211,23 @@ void ExportProjectDialog::onFileFormatChanged(int index)
ProjectRenderer::ExportFileFormats exportFormat =
convertIndexToExportFileFormat(index);
bool bitRateControlsEnabled = exportFormat == ProjectRenderer::OggFile;
bool stereoModeVisible = exportFormat == ProjectRenderer::MP3File;
bool sampleRateControlsVisible = exportFormat != ProjectRenderer::MP3File;
bool bitRateControlsEnabled =
(exportFormat == ProjectRenderer::OggFile ||
exportFormat == ProjectRenderer::MP3File);
bool bitDepthControlEnabled = exportFormat == ProjectRenderer::WaveFile;
bool variableBitrateVisible = exportFormat != ProjectRenderer::MP3File;
stereoModeWidget->setVisible(stereoModeVisible);
sampleRateWidget->setVisible(sampleRateControlsVisible);
bitrateWidget->setVisible(bitRateControlsEnabled);
checkBoxVariableBitRate->setVisible(variableBitrateVisible);
depthWidget->setVisible(bitDepthControlEnabled);
}

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>715</width>
<height>447</height>
<height>491</height>
</rect>
</property>
<property name="minimumSize">
@@ -45,39 +45,48 @@
<widget class="QComboBox" name="fileFormatCB"/>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Samplerate:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="samplerateCB">
<item>
<property name="text">
<string>44100 Hz</string>
<widget class="QWidget" name="sampleRateWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout">
<property name="margin">
<number>0</number>
</property>
</item>
<item>
<property name="text">
<string>48000 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>88200 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>96000 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>192000 Hz</string>
</property>
</item>
<item>
<widget class="QLabel" name="labelSampleRate">
<property name="text">
<string>Samplerate:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="samplerateCB">
<item>
<property name="text">
<string>44100 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>48000 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>88200 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>96000 Hz</string>
</property>
</item>
<item>
<property name="text">
<string>192000 Hz</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
@@ -124,6 +133,44 @@
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="stereoModeWidget" native="true">
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="margin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="labelStereoMode_3">
<property name="text">
<string>Stereo mode:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="stereoModeComboBox">
<property name="currentIndex">
<number>1</number>
</property>
<item>
<property name="text">
<string>Stereo</string>
</property>
</item>
<item>
<property name="text">
<string>Joint Stereo</string>
</property>
</item>
<item>
<property name="text">
<string>Mono</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="bitrateWidget" native="true">
<layout class="QVBoxLayout">

View File

@@ -11,6 +11,7 @@
#cmakedefine LMMS_HAVE_ALSA
#cmakedefine LMMS_HAVE_FLUIDSYNTH
#cmakedefine LMMS_HAVE_JACK
#cmakedefine LMMS_HAVE_MP3LAME
#cmakedefine LMMS_HAVE_OGGVORBIS
#cmakedefine LMMS_HAVE_OSS
#cmakedefine LMMS_HAVE_SNDIO