mirror of
https://github.com/thestk/stk
synced 2026-01-12 04:21:52 +00:00
887 lines
28 KiB
C++
887 lines
28 KiB
C++
/***************************************************/
|
|
/*! \class FileRead
|
|
\brief STK audio file input class.
|
|
|
|
This class provides input support for various
|
|
audio file formats. Multi-channel (>2)
|
|
soundfiles are supported. The file data is
|
|
returned via an external StkFrames object
|
|
passed to the read() function. This class
|
|
does not store its own copy of the file data,
|
|
rather the data is read directly from disk.
|
|
|
|
FileRead currently supports uncompressed WAV,
|
|
AIFF/AIFC, SND (AU), MAT-file (Matlab), and
|
|
STK RAW file formats. Signed integer (8-,
|
|
16-, 24- and 32-bit) and floating-point (32- and
|
|
64-bit) data types are supported. Compressed
|
|
data types are not supported.
|
|
|
|
STK RAW files have no header and are assumed to
|
|
contain a monophonic stream of 16-bit signed
|
|
integers in big-endian byte order at a sample
|
|
rate of 22050 Hz. MAT-file data should be saved
|
|
in an array with each data channel filling a
|
|
matrix row. The sample rate for MAT-files should
|
|
be specified in a variable named "fs". If no
|
|
such variable is found, the sample rate is
|
|
assumed to be 44100 Hz.
|
|
|
|
by Perry R. Cook and Gary P. Scavone, 1995 - 2007.
|
|
*/
|
|
/***************************************************/
|
|
|
|
#include "FileRead.h"
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
|
|
namespace stk {
|
|
|
|
FileRead :: FileRead()
|
|
: fd_(0), fileSize_(0), channels_(0), dataType_(0), fileRate_(0.0)
|
|
{
|
|
}
|
|
|
|
FileRead :: FileRead( std::string fileName, bool typeRaw, unsigned int nChannels,
|
|
StkFormat format, StkFloat rate )
|
|
: fd_(0)
|
|
{
|
|
open( fileName, typeRaw, nChannels, format, rate );
|
|
}
|
|
|
|
FileRead :: ~FileRead()
|
|
{
|
|
if ( fd_ )
|
|
fclose( fd_ );
|
|
}
|
|
|
|
void FileRead :: close( void )
|
|
{
|
|
if ( fd_ ) fclose( fd_ );
|
|
fd_ = 0;
|
|
wavFile_ = false;
|
|
fileSize_ = 0;
|
|
channels_ = 0;
|
|
dataType_ = 0;
|
|
fileRate_ = 0.0;
|
|
}
|
|
|
|
bool FileRead :: isOpen( void )
|
|
{
|
|
if ( fd_ ) return true;
|
|
else return false;
|
|
}
|
|
|
|
void FileRead :: open( std::string fileName, bool typeRaw, unsigned int nChannels,
|
|
StkFormat format, StkFloat rate )
|
|
{
|
|
// If another file is open, close it.
|
|
close();
|
|
|
|
// Try to open the file.
|
|
fd_ = fopen( fileName.c_str(), "rb" );
|
|
if ( !fd_ ) {
|
|
oStream_ << "FileRead::open: could not open or find file (" << fileName << ")!";
|
|
handleError( StkError::FILE_NOT_FOUND );
|
|
}
|
|
|
|
// Attempt to determine file type from header (unless RAW).
|
|
bool result = false;
|
|
if ( typeRaw )
|
|
result = getRawInfo( fileName.c_str(), nChannels, format, rate );
|
|
else {
|
|
char header[12];
|
|
if ( fread( &header, 4, 3, fd_ ) != 3 ) goto error;
|
|
if ( !strncmp( header, "RIFF", 4 ) &&
|
|
!strncmp( &header[8], "WAVE", 4 ) )
|
|
result = getWavInfo( fileName.c_str() );
|
|
else if ( !strncmp( header, ".snd", 4 ) )
|
|
result = getSndInfo( fileName.c_str() );
|
|
else if ( !strncmp( header, "FORM", 4 ) &&
|
|
( !strncmp( &header[8], "AIFF", 4 ) || !strncmp(&header[8], "AIFC", 4) ) )
|
|
result = getAifInfo( fileName.c_str() );
|
|
else {
|
|
if ( fseek( fd_, 126, SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( &header, 2, 1, fd_ ) != 1 ) goto error;
|
|
if ( !strncmp( header, "MI", 2 ) ||
|
|
!strncmp( header, "IM", 2 ) )
|
|
result = getMatInfo( fileName.c_str() );
|
|
else {
|
|
oStream_ << "FileRead::open: file (" << fileName << ") format unknown.";
|
|
handleError( StkError::FILE_UNKNOWN_FORMAT );
|
|
}
|
|
}
|
|
}
|
|
|
|
// If here, we had a file type candidate but something else went wrong.
|
|
if ( result == false )
|
|
handleError( StkError::FILE_ERROR );
|
|
|
|
// Check for empty files.
|
|
if ( fileSize_ == 0 ) {
|
|
oStream_ << "FileRead::open: file (" << fileName << ") data size is zero!";
|
|
handleError( StkError::FILE_ERROR );
|
|
}
|
|
|
|
return;
|
|
|
|
error:
|
|
oStream_ << "FileRead::open: error reading file (" << fileName << ")!";
|
|
handleError( StkError::FILE_ERROR );
|
|
}
|
|
|
|
bool FileRead :: getRawInfo( const char *fileName, unsigned int nChannels, StkFormat format, StkFloat rate )
|
|
{
|
|
// Use the system call "stat" to determine the file length.
|
|
struct stat filestat;
|
|
if ( stat(fileName, &filestat) == -1 ) {
|
|
oStream_ << "FileRead: Could not stat RAW file (" << fileName << ").";
|
|
return false;
|
|
}
|
|
|
|
// Rawwave files have no header and by default, are assumed to
|
|
// contain a monophonic stream of 16-bit signed integers in
|
|
// big-endian byte order at a sample rate of 22050 Hz. However,
|
|
// different parameters can be specified if desired.
|
|
dataOffset_ = 0;
|
|
channels_ = nChannels;
|
|
dataType_ = format;
|
|
fileRate_ = rate;
|
|
int sampleBytes = 0;
|
|
if ( format == STK_SINT8 ) sampleBytes = 1;
|
|
else if ( format == STK_SINT16 ) sampleBytes = 2;
|
|
else if ( format == STK_SINT32 || format == STK_FLOAT32 ) sampleBytes = 4;
|
|
else if ( format == STK_FLOAT64 ) sampleBytes = 8;
|
|
|
|
fileSize_ = (long) filestat.st_size / sampleBytes / channels_; // length in frames
|
|
|
|
byteswap_ = false;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
byteswap_ = true;
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FileRead :: getWavInfo( const char *fileName )
|
|
{
|
|
// Find "format" chunk ... it must come before the "data" chunk.
|
|
char id[4];
|
|
SINT32 chunkSize;
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
while ( strncmp(id, "fmt ", 4) ) {
|
|
if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&chunkSize);
|
|
#endif
|
|
if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error;
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
}
|
|
|
|
// Check that the data is not compressed.
|
|
unsigned short format_tag;
|
|
if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; // Read fmt chunk size.
|
|
if ( fread(&format_tag, 2, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&format_tag);
|
|
swap32((unsigned char *)&chunkSize);
|
|
#endif
|
|
if ( format_tag == 0xFFFE ) { // WAVE_FORMAT_EXTENSIBLE
|
|
dataOffset_ = ftell(fd_);
|
|
if ( fseek(fd_, 14, SEEK_CUR) == -1 ) goto error;
|
|
unsigned short extSize;
|
|
if ( fread(&extSize, 2, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&extSize);
|
|
#endif
|
|
if ( extSize == 0 ) goto error;
|
|
if ( fseek(fd_, 6, SEEK_CUR) == -1 ) goto error;
|
|
if ( fread(&format_tag, 2, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&format_tag);
|
|
#endif
|
|
if ( fseek(fd_, dataOffset_, SEEK_SET) == -1 ) goto error;
|
|
}
|
|
if ( format_tag != 1 && format_tag != 3 ) { // PCM = 1, FLOAT = 3
|
|
oStream_ << "FileRead: "<< fileName << " contains an unsupported data format type (" << format_tag << ").";
|
|
return false;
|
|
}
|
|
|
|
// Get number of channels from the header.
|
|
SINT16 temp;
|
|
if ( fread(&temp, 2, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&temp);
|
|
#endif
|
|
channels_ = (unsigned int ) temp;
|
|
|
|
// Get file sample rate from the header.
|
|
SINT32 srate;
|
|
if ( fread(&srate, 4, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&srate);
|
|
#endif
|
|
fileRate_ = (StkFloat) srate;
|
|
|
|
// Determine the data type.
|
|
dataType_ = 0;
|
|
if ( fseek(fd_, 6, SEEK_CUR) == -1 ) goto error; // Locate bits_per_sample info.
|
|
if ( fread(&temp, 2, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&temp);
|
|
#endif
|
|
if ( format_tag == 1 ) {
|
|
if ( temp == 8 )
|
|
dataType_ = STK_SINT8;
|
|
else if ( temp == 16 )
|
|
dataType_ = STK_SINT16;
|
|
else if ( temp == 24 )
|
|
dataType_ = STK_SINT24;
|
|
else if ( temp == 32 )
|
|
dataType_ = STK_SINT32;
|
|
}
|
|
else if ( format_tag == 3 ) {
|
|
if ( temp == 32 )
|
|
dataType_ = STK_FLOAT32;
|
|
else if ( temp == 64 )
|
|
dataType_ = STK_FLOAT64;
|
|
}
|
|
if ( dataType_ == 0 ) {
|
|
oStream_ << "FileRead: " << temp << " bits per sample with data format " << format_tag << " are not supported (" << fileName << ").";
|
|
return false;
|
|
}
|
|
|
|
// Jump over any remaining part of the "fmt" chunk.
|
|
if ( fseek(fd_, chunkSize-16, SEEK_CUR) == -1 ) goto error;
|
|
|
|
// Find "data" chunk ... it must come after the "fmt" chunk.
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
|
|
while ( strncmp(id, "data", 4) ) {
|
|
if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&chunkSize);
|
|
#endif
|
|
chunkSize += chunkSize % 2; // chunk sizes must be even
|
|
if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error;
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
}
|
|
|
|
// Get length of data from the header.
|
|
SINT32 bytes;
|
|
if ( fread(&bytes, 4, 1, fd_) != 1 ) goto error;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&bytes);
|
|
#endif
|
|
fileSize_ = bytes / temp / channels_; // sample frames
|
|
fileSize_ *= 8; // sample frames
|
|
|
|
dataOffset_ = ftell(fd_);
|
|
byteswap_ = false;
|
|
#ifndef __LITTLE_ENDIAN__
|
|
byteswap_ = true;
|
|
#endif
|
|
|
|
wavFile_ = true;
|
|
return true;
|
|
|
|
error:
|
|
oStream_ << "FileRead: error reading WAV file (" << fileName << ").";
|
|
return false;
|
|
}
|
|
|
|
bool FileRead :: getSndInfo( const char *fileName )
|
|
{
|
|
// Determine the data type.
|
|
UINT32 format;
|
|
if ( fseek(fd_, 12, SEEK_SET) == -1 ) goto error; // Locate format
|
|
if ( fread(&format, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&format);
|
|
#endif
|
|
|
|
if (format == 2) dataType_ = STK_SINT8;
|
|
else if (format == 3) dataType_ = STK_SINT16;
|
|
else if (format == 4) dataType_ = STK_SINT24;
|
|
else if (format == 5) dataType_ = STK_SINT32;
|
|
else if (format == 6) dataType_ = STK_FLOAT32;
|
|
else if (format == 7) dataType_ = STK_FLOAT64;
|
|
else {
|
|
oStream_ << "FileRead: data format in file " << fileName << " is not supported.";
|
|
return false;
|
|
}
|
|
|
|
// Get file sample rate from the header.
|
|
UINT32 srate;
|
|
if ( fread(&srate, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&srate);
|
|
#endif
|
|
fileRate_ = (StkFloat) srate;
|
|
|
|
// Get number of channels from the header.
|
|
UINT32 chans;
|
|
if ( fread(&chans, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&chans);
|
|
#endif
|
|
channels_ = chans;
|
|
|
|
UINT32 offset;
|
|
if ( fseek(fd_, 4, SEEK_SET) == -1 ) goto error;
|
|
if ( fread(&offset, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&offset);
|
|
#endif
|
|
dataOffset_ = offset;
|
|
|
|
// Get length of data from the header.
|
|
if ( fread(&fileSize_, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&fileSize_);
|
|
#endif
|
|
// Convert to sample frames.
|
|
if ( dataType_ == STK_SINT8 )
|
|
fileSize_ /= channels_;
|
|
if ( dataType_ == STK_SINT16 )
|
|
fileSize_ /= 2 * channels_;
|
|
else if ( dataType_ == STK_SINT24 )
|
|
fileSize_ /= 3 * channels_;
|
|
else if ( dataType_ == STK_SINT32 || dataType_ == STK_FLOAT32 )
|
|
fileSize_ /= 4 * channels_;
|
|
else if ( dataType_ == STK_FLOAT64 )
|
|
fileSize_ /= 8 * channels_;
|
|
|
|
byteswap_ = false;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
byteswap_ = true;
|
|
#endif
|
|
|
|
return true;
|
|
|
|
error:
|
|
oStream_ << "FileRead: Error reading SND file (" << fileName << ").";
|
|
return false;
|
|
}
|
|
|
|
bool FileRead :: getAifInfo( const char *fileName )
|
|
{
|
|
bool aifc = false;
|
|
char id[4];
|
|
|
|
// Determine whether this is AIFF or AIFC.
|
|
if ( fseek(fd_, 8, SEEK_SET) == -1 ) goto error;
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
if ( !strncmp(id, "AIFC", 4) ) aifc = true;
|
|
|
|
// Find "common" chunk
|
|
SINT32 chunkSize;
|
|
if ( fread(&id, 4, 1, fd_) != 1) goto error;
|
|
while ( strncmp(id, "COMM", 4) ) {
|
|
if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&chunkSize);
|
|
#endif
|
|
chunkSize += chunkSize % 2; // chunk sizes must be even
|
|
if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error;
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
}
|
|
|
|
// Get number of channels from the header.
|
|
SINT16 temp;
|
|
if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error; // Jump over chunk size
|
|
if ( fread(&temp, 2, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&temp);
|
|
#endif
|
|
channels_ = temp;
|
|
|
|
// Get length of data from the header.
|
|
SINT32 frames;
|
|
if ( fread(&frames, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&frames);
|
|
#endif
|
|
fileSize_ = frames; // sample frames
|
|
|
|
// Read the number of bits per sample.
|
|
if ( fread(&temp, 2, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap16((unsigned char *)&temp);
|
|
#endif
|
|
|
|
// Get file sample rate from the header. For AIFF files, this value
|
|
// is stored in a 10-byte, IEEE Standard 754 floating point number,
|
|
// so we need to convert it first.
|
|
unsigned char srate[10];
|
|
unsigned char exp;
|
|
unsigned long mantissa;
|
|
unsigned long last;
|
|
if ( fread(&srate, 10, 1, fd_) != 1 ) goto error;
|
|
mantissa = (unsigned long) *(unsigned long *)(srate+2);
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&mantissa);
|
|
#endif
|
|
exp = 30 - *(srate+1);
|
|
last = 0;
|
|
while (exp--) {
|
|
last = mantissa;
|
|
mantissa >>= 1;
|
|
}
|
|
if (last & 0x00000001) mantissa++;
|
|
fileRate_ = (StkFloat) mantissa;
|
|
|
|
// Determine the data format.
|
|
dataType_ = 0;
|
|
if ( aifc == false ) {
|
|
if ( temp <= 8 ) dataType_ = STK_SINT8;
|
|
else if ( temp <= 16 ) dataType_ = STK_SINT16;
|
|
else if ( temp <= 24 ) dataType_ = STK_SINT24;
|
|
else if ( temp <= 32 ) dataType_ = STK_SINT32;
|
|
}
|
|
else {
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
if ( !strncmp(id, "NONE", 4) ) {
|
|
if ( temp <= 8 ) dataType_ = STK_SINT8;
|
|
else if ( temp <= 16 ) dataType_ = STK_SINT16;
|
|
else if ( temp <= 24 ) dataType_ = STK_SINT24;
|
|
else if ( temp <= 32 ) dataType_ = STK_SINT32;
|
|
}
|
|
else if ( (!strncmp(id, "fl32", 4) || !strncmp(id, "FL32", 4)) && temp == 32 ) dataType_ = STK_FLOAT32;
|
|
else if ( (!strncmp(id, "fl64", 4) || !strncmp(id, "FL64", 4)) && temp == 64 ) dataType_ = STK_FLOAT64;
|
|
}
|
|
if ( dataType_ == 0 ) {
|
|
oStream_ << "FileRead: AIFF/AIFC file (" << fileName << ") has unsupported data type (" << id << ").";
|
|
return false;
|
|
}
|
|
|
|
// Start at top to find data (SSND) chunk ... chunk order is undefined.
|
|
if ( fseek(fd_, 12, SEEK_SET) == -1 ) goto error;
|
|
|
|
// Find data (SSND) chunk
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
while ( strncmp(id, "SSND", 4) ) {
|
|
if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
swap32((unsigned char *)&chunkSize);
|
|
#endif
|
|
chunkSize += chunkSize % 2; // chunk sizes must be even
|
|
if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error;
|
|
if ( fread(&id, 4, 1, fd_) != 1 ) goto error;
|
|
}
|
|
|
|
// Skip over chunk size, offset, and blocksize fields
|
|
if ( fseek(fd_, 12, SEEK_CUR) == -1 ) goto error;
|
|
|
|
dataOffset_ = ftell(fd_);
|
|
byteswap_ = false;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
byteswap_ = true;
|
|
#endif
|
|
|
|
return true;
|
|
|
|
error:
|
|
oStream_ << "FileRead: Error reading AIFF file (" << fileName << ").";
|
|
return false;
|
|
}
|
|
|
|
bool FileRead :: findNextMatArray( SINT32 *chunkSize, SINT32 *rows, SINT32 *columns, SINT32 *nametype )
|
|
{
|
|
// Look for the next data array element. The file pointer should be
|
|
// at the data element type when this function is called.
|
|
SINT32 datatype;
|
|
*chunkSize = 0;
|
|
do {
|
|
if ( fseek(fd_, *chunkSize, SEEK_CUR) == -1 ) return false;
|
|
if ( fread(&datatype, 4, 1, fd_) != 1 ) return false;
|
|
if ( byteswap_ ) swap32((unsigned char *)&datatype);
|
|
if ( fread(chunkSize, 4, 1, fd_) != 1 ) return false;
|
|
if ( byteswap_ ) swap32((unsigned char *)chunkSize);
|
|
} while ( datatype != 14 );
|
|
|
|
// Check dimension subelement size to make sure 2D
|
|
if ( fseek(fd_, 20, SEEK_CUR) == -1 ) return false;
|
|
SINT32 size;
|
|
if ( fread(&size, 4, 1, fd_) != 1 ) return false;
|
|
if ( byteswap_ ) swap32((unsigned char *)&size);
|
|
if ( size != 8 ) return false;
|
|
|
|
// Read dimensions data
|
|
if ( fread(rows, 4, 1, fd_) != 1 ) return false;
|
|
if ( byteswap_ ) swap32((unsigned char *)rows);
|
|
if ( fread(columns, 4, 1, fd_) != 1 ) return false;
|
|
if ( byteswap_ ) swap32((unsigned char *)columns);
|
|
|
|
// Read array name subelement type
|
|
if ( fread(nametype, 4, 1, fd_) != 1 ) return false;
|
|
if ( byteswap_ ) swap32((unsigned char *)nametype);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FileRead :: getMatInfo( const char *fileName )
|
|
{
|
|
// MAT-file formatting information is available at:
|
|
// http://www.mathworks.com/access/helpdesk/help/pdf_doc/matlab/matfile_format.pdf
|
|
|
|
// Verify this is a version 5 MAT-file format.
|
|
char head[5];
|
|
if ( fseek(fd_, 0, SEEK_SET) == -1 ) goto error;
|
|
if ( fread(&head, 4, 1, fd_) != 1 ) goto error;
|
|
// If any of the first 4 characters of the header = 0, then this is
|
|
// a Version 4 MAT-file.
|
|
head[4] = '\0';
|
|
if ( strstr(head, "0") ) {
|
|
oStream_ << "FileRead: " << fileName << " appears to be a Version 4 MAT-file, which is not currently supported.";
|
|
return false;
|
|
}
|
|
|
|
// Determine the endian-ness of the file.
|
|
char mi[2];
|
|
byteswap_ = false;
|
|
// Locate "M" and "I" characters in header.
|
|
if ( fseek(fd_, 126, SEEK_SET) == -1 ) goto error;
|
|
if ( fread(&mi, 2, 1, fd_) != 1) goto error;
|
|
#ifdef __LITTLE_ENDIAN__
|
|
if ( !strncmp(mi, "MI", 2) )
|
|
byteswap_ = true;
|
|
else if ( strncmp(mi, "IM", 2) ) goto error;
|
|
#else
|
|
if ( !strncmp(mi, "IM", 2))
|
|
byteswap_ = true;
|
|
else if ( strncmp(mi, "MI", 2) ) goto error;
|
|
#endif
|
|
|
|
// We are expecting a data element containing the audio data and an
|
|
// optional data element containing the sample rate (with an array
|
|
// name of "fs"). Both elements should be stored as a Matlab array
|
|
// type (14).
|
|
|
|
bool doneParsing, haveData, haveSampleRate;
|
|
SINT32 chunkSize, rows, columns, nametype;
|
|
int dataoffset;
|
|
doneParsing = false;
|
|
haveData = false;
|
|
haveSampleRate = false;
|
|
while ( !doneParsing ) {
|
|
|
|
dataoffset = ftell( fd_ ); // save location in file
|
|
if ( findNextMatArray( &chunkSize, &rows, &columns, &nametype ) == false ) {
|
|
// No more Matlab array type chunks found.
|
|
if ( !haveData ) {
|
|
oStream_ << "FileRead: No audio data found in MAT-file (" << fileName << ").";
|
|
return false;
|
|
}
|
|
else if ( !haveSampleRate ) {
|
|
fileRate_ = 44100.0;
|
|
oStream_ << "FileRead: No sample rate found ... assuming 44100.0";
|
|
handleError( StkError::WARNING );
|
|
return true;
|
|
}
|
|
else return true;
|
|
}
|
|
|
|
if ( !haveSampleRate && rows == 1 && columns == 1 ) { // Parse for sample rate.
|
|
|
|
SINT32 namesize = 4;
|
|
if ( nametype == 1 ) { // array name > 4 characters
|
|
if ( fread(&namesize, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)namesize);
|
|
if ( namesize != 2 ) goto tryagain; // expecting name = "fs"
|
|
namesize = 8; // field must be padded to multiple of 8 bytes
|
|
}
|
|
char name[3]; name[2] = '\0';
|
|
if ( fread(&name, 2, 1, fd_) != 1) goto error;
|
|
if ( strncmp(name, "fs", 2) ) goto tryagain;
|
|
|
|
// Jump to real part data subelement, which is likely to be in a
|
|
// small data format.
|
|
if ( fseek(fd_, namesize-2, SEEK_CUR) == -1 ) goto error;
|
|
UINT32 type;
|
|
StkFloat srate;
|
|
if ( fread(&type, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)&type);
|
|
if ( (type & 0xffff0000) != 0 ) // small data format
|
|
type = (type & 0x0000ffff);
|
|
else
|
|
if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error;
|
|
if ( type == 1 ) { // SINT8
|
|
signed char rate;
|
|
if ( fread(&rate, 1, 1, fd_) != 1 ) goto error;
|
|
srate = (StkFloat) rate;
|
|
}
|
|
if ( type == 2 ) { // UINT8
|
|
unsigned char rate;
|
|
if ( fread(&rate, 1, 1, fd_) != 1 ) goto error;
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else if ( type == 3 ) { // SINT16
|
|
SINT16 rate;
|
|
if ( fread(&rate, 2, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap16((unsigned char *)&rate);
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else if ( type == 4 ) { // UINT16
|
|
UINT16 rate;
|
|
if ( fread(&rate, 2, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap16((unsigned char *)&rate);
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else if ( type == 5 ) { // SINT32
|
|
SINT32 rate;
|
|
if ( fread(&rate, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)&rate);
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else if ( type == 6 ) { // UINT32
|
|
UINT32 rate;
|
|
if ( fread(&rate, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)&rate);
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else if ( type == 7 ) { // FLOAT32
|
|
FLOAT32 rate;
|
|
if ( fread(&rate, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)&rate);
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else if ( type == 9 ) { // FLOAT64
|
|
FLOAT64 rate;
|
|
if ( fread(&rate, 8, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap64((unsigned char *)&rate);
|
|
srate = (StkFloat) rate;
|
|
}
|
|
else
|
|
goto tryagain;
|
|
|
|
if ( srate > 0 ) fileRate_ = srate;
|
|
haveSampleRate = true;
|
|
}
|
|
else if ( !haveData ) { // Parse for data.
|
|
|
|
// Assume channels = smaller of rows or columns.
|
|
if ( rows < columns ) {
|
|
channels_ = rows;
|
|
fileSize_ = columns;
|
|
}
|
|
else {
|
|
oStream_ << "FileRead: Transpose the MAT-file array so that audio channels fill matrix rows (not columns).";
|
|
return false;
|
|
}
|
|
|
|
SINT32 namesize = 4;
|
|
if ( nametype == 1 ) { // array name > 4 characters
|
|
if ( fread(&namesize, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)namesize);
|
|
namesize = (SINT32) ceil((float)namesize / 8);
|
|
if ( fseek( fd_, namesize*8, SEEK_CUR) == -1 ) goto error; // jump over array name
|
|
}
|
|
else {
|
|
if ( fseek( fd_, 4, SEEK_CUR ) == -1 ) goto error;
|
|
}
|
|
|
|
// Now at real part data subelement
|
|
SINT32 type;
|
|
if ( fread(&type, 4, 1, fd_) != 1 ) goto error;
|
|
if ( byteswap_ ) swap32((unsigned char *)&type);
|
|
if ( type == 1 ) dataType_ = STK_SINT8;
|
|
else if ( type == 3 ) dataType_ = STK_SINT16;
|
|
else if ( type == 5 ) dataType_ = STK_SINT32;
|
|
else if ( type == 7 ) dataType_ = STK_FLOAT32;
|
|
else if ( type == 9 ) dataType_ = STK_FLOAT64;
|
|
else {
|
|
oStream_ << "FileRead: The MAT-file array data format (" << type << ") is not supported.";
|
|
return false;
|
|
}
|
|
|
|
// Jump to the data.
|
|
if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error;
|
|
dataOffset_ = ftell(fd_);
|
|
haveData = true;
|
|
}
|
|
|
|
tryagain:
|
|
if ( haveData && haveSampleRate ) doneParsing = true;
|
|
else // jump to end of data element and keep trying
|
|
if ( fseek( fd_, dataoffset+chunkSize+8, SEEK_SET) == -1 ) goto error;
|
|
}
|
|
|
|
return true;
|
|
|
|
error:
|
|
oStream_ << "FileRead: Error reading MAT-file (" << fileName << ") header.";
|
|
return false;
|
|
}
|
|
|
|
void FileRead :: read( StkFrames& buffer, unsigned long startFrame, bool doNormalize )
|
|
{
|
|
// Make sure we have an open file.
|
|
if ( fd_ == 0 ) {
|
|
oStream_ << "FileRead::read: a file is not open!";
|
|
Stk::handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
// Check the buffer size.
|
|
unsigned int nFrames = buffer.frames();
|
|
if ( nFrames == 0 ) {
|
|
oStream_ << "FileRead::read: StkFrames buffer size is zero ... no data read!";
|
|
Stk::handleError( StkError::WARNING ); return;
|
|
}
|
|
|
|
if ( buffer.channels() != channels_ ) {
|
|
oStream_ << "FileRead::read: StkFrames argument has incompatible number of channels!";
|
|
Stk::handleError( StkError::FUNCTION_ARGUMENT );
|
|
}
|
|
|
|
// Check for file end.
|
|
if ( startFrame + nFrames >= fileSize_ )
|
|
nFrames = fileSize_ - startFrame;
|
|
|
|
long i, nSamples = (long) ( nFrames * channels_ );
|
|
unsigned long offset = startFrame * channels_;
|
|
|
|
// Read samples into StkFrames data buffer.
|
|
if ( dataType_ == STK_SINT16 ) {
|
|
SINT16 *buf = (SINT16 *) &buffer[0];
|
|
if ( fseek( fd_, dataOffset_+(offset*2), SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( buf, nSamples * 2, 1, fd_ ) != 1 ) goto error;
|
|
if ( byteswap_ ) {
|
|
SINT16 *ptr = buf;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
swap16( (unsigned char *) ptr++ );
|
|
}
|
|
if ( doNormalize ) {
|
|
StkFloat gain = 1.0 / 32768.0;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i] * gain;
|
|
}
|
|
else {
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i];
|
|
}
|
|
}
|
|
else if ( dataType_ == STK_SINT32 ) {
|
|
SINT32 *buf = (SINT32 *) &buffer[0];
|
|
if ( fseek( fd_, dataOffset_+(offset*4 ), SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( buf, nSamples * 4, 1, fd_ ) != 1 ) goto error;
|
|
if ( byteswap_ ) {
|
|
SINT32 *ptr = buf;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
swap32( (unsigned char *) ptr++ );
|
|
}
|
|
if ( doNormalize ) {
|
|
StkFloat gain = 1.0 / 2147483648.0;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i] * gain;
|
|
}
|
|
else {
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i];
|
|
}
|
|
}
|
|
else if ( dataType_ == STK_FLOAT32 ) {
|
|
FLOAT32 *buf = (FLOAT32 *) &buffer[0];
|
|
if ( fseek( fd_, dataOffset_+(offset*4), SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( buf, nSamples * 4, 1, fd_ ) != 1 ) goto error;
|
|
if ( byteswap_ ) {
|
|
FLOAT32 *ptr = buf;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
swap32( (unsigned char *) ptr++ );
|
|
}
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i];
|
|
}
|
|
else if ( dataType_ == STK_FLOAT64 ) {
|
|
FLOAT64 *buf = (FLOAT64 *) &buffer[0];
|
|
if ( fseek( fd_, dataOffset_+(offset*8), SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( buf, nSamples * 8, 1, fd_ ) != 1 ) goto error;
|
|
if ( byteswap_ ) {
|
|
FLOAT64 *ptr = buf;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
swap64( (unsigned char *) ptr++ );
|
|
}
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i];
|
|
}
|
|
else if ( dataType_ == STK_SINT8 && wavFile_ ) { // 8-bit WAV data is unsigned!
|
|
unsigned char *buf = (unsigned char *) &buffer[0];
|
|
if ( fseek( fd_, dataOffset_+offset, SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( buf, nSamples, 1, fd_) != 1 ) goto error;
|
|
if ( doNormalize ) {
|
|
StkFloat gain = 1.0 / 128.0;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = ( buf[i] - 128 ) * gain;
|
|
}
|
|
else {
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i] - 128.0;
|
|
}
|
|
}
|
|
else if ( dataType_ == STK_SINT8 ) { // signed 8-bit data
|
|
char *buf = (char *) &buffer[0];
|
|
if ( fseek( fd_, dataOffset_+offset, SEEK_SET ) == -1 ) goto error;
|
|
if ( fread( buf, nSamples, 1, fd_ ) != 1 ) goto error;
|
|
if ( doNormalize ) {
|
|
StkFloat gain = 1.0 / 128.0;
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i] * gain;
|
|
}
|
|
else {
|
|
for ( i=nSamples-1; i>=0; i-- )
|
|
buffer[i] = buf[i];
|
|
}
|
|
}
|
|
else if ( dataType_ == STK_SINT24 ) {
|
|
// 24-bit values are harder to import efficiently since there is
|
|
// no native 24-bit type. The following routine works but is much
|
|
// less efficient than that used for the other data types.
|
|
SINT32 temp;
|
|
unsigned char *ptr = (unsigned char *) &temp;
|
|
StkFloat gain = 1.0 / 2147483648.0;
|
|
if ( fseek(fd_, dataOffset_+(offset*3), SEEK_SET ) == -1 ) goto error;
|
|
for ( i=0; i<nSamples; i++ ) {
|
|
#ifdef __LITTLE_ENDIAN__
|
|
if ( byteswap_ ) {
|
|
if ( fread( ptr, 3, 1, fd_ ) != 1 ) goto error;
|
|
temp &= 0x00ffffff;
|
|
swap32( (unsigned char *) ptr );
|
|
}
|
|
else {
|
|
if ( fread( ptr+1, 3, 1, fd_ ) != 1 ) goto error;
|
|
temp &= 0xffffff00;
|
|
}
|
|
#else
|
|
if ( byteswap_ ) {
|
|
if ( fread( ptr+1, 3, 1, fd_ ) != 1 ) goto error;
|
|
temp &= 0xffffff00;
|
|
swap32( (unsigned char *) ptr );
|
|
}
|
|
else {
|
|
if ( fread( ptr, 3, 1, fd_ ) != 1 ) goto error;
|
|
temp &= 0x00ffffff;
|
|
}
|
|
#endif
|
|
|
|
if ( doNormalize ) {
|
|
buffer[i] = (StkFloat) temp * gain; // "gain" also includes 1 / 256 factor.
|
|
}
|
|
else
|
|
buffer[i] = (StkFloat) temp / 256; // right shift without affecting the sign bit
|
|
}
|
|
}
|
|
|
|
buffer.setDataRate( fileRate_ );
|
|
|
|
return;
|
|
|
|
error:
|
|
oStream_ << "FileRead: Error reading file data.";
|
|
handleError( StkError::FILE_ERROR);
|
|
}
|
|
|
|
} // stk namespace
|