Rivendellaudio/lib/rdaudioconvert.cpp
Fred Gleason 929ab22613 2020-09-29 Fred Gleason <fredg@paravelsystems.com>
* Added the name of the Rivendell library component to 'Unknown Error'
	messages.

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
2020-09-29 20:46:07 -04:00

2092 lines
53 KiB
C++

// rdaudioconvert.cpp
//
// Convert Audio File Formats
//
// (C) Copyright 2010-2019 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <stdint.h>
#include <stdlib.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <math.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#include <rdapplication.h>
#include <rdaudioconvert.h>
#include <rdcart.h>
#include <rdconf.h>
#include <rd.h>
#include <rdtempdirectory.h>
#include <sndfile.h>
#include <samplerate.h>
#include <soundtouch/SoundTouch.h>
#ifdef HAVE_VORBIS
#include <ogg/ogg.h>
#include <vorbis/vorbisenc.h>
#endif // HAVE_VORBIS
#ifdef HAVE_FLAC
#include <FLAC++/encoder.h>
#include <rdflacdecode.h>
#endif // HAVE_FLAC
#ifdef HAVE_MP4_LIBS
#include <mp4v2/mp4v2.h>
#include <neaacdec.h>
#endif // HAVE_MP4_LIBS
//#include <id3/tag.h>
#include <id3v2tag.h>
#include <textidentificationframe.h>
#include <mpegfile.h>
#include <qfile.h>
#define STAGE2_XFER_SIZE 2048
#define STAGE2_BUFFER_SIZE 49152
RDAudioConvert::RDAudioConvert(QObject *parent)
: QObject(parent)
{
conv_start_point=-1;
conv_end_point=-1;
conv_speed_ratio=1.0;
conv_peak_sample=0.0;
conv_settings=NULL;
conv_src_wavedata=new RDWaveData();
conv_dst_wavedata=NULL;
conv_src_converter=rda->libraryConf()->srcConverter();
conv_transcoding_delay=rda->config()->transcodingDelay();
//
// Load MPEG Libraries
//
conv_mad_handle=dlopen("libmad.so.0",RTLD_LAZY);
conv_lame_handle=dlopen("libmp3lame.so.0",RTLD_LAZY);
conv_twolame_handle=dlopen("libtwolame.so.0",RTLD_LAZY);
}
RDAudioConvert::~RDAudioConvert()
{
delete conv_src_wavedata;
}
void RDAudioConvert::setSourceFile(const QString &filename)
{
conv_src_filename=filename;
}
void RDAudioConvert::setDestinationFile(const QString &filename)
{
conv_dst_filename=filename;
}
void RDAudioConvert::setDestinationSettings(RDSettings *settings)
{
conv_settings=settings;
}
RDWaveData *RDAudioConvert::sourceWaveData() const
{
return conv_src_wavedata;
}
QString RDAudioConvert::sourceRdxl() const
{
return conv_src_rdxl;
}
void RDAudioConvert::setDestinationWaveData(RDWaveData *wavedata)
{
conv_dst_wavedata=wavedata;
}
void RDAudioConvert::setDestinationRdxl(const QString &xml)
{
conv_dst_rdxl=xml;
}
void RDAudioConvert::setRange(int start_pt,int end_pt)
{
conv_start_point=start_pt;
conv_end_point=end_pt;
}
void RDAudioConvert::setSpeedRatio(float ratio)
{
conv_speed_ratio=ratio;
}
RDAudioConvert::ErrorCode RDAudioConvert::convert()
{
RDAudioConvert::ErrorCode err;
QString tmpfile1;
QString tmpfile2;
RDTempDirectory *temp_dir=NULL;
//
// Make sure we're all set to go...
//
if(conv_settings==NULL) {
return RDAudioConvert::ErrorInvalidSettings;
}
if(!RDAudioConvert::settingsValid(conv_settings)) {
return RDAudioConvert::ErrorInvalidSettings;
}
struct stat stats;
memset(&stats,0,sizeof(stats));
if(stat((const char *)conv_src_filename.toUtf8(),&stats)!=0) {
return RDAudioConvert::ErrorNoSource;
}
if(conv_dst_filename.isEmpty()) {
return RDAudioConvert::ErrorNoDestination;
}
if((conv_speed_ratio<RD_TIMESCALE_MIN)||(conv_speed_ratio>RD_TIMESCALE_MAX)) {
return RDAudioConvert::ErrorInvalidSpeed;
}
//
// Generate Temporary Filenames
//
temp_dir=new RDTempDirectory("rdaudioconvert");
QString err_msg;
if(!temp_dir->create(&err_msg)) {
delete temp_dir;
rda->syslog(LOG_WARNING,"Could not create %s",
(const char *)err_msg.toUtf8());
return RDAudioConvert::ErrorInternal;
}
tmpfile1=QString(temp_dir->path())+"/signed32_1.wav";
tmpfile2=QString(temp_dir->path())+"/signed32_2.wav";
//
// Stage One -- Convert Source Format to Signed 32 Bit Integer
//
if((err=Stage1Convert(conv_src_filename,tmpfile1))!=
RDAudioConvert::ErrorOk) {
delete temp_dir;
return err;
}
//
// Stage Two -- Convert Levels, Sample Rate, Channelization, Speed
//
if((err=Stage2Convert(tmpfile1,tmpfile2))!=
RDAudioConvert::ErrorOk) {
delete temp_dir;
return err;
}
//
// Stage Three -- Write Out Destination Format
//
if((err=Stage3Convert(tmpfile2,conv_dst_filename))!=
RDAudioConvert::ErrorOk) {
delete temp_dir;
return err;
}
//
// Clean Up
//
delete temp_dir;
return RDAudioConvert::ErrorOk;
}
bool RDAudioConvert::settingsValid(RDSettings *settings)
{
return true;
}
QString RDAudioConvert::errorText(RDAudioConvert::ErrorCode err)
{
QString ret=QString().sprintf("Unknown RDAudioConvert Error [%u]",err);
switch(err) {
case RDAudioConvert::ErrorOk:
ret=tr("OK");
break;
case RDAudioConvert::ErrorInvalidSettings:
ret=tr("Invalid/Unsupported Settings");
break;
case RDAudioConvert::ErrorNoSource:
ret=tr("Unable to access source file");
break;
case RDAudioConvert::ErrorNoDestination:
ret=tr("Unable to create destination file");
break;
case RDAudioConvert::ErrorInvalidSource:
ret=tr("Unrecognized source format");
break;
case RDAudioConvert::ErrorInternal:
ret=tr("Internal Error");
break;
case RDAudioConvert::ErrorFormatNotSupported:
ret=tr("Unsupported Format");
break;
case RDAudioConvert::ErrorNoDisc:
ret=tr("No CD found in drive");
break;
case RDAudioConvert::ErrorNoTrack:
ret=tr("No such track on CD");
break;
case RDAudioConvert::ErrorInvalidSpeed:
ret=tr("Invalid speed ratio");
break;
case RDAudioConvert::ErrorFormatError:
ret=tr("Source format error");
break;
case RDAudioConvert::ErrorNoSpace:
ret=tr("No space left on device");
break;
}
return ret;
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage1Convert(const QString &srcfile,
const QString &dstfile)
{
SNDFILE *sf_src=NULL;
SF_INFO sf_src_info;
RDWaveFile *wave=NULL;
RDAudioConvert::ErrorCode err=RDAudioConvert::ErrorInvalidSource;
//
// Try RDWaveFile
//
wave=new RDWaveFile(srcfile);
if(wave->openWave(conv_src_wavedata)) {
switch(wave->type()) {
case RDWaveFile::Wave:
if(wave->getFormatTag()==WAVE_FORMAT_MPEG) {
err=Stage1Mpeg(dstfile,wave);
delete wave;
return err;
}
break;
case RDWaveFile::Mpeg:
case RDWaveFile::Atx:
case RDWaveFile::Tmc:
case RDWaveFile::Ambos:
err=Stage1Mpeg(dstfile,wave);
delete wave;
return err;
case RDWaveFile::Ogg:
err=Stage1Vorbis(dstfile,wave);
delete wave;
return err;
case RDWaveFile::Flac:
err=Stage1Flac(dstfile,wave);
delete wave;
return err;
case RDWaveFile::M4A:
err=Stage1M4A(dstfile,wave);
delete wave;
return err;
case RDWaveFile::Aiff:
case RDWaveFile::Unknown:
break;
}
}
delete wave;
//
// Try Libsndfile
//
memset(&sf_src_info,0,sizeof(sf_src_info));
if((sf_src=sf_open(srcfile.toUtf8(),SFM_READ,&sf_src_info))!=NULL) {
err=Stage1SndFile(dstfile,sf_src,&sf_src_info);
sf_close(sf_src);
return RDAudioConvert::ErrorOk;
}
return err;
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage1Flac(const QString &dstfile,
RDWaveFile *wave)
{
#ifdef HAVE_FLAC
SNDFILE *sf_dst=NULL;
SF_INFO sf_dst_info;
RDFlacDecode *flac=NULL;
//
// Open Destination
//
memset(&sf_dst_info,0,sizeof(sf_dst_info));
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
sf_dst_info.channels=wave->getChannels();
sf_dst_info.samplerate=wave->getSamplesPerSec();
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Decode
//
flac=new RDFlacDecode(sf_dst);
flac->setRange(conv_start_point,conv_end_point);
flac->decode(wave,&conv_peak_sample);
//
// Clean Up
//
delete flac;
sf_close(sf_dst);
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_FLAC
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage1Vorbis(const QString &dstfile,
RDWaveFile *wave)
{
#ifdef HAVE_VORBIS
SNDFILE *sf_dst=NULL;
SF_INFO sf_dst_info;
ogg_sync_state ogg_sync;
ogg_stream_state ogg_stream;
ogg_packet ogg_packet;
ogg_page ogg_page;
vorbis_info vorbis_info;
vorbis_comment vorbis_comment;
vorbis_dsp_state vorbis_dsp;
vorbis_block vorbis_block;
int fd;
ssize_t n;
long serialno=-1;
bool vorbis_ready=false;
int frames;
float **pcm;
float pcmbuf[32768];
sf_count_t start=0;
sf_count_t end=wave->getSampleLength();
sf_count_t total_frames=0;
//
// Open Destination
//
memset(&sf_dst_info,0,sizeof(sf_dst_info));
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
sf_dst_info.channels=wave->getChannels();
sf_dst_info.samplerate=wave->getSamplesPerSec();
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Initialize Decoder
//
if((fd=open(wave->getName().toUtf8(),O_RDONLY))<0) {
sf_close(sf_dst);
return RDAudioConvert::ErrorNoSource;
}
ogg_sync_init(&ogg_sync);
vorbis_info_init(&vorbis_info);
vorbis_comment_init(&vorbis_comment);
//
// Decode
//
if(conv_start_point>0) {
start=(double)conv_start_point*(double)wave->getSamplesPerSec()/1000.0;
}
if(conv_end_point>=0) {
end=(double)conv_end_point*(double)wave->getSamplesPerSec()/1000.0;
}
while((n=read(fd,ogg_sync_buffer(&ogg_sync,4096),4096))>0) {
ogg_sync_wrote(&ogg_sync,n);
while(ogg_sync_pageout(&ogg_sync,&ogg_page)==1) {
if(serialno<0) {
serialno=ogg_page_serialno(&ogg_page);
ogg_stream_init(&ogg_stream,serialno);
}
if(ogg_stream_pagein(&ogg_stream,&ogg_page)==0) {
while(ogg_stream_packetout(&ogg_stream,&ogg_packet)==1) {
switch(ogg_packet.packetno) {
case 0: // Start Packet
case 1: // Comment Packet
vorbis_synthesis_headerin(&vorbis_info,&vorbis_comment,&ogg_packet);
break;
case 2: // Codebook Packet
vorbis_synthesis_headerin(&vorbis_info,&vorbis_comment,&ogg_packet);
vorbis_synthesis_init(&vorbis_dsp,&vorbis_info);
vorbis_block_init(&vorbis_dsp,&vorbis_block);
vorbis_ready=true;
break;
default: // Audio Packets
if(vorbis_synthesis(&vorbis_block,&ogg_packet)==0) {
vorbis_synthesis_blockin(&vorbis_dsp,&vorbis_block);
}
while((frames=vorbis_synthesis_pcmout(&vorbis_dsp,&pcm))>0) {
for(int i=0;i<frames;i++) {
for(int j=0;j<wave->getChannels();j++) {
pcmbuf[wave->getChannels()*i+j]=pcm[j][i];
}
}
if(total_frames>=start) {
if((total_frames+frames)<end) { // Write entire buffer
UpdatePeak(pcmbuf,frames*wave->getChannels());
sf_writef_float(sf_dst,pcmbuf,frames);
}
else {
if(total_frames<(total_frames+frames)) { // Write start of buffer
UpdatePeak(pcmbuf,
(total_frames+frames-end)*wave->getChannels());
sf_writef_float(sf_dst,pcmbuf,total_frames+frames-end);
//
// Done -- no need to decode the rest
//
if(vorbis_ready) {
vorbis_block_clear(&vorbis_block);
vorbis_dsp_clear(&vorbis_dsp);
}
vorbis_info_clear(&vorbis_info);
vorbis_comment_clear(&vorbis_comment);
ogg_stream_clear(&ogg_stream);
ogg_sync_clear(&ogg_sync);
::close(fd);
sf_close(sf_dst);
return RDAudioConvert::ErrorOk;
}
}
}
else {
int diff=total_frames+frames-start;
if(diff>0) { // Write end of buffer
UpdatePeak(pcmbuf+diff,(frames-diff)*wave->getChannels());
sf_writef_float(sf_dst,pcmbuf+diff,frames-diff);
}
}
total_frames+=frames;
vorbis_synthesis_read(&vorbis_dsp,frames);
}
break;
}
}
}
}
}
//
// Clean Up
//
if(vorbis_ready) {
vorbis_block_clear(&vorbis_block);
vorbis_dsp_clear(&vorbis_dsp);
}
vorbis_info_clear(&vorbis_info);
vorbis_comment_clear(&vorbis_comment);
ogg_stream_clear(&ogg_stream);
ogg_sync_clear(&ogg_sync);
::close(fd);
sf_close(sf_dst);
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_VORBIS
}
#define STAGE1BUFSIZE 16384
RDAudioConvert::ErrorCode RDAudioConvert::Stage1Mpeg(const QString &dstfile,
RDWaveFile *wave)
{
#ifdef HAVE_MAD
SNDFILE *sf_dst=NULL;
SF_INFO sf_dst_info;
struct mad_stream mad_stream;
struct mad_frame mad_frame;
struct mad_synth mad_synth;
int left_over=0;
int fsize;
int n;
unsigned char buffer[STAGE1BUFSIZE];
float sf_buffer[1152*2];
sf_count_t start=0;
int64_t end=-1;
sf_count_t frames=0;
//
// Load MAD
//
if(!LoadMad()) {
return RDAudioConvert::ErrorFormatNotSupported;
}
//
// Open Destination
//
memset(&sf_dst_info,0,sizeof(sf_dst_info));
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
sf_dst_info.channels=wave->getChannels();
sf_dst_info.samplerate=wave->getSamplesPerSec();
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
return RDAudioConvert::ErrorNoDestination;
}
sf_command(sf_dst,SFC_SET_NORM_DOUBLE,NULL,SF_FALSE);
//
// Initialize Decoder
//
mad_stream_init(&mad_stream);
mad_frame_init(&mad_frame);
mad_synth_init(&mad_synth);
fsize=144*wave->getHeadBitRate()/wave->getSamplesPerSec();
//
// Decode
//
if(conv_start_point>0) {
start=(double)conv_start_point*(double)wave->getSamplesPerSec()/1000.0;
}
if(conv_end_point>=0) {
end=(double)conv_end_point*(double)wave->getSamplesPerSec()/1000.0;
}
while((n=wave->readWave(buffer+left_over,fsize))>0) {
if((buffer[left_over]==0xff)&&(buffer[2+left_over]&0x02)!=0) {
n+=wave->readWave(buffer+left_over+n,1); // Padding slot
}
mad_stream_buffer(&mad_stream,buffer,n+left_over);
//printf("mad err: %d\n",mad_stream.error);
while(1) {
int thiserr=mad_frame_decode(&mad_frame,&mad_stream);
if(thiserr!=0) {
if(!MAD_RECOVERABLE(mad_stream.error)) {
break;
}
else {
continue;
}
}
mad_synth_frame(&mad_synth,&mad_frame);
for(int i=0;i<mad_synth.pcm.length;i++) {
for(int j=0;j<mad_synth.pcm.channels;j++) {
sf_buffer[i*mad_synth.pcm.channels+j]=
(float)mad_f_todouble(mad_synth.pcm.samples[j][i]);
}
}
if(frames>=start) {
if((end<0)||((frames+mad_synth.pcm.length)<end)) { // Write full buffer
UpdatePeak(sf_buffer,mad_synth.pcm.length*wave->getChannels());
sf_writef_float(sf_dst,sf_buffer,mad_synth.pcm.length);
}
else {
if(frames<(frames+mad_synth.pcm.length)) { // Write start of buffer
UpdatePeak(sf_buffer,
(frames+mad_synth.pcm.length-end)*wave->getChannels());
sf_writef_float(sf_dst,sf_buffer,frames+mad_synth.pcm.length-end);
//
// Done -- no need to decode the rest
//
mad_synth_finish(&mad_synth);
mad_frame_finish(&mad_frame);
mad_stream_finish(&mad_stream);
wave->closeWave();
sf_close(sf_dst);
return RDAudioConvert::ErrorOk;
}
}
}
else {
int diff=frames+mad_synth.pcm.length-start;
if(diff>0) { // Write end of buffer
UpdatePeak(sf_buffer+diff,
(mad_synth.pcm.length-diff)*wave->getChannels());
sf_writef_float(sf_dst,sf_buffer+diff,mad_synth.pcm.length-diff);
}
}
frames+=mad_synth.pcm.length;
}
left_over=mad_stream.bufend-mad_stream.next_frame;
// Prevent buffer overflow on malformed files.
// The amount checked for should match the maximum amount that may be read
// by the next top-of-loop wave->readWave call.
if(left_over + fsize + 1 > STAGE1BUFSIZE) {
return RDAudioConvert::ErrorFormatError;
}
memmove(buffer,mad_stream.next_frame,left_over);
usleep(conv_transcoding_delay);
}
memset(buffer+left_over,0,MAD_BUFFER_GUARD);
mad_stream_buffer(&mad_stream,buffer,MAD_BUFFER_GUARD+left_over);
if(mad_frame_decode(&mad_frame,&mad_stream)==0) {
mad_synth_frame(&mad_synth,&mad_frame);
for(int i=0;i<mad_synth.pcm.length;i++) {
for(int j=0;j<mad_synth.pcm.channels;j++) {
sf_buffer[i*mad_synth.pcm.channels+j]=
(float)mad_f_todouble(mad_synth.pcm.samples[j][i]);
}
}
UpdatePeak(sf_buffer,mad_synth.pcm.length*wave->getChannels());
sf_writef_float(sf_dst,sf_buffer,mad_synth.pcm.length);
}
//
// Clean Up
//
mad_synth_finish(&mad_synth);
mad_frame_finish(&mad_frame);
mad_stream_finish(&mad_stream);
wave->closeWave();
sf_close(sf_dst);
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_MAD
}
// Based on libfaad's frontend/main.c, but using libmp4v2 for MP4 access.
RDAudioConvert::ErrorCode RDAudioConvert::Stage1M4A(const QString &dstfile,
RDWaveFile *wave)
{
#ifdef HAVE_MP4_LIBS
SNDFILE *sf_dst=NULL;
SF_INFO sf_dst_info;
MP4FileHandle f;
MP4TrackId audioTrack;
MP4SampleId firstSample, lastSample;
uint32_t aacBufSize, aacConfigSize;
uint8_t *aacBuf, *aacConfigBuffer;
NeAACDecHandle hDecoder;
NeAACDecConfigurationPtr config;
unsigned long foundSampleRate;
unsigned char foundChannels;
RDAudioConvert::ErrorCode ret = RDAudioConvert::ErrorOk;
if(!dlmp4.load()) {
return RDAudioConvert::ErrorFormatNotSupported;
}
//
// Open source
//
f = dlmp4.MP4Read(wave->getName().toUtf8());
if(f == MP4_INVALID_FILE_HANDLE)
return RDAudioConvert::ErrorNoSource;
audioTrack = dlmp4.getMP4AACTrack(f);
firstSample = 1;
lastSample = dlmp4.MP4GetTrackNumberOfSamples(f, audioTrack);
if(conv_start_point > 0) {
double startsecs = ((double)conv_start_point) / 1000;
MP4Timestamp startts = (MP4Timestamp)(startsecs * wave->getSamplesPerSec());
firstSample = dlmp4.MP4GetSampleIdFromTime(f, audioTrack, startts, /*need_sync=*/false);
if(firstSample == MP4_INVALID_SAMPLE_ID) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4;
}
}
if(conv_end_point > 0) {
double stopsecs = ((double)conv_end_point) / 1000;
MP4Timestamp stopts = (MP4Timestamp)(stopsecs * wave->getSamplesPerSec());
lastSample = dlmp4.MP4GetSampleIdFromTime(f, audioTrack, stopts, /*need_sync=*/false);
if(lastSample == MP4_INVALID_SAMPLE_ID) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4;
}
}
aacBufSize = dlmp4.MP4GetTrackMaxSampleSize(f, audioTrack);
aacBuf = (uint8_t*)malloc(aacBufSize);
if(!aacBufSize || !aacBuf) {
// Probably the source's fault for specifying a massive buffer.
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4;
}
dlmp4.MP4GetTrackESConfiguration(f, audioTrack, &aacConfigBuffer, &aacConfigSize);
if(!aacConfigBuffer) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_mp4_buf;
}
//
// Open Destination
//
memset(&sf_dst_info,0,sizeof(sf_dst_info));
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
sf_dst_info.channels=wave->getChannels();
sf_dst_info.samplerate=wave->getSamplesPerSec();
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
ret = RDAudioConvert::ErrorNoDestination;
goto out_mp4_configbuf;
}
sf_command(sf_dst,SFC_SET_NORM_DOUBLE,NULL,SF_FALSE);
//
// Initialize Decoder
//
hDecoder = dlmp4.NeAACDecOpen();
config = dlmp4.NeAACDecGetCurrentConfiguration(hDecoder);
config->outputFormat = FAAD_FMT_FLOAT;
config->downMatrix = 1; // Downmix >2 channels to stereo.
if(!dlmp4.NeAACDecSetConfiguration(hDecoder, config)) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_decoder;
}
if(dlmp4.NeAACDecInit2(hDecoder, aacConfigBuffer, aacConfigSize, &foundSampleRate, &foundChannels) < 0) {
ret = RDAudioConvert::ErrorInvalidSource;
goto out_decoder;
}
if(foundSampleRate != wave->getSamplesPerSec() || foundChannels != wave->getChannels()) {
fprintf(stderr, "M4A header information inconsistent with actual file? Header: %u/%u; file: %lu/%u\n",
wave->getSamplesPerSec(), (unsigned)wave->getChannels(), foundSampleRate, (unsigned)foundChannels);
ret = RDAudioConvert::ErrorInvalidSource;
goto out_decoder;
}
//
// Decode
//
for(MP4SampleId i = firstSample; i <= lastSample; ++i) {
uint32_t aacBytes = aacBufSize;
if(!dlmp4.MP4ReadSample(f, audioTrack, i, &aacBuf, &aacBytes, 0, 0, 0, 0)) {
ret = RDAudioConvert::ErrorInvalidSource;
break;
}
NeAACDecFrameInfo frameInfo;
// The library docs are not clear about the lifetime or cleanup of sample_buffer.
// I hope it lives until the next NeAACDecDecode call, and is cleaned up by NeAACDecClose
void* sample_buffer = dlmp4.NeAACDecDecode(hDecoder, &frameInfo, aacBuf, aacBytes);
if(!sample_buffer) {
ret = RDAudioConvert::ErrorInvalidSource;
break;
}
UpdatePeak((const float*)sample_buffer, frameInfo.samples);
if(sf_write_float(sf_dst, (const float*)sample_buffer, frameInfo.samples) != (sf_count_t)frameInfo.samples) {
rda->syslog(LOG_WARNING,"%s",sf_strerror(sf_dst));
ret = RDAudioConvert::ErrorInternal;
break;
}
}
//
// Cleanup
//
out_decoder:
dlmp4.NeAACDecClose(hDecoder);
// out_sf:
sf_close(sf_dst);
out_mp4_configbuf:
free(aacConfigBuffer);
out_mp4_buf:
free(aacBuf);
out_mp4:
dlmp4.MP4Close(f, 0);
return ret;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage1SndFile(const QString &dstfile,
SNDFILE *sf_src,
SF_INFO *sf_src_info)
{
SNDFILE *sf_dst=NULL;
SF_INFO sf_dst_info;
sf_count_t start=0;
sf_count_t end=sf_src_info->frames;
//
// Open Destination
//
sf_dst_info=*sf_src_info;
sf_dst_info.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
if((sf_dst=sf_open(dstfile,SFM_WRITE,&sf_dst_info))==NULL) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Transfer Data
//
sf_count_t buffer_size=2048/sf_src_info->channels;
float *buffer=new float[2048];
sf_count_t n=0;
if(conv_start_point>0) {
start=sf_seek(sf_src,(double)conv_start_point*
(double)sf_src_info->samplerate/1000.0,SEEK_SET);
}
if(conv_end_point>=0) {
end=(double)conv_end_point*(double)sf_src_info->samplerate/1000.0;
}
while((n=sf_readf_float(sf_src,buffer,buffer_size))>0) {
UpdatePeak(buffer,n*sf_src_info->channels);
sf_writef_float(sf_dst,buffer,n);
start+=n;
if((end-start)<buffer_size) {
buffer_size=end-start;
}
usleep(conv_transcoding_delay);
}
delete buffer;
sf_close(sf_dst);
return RDAudioConvert::ErrorOk;
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage2Convert(const QString &srcfile,
const QString &dstfile)
{
soundtouch::SoundTouch *st_conv=NULL;
SNDFILE *src_sf=NULL;
SNDFILE *dst_sf=NULL;
SF_INFO src_info;
SF_INFO dst_info;
SRC_STATE *src_state=NULL;
SRC_DATA src_data;
float *pcm[3]={NULL,NULL,NULL};
bool free_pcm[3]={false,false,false};
int err;
sf_count_t n;
float ratio=1.0;
//
// Open Files
//
memset(&src_info,0,sizeof(src_info));
if((src_sf=sf_open(srcfile,SFM_READ,&src_info))==NULL) {
rda->syslog(LOG_WARNING,"Could not open %s",(const char *)srcfile.toUtf8());
return RDAudioConvert::ErrorInternal;
}
sf_command(src_sf,SFC_SET_NORM_FLOAT,NULL,SF_FALSE);
sf_command(dst_sf,SFC_SET_CLIPPING,NULL,SF_TRUE);
memset(&dst_info,0,sizeof(dst_info));
dst_info.format=SF_FORMAT_WAV|SF_FORMAT_PCM_32;
dst_info.channels=conv_settings->channels();
dst_info.samplerate=conv_settings->sampleRate();
if((dst_sf=sf_open(dstfile,SFM_WRITE,&dst_info))==NULL) {
sf_close(src_sf);
rda->syslog(LOG_WARNING,"Could not open %s",(const char *)dstfile.toUtf8());
return RDAudioConvert::ErrorInternal;
}
//
// Allocate Buffers
//
pcm[0]=new float[STAGE2_BUFFER_SIZE];
free_pcm[0]=true;
if(dst_info.samplerate!=src_info.samplerate) {
pcm[1]=new float[STAGE2_BUFFER_SIZE];
free_pcm[1]=true;
if(dst_info.channels!=src_info.channels) {
pcm[2]=new float[STAGE2_BUFFER_SIZE];
free_pcm[2]=true;
}
else {
pcm[2]=pcm[1];
}
}
else {
pcm[1]=pcm[0];
if(dst_info.channels!=src_info.channels) {
pcm[2]=new float[STAGE2_BUFFER_SIZE];
free_pcm[2]=true;
}
else {
pcm[2]=pcm[0];
}
}
//
// Initialize Rate Converter
//
if(dst_info.samplerate!=src_info.samplerate) {
if((src_state=src_new(conv_src_converter,src_info.channels,&err))==NULL) {
sf_close(src_sf);
sf_close(dst_sf);
rda->syslog(LOG_WARNING,"%s",src_strerror(err));
return RDAudioConvert::ErrorInternal;
}
memset(&src_data,0,sizeof(src_data));
src_data.src_ratio=(double)dst_info.samplerate/(double)src_info.samplerate;
src_data.data_in=pcm[0];
src_data.data_out=pcm[1];
src_data.output_frames=STAGE2_XFER_SIZE*dst_info.samplerate/
src_info.samplerate+src_info.channels;
}
//
// Initialize Speed Converter
//
if(conv_speed_ratio!=1.0) {
st_conv=new soundtouch::SoundTouch();
st_conv->setTempo(conv_speed_ratio);
st_conv->setSampleRate(dst_info.samplerate);
st_conv->setChannels(dst_info.channels);
}
//
// Calculate Gain Ratio
//
if(conv_settings->normalizationLevel()!=0) {
float gain=
(float)conv_settings->normalizationLevel()-20.0*log10f(conv_peak_sample);
ratio=exp10f(gain/20.0);
}
//
// Convert
//
while((n=sf_readf_float(src_sf,pcm[0],STAGE2_XFER_SIZE))>0) {
//
// Levels
//
if(ratio!=1.0) {
for(unsigned i=0;i<(n*src_info.channels);i++) {
pcm[0][i]=ratio*pcm[0][i];
}
}
//
// Sample Rate
//
if(src_state!=NULL) {
src_data.input_frames=n;
if((err=src_process(src_state,&src_data))!=0) {
fprintf(stderr,"SRC Error: %s\n",src_strerror(err));
rda->syslog(LOG_WARNING,"%s",src_strerror(err));
return RDAudioConvert::ErrorInternal;
}
n=src_data.output_frames_gen;
}
//
// Channelization
//
switch(src_info.channels) {
case 1:
switch(dst_info.channels) {
case 1: // Nothing to do
break;
case 2:
for(unsigned i=0;i<n;i++) {
pcm[2][2*i]=pcm[1][i];
pcm[2][2*i+1]=pcm[1][i];
}
break;
}
break;
case 2:
switch(dst_info.channels) {
case 1:
for(unsigned i=0;i<n;i++) {
pcm[2][i]=(pcm[1][2*i]+pcm[1][2*i+1])/2;
}
break;
case 2: // Nothing to do
break;
}
break;
}
//
// Speed
//
if(st_conv!=NULL) {
st_conv->putSamples((soundtouch::SAMPLETYPE *)pcm[2],n);
n=st_conv->receiveSamples((soundtouch::SAMPLETYPE *)pcm[2],STAGE2_BUFFER_SIZE/dst_info.channels);
}
//
// Write Output
//
if(sf_writef_float(dst_sf,pcm[2],n)!=n) {
for(unsigned i=0;i<3;i++) {
if(free_pcm[i]) {
delete pcm[i];
}
}
if(src_state!=NULL) {
src_delete(src_state);
}
sf_close(src_sf);
sf_close(dst_sf);
return RDAudioConvert::ErrorNoSpace;
}
usleep(conv_transcoding_delay);
}
//
// Finish Up Speed Conversion
//
if(st_conv!=NULL) {
st_conv->flush();
while((n=st_conv->
receiveSamples((soundtouch::SAMPLETYPE *)pcm[2],
STAGE2_BUFFER_SIZE/dst_info.channels))>0) {
if(sf_writef_float(dst_sf,pcm[2],n)!=n) {
for(unsigned i=0;i<3;i++) {
if(free_pcm[i]) {
delete pcm[i];
}
}
if(src_state!=NULL) {
src_delete(src_state);
}
sf_close(src_sf);
sf_close(dst_sf);
return RDAudioConvert::ErrorNoSpace;
}
usleep(conv_transcoding_delay);
}
delete st_conv;
}
//
// Clean Up
//
for(unsigned i=0;i<3;i++) {
if(free_pcm[i]) {
delete pcm[i];
}
}
if(src_state!=NULL) {
src_delete(src_state);
}
sf_close(src_sf);
sf_close(dst_sf);
return RDAudioConvert::ErrorOk;
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Convert(const QString &srcfile,
const QString &dstfile)
{
SNDFILE *src_sf=NULL;
SF_INFO src_sf_info;
RDAudioConvert::ErrorCode ret;
//
// Open Source File
//
if((src_sf=sf_open(srcfile,SFM_READ,&src_sf_info))==NULL) {
rda->syslog(LOG_WARNING,"%s",sf_strerror(NULL));
return RDAudioConvert::ErrorInternal;
}
switch(conv_settings->format()) {
case RDSettings::Pcm16:
ret=Stage3Pcm16(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::Pcm24:
ret=Stage3Pcm24(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::MpegL2:
ret=Stage3Layer2(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::MpegL2Wav:
ret=Stage3Layer2Wav(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::MpegL3:
ret=Stage3Layer3(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::Flac:
ret=Stage3Flac(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::OggVorbis:
ret=Stage3Vorbis(src_sf,&src_sf_info,dstfile);
break;
case RDSettings::MpegL1:
default:
ret=RDAudioConvert::ErrorInvalidSettings;
}
sf_close(src_sf);
return ret;
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Flac(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
#ifdef HAVE_FLAC
sf_count_t n;
int32_t *pcm;
//
// Initialize Encoder
//
FLAC::Encoder::File *flac=new FLAC::Encoder::File();
flac->set_channels(src_sf_info->channels);
flac->set_bits_per_sample(16); // FIXME: Should vary by input file
flac->set_sample_rate(src_sf_info->samplerate);
//flac->set_compression_level(8);
flac->set_blocksize(0);
unlink(dstfile);
/*
* FLAC <1.2.x
*
flac->set_filename(dstfile.ascii());
switch(flac->init()) {
case 0:
break;
default:
delete flac;
rda->log(RDConfig::LogWarning,QString("flac->init() failure"));
return RDAudioConvert::ErrorInternal;
}
*/
/*
* FLAC 1.2.x
*/
switch(flac->init(dstfile.ascii())) {
case FLAC__STREAM_ENCODER_INIT_STATUS_OK:
break;
case FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_NUMBER_OF_CHANNELS:
case FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_BITS_PER_SAMPLE:
case FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_SAMPLE_RATE:
delete flac;
return RDAudioConvert::ErrorInvalidSettings;
case FLAC__STREAM_ENCODER_INIT_STATUS_ENCODER_ERROR:
case FLAC__STREAM_ENCODER_INIT_STATUS_UNSUPPORTED_CONTAINER:
case FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_MAX_LPC_ORDER:
case FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_QLP_COEFF_PRECISION:
case FLAC__STREAM_ENCODER_INIT_STATUS_BLOCK_SIZE_TOO_SMALL_FOR_LPC_ORDER:
case FLAC__STREAM_ENCODER_INIT_STATUS_NOT_STREAMABLE:
case FLAC__STREAM_ENCODER_INIT_STATUS_INVALID_METADATA:
default:
delete flac;
rda->syslog(LOG_WARNING,"flac->init() failure");
return RDAudioConvert::ErrorInternal;
}
pcm=new int32_t[2048*src_sf_info->channels];
//
// Encode
//
while((n=sf_readf_int(src_sf,pcm,2048))>0) {
for(unsigned i=0;i<(n*src_sf_info->channels);i++) {
pcm[i]=pcm[i]>>16;
}
flac->process_interleaved(pcm,n);
}
flac->finish();
//
// Clean Up
//
delete pcm;
delete flac;
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_FLAC
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Vorbis(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
#ifdef HAVE_VORBIS
ogg_stream_state ogg_stream;
ogg_page ogg_page;
ogg_packet header;
ogg_packet comment;
ogg_packet codebook;
ogg_packet ogg_packet;
vorbis_info vorbis_info;
vorbis_comment vorbis_comment;
vorbis_dsp_state vorbis_dsp;
vorbis_block vorbis_block;
float *pcm=NULL;
float **vorbis;
sf_count_t n;
int dst_fd=-1;
//
// Open Destination File
//
unlink(dstfile);
if((dst_fd=open(dstfile,O_WRONLY|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))<0) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Initialize the Encoder
//
vorbis_info_init(&vorbis_info);
switch(vorbis_encode_init_vbr(&vorbis_info,src_sf_info->channels,
src_sf_info->samplerate,
conv_settings->quality())) {
case OV_EFAULT:
default:
rda->syslog(LOG_WARNING,"vorbis_encode_init_vbr() failure");
return RDAudioConvert::ErrorInternal;
case OV_EINVAL:
case OV_EIMPL:
return RDAudioConvert::ErrorInvalidSettings;
case 0:
break;
}
vorbis_comment_init(&vorbis_comment);
// Metadata stuff goes here...
vorbis_analysis_init(&vorbis_dsp,&vorbis_info);
vorbis_block_init(&vorbis_dsp,&vorbis_block);
vorbis_analysis_headerout(&vorbis_dsp,&vorbis_comment,
&header,&comment,&codebook);
ogg_stream_init(&ogg_stream,rand());
ogg_stream_packetin(&ogg_stream,&header);
ogg_stream_packetin(&ogg_stream,&comment);
ogg_stream_packetin(&ogg_stream,&codebook);
pcm=new float[2048*src_sf_info->channels];
//
// Encode
//
while((n=sf_readf_float(src_sf,pcm,2048))>0) {
vorbis=vorbis_analysis_buffer(&vorbis_dsp,n);
for(unsigned i=0;i<n;i++) {
for(int j=0;j<src_sf_info->channels;j++) {
vorbis[j][i]=pcm[src_sf_info->channels*i+j];
}
}
vorbis_analysis_wrote(&vorbis_dsp,n);
while(vorbis_analysis_blockout(&vorbis_dsp,&vorbis_block)>0) {
vorbis_analysis(&vorbis_block,&ogg_packet);
ogg_stream_packetin(&ogg_stream,&ogg_packet);
while(ogg_stream_pageout(&ogg_stream,&ogg_page)!=0) {
if(write(dst_fd,ogg_page.header,ogg_page.header_len)!=
ogg_page.header_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
if(write(dst_fd,ogg_page.body,ogg_page.body_len)!=
ogg_page.body_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
}
}
while(ogg_stream_flush(&ogg_stream,&ogg_page)!=0) {
if(write(dst_fd,ogg_page.header,ogg_page.header_len)!=
ogg_page.header_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
}
if(write(dst_fd,ogg_page.body,ogg_page.body_len)!=
ogg_page.body_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
}
vorbis=vorbis_analysis_buffer(&vorbis_dsp,0);
vorbis_analysis_wrote(&vorbis_dsp,0);
while(vorbis_analysis_blockout(&vorbis_dsp,&vorbis_block)>0) {
vorbis_analysis(&vorbis_block,&ogg_packet);
ogg_stream_packetin(&ogg_stream,&ogg_packet);
while(ogg_stream_pageout(&ogg_stream,&ogg_page)!=0) {
if(write(dst_fd,ogg_page.header,ogg_page.header_len)!=
ogg_page.header_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
if(write(dst_fd,ogg_page.body,ogg_page.body_len)!=
ogg_page.body_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
}
}
while(ogg_stream_flush(&ogg_stream,&ogg_page)!=0) {
if(write(dst_fd,ogg_page.header,ogg_page.header_len)!=
ogg_page.header_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
if(write(dst_fd,ogg_page.body,ogg_page.body_len)!=
ogg_page.body_len) {
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorNoSpace;
}
}
//
// Clean Up
//
::close(dst_fd);
delete pcm;
ogg_stream_clear(&ogg_stream);
vorbis_comment_clear(&vorbis_comment);
vorbis_info_clear(&vorbis_info);
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_VORBIS
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Layer3(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
#ifdef HAVE_LAME
MPEG_mode mpeg_mode=STEREO;
lame_global_flags *lameopts=NULL;
int dst_fd=-1;
int16_t pcm[2304];
unsigned char mpeg[2048];
sf_count_t n;
sf_count_t s;
//
// Load LAME
//
if(!LoadLame()) {
return RDAudioConvert::ErrorFormatNotSupported;
}
//
// Determine MPEG Mode
//
switch(src_sf_info->channels) {
case 1:
mpeg_mode=MONO;
break;
case 2:
mpeg_mode=STEREO;
break;
default:
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Open Destination File
//
unlink(dstfile);
if((dst_fd=open(dstfile,O_WRONLY|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))<0) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Initialize Encoder
//
if((lameopts=lame_init())==NULL) {
lame_close(lameopts);
::close(dst_fd);
rda->syslog(LOG_WARNING,"lame_init() failure");
return RDAudioConvert::ErrorInternal;
}
lame_set_mode(lameopts,mpeg_mode);
lame_set_num_channels(lameopts,src_sf_info->channels);
lame_set_in_samplerate(lameopts,src_sf_info->samplerate);
lame_set_out_samplerate(lameopts,src_sf_info->samplerate);
lame_set_brate(lameopts,conv_settings->bitRate()/1000);
lame_set_bWriteVbrTag(lameopts,0);
if(lame_init_params(lameopts)!=0) {
lame_close(lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Encode
//
if(src_sf_info->channels==2) {
while((n=sf_readf_short(src_sf,pcm,1152))>0) {
if((s=lame_encode_buffer_interleaved(lameopts,pcm,n,mpeg,2048))>=0) {
if(write(dst_fd,mpeg,s)!=s) {
lame_close(lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorNoSpace;
}
}
usleep(conv_transcoding_delay);
}
}
else {
while((n=sf_readf_short(src_sf,pcm,1152))>0) {
if((s=lame_encode_buffer(lameopts,pcm,NULL,n,mpeg,2048))>=0) {
if(write(dst_fd,mpeg,s)!=s) {
lame_close(lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorNoSpace;
}
usleep(conv_transcoding_delay);
}
}
}
if((s=lame_encode_flush(lameopts,mpeg,2048))>=0) {
if(write(dst_fd,mpeg,s)!=s) {
lame_close(lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorNoSpace;
}
}
//
// Clean Up
//
lame_close(lameopts);
::close(dst_fd);
//
// Apply Metadata
//
if(conv_dst_wavedata!=NULL) {
ApplyId3Tag(dstfile,conv_dst_wavedata);
}
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_LAME
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Layer2Wav(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
#ifdef HAVE_TWOLAME
sf_count_t n;
ssize_t s;
RDWaveFile *wave=NULL;
TWOLAME_MPEG_mode mpeg_mode=TWOLAME_STEREO;
twolame_options *lameopts=NULL;
float pcm[2304];
unsigned char mpeg[2048];
//
// Load TwoLAME
//
if(!LoadTwoLame()) {
return RDAudioConvert::ErrorFormatNotSupported;
}
//
// Determine MPEG Mode
//
switch(src_sf_info->channels) {
case 1:
mpeg_mode=TWOLAME_MONO;
break;
case 2:
mpeg_mode=TWOLAME_STEREO;
break;
default:
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Open Destination File
//
wave=new RDWaveFile(dstfile);
wave->setFormatTag(WAVE_FORMAT_MPEG);
wave->setChannels(src_sf_info->channels);
switch(src_sf_info->channels) {
case 1:
wave->setHeadMode(ACM_MPEG_SINGLECHANNEL);
break;
case 2:
wave->setHeadMode(ACM_MPEG_STEREO);
break;
}
wave->setSamplesPerSec(src_sf_info->samplerate);
wave->setHeadLayer(2);
wave->setHeadBitRate(conv_settings->bitRate());
wave->setBextChunk(true);
wave->setMextChunk(true);
wave->setCartChunk(conv_dst_wavedata!=NULL);
wave->setLevlChunk(true);
wave->setRdxlContents(conv_dst_rdxl);
unlink(dstfile);
if(!wave->createWave(conv_dst_wavedata,conv_start_point)) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Initialize Encoder
//
if((lameopts=twolame_init())==NULL) {
wave->closeWave();
rda->syslog(LOG_WARNING,"twolame_init() failure");
return RDAudioConvert::ErrorInternal;
}
twolame_set_mode(lameopts,mpeg_mode);
twolame_set_num_channels(lameopts,src_sf_info->channels);
twolame_set_in_samplerate(lameopts,src_sf_info->samplerate);
twolame_set_out_samplerate(lameopts,src_sf_info->samplerate);
twolame_set_bitrate(lameopts,conv_settings->bitRate()/1000);
twolame_set_energy_levels(lameopts,1);
if(twolame_init_params(lameopts)!=0) {
twolame_close(&lameopts);
wave->closeWave();
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Encode
//
while((n=sf_readf_float(src_sf,pcm,1152))>0) {
if((s=twolame_encode_buffer_float32_interleaved(lameopts,
pcm,n,mpeg,2048))>=0) {
if(wave->writeWave(mpeg,s)!=s) {
twolame_close(&lameopts);
wave->closeWave(src_sf_info->frames);
return RDAudioConvert::ErrorNoSpace;
}
}
else {
fprintf(stderr,"TwoLAME encode error\n");
}
usleep(conv_transcoding_delay);
}
if((s=twolame_encode_flush(lameopts,mpeg,2048))>=0) {
if(wave->writeWave(mpeg,s)!=s) {
twolame_close(&lameopts);
wave->closeWave(src_sf_info->frames);
return RDAudioConvert::ErrorNoSpace;
}
}
else {
fprintf(stderr,"TwoLAME encode error\n");
}
//
// Clean Up
//
twolame_close(&lameopts);
wave->closeWave(src_sf_info->frames);
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_TWOLAME
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Layer2(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
#ifdef HAVE_TWOLAME
sf_count_t n;
ssize_t s;
int dst_fd=-1;
TWOLAME_MPEG_mode mpeg_mode=TWOLAME_STEREO;
twolame_options *lameopts=NULL;
float pcm[2304];
unsigned char mpeg[2048];
if(!LoadTwoLame()) {
return RDAudioConvert::ErrorFormatNotSupported;
}
if((conv_settings->bitRate()>192000)&&(src_sf_info->channels<2)) {
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Determine MPEG Mode
//
switch(src_sf_info->channels) {
case 1:
mpeg_mode=TWOLAME_MONO;
break;
case 2:
mpeg_mode=TWOLAME_STEREO;
break;
default:
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Open Destination File
//
unlink(dstfile);
if((dst_fd=open(dstfile,O_WRONLY|O_CREAT|O_TRUNC,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))<0) {
return RDAudioConvert::ErrorNoDestination;
}
//
// Initialize Encoder
//
if((lameopts=twolame_init())==NULL) {
::close(dst_fd);
rda->syslog(LOG_WARNING,"twolame_init() failure");
return RDAudioConvert::ErrorInternal;
}
twolame_set_mode(lameopts,mpeg_mode);
twolame_set_num_channels(lameopts,src_sf_info->channels);
twolame_set_in_samplerate(lameopts,src_sf_info->samplerate);
twolame_set_out_samplerate(lameopts,src_sf_info->samplerate);
twolame_set_bitrate(lameopts,conv_settings->bitRate()/1000);
if(twolame_init_params(lameopts)!=0) {
twolame_close(&lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorInvalidSettings;
}
//
// Encode
//
while((n=sf_readf_float(src_sf,pcm,1152))>0) {
if((s=twolame_encode_buffer_float32_interleaved(lameopts,
pcm,n,mpeg,2048))>=0) {
if(write(dst_fd,mpeg,s)!=s) {
twolame_close(&lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorNoSpace;
}
}
else {
fprintf(stderr,"TwoLAME encode error\n");
}
usleep(conv_transcoding_delay);
}
if((s=twolame_encode_flush(lameopts,mpeg,2048))>=0) {
if(write(dst_fd,mpeg,s)!=s) {
twolame_close(&lameopts);
::close(dst_fd);
return RDAudioConvert::ErrorNoSpace;
}
}
else {
fprintf(stderr,"TwoLAME encode error\n");
}
//
// Clean Up
//
twolame_close(&lameopts);
::close(dst_fd);
//
// Apply Metadata
//
if(conv_dst_wavedata!=NULL) {
ApplyId3Tag(dstfile,conv_dst_wavedata);
}
return RDAudioConvert::ErrorOk;
#else
return RDAudioConvert::ErrorFormatNotSupported;
#endif // HAVE_TWOLAME
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Pcm16(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
short *sf_buffer=NULL;
ssize_t n;
RDWaveFile *wave=new RDWaveFile(dstfile);
wave->setFormatTag(WAVE_FORMAT_PCM);
wave->setChannels(src_sf_info->channels);
wave->setSamplesPerSec(src_sf_info->samplerate);
wave->setBitsPerSample(16);
wave->setBextChunk(true);
wave->setCartChunk(conv_dst_wavedata!=NULL);
wave->setRdxlContents(conv_dst_rdxl);
if((conv_dst_wavedata!=NULL)&&(conv_settings->normalizationLevel()!=0)) {
wave->setCartLevelRef(32768*
exp10((double)conv_settings->normalizationLevel()/20.0));
}
wave->setLevlChunk(true);
sf_buffer=new int16_t[2048*src_sf_info->channels];
unlink(dstfile);
if(!wave->createWave(conv_dst_wavedata,conv_start_point)) {
return RDAudioConvert::ErrorNoDestination;
}
while((n=sf_readf_short(src_sf,sf_buffer,2048))>0) {
if((unsigned)wave->
writeWave(sf_buffer,n*sizeof(short)*src_sf_info->channels)!=
(n*sizeof(short)*src_sf_info->channels)) {
delete sf_buffer;
wave->closeWave();
delete wave;
return RDAudioConvert::ErrorNoSpace;
}
usleep(conv_transcoding_delay);
}
delete sf_buffer;
wave->closeWave();
delete wave;
return RDAudioConvert::ErrorOk;
}
RDAudioConvert::ErrorCode RDAudioConvert::Stage3Pcm24(SNDFILE *src_sf,
SF_INFO *src_sf_info,
const QString &dstfile)
{
int *sf_buffer=NULL;
uint8_t *pcm24=NULL;
ssize_t n;
RDWaveFile *wave=new RDWaveFile(dstfile);
wave->setFormatTag(WAVE_FORMAT_PCM);
wave->setChannels(src_sf_info->channels);
wave->setSamplesPerSec(src_sf_info->samplerate);
wave->setBitsPerSample(24);
wave->setBextChunk(true);
wave->setCartChunk(conv_dst_wavedata!=NULL);
wave->setRdxlContents(conv_dst_rdxl);
if((conv_dst_wavedata!=NULL)&&(conv_settings->normalizationLevel()!=0)) {
wave->setCartLevelRef(32768*
exp10((double)conv_settings->normalizationLevel()/20.0));
}
wave->setLevlChunk(true);
sf_buffer=new int[2048*src_sf_info->channels];
pcm24=new uint8_t[2048*src_sf_info->channels*sizeof(int)];
unlink(dstfile);
if(!wave->createWave(conv_dst_wavedata,conv_start_point)) {
return RDAudioConvert::ErrorNoDestination;
}
while((n=sf_readf_int(src_sf,sf_buffer,2048))>0) {
for(ssize_t i=0;i<(n*src_sf_info->channels);i++) {
pcm24[3*i]=0xFF&(sf_buffer[i]>>8);
pcm24[3*i+1]=0xFF&(sf_buffer[i]>>16);
pcm24[3*i+2]=0xFF&(sf_buffer[i]>>24);
}
if((unsigned)wave->writeWave(pcm24,n*3*src_sf_info->channels)!=
(n*3*src_sf_info->channels)) {
delete sf_buffer;
delete pcm24;
wave->closeWave();
delete wave;
return RDAudioConvert::ErrorNoSpace;
}
usleep(conv_transcoding_delay);
}
delete sf_buffer;
delete pcm24;
wave->closeWave();
delete wave;
return RDAudioConvert::ErrorOk;
}
void RDAudioConvert::ApplyId3Tag(const QString &filename,RDWaveData *wavedata)
{
TagLib::MPEG::File *file=new TagLib::MPEG::File(filename.toUtf8(),false);
TagLib::PropertyMap *map=new TagLib::PropertyMap();
TagLib::ID3v2::Tag *tag=file->ID3v2Tag();
AddId3Property(map,"TITLE",wavedata->title());
if(!wavedata->artist().isEmpty()) {
AddId3Property(map,"ARTIST",wavedata->artist());
}
if(!wavedata->album().isEmpty()) {
AddId3Property(map,"ALBUM",wavedata->album());
}
if(!wavedata->label().isEmpty()) {
AddId3Property(map,"LABEL",wavedata->label());
}
if(!wavedata->conductor().isEmpty()) {
AddId3Property(map,"CONDUCTOR",wavedata->conductor());
}
if(!wavedata->composer().isEmpty()) {
AddId3Property(map,"COMPOSER",wavedata->composer());
}
if(!wavedata->publisher().isEmpty()) {
AddId3Property(map,"PUBLISHER",wavedata->publisher());
}
if(!wavedata->copyrightNotice().isEmpty()) {
AddId3Property(map,"COPYRIGHT",wavedata->copyrightNotice());
}
if(!wavedata->isrc().isEmpty()) {
AddId3Property(map,"ISRC",wavedata->isrc());
}
if(wavedata->releaseYear()>0) {
AddId3Property(map,"YEAR",QString().sprintf("%d",wavedata->releaseYear()));
}
if(wavedata->beatsPerMinute()>0) {
AddId3Property(map,"BPM",
QString().sprintf("%d",wavedata->beatsPerMinute()));
}
tag->setProperties(*map);
RDCart *cart=new RDCart(wavedata->cartNumber());
if(cart->exists()) {
QString xml=
cart->xml(true,conv_start_point<0,conv_settings,wavedata->cutNumber());
TagLib::ID3v2::UserTextIdentificationFrame *frame=
new TagLib::ID3v2::UserTextIdentificationFrame(TagLib::String::UTF8);
frame->setDescription("rdxl");
frame->setText(TagLib::String((const char *)xml.toUtf8(),
TagLib::String::UTF8));
tag->addFrame(frame);
}
delete cart;
file->save();
delete map;
delete file;
}
void RDAudioConvert::AddId3Property(TagLib::PropertyMap *map,const QString &key,
const QString &value) const
{
TagLib::StringList args;
args.
append(TagLib::String((const char *)value.toUtf8(),TagLib::String::UTF8));
map->insert((const char *)key.toUtf8(),args);
}
void RDAudioConvert::UpdatePeak(const float data[],ssize_t len)
{
float peak;
for(ssize_t i=0;i<len;i++) {
if((peak=fabsf(data[i]))>conv_peak_sample) {
conv_peak_sample=peak;
}
}
}
void RDAudioConvert::UpdatePeak(const double data[],ssize_t len)
{
float peak;
for(ssize_t i=0;i<len;i++) {
if((peak=(float)fabsf(data[i]))>conv_peak_sample) {
conv_peak_sample=peak;
}
}
}
bool RDAudioConvert::LoadMad()
{
#ifdef HAVE_MAD
if(conv_mad_handle==NULL) {
return false;
}
*(void **)(&mad_stream_init)=
dlsym(conv_mad_handle,"mad_stream_init");
*(void **)(&mad_frame_init)=
dlsym(conv_mad_handle,"mad_frame_init");
*(void **)(&mad_synth_init)=
dlsym(conv_mad_handle,"mad_synth_init");
*(void **)(&mad_stream_buffer)=
dlsym(conv_mad_handle,"mad_stream_buffer");
*(void **)(&mad_frame_decode)=
dlsym(conv_mad_handle,"mad_frame_decode");
*(void **)(&mad_synth_frame)=
dlsym(conv_mad_handle,"mad_synth_frame");
*(void **)(&mad_frame_finish)=
dlsym(conv_mad_handle,"mad_frame_finish");
*(void **)(&mad_stream_finish)=
dlsym(conv_mad_handle,"mad_stream_finish");
return true;
#else
return false;
#endif // HAVE_MAD
}
bool RDAudioConvert::LoadTwoLame()
{
#ifdef HAVE_TWOLAME
if(conv_twolame_handle==NULL) {
return false;
}
*(void **)(&twolame_init)=dlsym(conv_twolame_handle,"twolame_init");
*(void **)(&twolame_set_mode)=dlsym(conv_twolame_handle,"twolame_set_mode");
*(void **)(&twolame_set_num_channels)=
dlsym(conv_twolame_handle,"twolame_set_num_channels");
*(void **)(&twolame_set_in_samplerate)=
dlsym(conv_twolame_handle,"twolame_set_in_samplerate");
*(void **)(&twolame_set_out_samplerate)=
dlsym(conv_twolame_handle,"twolame_set_out_samplerate");
*(void **)(&twolame_set_bitrate)=
dlsym(conv_twolame_handle,"twolame_set_bitrate");
*(void **)(&twolame_init_params)=
dlsym(conv_twolame_handle,"twolame_init_params");
*(void **)(&twolame_close)=dlsym(conv_twolame_handle,"twolame_close");
*(void **)(&twolame_encode_buffer_float32_interleaved)=
dlsym(conv_twolame_handle,"twolame_encode_buffer_float32_interleaved");
*(void **)(&twolame_encode_flush)=
dlsym(conv_twolame_handle,"twolame_encode_flush");
*(void **)(&twolame_set_energy_levels)=
dlsym(conv_twolame_handle,"twolame_set_energy_levels");
return true;
#else
return false;
#endif // HAVE_TWOLAME
}
bool RDAudioConvert::LoadLame()
{
#ifdef HAVE_LAME
if(conv_lame_handle==NULL) {
return false;
}
*(void **)(&lame_init)=dlsym(conv_lame_handle,"lame_init");
*(void **)(&lame_set_mode)=
dlsym(conv_lame_handle,"lame_set_mode");
*(void **)(&lame_set_num_channels)=
dlsym(conv_lame_handle,"lame_set_num_channels");
*(void **)(&lame_set_in_samplerate)=
dlsym(conv_lame_handle,"lame_set_in_samplerate");
*(void **)(&lame_set_out_samplerate)=
dlsym(conv_lame_handle,"lame_set_out_samplerate");
*(void **)(&lame_set_brate)=dlsym(conv_lame_handle,"lame_set_brate");
*(void **)(&lame_init_params)=dlsym(conv_lame_handle,"lame_init_params");
*(void **)(&lame_close)=dlsym(conv_lame_handle,"lame_close");
*(void **)(&lame_encode_buffer_interleaved)=
dlsym(conv_lame_handle,"lame_encode_buffer_interleaved");
*(void **)(&lame_encode_buffer)=
dlsym(conv_lame_handle,"lame_encode_buffer");
*(void **)(&lame_encode_flush)=dlsym(conv_lame_handle,"lame_encode_flush");
*(void **)(&lame_set_bWriteVbrTag)=
dlsym(conv_lame_handle,"lame_set_bWriteVbrTag");
return true;
#else
return false;
#endif // HAVE_LAME
}