File contents
Index: ChangeLog
===================================================================
RCS file: /home/cvs/srl/libradio/ChangeLog,v
retrieving revision 1.366
diff -u -B -b -w -r1.366 ChangeLog
--- ChangeLog 15 Dec 2006 21:04:13 -0000 1.366
+++ ChangeLog 27 Dec 2006 00:01:46 -0000
@@ -1372,3 +1372,8 @@
2006-12-15 Fred Gleason <fredg@salemradiolabs.com>
* Added a BNV_HAVE_QT macro in 'autoinclude.m4'.
* Modified the build system to autodetect the presence of Qt.
+2006-12-18 Darrick Servis <darrick@dcn.org>
+ * Added Stefans patch to handle energy data cache files for oggvorbis
+ encoding.
+2006-12-26
+ * Added error checking to ReadEnergyFile() in radio/rwavefile.cpp.
Index: radio/rwavefile.cpp
===================================================================
RCS file: /home/cvs/srl/libradio/radio/rwavefile.cpp,v
retrieving revision 1.58
diff -u -B -b -w -r1.58 rwavefile.cpp
--- radio/rwavefile.cpp 20 Oct 2006 17:12:31 -0000 1.58
+++ radio/rwavefile.cpp 27 Dec 2006 00:01:46 -0000
@@ -52,6 +52,8 @@
recordable=false;
format_chunk=false;
format_tag=0;
+ energy_tag=0;
+ normalize_level=1.0;
channels=0;
samples_per_sec=0;
avg_bytes_per_sec=0;
@@ -287,7 +289,9 @@
time_length=(unsigned)ov_time_total(&vorbis_file,-1);
data_chunk=true;
format_chunk=true;
+ ReadEnergyFile(wave_file.name());
wave_type=RWaveFile::Ogg;
+ ReadOggMetadata();
return true;
#else
return false;
@@ -432,6 +436,10 @@
case WAVE_FORMAT_VORBIS:
#ifdef HAVE_VORBIS
+ energy_data.clear();
+ for(int i=0;i<channels;i++) {
+ energy_data.push_back(0);
+ }
avg_bytes_per_sec=2*channels*samples_per_sec;
vorbis_info_init(&vorbis_inf);
if(vorbis_encode_init_vbr(&vorbis_inf,channels,samples_per_sec,
@@ -491,8 +499,54 @@
unsigned csize;
unsigned lsize=0;
unsigned cptr;
+ QFile energy_file;
+ QString str;
+ mode_t prev_mask;
+ bool rc;
+
if(recordable) {
+ if(energy_tag==1) {
+ // Calculate peak amplitude.
+ //
+ // The way sox does this:
+ // - Find the sample with the highest value.
+ // Should be looking at minimum values too and take the absolute value.
+ double peak_level = 0;
+ for(unsigned i=0;i<energy_data.size();i++) {
+ if(energy_data[i] > peak_level) {
+ peak_level = energy_data[i];
+ }
+ }
+ // - Then we need a ratio to multiple all our samples by. i.e.
+ // vol_scale * sample will never be greater then SIGNED_SHORT_MAX.
+ double vol_scale = 32768.0f/peak_level;
+ // - Then we want to allow for adjusting the volume so it's not so full on loud.
+ // Like set it 13db below the max. This is where the user selectable level comes in.
+ normalize_level = normalize_level*vol_scale;
+ if( normalize_level==0){
+ normalize_level=1.0f;
+ }else{
+ for(unsigned i=0;i<energy_data.size();i++) {
+ energy_data[i] *= normalize_level;
+ }
+ }
+ // I Propose the normalize_level here is the same as sent to rd_import_file.
+ // i.e. normal=pow(10.0,(double)(library_conf->ripperLevel())/20.0);
+ // failing that change it to a int and set the normalize_level to ripperLevel() and
+ // keep the gory details of converting that to double within this library.
+ str=str.sprintf("%f\n",normalize_level);
+ energy_file.setName(wave_file.name()+".energy");
+ energy_file.remove();
+ prev_mask = umask(0113); // Set umask so files are user and group writable.
+ rc=energy_file.open(IO_ReadWrite);
+ umask(prev_mask);
+ if(rc) {
+ write(energy_file.handle(),(const char*)str,strlen(str));
+ write(energy_file.handle(),&(energy_data[0]),2*energy_data.size());
+ energy_file.close();
+ }
+ }
switch(wave_type) {
case RWaveFile::Wave:
//
@@ -632,6 +686,8 @@
time_length=0;
format_chunk=false;
format_tag=0;
+ energy_tag=0;
+ normalize_level=1.0;
channels=0;
samples_per_sec=0;
avg_bytes_per_sec=0;
@@ -816,11 +872,23 @@
int stream;
int n;
unsigned int pos;
+ int16_t *sample;
switch(wave_type) {
case RWaveFile::Ogg:
#ifdef HAVE_VORBIS
- n=ov_read(&vorbis_file,(char *)buf,count,0,2,1,&stream);
+ n = 0;
+ while(n!=count){
+ int ret = ov_read(&vorbis_file,(char *)buf+n,count-n,0,2,1,&stream);
+ if (!ret) break;
+ n+=ret;
+ }
+ if(normalize_level != 1.0f){
+ for (int i=0;i<n/2;i++) {
+ sample=(int16_t *)buf+i;
+ *sample=(int16_t)(normalize_level*(double)*sample);
+ }
+ }
return n;
#endif // HAVE_VORBIS
return 0;
@@ -951,6 +1019,55 @@
return write(wave_file.handle(),buf,count);
case WAVE_FORMAT_VORBIS:
+ for(int i=0;i<count;i++) {
+ switch(levl_istate) {
+ case 0: // Left Channel, LSB
+ levl_accum=((char *)buf)[i]&0xff;
+ levl_istate=1;
+ break;
+
+ case 1: // Left Channel, MSB
+ levl_accum|=((((char *)buf)[i]&0xff)<<8);
+ switch(channels) {
+ case 1:
+ if(levl_accum>energy_data[energy_data.size()-1]) {
+ energy_data[energy_data.size()-1]=levl_accum;
+ }
+ if(++levl_block_ptr==1152) {
+ energy_data.push_back(0);
+ levl_block_ptr=0;
+ }
+ levl_istate=0;
+ break;
+
+ case 2:
+ if(levl_accum>energy_data[energy_data.size()-2]) {
+ energy_data[energy_data.size()-2]=levl_accum;
+ }
+ levl_istate=2;
+ break;
+ }
+ break;
+
+ case 2: // Right Channel, LSB
+ levl_accum=((char *)buf)[i]&0xff;
+ levl_istate=3;
+ break;
+
+ case 3: // Right Channel, MSB
+ levl_accum|=((((char *)buf)[i]&0xff)<<8);
+ if(levl_accum>energy_data[energy_data.size()-1]) {
+ energy_data[energy_data.size()-1]=levl_accum;
+ }
+ if(++levl_block_ptr==1152) {
+ energy_data.push_back(0);
+ energy_data.push_back(0);
+ levl_block_ptr=0;
+ }
+ levl_istate=0;
+ break;
+ }
+ }
WriteOggBuffer((char *)buf,count);
break;
}
@@ -1130,6 +1247,30 @@
}
+void RWaveFile::setEnergyTag(int format)
+{
+ energy_tag=format;
+}
+
+
+int RWaveFile::getEnergyTag() const
+{
+ return energy_tag;
+}
+
+
+double RWaveFile::getNormalizeLevel() const
+{
+ return normalize_level;
+}
+
+
+void RWaveFile::setNormalizeLevel(double level)
+{
+ normalize_level=level;
+}
+
+
unsigned short RWaveFile::getChannels() const
{
return channels;
@@ -2322,6 +2463,35 @@
}
+
+bool RWaveFile::ReadEnergyFile(QString wave_file_name)
+{
+ if(has_energy && energy_loaded) return true;
+ QFile energy_file;
+ QString str;
+ unsigned char frame[50];
+
+ energy_file.setName(wave_file_name+".energy");
+ if(!energy_file.open(IO_ReadOnly))
+ return false;
+ if(energy_file.readLine(str,20) <= 0)
+ return false;
+ normalize_level=str.toDouble();
+ energy_file.close();
+ if(!energy_file.open(IO_ReadOnly))
+ return false;
+ read(energy_file.handle(),frame,str.length());
+ for(unsigned i=1;i<((getSampleLength()*getChannels()/1152));i++) {
+ if(read(energy_file.handle(),frame,2) != 2) return false;
+ energy_data.push_back(frame[0]+256*frame[1]);
+ }
+ energy_file.close();
+ energy_loaded=true;
+ has_energy=true;
+ return true;
+}
+
+
bool RWaveFile::GetLevl(int fd)
{
unsigned size=LEVL_CHUNK_SIZE;
@@ -2587,6 +2757,23 @@
}
+void RWaveFile::ReadOggMetadata()
+{
+ /* if(wave_data==NULL) {
+ return;
+ }
+ TagLib::FileRef f(wave_file.name());
+ if(!f.isNull() && f.tag()){
+ TagLib::Tag *tag=f.tag();
+ wave_data->setTitle(TStringToQString(tag->title()));
+ wave_data->setArtist(TStringToQString(tag->artist()));
+ wave_data->setAlbum(TStringToQString(tag->album()));
+ wave_data->setReleaseYear((int)tag->year());
+ wave_data->setMetadataFound(true);
+ } */
+}
+
+
void RWaveFile::ReadId3Metadata()
{
if(wave_data==NULL) {
@@ -3652,6 +3839,29 @@
return i;
break;
+ case WAVE_FORMAT_VORBIS:
+ block_size=2304*channels;
+ while(i<energy_size) {
+ if(readWave(pcm,block_size)!=block_size) {
+ has_energy=true;
+ return i;
+ }
+ for(int j=0;j<channels;j++) {
+ max=0;
+ energy_data.push_back(0);
+ for(int k=0;k<1152;k++) {
+ offset=2*k*channels+2*j;
+ if((pcm[offset]+256*pcm[offset+1])>energy_data[i]) {
+ energy_data[i]=pcm[offset]+256*pcm[offset+1];
+ }
+ }
+ i++;
+ }
+ }
+ has_energy=true;
+ return i;
+ break;
+
default:
has_energy=false;
return 0;
@@ -3697,9 +3907,13 @@
high=buffer[j][i];
}
*/
+ buffer[j][i] = ((buf[i*2*channels + 2*j + 1]<<8) |
+ (buf[i*2*channels + 2*j] & 0xff))/32768.0f;
+ /* My compilier doesn't like these casts.
buffer[j][i]=
((float)(buf[2*channels*i+2*j]&0xff)+
(256.0f*(float)(buf[2*channels*i+2*j+1]&0xff)))/32768.0f;
+ */
}
// printf("HIGH: %5.3f\n",high);
}
Index: radio/rwavefile.h
===================================================================
RCS file: /home/cvs/srl/libradio/radio/rwavefile.h,v
retrieving revision 1.31
diff -u -B -b -w -r1.31 rwavefile.h
--- radio/rwavefile.h 19 Oct 2006 12:40:52 -0000 1.31
+++ radio/rwavefile.h 27 Dec 2006 00:01:46 -0000
@@ -311,6 +311,18 @@
**/
void setFormatTag(unsigned short format);
+ void setEnergyTag(int format);
+ int getEnergyTag() const;
+
+ /**
+ * <level> = Normalization level, expressed as linear ratio (0 = No normalization)
+ */
+ double getNormalizeLevel() const;
+
+
+ void setNormalizeLevel(double level);
+
+
/**
* Returns the number of audio channels recorded in the WAV file, as
* represented by the 'FMT chunk.
@@ -1031,12 +1043,14 @@
bool GetBext(int);
bool GetMext(int);
bool GetLevl(int);
+ bool ReadEnergyFile (QString);
bool GetList(int);
bool ReadListElement(unsigned char *buffer,unsigned *offset,unsigned size);
bool ReadTmcMetadata(int);
void ReadTmcTag(const QString tag,const QString value);
bool GetLine(int fd,char *buffer,int max_len);
void ReadId3Metadata();
+ void ReadOggMetadata();
bool GetMpegHeader(int fd,int offset);
int GetAtxOffset(int fd);
bool GetFlacStreamInfo();
@@ -1064,6 +1078,7 @@
unsigned ext_time_length; // Audio length in msec
bool format_chunk; // Does 'fmt ' chunk exist?
unsigned short format_tag; // Encoding Format
+ int energy_tag; // 0: Include Energy Data 1: Extra File
unsigned short channels; // Number of channels
unsigned samples_per_sec; // Samples/sec/channel
unsigned avg_bytes_per_sec; // Average bytes/sec overall
@@ -1173,6 +1188,7 @@
float encode_quality;
int serial_number;
int atx_offset;
+ double normalize_level;
#ifdef HAVE_VORBIS
OggVorbis_File vorbis_file;
vorbis_info vorbis_inf;