diff options
Diffstat (limited to 'src/loris/AiffData.C')
-rw-r--r-- | src/loris/AiffData.C | 952 |
1 files changed, 952 insertions, 0 deletions
diff --git a/src/loris/AiffData.C b/src/loris/AiffData.C new file mode 100644 index 0000000..6feabbf --- /dev/null +++ b/src/loris/AiffData.C @@ -0,0 +1,952 @@ +/* + * This is the Loris C++ Class Library, implementing analysis, + * manipulation, and synthesis of digitized sounds using the Reassigned + * Bandwidth-Enhanced Additive Sound Model. + * + * Loris is Copyright (c) 1999-2010 by Kelly Fitz and Lippold Haken + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * AiffData.C + * + * Implementation of import and export functions. + * + * Kelly Fitz, 17 Sept 2003 + * loris@cerlsoundgroup.org + * + * http://www.cerlsoundgroup.org/Loris/ + * + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include "AiffData.h" + +#include "BigEndian.h" +#include "LorisExceptions.h" +#include "Marker.h" +#include "Notifier.h" + +#include <climits> +#include <cmath> +#include <fstream> +#include <iostream> + +// begin namespace +namespace Loris { + + +// --------------------------------------------------------------------------- +// extended80 +// --------------------------------------------------------------------------- +// IEEE floating point conversions, functions defined at end of file +// (used to be in a separate file, but they aren't used anywhere else). +// + +/* struct extended80 defined below */ +struct extended80; + +/* conversion functions */ +extern void ConvertToIeeeExtended(double num, extended80 * x) ; +extern double ConvertFromIeeeExtended(const extended80 * x) ; + +/* struct extended80 definition, with + constructors and conversion to double + */ +struct extended80 +{ + char data[10]; + + extended80( double x = 0. ) { ConvertToIeeeExtended( x, this ); } + operator double( void ) const { return ConvertFromIeeeExtended( this ); } +}; + +// -- AIFF import -- + +// --------------------------------------------------------------------------- +// readChunkHeader +// --------------------------------------------------------------------------- +// Read the id and chunk size from the current file position. +// +std::istream & +readChunkHeader( std::istream & s, CkHeader & h ) +{ + ID id = 0; + Uint_32 sz = 0; + BigEndian::read( s, 1, sizeof(ID), (char *)&id ); + BigEndian::read( s, 1, sizeof(Uint_32), (char *)&sz ); + + if ( s ) + { + h.id = id; + h.size = sz; + } + + return s; +} + +// --------------------------------------------------------------------------- +// readApplicationSpecifcData +// --------------------------------------------------------------------------- +// Read the data in the ApplicationSpecific chunk, assume the stream is +// correctly positioned, and that the chunk header has already been read. +// +// Look for data specific to SPC files. Any other kind of Application +// Specific data is ignored. +// +std::istream & +readApplicationSpecifcData( std::istream & s, SosEnvelopesCk & ck, unsigned long chunkSize ) +{ + Int_32 tmp_signature; + BigEndian::read( s, 1, sizeof(Int_32), (char *)&tmp_signature ); + + if ( tmp_signature == SosEnvelopesId ) + { + ck.header.id = ApplicationSpecificId; + ck.header.size = chunkSize; + ck.signature = SosEnvelopesId; + + // lookout! The format of this chunk is a mess, due + // to obsolete stuff lying around! + BigEndian::read( s, 1, sizeof(Int_32), (char *)&ck.enhanced ); + BigEndian::read( s, 1, sizeof(Int_32), (char *)&ck.validPartials ); + s.ignore( ck.validPartials * sizeof(Int_32) ); + BigEndian::read( s, 1, sizeof(Int_32), (char *)&ck.resolution ); + s.ignore( chunkSize - (4 + ck.validPartials) * sizeof(Int_32) ); + } + else + { + s.ignore( chunkSize - sizeof(Int_32) ); + } + + if ( !s ) + { + Throw( FileIOException, "Failed to read badly-formatted AIFF file (bad ApplicationSpecific chunk)." ); + } + + return s; +} + +// --------------------------------------------------------------------------- +// readCommonData +// --------------------------------------------------------------------------- +// Read the data in the Common chunk, assume the stream is correctly +// positioned, and that the chunk header has already been read. +// +std::istream & +readCommonData( std::istream & s, CommonCk & ck, unsigned long chunkSize ) +{ + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.channels ); + BigEndian::read( s, 1, sizeof(Int_32), (char *)&ck.sampleFrames ); + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.bitsPerSample ); + + if ( !s ) + { + Throw( FileIOException, "Failed to read badly-formatted AIFF file (bad Common chunk)." ); + } + + ck.header.id = CommonId; + ck.header.size = chunkSize; + + // don't let this get byte-reversed: + extended80 read_rate; + BigEndian::read( s, sizeof(extended80), sizeof(char), (char *)&read_rate ); + ck.srate = read_rate; + + return s; +} + +// --------------------------------------------------------------------------- +// readContainer +// --------------------------------------------------------------------------- +// +std::istream & +readContainer( std::istream & s, ContainerCk & ck, unsigned long chunkSize ) +{ + ck.header.id = ContainerId; + ck.header.size = chunkSize; + + // read in the chunk data: + BigEndian::read( s, 1, sizeof(ID), (char *)&ck.formType ); + if ( !s ) + { + Throw( FileIOException, "Failed to read badly-formatted AIFF file (bad Container chunk)." ); + } + + // make sure its really AIFF: + if ( ck.formType != AiffType ) + { + std::string err("Bad form type in AIFF file: "); + err += std::string( ck.formType, 4 ); + Throw( FileIOException, err ); + } + + return s; +} + +// --------------------------------------------------------------------------- +// readInstrumentData +// --------------------------------------------------------------------------- +// +std::istream & +readInstrumentData( std::istream & s, InstrumentCk & ck, unsigned long chunkSize ) +{ + ck.header.id = InstrumentId; + ck.header.size = chunkSize; + + BigEndian::read( s, 1, sizeof(Byte), (char *)&ck.baseNote ); + BigEndian::read( s, 1, sizeof(Byte), (char *)&ck.detune ); + BigEndian::read( s, 1, sizeof(Byte), (char *)&ck.lowNote ); + BigEndian::read( s, 1, sizeof(Byte), (char *)&ck.highNote ); + BigEndian::read( s, 1, sizeof(Byte), (char *)&ck.lowVelocity ); + BigEndian::read( s, 1, sizeof(Byte), (char *)&ck.highVelocity ); + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.gain ); + + // AIFFLoop is three Int_16s: + BigEndian::read( s, 1, sizeof(Uint_16), (char *)&ck.sustainLoop.playMode ); + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.sustainLoop.beginLoop ); + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.sustainLoop.endLoop ); + + BigEndian::read( s, 1, sizeof(Uint_16), (char *)&ck.releaseLoop.playMode ); + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.releaseLoop.beginLoop ); + BigEndian::read( s, 1, sizeof(Int_16), (char *)&ck.releaseLoop.endLoop ); + + if ( !s ) + { + Throw( FileIOException, "Failed to read badly-formatted AIFF file (bad Common chunk)." ); + } + + return s; +} + +// --------------------------------------------------------------------------- +// readMarkerData +// --------------------------------------------------------------------------- +// +std::istream & +readMarkerData( std::istream & s, MarkerCk & ck, unsigned long chunkSize ) +{ + ck.header.id = MarkerId; + ck.header.size = chunkSize; + + Uint_32 bytesToRead = chunkSize; + + // read in the number of Markers + BigEndian::read( s, 1, sizeof(Uint_16), (char *)&ck.numMarkers ); + bytesToRead -= sizeof(Uint_16); + + for ( int i = 0; i < ck.numMarkers; ++i ) + { + MarkerCk::Marker marker; + BigEndian::read( s, 1, sizeof(Uint_16), (char *)&marker.markerID ); + bytesToRead -= sizeof(Uint_16); + + BigEndian::read( s, 1, sizeof(Uint_32), (char *)&marker.position ); + bytesToRead -= sizeof(Uint_32); + + // read the size of the name string, then the characters: + unsigned char namelength; + BigEndian::read( s, 1, sizeof(unsigned char), (char *)&namelength ); + bytesToRead -= sizeof(unsigned char); + + // need to add one to the length, because, like C-strings, + // Pascal strings are null-terminated, but the null character + // is _not_ counted in the length. + // + // Correction? At least one web source says that Pascal strings + // are _not_ null terminated, but are padded (with an extra byte + // that is not part of the count) if necessary to make the total + // number of bytes even (that is, the char bytes, _plus_ the count). + // + // This way seems to work with all the files I have tried (Kyma and + // Peak, mostly) for AIFF and Spc. + int ncharbytes = namelength; + if ( ncharbytes%2 == 0 ) + ++ncharbytes; + static char tmpChars[256]; + BigEndian::read( s, ncharbytes, sizeof(char), tmpChars ); + bytesToRead -= ncharbytes * sizeof(char); + tmpChars[ namelength ] = '\0'; + + // convert to a string: + marker.markerName = std::string( tmpChars ); + + ck.markers.push_back( marker ); + } + + if ( bytesToRead > 0 ) + { + s.ignore( bytesToRead ); + } + + if ( !s ) + { + Throw( FileIOException, "Failed to read badly-formatted AIFF file (bad Marker chunk)." ); + } + + return s; +} + +// --------------------------------------------------------------------------- +// readSamples +// --------------------------------------------------------------------------- +// Let exceptions propogate. +// +static std::istream & +readSamples( std::istream & s, std::vector< Byte > & bytes ) +{ + debugger << "reading " << bytes.size() << " bytes of sample data" << endl; + + // read integer samples without byte swapping: + BigEndian::read( s, bytes.size(), 1, (char*)(&bytes[0]) ); + + return s; +} + +// --------------------------------------------------------------------------- +// readSampleData +// --------------------------------------------------------------------------- +// Read the data in the Sound Data chunk, assume the stream is correctly +// positioned, and that the chunk header has already been read. +// +std::istream & +readSampleData( std::istream & s, SoundDataCk & ck, unsigned long chunkSize ) +{ + ck.header.id = SoundDataId; + ck.header.size = chunkSize; + + BigEndian::read( s, 1, sizeof(Uint_32), (char *)&ck.offset ); + BigEndian::read( s, 1, sizeof(Uint_32), (char *)&ck.blockSize ); + + // compute the actual number of bytes that + // can be read from this chunk: + // (chunkSize is everything after the header) + const unsigned long howManyBytes = + ( chunkSize - ck.offset ) - (2 * sizeof(Uint_32)); + + ck.sampleBytes.resize( howManyBytes, 0 ); // could throw bad_alloc + + // skip ahead to the samples and read them: + s.ignore( ck.offset ); + readSamples( s, ck.sampleBytes ); + + if ( !s ) + { + Throw( FileIOException, "Failed to read badly-formatted AIFF file (bad Sound Data chunk)." ); + } + + return s; +} + +// -- Chunk construction -- + +// --------------------------------------------------------------------------- +// configureCommonCk +// --------------------------------------------------------------------------- +void +configureCommonCk( CommonCk & ck, unsigned long nFrames, unsigned int nChans, + unsigned int bps, double srate ) +{ + ck.header.id = CommonId; + + // size is everything after the header: + ck.header.size = sizeof(Int_16) + // num channels + sizeof(Int_32) + // num frames + sizeof(Int_16) + // bits per sample + sizeof(extended80); // sample rate + + ck.channels = nChans; + ck.sampleFrames = nFrames; + ck.bitsPerSample = bps; + ck.srate = srate; + // ConvertToIeeeExtended( srate, &ck.srate ); + +} + +// --------------------------------------------------------------------------- +// configureContainer +// --------------------------------------------------------------------------- +// dataSize is the combined size of all other chunks in file. Configure +// them first, then add their sizes (with headers!). +// +void +configureContainer( ContainerCk & ck, unsigned long dataSize ) +{ + ck.header.id = ContainerId; + + // size is everything after the header: + ck.header.size = sizeof(Int_32) + dataSize; + + ck.formType = AiffType; +} + +// --------------------------------------------------------------------------- +// configureInstrumentCk +// --------------------------------------------------------------------------- +void +configureInstrumentCk( InstrumentCk & ck, double midiNoteNum ) +{ + ck.header.id = InstrumentId; + + // size is everything after the header: + ck.header.size = + sizeof(Byte) + // baseFrequency + sizeof(Byte) + // detune + sizeof(Byte) + // lowFrequency + sizeof(Byte) + // highFrequency + sizeof(Byte) + // lowVelocity + sizeof(Byte) + // highVelocity + sizeof(Int_16) + // gain + 2 * sizeof(Int_16) + // playmode for sustainLoop and releaseLoop + 2 * sizeof(Uint_16) + // beginLoop for sustainLoop and releaseLoop + 2 * sizeof(Uint_16); // loopEnd for sustainLoop and releaseLoop + + ck.baseNote = Byte( midiNoteNum ); + ck.detune = Byte(long( 100 * midiNoteNum ) % 100); + if (ck.detune > 50) + { + ck.baseNote++; + ck.detune -= 100; + } + ck.detune *= -1; + + ck.lowNote = 0; + ck.highNote = 127; + ck.lowVelocity = 1; + ck.highVelocity = 127; + ck.gain = 0; + ck.sustainLoop.playMode = 0; // Sustain looping done by name, not by this + ck.sustainLoop.beginLoop = 0; + ck.sustainLoop.endLoop = 0; + ck.releaseLoop.playMode = 0; // No Looping + ck.releaseLoop.beginLoop = 0; + ck.releaseLoop.endLoop = 0; +} + +// --------------------------------------------------------------------------- +// configureMarkerCk +// --------------------------------------------------------------------------- +void +configureMarkerCk( MarkerCk & ck, const std::vector< Marker > & markers, double srate ) +{ + ck.header.id = MarkerId; + + // accumulate data size + Uint_32 dataSize = sizeof(Uint_16); // num markers + + ck.numMarkers = markers.size(); + ck.markers.resize( markers.size() ); + for ( unsigned int j = 0; j < markers.size(); ++j ) + { + MarkerCk::Marker & m = ck.markers[j]; + m.markerID = j+1; + m.position = Uint_32((markers[j].time() * srate) + 0.5); + m.markerName = markers[j].name(); + + #define MAX_PSTRING_CHARS 254 + if ( m.markerName.size() > MAX_PSTRING_CHARS ) + m.markerName.resize( MAX_PSTRING_CHARS ); + + // the size of a pascal string is the number of + // characters plus the size byte, plus the terminal '\0'. + // + // Actualy, at least one web source indicates that Pascal + // strings are not null-terminated, but that they _are_ + // padded with an extra (not part of the count) byte + // if necessary to ensure that the total length (including + // count) is even, and this seems to work better with other + // programs (e.g. Kyma) + if ( m.markerName.size()%2 == 0 ) + m.markerName += '\0'; + dataSize += sizeof(Uint_16) + sizeof(Uint_32) + (m.markerName.size() + 1); + } + + // must be an even number of bytes + if ( dataSize%2 ) + ++dataSize; + + ck.header.size = dataSize; +} + +// --------------------------------------------------------------------------- +// configureSoundDataCk +// --------------------------------------------------------------------------- +// +void +configureSoundDataCk( SoundDataCk & ck, const std::vector< double > & samples, + unsigned int bps ) +{ + Uint_32 dataSize = samples.size() * (bps/8); + // must be an even number of bytes: + if ( dataSize % 2 ) + { + ++dataSize; + } + + ck.header.id = SoundDataId; + + // size is everything after the header: + ck.header.size = sizeof(Uint_32) + // offset + sizeof(Uint_32) + // block size + dataSize; // sample data + + + // no block alignment: + ck.offset = 0; + ck.blockSize = 0; + + // convert the samples to integers stored in + // big endian order in the byte vector: + convertSamplesToBytes( samples, ck.sampleBytes, bps ); +} + + +// -- AIFF export -- + +// --------------------------------------------------------------------------- +// writeCommon +// --------------------------------------------------------------------------- +// +std::ostream & +writeCommonData( std::ostream & s, const CommonCk & ck ) +{ +/* + debugger << "writing common chunk: " << endl; + debugger << "header id: " << ck.header.id << endl; + debugger << "size: " << ck.header.size << endl; + debugger << "channels: " << ck.channels << endl; + debugger << "sample frames: " << ck.sampleFrames << endl; + debugger << "bits per sample: " << ck.bitsPerSample << endl; + //debugger << "rate: " << _sampleRate << endl; +*/ + + // write it out: + try + { + BigEndian::write( s, 1, sizeof(ID), (char *)&ck.header.id ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.header.size ); + BigEndian::write( s, 1, sizeof(Int_16), (char *)&ck.channels ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.sampleFrames ); + BigEndian::write( s, 1, sizeof(Int_16), (char *)&ck.bitsPerSample ); + // don't let this get byte-reversed: + extended80 write_rate( ck.srate ); + //ConvertToIeeeExtended( &ck.srate, write_rate ); + + BigEndian::write( s, sizeof(extended80), sizeof(char), (char *)&write_rate ); + } + catch( FileIOException & ex ) + { + ex.append( "Failed to write AIFF file Common chunk." ); + throw; + } + + return s; +} + +// --------------------------------------------------------------------------- +// writeContainer +// --------------------------------------------------------------------------- +// +std::ostream & +writeContainer( std::ostream & s, const ContainerCk & ck ) +{ +/* + debugger << "writing container: " << endl; + debugger << "header id: " << ck.header.id << endl; + debugger << "size: " << ck.header.size << endl; + debugger << "type: " << ck.formType << endl; +*/ + + // write it out: + try + { + BigEndian::write( s, 1, sizeof(ID), (char *)&ck.header.id ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.header.size ); + BigEndian::write( s, 1, sizeof(ID), (char *)&ck.formType ); + } + catch( FileIOException & ex ) + { + ex.append( "Failed to write AIFF file Container chunk." ); + throw; + } + + return s; +} + +// --------------------------------------------------------------------------- +// writeInstrumentData +// --------------------------------------------------------------------------- +std::ostream & +writeInstrumentData( std::ostream & s, const InstrumentCk & ck ) +{ + try + { + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.header.id ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.header.size ); + + BigEndian::write( s, 1, sizeof(Byte), (char *)&ck.baseNote ); + BigEndian::write( s, 1, sizeof(Byte), (char *)&ck.detune ); + BigEndian::write( s, 1, sizeof(Byte), (char *)&ck.lowNote ); + BigEndian::write( s, 1, sizeof(Byte), (char *)&ck.highNote ); + BigEndian::write( s, 1, sizeof(Byte), (char *)&ck.lowVelocity ); + BigEndian::write( s, 1, sizeof(Byte), (char *)&ck.highVelocity ); + BigEndian::write( s, 1, sizeof(Int_16), (char *)&ck.gain ); + + // AIFFLoop is three Int_16s: + BigEndian::write( s, 3, sizeof(Int_16), (char *)&ck.sustainLoop ); + BigEndian::write( s, 3, sizeof(Int_16), (char *)&ck.releaseLoop ); + } + catch( FileIOException & ex ) + { + ex.append( " Failed to write SPC file Instrument chunk." ); + throw; + } + + return s; +} + +// --------------------------------------------------------------------------- +// writeMarkerData +// --------------------------------------------------------------------------- +std::ostream & +writeMarkerData( std::ostream & s, const MarkerCk & ck ) +{ + try + { + BigEndian::write( s, 1, sizeof(ID), (char *)&ck.header.id ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.header.size ); + BigEndian::write( s, 1, sizeof(Uint_16), (char *)&ck.numMarkers ); + + int markerbytes = 0; + for ( unsigned int j = 0; j < ck.markers.size(); ++j ) + { + const MarkerCk::Marker & m = ck.markers[j]; + BigEndian::write( s, 1, sizeof(Uint_16), (char *)&m.markerID ); + BigEndian::write( s, 1, sizeof(Uint_32), (char *)&m.position ); + + // the size of a pascal string is the number of + // characters plus the size byte, plus the terminal '\0': + // Actualy, at least one web source indicates that Pascal + // strings are not null-terminated, but that they _are_ + // padded with an extra (not part of the count) byte + // if necessary to ensure that the total length (including + // count) is even, and this seems to work better with other + // programs (e.g. Kyma) + Uint_32 bytesToWrite = (m.markerName.size() + 1) * sizeof(char); + + // format pascal string: + static char tmpChars[256]; + tmpChars[0] = m.markerName.size(); + std::copy( m.markerName.begin(), m.markerName.end(), tmpChars + 1 ); + tmpChars[m.markerName.size()+1] = '\0'; + + BigEndian::write( s, bytesToWrite, sizeof(char), tmpChars ); + markerbytes += bytesToWrite; + } + + // be sure to write an even number of bytes + if ( markerbytes%2 ) + BigEndian::write( s, 1, sizeof(char), "\0" ); + + } + catch( FileIOException & ex ) + { + ex.append( "Failed to write AIFF file Marker chunk." ); + throw; + } + + return s; +} + +// --------------------------------------------------------------------------- +// writeSamples +// --------------------------------------------------------------------------- +// Let exceptions propogate. +// +static std::ostream & +writeSamples( std::ostream & s, const std::vector< Byte > & bytes ) +{ +// debugger << "writing " << bytes.size() << " bytes of sample data" << endl; + + // write integer samples without byte swapping, + // the bytes were constructed in the correct + // big endian order (see convertSamplesToBytes): + BigEndian::write( s, bytes.size(), 1, (char*)(&bytes[0]) ); + + return s; +} + +// --------------------------------------------------------------------------- +// writeSampleData +// --------------------------------------------------------------------------- +// +std::ostream & +writeSampleData( std::ostream & s, const SoundDataCk & ck ) +{ +/* + debugger << "writing sample data: " << endl; + debugger << "header id: " << ck.header.id << endl; + debugger << "size: " << ck.header.size << endl; + debugger << "offset: " << ck.offset << endl; + debugger << "block size: " << ck.blockSize << endl; +*/ + + // write it out: + try + { + BigEndian::write( s, 1, sizeof(ID), (char *)&ck.header.id ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.header.size ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.offset ); + BigEndian::write( s, 1, sizeof(Int_32), (char *)&ck.blockSize ); + + writeSamples( s, ck.sampleBytes ); + } + catch( FileIOException & ex ) + { + ex.append( "Failed to write AIFF file Container chunk." ); + throw; + } + + return s; +} + +// -- sample conversion -- + +// --------------------------------------------------------------------------- +// convertBytesToSamples +// --------------------------------------------------------------------------- +// Convert sample bytes to double precision floating point samples +// (-1.0, 1.0). The samples vector is resized to fit exactly as many +// samples as are represented in the bytes vector, and any prior +// contents are overwritten. +// +void +convertBytesToSamples( const std::vector< Byte > & bytes, + std::vector< double > & samples, unsigned int bps ) +{ + Assert( bps <= 32 ); + + typedef std::vector< double >::size_type size_type; + + const int bytesPerSample = bps / 8; + samples.resize( bytes.size() / bytesPerSample ); + + debugger << "converting " << samples.size() << " samples of size " + << bps << " bits" << endl; + + // shift sample bytes into a long integer, and + // scale to make a double: + const double oneOverMax = std::pow(0.5, double(bps-1)); + long samp; + + std::vector< Byte >::const_iterator bytePos = bytes.begin(); + std::vector< double >::iterator samplePos = samples.begin(); + while ( samplePos != samples.end() ) + { + // assign the leading byte, so that the sign + // is preserved: + samp = static_cast<char>(*(bytePos++)); + for ( size_type j = 1; j < bytesPerSample; ++j ) + { + Assert( bytePos != bytes.end() ); + + // OR bytes after the most significant, so + // that their sign is ignored: + samp = (samp << 8) + (unsigned char)*(bytePos++); + + // cannot decide why this didn't work, + // instead of the add above. + //samp |= (unsigned long)*(bytePos++); + } + + *(samplePos++) = oneOverMax * samp; + } +} + +// --------------------------------------------------------------------------- +// convertSamplesToBytes +// --------------------------------------------------------------------------- +// Convert floating point samples (-1.0, 1.0) to bytes. +// The bytes vector is resized to fit exactly as many +// samples as are stored in the samples vector, and any prior +// contents are overwritten. +// +// This formats the samples as big endian bytes, that is, the most +// significant bytes are stored earlier in the byte vector, so +// no byte-swapping should be performed when this byte array is +// written to disk, and moreover this function is specific to +// big-endian data storage (like AIFF files). +void +convertSamplesToBytes( const std::vector< double > & samples, + std::vector< Byte > & bytes, unsigned int bps ) +{ + Assert( bps <= 32 ); + + typedef std::vector< Byte >::size_type size_type; + + // grow the bytes vector, must be an even number + // of bytes: + const int bytesPerSample = bps / 8; + size_type howManyBytes = samples.size() * bytesPerSample; + if ( howManyBytes%2 ) + ++howManyBytes; + bytes.resize( howManyBytes ); + + debugger << "converting " << samples.size() << " samples to size " + << bps << " bits" << endl; + + // shift sample bytes into a long integer, and + // scale to make a double: + const double maxSample = std::pow(2., double(bps-1)); + long samp; + + std::vector< Byte >::iterator bytePos = bytes.begin(); + std::vector< double >::const_iterator samplePos = samples.begin(); + while ( samplePos != samples.end() ) + { + samp = long(*(samplePos++) * maxSample); + + // store the sample bytes in big endian order, + // most significant byte first: + for ( size_type j = bytesPerSample; j > 0; --j ) + { + //Assert( bytePos != bytes.end() ); + // mask the lowest byte after shifting: + *(bytePos++) = 0xFF & (samp >> (8*(j-1))); + } + } +} + +// --------------------------------------------------------------------------- +// extended80 +// --------------------------------------------------------------------------- +// IEEE floating point conversions. +// + +#ifndef HUGE_VAL +# define HUGE_VAL HUGE +#endif /* HUGE_VAL */ + +#define FloatToUnsigned(f)((Int_32)((Int_32(f - 2147483648.0)) + (Int_32)(2147483647)) + 1) + +void ConvertToIeeeExtended(double num, extended80 * x) +{ + int sign; + int expon; + double fMant, fsMant; + Int_32 hiMant, loMant; + char * bytes = x->data; + + if (num < 0) { + sign = 0x8000; + num *= -1; + } else + sign = 0; + + if (num == 0) { + expon = 0; + hiMant = 0; + loMant = 0; + } else { + fMant = frexp(num, &expon); + if ((expon > 16384) || !(fMant < 1)) { /* Infinity or NaN */ + expon = sign | 0x7FFF; + hiMant = 0; + loMant = 0; + } + /* infinity */ + else { /* Finite */ + expon += 16382; + if (expon < 0) { /* denormalized */ + fMant = ldexp(fMant, expon); + expon = 0; + } + expon |= sign; + fMant = ldexp(fMant, 32); + fsMant = floor(fMant); + hiMant = FloatToUnsigned(fsMant); + fMant = ldexp(fMant - fsMant, 32); + fsMant = floor(fMant); + loMant = FloatToUnsigned(fsMant); + } + } + + bytes[0] = expon >> 8; + bytes[1] = expon; + + bytes[2] = hiMant >> 24; + bytes[3] = hiMant >> 16; + bytes[4] = hiMant >> 8; + bytes[5] = hiMant; + + bytes[6] = loMant >> 24; + bytes[7] = loMant >> 16; + bytes[8] = loMant >> 8; + bytes[9] = loMant; + +} + +#ifndef HUGE_VAL +# define HUGE_VAL HUGE +#endif /* HUGE_VAL */ + +# define UnsignedToFloat(u)(((double)((Int_32)(u - (Int_32)(2147483647) - 1))) + 2147483648.0) + +/**************************************************************** + * Extended precision IEEE floating-point conversion routine. + ****************************************************************/ + +double ConvertFromIeeeExtended(const extended80 * x) +{ /* LCN */ /* ? */ + double f; + int expon; + Int_32 hiMant, loMant; + const char * bytes = x->data; + + expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); + hiMant = ((int)(bytes[2] & 0xFF) << 24) + | ((int)(bytes[3] & 0xFF) << 16) + | ((int)(bytes[4] & 0xFF) << 8) + | ((int)(bytes[5] & 0xFF)); + loMant = ((int)(bytes[6] & 0xFF) << 24) + | ((int)(bytes[7] & 0xFF) << 16) + | ((int)(bytes[8] & 0xFF) << 8) + | ((int)(bytes[9] & 0xFF)); + + if (expon == 0 && hiMant == 0 && loMant == 0) + f = 0; + else { + if (expon == 0x7FFF) /* Infinity or NaN */ + f = HUGE_VAL; + else { + expon -= 16383; + f = ldexp(UnsignedToFloat(hiMant), expon -= 31); + f += ldexp(UnsignedToFloat(loMant), expon -= 32); + } + } + + if (bytes[0] & 0x80) + { + f = -f; + } + + return f; +} + + +} // end of namespace Loris |