/* * Copyright: (C) 2000 Bruce W. Forsberg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Bruce Forsberg forsberg@tns.net * */ // class for WAV audio file reading and writing #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include /* for fstat() */ #include /* for fstat() */ #include /* strncmp() and memcpy() */ #include "aflibSoxFile.h" #include "aflibDebug.h" #include "aflibFileItem.h" #include "aflibData.h" #define MODULE_NAME "aflibSoxFile" extern "C" { aflibFile * getAFileObject() { return ((aflibFile *)new aflibSoxFile()); } void cleanup(){} void query(list& support_list) { aflibFileItem* item; char** list; for(int i = 0; st_formats[i].names; i++){ for(list = st_formats[i].names; *list;list++){ string format = *list; // These file types currently aren't working right yet. if(format == "dat") continue; if(format == "gsm") continue; if(format == "nul") continue; if(format == "la") continue; if(format == "lu") continue; item = new aflibFileItem(); item->setFormat(format); if(format == "aif" || format == "aiff") item->setDescription("SGI/Apple AIFF Sound File"); else if(format == "al") item->setDescription("a-law byte raw"); else if(format == "alsa") item->setDescription("ALSA /dev/snd/pcmXX Device"); else if(format == "au" || format == "snd") item->setDescription("SPARC .au with header"); else if(format == "auto") item->setDescription("Sox Module Sound File Detection"); else if(format == "cdr") item->setDescription("CDDA Audio"); else if(format == "cvs" || format == "cvsd") item->setDescription("Cont. Varible Slope Delta"); else if(format == "vms" || format == "dvms") item->setDescription("Cont. Varible Solot Delta"); else if(format == "dat" ) item->setDescription("Text Data Samples"); else if(format == "gsm" ) item->setDescription("GSM 06.10"); else if(format == "hcom" ) item->setDescription("Mac FSSD/HCOM"); else if(format == "la" ) item->setDescription("Inverse a-law byte raw"); else if(format == "lu" ) item->setDescription("Inverse u-law byte raw"); else if(format == "maud" ) item->setDescription("Amiga MAUD"); else if(format == "8svx" ) item->setDescription("Amiga 8SVX"); else if(format == "voc" ) item->setDescription("Sound Blaster .VOC"); else if(format == "wav" ) item->setDescription("Microsoft RIFF"); else if(format == "wve" ) item->setDescription("Pison .wve"); else if(format == "txw" ) item->setDescription("Yamaha TX16W and SY99 Sound Files"); else if(format == "ossdsp" ) item->setDescription("OSS /dev/dsp Device"); else if(format == "raw" ) item->setDescription("Raw File. Must specify rate, channels and byte type."); else if(format == "sb" ) item->setDescription("Raw Signed Bytes"); else if(format == "sl" ) item->setDescription("Raw Signed Longs"); else if(format == "sf" ) item->setDescription("IRCAM Sound File"); else if(format == "smp" ) item->setDescription("SampleVision Sound File"); else if(format == "sndt" ) item->setDescription("Sndtool Sound File"); else if(format == "sph" ) item->setDescription("NIST Sphere Sound File"); else if(format == "sunau" ) item->setDescription("Sun /dev/audio Device"); item->setExtension(format); item->setName(MODULE_NAME); item->setValue1(format); support_list.push_back(item); } } } } aflibSoxFile::aflibSoxFile() { _st_format.info.rate = 0; _st_format.info.size = -1; _st_format.info.encoding = -1; _st_format.info.channels = -1; _st_format.comment = NULL; _st_format.seekable = 0; _st_format.swap = 0; _st_format.filetype = (char *) 0; _st_format.fp = NULL; _st_format.filename = NULL; _st_format.length = 0; _write = FALSE; _position = 0; } aflibSoxFile::~aflibSoxFile() { if (_st_format.fp) { if (_st_format.h) { if(_write) _st_format.h->stopwrite(&_st_format); else _st_format.h->stopread(&_st_format); } fclose(_st_format.fp); } } aflibStatus aflibSoxFile::afopen( const char * file, aflibConfig* cfg) { aflibStatus status = AFLIB_SUCCESS; aflibConfig input_cfg(*cfg); aflibData data(1); configToFormat(input_cfg,&_st_format); _st_format.file.eof = 0; _st_format.filetype = (char*)_value1.c_str(); if(!strcmp(file,"-")){ _st_format.fp = stdin; } else { _st_format.filename = (char*)calloc(strlen(file)+1,1); strcpy(_st_format.filename,file); _st_format.fp = fopen(_st_format.filename,"rb"); } if (!_st_format.fp) { aflib_warning("aflibSoxFile: fileopen %s", strerror(errno)); return (AFLIB_ERROR_OPEN); } /* from Sox */ #if defined(DUMB_FILESYSETM) _st_format.seekable = 0; #else _st_format.seekable = (filetype(fileno(_st_format.fp)) == S_IFREG); #endif if ( st_gettype(&_st_format) ){ aflib_warning("aflibSoxFile: couldn't st_gettype: %s %s", _st_format.filetype,_st_format.st_errstr); return(AFLIB_ERROR_OPEN); } aflib_debug("file=%s, type=%s",_st_format.filename, _st_format.filetype); /* Read and write starters can change their formats. */ if(_st_format.h->startread(&_st_format)){ aflib_warning("aflibSoxFile:: couldn't start read %s", _st_format.st_errstr); return(AFLIB_ERROR_OPEN); } aflib_debug("rate=%i\t,size=%s\t,encoding=%s\t,channels=%i",_st_format.info.rate,st_sizes_str[_st_format.info.size],st_encodings_str[_st_format.info.encoding],_st_format.info.channels); if ( st_checkformat(&_st_format) ){ aflib_warning("aflibSoxFile: check %s",_st_format.st_errstr); return(AFLIB_ERROR_OPEN); } // Set input and output audio configuration formatToConfig(input_cfg,&_st_format); setInputConfig(input_cfg); setOutputConfig(input_cfg); return (status); } aflibStatus aflibSoxFile::afcreate( const char * file, const aflibConfig& cfg) { aflibStatus status = AFLIB_SUCCESS; aflibConfig output_cfg(cfg); (void)unlink(file); configToFormat(output_cfg,&_st_format); if( !strcmp(file,"-") ){ _st_format.fp = stdout; } else { _st_format.filename = (char*)calloc(strlen(file)+1,1); strcpy(_st_format.filename,file); _st_format.fp = fopen(_st_format.filename,"wb"); } if (!_st_format.fp) { aflib_warning(strerror(errno)); return (AFLIB_ERROR_OPEN); } _st_format.filetype = (char*)_value1.c_str(); _st_format.comment = _st_format.filename; /* Note: The sox modules expect info.size = ST_SIZE_BYTE for * these encodings to init properly. aflib actually gives * ST_SIZE_WORD */ if(_value2 == "ulaw"){ _st_format.info.encoding = ST_ENCODING_ULAW; aflib_info("Encoding: ULAW"); } else if(_value2 == "alaw"){ _st_format.info.encoding = ST_ENCODING_ALAW; aflib_info("Encoding: ALAW"); } else if(string(_st_format.filetype) == "au"){ // _st_format.info.encoding = ST_ENCODING_ULAW; // aflib_info("Encoding: ALAW"); } else if(_value2 == "adpcm") _st_format.info.encoding = ST_ENCODING_ADPCM; else if(_value2 == "ima") _st_format.info.encoding = ST_ENCODING_IMA_ADPCM; if( st_gettype(&_st_format)) { aflib_warning(_st_format.st_errstr); return (AFLIB_ERROR_OPEN); } if(_st_format.info.size == ST_SIZE_DWORD){ if(string(_st_format.filetype) == "ossdsp"){ _st_format.info.encoding = ST_ENCODING_SIGN2; _st_format.info.size = ST_SIZE_WORD; aflib_debug("aflibSoxFile::afcreate: Forcing ossdsp to WORD"); } else if(string(_st_format.filetype) == "aiff"){ _st_format.info.encoding = ST_ENCODING_SIGN2; _st_format.info.size = ST_SIZE_WORD; aflib_debug("aflibSoxFile::afcreate: Forcing aiff to WORD"); } else if(string(_st_format.filetype) == "avr"){ _st_format.info.encoding = ST_ENCODING_SIGN2; _st_format.info.size = ST_SIZE_WORD; aflib_debug("aflibSoxFile::afcreate: Forcing avr to WORD"); } else if(string(_st_format.filetype) == "maud"){ _st_format.info.encoding = ST_ENCODING_SIGN2; _st_format.info.size = ST_SIZE_WORD; aflib_debug("aflibSoxFile::afcreate: Forcing avr to WORD"); } } /* from Sox */ #if defined(DUMB_FILESYSETM) _st_format.seekable = 0; #else _st_format.seekable = (filetype(fileno(_st_format.fp)) == S_IFREG); #endif /* Read and write starters can change their formats. */ if(_st_format.h->startwrite(&_st_format)) { aflib_warning(_st_format.st_errstr); return (AFLIB_ERROR_OPEN); } aflib_debug("rate=%i\t,size=%s\t,encoding=%s\t,channels=%i",_st_format.info.rate,st_sizes_str[_st_format.info.size],st_encodings_str[_st_format.info.encoding],_st_format.info.channels); if (st_checkformat(&_st_format)) { aflib_warning(_st_format.st_errstr); return (AFLIB_ERROR_OPEN); } // Set input and output audio configuration formatToConfig(output_cfg,&_st_format); _write = TRUE; setInputConfig(output_cfg); setOutputConfig(output_cfg); return(status); } aflibStatus aflibSoxFile::afread( aflibData& data, long long position ) { aflibStatus status = AFLIB_SUCCESS; st_sample_t* p_data; long long new_length; aflibConfig sox_config(getInputConfig()); sox_config.setSampleSize(AFLIB_DATA_32S); /* Allocate data object with info from config */ data.setConfig(sox_config); p_data = (st_sample_t*)data.getDataPointer(); /*Return EOF if position out of range */ if ((position >= _st_format.length/_st_format.info.channels) && (_st_format.length)) { // data.adjustLength(0); aflib_debug("aflibSoxFile::afread: Can't seek on file"); // return(AFLIB_END_OF_FILE); } // Seek to correct position if(( position != -1 ) && (_st_format.h->seek(&_st_format,position*_st_format.info.channels)==0)){ _position = position*_st_format.info.channels; } new_length = data.getLength()*_st_format.info.channels; // Don't read past total samples available if(_st_format.length > 0){ new_length = _st_format.length - _position < new_length ? _st_format.length - _position : new_length; } if (new_length){ new_length = _st_format.h->read(&_st_format,p_data,new_length); } // aflib_debug("read: position=%i\tnew_lenght=%i\tst_error: %s",_position,new_length,_st_format.st_errstr); // IF we reached the end of the file then return error // premature EOF. // IF we read the last chunk if (new_length != data.getLength()*_st_format.info.channels) { // When we only have a partial read then readjust the length of data data.adjustLength(new_length/_st_format.info.channels); } data.convertToSize(getInputConfig().getSampleSize()); if (new_length==0) // if (_st_format.file.eof) { status = AFLIB_END_OF_FILE; aflib_debug("aflibSoxFile::afread: EOF"); } _position += new_length; aflib_debug("aflibSoxFile::afread: new_length=%i",new_length); return(status); } aflibStatus aflibSoxFile::afwrite( aflibData& data, long long position ) { aflibStatus status = AFLIB_SUCCESS; st_sample_t* p_data; st_sample_t new_length; aflib_data_size orig_size = data.getConfig().getSampleSize(); data.convertToSize(AFLIB_DATA_32S); p_data = (st_sample_t*)data.getDataPointer(); new_length = data.getLength()*_st_format.info.channels; if(new_length > 0) { new_length = _st_format.h->write(&_st_format, p_data, new_length); } // aflib_debug("wrote: position=%i\tnew_length=%i\t\n",position,new_length); /* Out of Disk Space ? */ if (new_length < data.getLength()*_st_format.info.channels) { status = AFLIB_END_OF_FILE; aflib_debug("aflibSoxFile::afwrite: EOF"); } data.convertToSize(orig_size); return(status); } bool aflibSoxFile::isDataSizeSupported(aflib_data_size size) { return (size==AFLIB_DATA_32S); } bool aflibSoxFile::isEndianSupported(aflib_data_endian end) { // If handle not yet allocated then indicate any endian is supported if (!_st_format.fp) return (TRUE); // Get the endian of the data if (_st_format.swap) return(end = AFLIB_ENDIAN_BIG); return(end = AFLIB_ENDIAN_LITTLE); } bool aflibSoxFile::isSampleRateSupported(int& rate) { // If handle not yet allocated then indicate any sample rate is supported if (_st_format.fp == NULL) return (TRUE); // IF same rate then TRUE else return desired rate if ((signed)_st_format.info.rate == rate) return TRUE; rate = _st_format.info.rate; return FALSE; } bool aflibSoxFile::isChannelsSupported(int& channels) { // If handle not yet allocated then indicate any sample rate is supported if (_st_format.fp == NULL) return (TRUE); // IF same rate then TRUE else return desired rate if (_st_format.info.channels == channels) return( TRUE ); channels = _st_format.info.channels; return FALSE; } bool aflibSoxFile::getItem( const char* item, void* value){ if(string(item) == "ft"){ memcpy(value,&_st_format,sizeof(st_soundstream)); return TRUE; } else if(string(item) == "filename" ){ memcpy(value,_st_format.filename,strlen(_st_format.filename)+1); return TRUE; } return FALSE; } /* from Sox */ int aflibSoxFile::filetype(int fd) { struct stat st; fstat(fd, &st); return st.st_mode & S_IFMT; } void aflibSoxFile::configToFormat( const aflibConfig& cfg, ft_t ft ){ ft->info.rate = cfg.getSamplesPerSecond(); if(cfg.getChannels()) ft->info.channels = cfg.getChannels(); switch(cfg.getSampleSize()){ case AFLIB_DATA_8S : ft->info.encoding = ST_ENCODING_SIGN2; ft->info.size = ST_SIZE_BYTE; break; case AFLIB_DATA_8U : ft->info.encoding = ST_ENCODING_UNSIGNED; ft->info.size = ST_SIZE_BYTE; break; case AFLIB_DATA_16S : ft->info.encoding = ST_ENCODING_SIGN2; ft->info.size = ST_SIZE_WORD; break; case AFLIB_DATA_16U : ft->info.encoding = ST_ENCODING_UNSIGNED; ft->info.size = ST_SIZE_WORD; break; case AFLIB_DATA_32S : ft->info.encoding = ST_ENCODING_SIGN2; ft->info.size = ST_SIZE_DWORD; break; case AFLIB_SIZE_UNDEFINED : break; } } void aflibSoxFile::formatToConfig( aflibConfig& cfg, const ft_t ft ){ aflibData data(1); cfg.setDataOrientation(AFLIB_INTERLEAVE); cfg.setDataEndian( data.getHostEndian() ); cfg.setChannels(ft->info.channels); cfg.setSamplesPerSecond((int)ft->info.rate); cfg.setTotalSamples(ft->length/ft->info.channels); if (ft->info.size==ST_SIZE_BYTE ) { switch(ft->info.encoding){ case ST_ENCODING_UNSIGNED: cfg.setSampleSize(AFLIB_DATA_8U); break; case ST_ENCODING_SIGN2: cfg.setSampleSize(AFLIB_DATA_8S); break; case ST_ENCODING_ULAW: case ST_ENCODING_ALAW: // Ulaw and Alaw converters return or expect signed 16 // see raw.c gXXX.c ... cfg.setSampleSize(AFLIB_DATA_16S); break; } } else if (ft->info.size == ST_SIZE_WORD ) { switch(ft->info.encoding){ case ST_ENCODING_UNSIGNED: cfg.setSampleSize(AFLIB_DATA_16U); break; case ST_ENCODING_SIGN2: case ST_ENCODING_ADPCM: case ST_ENCODING_IMA_ADPCM: case ST_ENCODING_ULAW: case ST_ENCODING_ALAW: cfg.setSampleSize(AFLIB_DATA_16S); break; } } else if (ft->info.size == ST_SIZE_DWORD ) { switch(ft->info.encoding){ case ST_ENCODING_SIGN2: cfg.setSampleSize(AFLIB_DATA_32S); } } // cfg.setSampleSize(AFLIB_DATA_32S); }