mirror of
				https://github.com/ElvishArtisan/rivendell.git
				synced 2025-11-04 08:04:12 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			2092 lines
		
	
	
		
			53 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			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 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
 | 
						|
}
 |