// rdaudioconvert.cpp // // Convert Audio File Formats // // (C) Copyright 2010-2019 Fred Gleason // // 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_VORBIS #include #include #endif // HAVE_VORBIS #ifdef HAVE_FLAC #include #include #endif // HAVE_FLAC #ifdef HAVE_MP4_LIBS #include #include #endif // HAVE_MP4_LIBS //#include #include #include #include #include #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_ratioRD_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;igetChannels();j++) { pcmbuf[wave->getChannels()*i+j]=pcm[j][i]; } } if(total_frames>=start) { if((total_frames+frames)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=start) { if((end<0)||((frames+mad_synth.pcm.length)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;igetChannels()); 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)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;iputSamples((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;ichannels;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;iconv_peak_sample) { conv_peak_sample=peak; } } } void RDAudioConvert::UpdatePeak(const double data[],ssize_t len) { float peak; for(ssize_t i=0;iconv_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 }