[Stk] WAV reading

Gary Scavone gary at ccrma.Stanford.EDU
Fri Mar 28 09:41:25 PST 2003


I forgot that there was a bug fix (not yet released) for 8-bit WAV
files in the WvIn and WvOut classes.  The WAV standard is
inconsistent, in that most formats use signed integers, but 8-bit data 
is unsigned.  So, if your files were recorded in 8-bit, try using the 
attached updates.

--gary

On Fri, 28 Mar 2003, Gary Scavone wrote:

>>
>>In almost all cases, this is caused by omission of the preprocessor 
>>definition __LITTLE_ENDIAN__.
>>
>>--gary
>>
>>On Fri, 28 Mar 2003 wborkowsk at poczta.onet.pl wrote:
>>
>>>>Hi,
>>>>I would like to use STK in my research, but I
>>>>can't force it to read a majority of my WAV 
>>>>files (by play example)
>>>>Expecially my own, which were recorded by 
>>>>Windows Sound Recorder.
>>>>I took a look into the archive of the list and
>>>>found that problem was mentioned, but is it
>>>>solved?
>>>>I attached the zip file including 4 files -
>>>>only sound002.wav work OK. For rest there 
>>>>are a reading error.
>>>>
>>>>Sincerelly
>>>>Wojciech Borkowski
>>>>=====================================
>>>>http://www.iss.uw.edu.pl/~borkowsk
>>>>=====================================
-------------- next part --------------
/***************************************************/
/*! \class WvOut
    \brief STK audio data output base class.

    This class provides output support for various
    audio file formats.  It also serves as a base
    class for "realtime" streaming subclasses.

    WvOut writes samples to an audio file.  It
    supports multi-channel data in interleaved
    format.  It is important to distinguish the
    tick() methods, which output single samples
    to all channels in a sample frame, from the
    tickFrame() method, which takes a pointer
    to multi-channel sample frame data.

    WvOut currently supports WAV, AIFF, AIFC, SND
    (AU), MAT-file (Matlab), and STK RAW file
    formats.  Signed integer (8-, 16-, and 32-bit)
    and floating- point (32- and 64-bit) data types
    are supported.  STK RAW files use 16-bit
    integers by definition.  MAT-files will always
    be written as 64-bit floats.  If a data type
    specification does not match the specified file
    type, the data type will automatically be
    modified.  Uncompressed data types are not
    supported.

    Currently, WvOut is non-interpolating and the
    output rate is always Stk::sampleRate().

    by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/

#include "WvOut.h"
#include "string.h"
#include "math.h"

const WvOut::FILE_TYPE WvOut :: WVOUT_RAW = 1;
const WvOut::FILE_TYPE WvOut :: WVOUT_WAV = 2;
const WvOut::FILE_TYPE WvOut :: WVOUT_SND = 3;
const WvOut::FILE_TYPE WvOut :: WVOUT_AIF = 4;
const WvOut::FILE_TYPE WvOut :: WVOUT_MAT = 5;

// WAV header structure. See ftp://ftp.isi.edu/in-notes/rfc2361.txt
// for information regarding format codes.
struct wavhdr {
  char riff[4];           // "RIFF"
  SINT32 file_size;      // in bytes
  char wave[4];           // "WAVE"
  char fmt[4];            // "fmt "
  SINT32 chunk_size;     // in bytes (16 for PCM)
  SINT16 format_tag;     // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law
  SINT16 num_chans;      // 1=mono, 2=stereo
  SINT32 sample_rate;
  SINT32 bytes_per_sec;
  SINT16 bytes_per_samp; // 2=16-bit mono, 4=16-bit stereo
  SINT16 bits_per_samp;
  char data[4];           // "data"
  SINT32 data_length;    // in bytes
};

// SND (AU) header structure (NeXT and Sun).
struct sndhdr {
  char pref[4];
  SINT32 hdr_length;
  SINT32 data_length;
  SINT32 format;
  SINT32 sample_rate;
  SINT32 num_channels;
  char comment[16];
};

// AIFF/AIFC header structure ... only the part common to both
// formats.
struct aifhdr {
  char form[4];               // "FORM"
  SINT32 form_size;          // in bytes
  char aiff[4];               // "AIFF" or "AIFC"
  char comm[4];               // "COMM"
  SINT32 comm_size;          // "COMM" chunk size (18 for AIFF, 24 for AIFC)
  SINT16 num_chans;          // number of channels
  unsigned long sample_frames; // sample frames of audio data
  SINT16 sample_size;        // in bits
  unsigned char srate[10];      // IEEE 754 floating point format
};

struct aifssnd {
  char ssnd[4];               // "SSND"
  SINT32 ssnd_size;          // "SSND" chunk size
  unsigned long offset;         // data offset in data block (should be 0)
  unsigned long block_size;     // not used by STK (should be 0)
};

// MAT-file 5 header structure.
struct mathdr {
  char heading[124];   // Header text field
  SINT16 hff[2];      // Header flag fields
  SINT32 adf[11];     // Array data format fields
  // There's more, but it's of variable length
};

WvOut :: WvOut()
{
  init();
}

WvOut::WvOut( const char *fileName, unsigned int nChannels, FILE_TYPE type, Stk::STK_FORMAT format )
{
  init();
  openFile( fileName, nChannels, type, format );
}

WvOut :: ~WvOut()
{
  closeFile();

  if (data)
    delete [] data;
}

void WvOut :: init()
{
  fd = 0;
  data = 0;
  fileType = 0;
  dataType = 0;
  channels = 0;
  counter = 0;
  totalCount = 0;
}

void WvOut :: closeFile( void )
{
  if ( fd ) {
    // If there's an existing file, close it first.
    writeData( counter );

    if ( fileType == WVOUT_RAW )
      fclose( fd );
    else if ( fileType == WVOUT_WAV )
      closeWavFile();
    else if ( fileType == WVOUT_SND )
      closeSndFile();
    else if ( fileType == WVOUT_AIF )
      closeAifFile();
    else if ( fileType == WVOUT_MAT )
      closeMatFile();
    fd = 0;

    printf("%f Seconds Computed\n\n", getTime() );
    totalCount = 0;
  }
}

void WvOut :: openFile( const char *fileName, unsigned int nChannels, WvOut::FILE_TYPE type, Stk::STK_FORMAT format )
{
  closeFile();

  if ( nChannels < 1 ) {
    sprintf(msg, "WvOut: the channels argument must be greater than zero!");
    handleError( msg, StkError::FUNCTION_ARGUMENT );
  }

  unsigned int lastChannels = channels;
  channels = nChannels;
  fileType = type;

  if ( format != STK_SINT8 && format != STK_SINT16 &&
       format != STK_SINT32 && format != STK_FLOAT32 && 
       format != STK_FLOAT64 ) {
    sprintf( msg, "WvOut: Unknown data type specified (%ld).", format );
    handleError(msg, StkError::FUNCTION_ARGUMENT);
  } 
  dataType = format;

  bool result = false;
  if ( fileType == WVOUT_RAW ) {
    if ( channels != 1 ) {
      sprintf(msg, "WvOut: STK RAW files are, by definition, always monaural (channels = %d not supported)!", nChannels);
      handleError( msg, StkError::FUNCTION_ARGUMENT );
    }
    result = setRawFile( fileName );
  }
  else if ( fileType == WVOUT_WAV )
    result = setWavFile( fileName );
  else if ( fileType == WVOUT_SND )
    result = setSndFile( fileName );
  else if ( fileType == WVOUT_AIF )
    result = setAifFile( fileName );
  else if ( fileType == WVOUT_MAT )
    result = setMatFile( fileName );
  else {
    sprintf(msg, "WvOut: Unknown file type specified (%ld).", fileType);
    handleError(msg, StkError::FUNCTION_ARGUMENT);
  }

  if ( result == false )
    handleError(msg, StkError::FILE_ERROR);

  // Allocate new memory if necessary.
  if ( lastChannels < channels ) {
    if ( data ) delete [] data;
    data = (MY_FLOAT *) new MY_FLOAT[BUFFER_SIZE*channels];
  }
  counter = 0;
}

bool WvOut :: setRawFile( const char *fileName )
{
  char name[128];
  strncpy(name, fileName, 128);
  if ( strstr(name, ".raw") == NULL) strcat(name, ".raw");
  fd = fopen(name, "wb");
  if ( !fd ) {
    sprintf(msg, "WvOut: Could not create RAW file: %s", name);
    return false;
  }

  if ( dataType != STK_SINT16 ) {
    dataType = STK_SINT16;
    sprintf(msg, "WvOut: Using 16-bit signed integer data format for file %s.", name);
    handleError(msg, StkError::WARNING);
  }

  byteswap = false;
#ifdef __LITTLE_ENDIAN__
  byteswap = true;
#endif

  printf("\nCreating RAW file: %s\n", name);
  return true;
}

bool WvOut :: setWavFile( const char *fileName )
{
  char name[128];
  strncpy(name, fileName, 128);
  if ( strstr(name, ".wav") == NULL) strcat(name, ".wav");
  fd = fopen(name, "wb");
  if ( !fd ) {
    sprintf(msg, "WvOut: Could not create WAV file: %s", name);
    return false;
  }



  struct wavhdr hdr = {"RIF", 44, "WAV", "fmt", 16, 1, 1,
                        (SINT32) Stk::sampleRate(), 0, 2, 16, "dat", 0};
  hdr.riff[3] = 'F';
  hdr.wave[3] = 'E';
  hdr.fmt[3]  = ' ';
  hdr.data[3] = 'a';
  hdr.num_chans = (SINT16) channels;
  if ( dataType == STK_SINT8 )
    hdr.bits_per_samp = 8;
  else if ( dataType == STK_SINT16 )
    hdr.bits_per_samp = 16;
  else if ( dataType == STK_SINT32 )
    hdr.bits_per_samp = 32;
  else if ( dataType == STK_FLOAT32 ) {
    hdr.format_tag = 3;
    hdr.bits_per_samp = 32;
  }
  else if ( dataType == STK_FLOAT64 ) {
    hdr.format_tag = 3;
    hdr.bits_per_samp = 64;
  }
  hdr.bytes_per_samp = (SINT16) (channels * hdr.bits_per_samp / 8);
  hdr.bytes_per_sec = (SINT32) (hdr.sample_rate * hdr.bytes_per_samp);

  byteswap = false;
#ifndef __LITTLE_ENDIAN__
  byteswap = true;
  swap32((unsigned char *)&hdr.file_size);
  swap32((unsigned char *)&hdr.chunk_size);
  swap16((unsigned char *)&hdr.format_tag);
  swap16((unsigned char *)&hdr.num_chans);
  swap32((unsigned char *)&hdr.sample_rate);
  swap32((unsigned char *)&hdr.bytes_per_sec);
  swap16((unsigned char *)&hdr.bytes_per_samp);
  swap16((unsigned char *)&hdr.bits_per_samp);
#endif

  if ( fwrite(&hdr, 4, 11, fd) != 11 ) {
    sprintf(msg, "WvOut: Could not write WAV header for file %s", name);
    return false;
  }

  printf("\nCreating WAV file: %s\n", name);
  return true;
}

void WvOut :: closeWavFile( void )
{
  int bytes_per_sample = 1;
  if ( dataType == STK_SINT16 )
    bytes_per_sample = 2;
  else if ( dataType == STK_SINT32 || dataType == STK_FLOAT32 )
    bytes_per_sample = 4;
  else if ( dataType == STK_FLOAT64 )
    bytes_per_sample = 8;

  SINT32 bytes = totalCount * channels * bytes_per_sample;
#ifndef __LITTLE_ENDIAN__
  swap32((unsigned char *)&bytes);
#endif
  fseek(fd, 40, SEEK_SET); // jump to data length
  fwrite(&bytes, 4, 1, fd);

  bytes = totalCount * channels * bytes_per_sample + 44;
#ifndef __LITTLE_ENDIAN__
  swap32((unsigned char *)&bytes);
#endif
  fseek(fd, 4, SEEK_SET); // jump to file size
  fwrite(&bytes, 4, 1, fd);
  fclose( fd );
}

bool WvOut :: setSndFile( const char *fileName )
{
  char name[128];
  strncpy(name, fileName, 128);
  if ( strstr(name, ".snd") == NULL) strcat(name, ".snd");
  fd = fopen(name, "wb");
  if ( !fd ) {
    sprintf(msg, "WvOut: Could not create SND file: %s", name);
    return false;
  }

  struct sndhdr hdr = {".sn", 40, 0, 3, (SINT32) Stk::sampleRate(), 1, "Created by STK"};
  hdr.pref[3] = 'd';
  hdr.num_channels = channels;
  if ( dataType == STK_SINT8 )
    hdr.format = 2;
  else if ( dataType == STK_SINT16 )
    hdr.format = 3;
  else if ( dataType == STK_SINT32 )
    hdr.format = 5;
  else if ( dataType == STK_FLOAT32 )
    hdr.format = 6;
  else if ( dataType == STK_FLOAT64 )
    hdr.format = 7;

  byteswap = false;
#ifdef __LITTLE_ENDIAN__
  byteswap = true;
  swap32 ((unsigned char *)&hdr.hdr_length);
  swap32 ((unsigned char *)&hdr.format);
  swap32 ((unsigned char *)&hdr.sample_rate);
  swap32 ((unsigned char *)&hdr.num_channels);
#endif

  if ( fwrite(&hdr, 4, 10, fd) != 10 ) {
    sprintf(msg, "WvOut: Could not write SND header for file %s", name);
    return false;
  }

  printf("\nCreating SND file: %s\n", name);
  return true;
}

void WvOut :: closeSndFile( void )
{
  int bytes_per_sample = 1;
  if ( dataType == STK_SINT16 )
    bytes_per_sample = 2;
  else if ( dataType == STK_SINT32 )
    bytes_per_sample = 4;
  else if ( dataType == STK_FLOAT32 )
    bytes_per_sample = 4;
  else if ( dataType == STK_FLOAT64 )
    bytes_per_sample = 8;

  SINT32 bytes = totalCount * bytes_per_sample * channels;
#ifdef __LITTLE_ENDIAN__
  swap32 ((unsigned char *)&bytes);
#endif
  fseek(fd, 8, SEEK_SET); // jump to data size
  fwrite(&bytes, 4, 1, fd);
  fclose(fd);
}

bool WvOut :: setAifFile( const char *fileName )
{
  char name[128];
  strncpy(name, fileName, 128);
  if ( strstr(name, ".aif") == NULL) strcat(name, ".aif");
  fd = fopen(name, "wb");
  if ( !fd ) {
    sprintf(msg, "WvOut: Could not create AIF file: %s", name);
    return false;
  }

  // Common parts of AIFF/AIFC header.
  struct aifhdr hdr = {"FOR", 46, "AIF", "COM", 18, 0, 0, 16, "0"};
  struct aifssnd ssnd = {"SSN", 8, 0, 0};
  hdr.form[3] = 'M';
  hdr.aiff[3] = 'F';
  hdr.comm[3] = 'M';
  ssnd.ssnd[3] = 'D';
  hdr.num_chans = channels;
  if ( dataType == STK_SINT8 )
    hdr.sample_size = 8;
  else if ( dataType == STK_SINT16 )
    hdr.sample_size = 16;
  else if ( dataType == STK_SINT32 )
    hdr.sample_size = 32;
  else if ( dataType == STK_FLOAT32 ) {
    hdr.aiff[3] = 'C';
    hdr.sample_size = 32;
    hdr.comm_size = 24;
  }
  else if ( dataType == STK_FLOAT64 ) {
    hdr.aiff[3] = 'C';
    hdr.sample_size = 64;
    hdr.comm_size = 24;
  }

  // For AIFF files, the sample rate is stored in a 10-byte,
  // IEEE Standard 754 floating point number, so we need to
  // convert to that.
  SINT16 i;
  unsigned long exp;
  unsigned long rate = (unsigned long) Stk::sampleRate();
  memset(hdr.srate, 0, 10);
  exp = rate;
  for (i=0; i<32; i++) {
    exp >>= 1;
    if (!exp) break;
  }
  i += 16383;
#ifdef __LITTLE_ENDIAN__
  swap16((unsigned char *)&i);
#endif
  *(SINT16 *)(hdr.srate) = (SINT16) i;

  for (i=32; i; i--) {
    if (rate & 0x80000000) break;
    rate <<= 1;
  }

#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&rate);
#endif
  *(unsigned long *)(hdr.srate+2) = (unsigned long) rate;

  byteswap = false;  
#ifdef __LITTLE_ENDIAN__
  byteswap = true;
  swap32((unsigned char *)&hdr.form_size);
  swap32((unsigned char *)&hdr.comm_size);
  swap16((unsigned char *)&hdr.num_chans);
  swap16((unsigned char *)&hdr.sample_size);
  swap32((unsigned char *)&ssnd.ssnd_size);
  swap32((unsigned char *)&ssnd.offset);
  swap32((unsigned char *)&ssnd.block_size);
#endif

  // The structure boundaries don't allow a single write of 54 bytes.
  if ( fwrite(&hdr, 4, 5, fd) != 5 ) goto error;
  if ( fwrite(&hdr.num_chans, 2, 1, fd) != 1 ) goto error;
  if ( fwrite(&hdr.sample_frames, 4, 1, fd) != 1 ) goto error;
  if ( fwrite(&hdr.sample_size, 2, 1, fd) != 1 ) goto error;
  if ( fwrite(&hdr.srate, 10, 1, fd) != 1 ) goto error;

  if ( dataType == STK_FLOAT32 ) {
    char type[4] = {'f','l','3','2'};
    char zeroes[2] = { 0, 0 };
    if ( fwrite(&type, 4, 1, fd) != 1 ) goto error;
    if ( fwrite(&zeroes, 2, 1, fd) != 1 ) goto error;
  }
  else if ( dataType == STK_FLOAT64 ) {
    char type[4] = {'f','l','6','4'};
    char zeroes[2] = { 0, 0 };
    if ( fwrite(&type, 4, 1, fd) != 1 ) goto error;
    if ( fwrite(&zeroes, 2, 1, fd) != 1 ) goto error;
  }
  
  if ( fwrite(&ssnd, 4, 4, fd) != 4 ) goto error;

  printf("\nCreating AIF file: %s\n", name);
  return true;

 error:
  sprintf(msg, "WvOut: Could not write AIF header for file %s", name);
  return false;
}

void WvOut :: closeAifFile( void )
{
  unsigned long frames = (unsigned long) totalCount;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&frames);
#endif
  fseek(fd, 22, SEEK_SET); // jump to "COMM" sample_frames
  fwrite(&frames, 4, 1, fd);

  int bytes_per_sample = 1;
  if ( dataType == STK_SINT16 )
    bytes_per_sample = 2;
  else if ( dataType == STK_SINT32 || dataType == STK_FLOAT32 )
    bytes_per_sample = 4;
  else if ( dataType == STK_FLOAT64 )
    bytes_per_sample = 8;

  unsigned long bytes = totalCount * bytes_per_sample * channels + 46;
  if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) bytes += 6;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&bytes);
#endif
  fseek(fd, 4, SEEK_SET); // jump to file size
  fwrite(&bytes, 4, 1, fd);

  bytes = totalCount * bytes_per_sample * channels + 8;
  if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) bytes += 6;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&bytes);
#endif
  if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 )
    fseek(fd, 48, SEEK_SET); // jump to "SSND" chunk size
  else
    fseek(fd, 42, SEEK_SET); // jump to "SSND" chunk size
  fwrite(&bytes, 4, 1, fd);

  fclose( fd );
}

bool WvOut :: setMatFile( const char *fileName )
{
  char name[128];
  strncpy(name, fileName, 128);
  if ( strstr(name, ".mat") == NULL) strcat(name, ".mat");
  fd = fopen(name, "w+b");
  if ( !fd ) {
    sprintf(msg, "WvOut: Could not create MAT file: %s", name);
    return false;
  }

  if ( dataType != STK_FLOAT64 ) {
    dataType = STK_FLOAT64;
    sprintf(msg, "WvOut: Using 64-bit floating-point data format for file %s", name);
    handleError(msg, StkError::WARNING);
  }

  struct mathdr hdr;
  strcpy(hdr.heading,"MATLAB 5.0 MAT-file, Generated using the Synthesis ToolKit in C++ (STK). By Perry R. Cook and Gary P. Scavone, 1995-2002.");

  int i;
  for (i=strlen(hdr.heading);i<124;i++) hdr.heading[i] = ' ';

  // Header Flag Fields
  hdr.hff[0] = (SINT16) 0x0100;   // Version field
  hdr.hff[1] = (SINT16) 'M';      // Endian indicator field ("MI")
  hdr.hff[1] <<= 8;
  hdr.hff[1] += 'I';

  hdr.adf[0] = (SINT32) 14;       // Matlab array data type value
  hdr.adf[1] = (SINT32) 0;        // Size of file after this point to end (in bytes)
                                 // Don't know size yet.

  // Numeric Array Subelements (4):
  // 1. Array Flags
  hdr.adf[2] = (SINT32) 6;        // Matlab 32-bit unsigned integer data type value
  hdr.adf[3] = (SINT32) 8;        // 8 bytes of data to follow
  hdr.adf[4] = (SINT32) 6;        // Double-precision array, no array flags set
  hdr.adf[5] = (SINT32) 0;        // 4 bytes undefined
  // 2. Array Dimensions
  hdr.adf[6] = (SINT32) 5;        // Matlab 32-bit signed integer data type value
  hdr.adf[7] = (SINT32) 8;        // 8 bytes of data to follow (2D array)
  hdr.adf[8] = (SINT32) channels; // This is the number of rows
  hdr.adf[9] = (SINT32) 0;        // This is the number of columns

  // 3. Array Name
  // We'll use fileName for the matlab array name (as well as the file name).
  // If fileName is 4 characters or less, we have to use a compressed data element
  // format for the array name data element.  Otherwise, the array name must
  // be formatted in 8-byte increments (up to 31 characters + NULL).
  SINT32 namelength = (SINT32) strlen(fileName);
  if (strstr(fileName, ".mat")) namelength -= 4;
  if (namelength > 31) namelength = 31; // Truncate name to 31 characters.
  char arrayName[64];
  strncpy(arrayName, fileName, namelength);
  arrayName[namelength] = '\0';
  if (namelength > 4) {
    hdr.adf[10] = (SINT32) 1;        // Matlab 8-bit signed integer data type value
  }
  else { // Compressed data element format
    hdr.adf[10] = namelength;
    hdr.adf[10] <<= 16;
    hdr.adf[10] += 1;
  }
  SINT32 headsize = 40;        // Number of bytes in data element so far.

  // Write the fixed portion of the header
  if ( fwrite(&hdr, 172, 1, fd) != 1 ) goto error;

  // Write MATLAB array name
  SINT32 tmp;
  if (namelength > 4) {
    if ( fwrite(&namelength, 4, 1, fd) != 1) goto error;
    if ( fwrite(arrayName, namelength, 1, fd) != 1 ) goto error;
    tmp = (SINT32) ceil((float)namelength / 8);
    if ( fseek(fd, tmp*8-namelength, SEEK_CUR) == -1 ) goto error;
    headsize += tmp * 8;
  }
  else { // Compressed data element format
    if ( fwrite(arrayName, namelength, 1, fd) != 1 ) goto error;
    tmp = 4 - namelength;
    if ( fseek(fd, tmp, SEEK_CUR) == -1 ) goto error;
  }

  // Finish writing known header information
  tmp = 9;        // Matlab IEEE 754 double data type
  if ( fwrite(&tmp, 4, 1, fd) != 1 ) goto error;
  tmp = 0;        // Size of real part subelement in bytes (8 per sample)
  if ( fwrite(&tmp, 4, 1, fd) != 1 ) goto error;
  headsize += 8;  // Total number of bytes in data element so far

  if ( fseek(fd, 132, SEEK_SET) == -1 ) goto error;
  if ( fwrite(&headsize, 4, 1, fd) != 1 ) goto error; // Write header size ... will update at end
  if ( fseek(fd, 0, SEEK_END) == -1 ) goto error;

  byteswap = false;
  printf("\nCreating MAT-file (%s) containing MATLAB array: %s\n", name, arrayName);
  return true;

 error:
  sprintf(msg, "WvOut: Could not write MAT-file header for file %s", name);
  return false;
}

void WvOut :: closeMatFile( void )
{
  fseek(fd, 164, SEEK_SET); // jump to number of columns
  fwrite(&totalCount, 4, 1, fd);

  SINT32 headsize, temp;
  fseek(fd, 132, SEEK_SET);  // jump to header size
  fread(&headsize, 4, 1, fd);
  temp = headsize;
  headsize += (SINT32) (totalCount * 8 * channels);
  fseek(fd, 132, SEEK_SET);
  // Write file size (minus some header info)
  fwrite(&headsize, 4, 1, fd);

  fseek(fd, temp+132, SEEK_SET); // jumpt to data size (in bytes)
  temp = totalCount * 8 * channels;
  fwrite(&temp, 4, 1, fd);

  fclose(fd);
}

unsigned long WvOut :: getFrames( void ) const
{
  return totalCount;
}

MY_FLOAT WvOut :: getTime( void ) const
{
  return (MY_FLOAT) totalCount / Stk::sampleRate();
}

void WvOut :: writeData( unsigned long frames )
{
  if ( dataType == STK_SINT8 ) {
    if ( fileType == WVOUT_WAV ) { // 8-bit WAV data is unsigned!
      unsigned char sample;
      for ( unsigned long k=0; k<frames*channels; k++ ) {
        sample = (unsigned char) (data[k] * 127.0 + 128.0);
        if ( fwrite(&sample, 1, 1, fd) != 1 ) goto error;
      }
    }
    else {
      signed char sample;
      for ( unsigned long k=0; k<frames*channels; k++ ) {
        sample = (signed char) (data[k] * 127.0);
        //sample = ((signed char) (( data[k] + 1.0 ) * 127.5 + 0.5)) - 128;
        if ( fwrite(&sample, 1, 1, fd) != 1 ) goto error;
      }
    }
  }
  else if ( dataType == STK_SINT16 ) {
    SINT16 sample;
    for ( unsigned long k=0; k<frames*channels; k++ ) {
      sample = (SINT16) (data[k] * 32767.0);
      //sample = ((SINT16) (( data[k] + 1.0 ) * 32767.5 + 0.5)) - 32768;
      if ( byteswap ) swap16( (unsigned char *)&sample );
      if ( fwrite(&sample, 2, 1, fd) != 1 ) goto error;
    }
  }
  else if ( dataType == STK_SINT32 ) {
    SINT32 sample;
    for ( unsigned long k=0; k<frames*channels; k++ ) {
      sample = (SINT32) (data[k] * 2147483647.0);
      //sample = ((SINT32) (( data[k] + 1.0 ) * 2147483647.5 + 0.5)) - 2147483648;
      if ( byteswap ) swap32( (unsigned char *)&sample );
      if ( fwrite(&sample, 4, 1, fd) != 1 ) goto error;
    }
  }
  else if ( dataType == STK_FLOAT32 ) {
    FLOAT32 sample;
    for ( unsigned long k=0; k<frames*channels; k++ ) {
      sample = (FLOAT32) (data[k]);
      if ( byteswap ) swap32( (unsigned char *)&sample );
      if ( fwrite(&sample, 4, 1, fd) != 1 ) goto error;
    }
  }
  else if ( dataType == STK_FLOAT64 ) {
    FLOAT64 sample;
    for ( unsigned long k=0; k<frames*channels; k++ ) {
      sample = (FLOAT64) (data[k]);
      if ( byteswap ) swap64( (unsigned char *)&sample );
      if ( fwrite(&sample, 8, 1, fd) != 1 ) goto error;
    }
  }
  return;

 error:
  sprintf(msg, "WvOut: Error writing data to file.");
  handleError(msg, StkError::FILE_ERROR);
}

void WvOut :: tick(const MY_FLOAT sample)
{
  if ( !fd ) return;

  for ( unsigned int j=0; j<channels; j++ )
    data[counter*channels+j] = sample;

  counter++;
  totalCount++;

  if ( counter == BUFFER_SIZE ) {
    writeData( BUFFER_SIZE );
    counter = 0;
  }
}

void WvOut :: tick(const MY_FLOAT *vector, unsigned int vectorSize)
{
  if ( !fd ) return;

  for (unsigned int i=0; i<vectorSize; i++)
    tick( vector[i] );
}

void WvOut :: tickFrame(const MY_FLOAT *frameVector, unsigned int frames)
{
  if ( !fd ) return;

  unsigned int j;
  for ( unsigned int i=0; i<frames; i++ ) {
    for ( j=0; j<channels; j++ ) {
      data[counter*channels+j] = frameVector[i*channels+j];
    }
    counter++;
    totalCount++;

    if ( counter == BUFFER_SIZE ) {
      writeData( BUFFER_SIZE );
      counter = 0;
    }
  }
}
-------------- next part --------------
/***************************************************/
/*! \class WvIn
    \brief STK audio data input base class.

    This class provides input support for various
    audio file formats.  It also serves as a base
    class for "realtime" streaming subclasses.

    WvIn loads the contents of an audio file for
    subsequent output.  Linear interpolation is
    used for fractional "read rates".

    WvIn supports multi-channel data in interleaved
    format.  It is important to distinguish the
    tick() methods, which return samples produced
    by averaging across sample frames, from the 
    tickFrame() methods, which return pointers to
    multi-channel sample frames.  For single-channel
    data, these methods return equivalent values.

    Small files are completely read into local memory
    during instantiation.  Large files are read
    incrementally from disk.  The file size threshold
    and the increment size values are defined in
    WvIn.h.

    WvIn currently supports WAV, AIFF, SND (AU),
    MAT-file (Matlab), and STK RAW file formats.
    Signed integer (8-, 16-, and 32-bit) and floating-
    point (32- and 64-bit) data types are supported.
    Uncompressed data types are not supported.  If
    using MAT-files, data should be saved in an array
    with each data channel filling a matrix row.

    by Perry R. Cook and Gary P. Scavone, 1995 - 2002.
*/
/***************************************************/

#include "WvIn.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <math.h>
#include <string.h>

WvIn :: WvIn()
{
  init();
}

WvIn :: WvIn( const char *fileName, bool raw )
{
  init();
  openFile( fileName, raw );
}

WvIn :: ~WvIn()
{
  if (fd)
    fclose(fd);

  if (data)
    delete [] data;

  if (lastOutput)
    delete [] lastOutput;
}

void WvIn :: init( void )
{
  fd = 0;
  data = 0;
  lastOutput = 0;
  chunking = false;
  finished = true;
  interpolate = false;
  bufferSize = 0;
  channels = 0;
  time = 0.0;
}

void WvIn :: closeFile( void )
{
  if ( fd ) fclose( fd );
  finished = true;
}

void WvIn :: openFile( const char *fileName, bool raw )
{
  closeFile();

  // Try to open the file.
  fd = fopen(fileName, "rb");
  if (!fd) {
    sprintf(msg, "WvIn: Could not open or find file (%s).", fileName);
    handleError(msg, StkError::FILE_NOT_FOUND);
  }

  unsigned long lastChannels = channels;
  unsigned long samples, lastSamples = (bufferSize+1)*channels;
  bool result = false;
  if ( raw )
    result = getRawInfo( fileName );
  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 );
    else if ( !strncmp(header, ".snd", 4) )
      result = getSndInfo( fileName );
    else if ( !strncmp(header, "FORM", 4) &&
              (!strncmp(&header[8], "AIFF", 4) || !strncmp(&header[8], "AIFC", 4) ) )
      result = getAifInfo( fileName );
    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 );
      else {
        sprintf(msg, "WvIn: File (%s) format unknown.", fileName);
        handleError(msg, StkError::FILE_UNKNOWN_FORMAT);
      }
    }
  }

  if ( result == false )
    handleError(msg, StkError::FILE_ERROR);

  // Allocate new memory if necessary.
  samples = (bufferSize+1)*channels;
  if ( lastSamples < samples ) {
    if ( data ) delete [] data;
    data = (MY_FLOAT *) new MY_FLOAT[samples];
  }
  if ( lastChannels < channels ) {
    if ( lastOutput ) delete [] lastOutput;
    lastOutput = (MY_FLOAT *) new MY_FLOAT[channels];
  }

  if ( fmod(rate, 1.0) != 0.0 ) interpolate = true;
  chunkPointer = 0;
  reset();
  readData( 0 );  // Load file data.
  normalize();
  finished = false;
  return;

 error:
  sprintf(msg, "WvIn: Error reading file (%s).", fileName);
  handleError(msg, StkError::FILE_ERROR);
}

bool WvIn :: getRawInfo( const char *fileName )
{
  // Use the system call "stat" to determine the file length.
  struct stat filestat;
  if ( stat(fileName, &filestat) == -1 ) {
    sprintf(msg, "WvIn: Could not stat RAW file (%s).", fileName);
    return false;
  }

  fileSize = (long) filestat.st_size / 2;  // length in 2-byte samples
  bufferSize = fileSize;
  if (fileSize > CHUNK_THRESHOLD) {
    chunking = true;
    bufferSize = CHUNK_SIZE;
    gain = 1.0 / 32768.0;
  }

  // STK rawwave files have no header and are assumed to contain a
  // monophonic stream of 16-bit signed integers in big-endian byte
  // order with a sample rate of 22050 Hz.
  channels = 1;
  dataOffset = 0;
  rate = (MY_FLOAT) 22050.0 / Stk::sampleRate();
  fileRate = 22050.0;
  interpolate = false;
  dataType = STK_SINT16;
  byteswap = false;
#ifdef __LITTLE_ENDIAN__
  byteswap = true;
#endif

  return true;
}

bool WvIn :: 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.
  SINT16 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);
#endif
  if (format_tag != 1 && format_tag != 3 ) { // PCM = 1, FLOAT = 3
    sprintf(msg, "WvIn: %s contains an unsupported data format type (%d).", fileName, 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 = (MY_FLOAT) srate;

  // Set default rate based on file sampling rate.
  rate = (MY_FLOAT) ( srate / Stk::sampleRate() );

  // 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 == 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 ) {
    sprintf(msg, "WvIn: %d bits per sample with data format %d are not supported (%s).", temp, format_tag, 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
    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 = 8 * bytes / temp / channels;  // sample frames
  bufferSize = fileSize;
  if (fileSize > CHUNK_THRESHOLD) {
    chunking = true;
    bufferSize = CHUNK_SIZE;
  }

  dataOffset = ftell(fd);
  byteswap = false;
#ifndef __LITTLE_ENDIAN__
  byteswap = true;
#endif

  return true;

 error:
  sprintf(msg, "WvIn: Error reading WAV file (%s).", fileName);
  return false;
}

bool WvIn :: getSndInfo( const char *fileName )
{
  // Determine the data type.
  SINT32 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 == 5) dataType = STK_SINT32;
  else if (format == 6) dataType = STK_FLOAT32;
  else if (format == 7) dataType = STK_FLOAT64;
  else {
    sprintf(msg, "WvIn: data format in file %s is not supported.", fileName);
    return false;
  }

  // Get file sample rate from the header.
  SINT32 srate;
  if ( fread(&srate, 4, 1, fd) != 1 ) goto error;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&srate);
#endif
  fileRate = (MY_FLOAT) srate;

  // Set default rate based on file sampling rate.
  rate = (MY_FLOAT) ( srate / sampleRate() );

  // Get number of channels from the header.
  SINT32 chans;
  if ( fread(&chans, 4, 1, fd) != 1 ) goto error;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&chans);
#endif
  channels = chans;

  if ( fseek(fd, 4, SEEK_SET) == -1 ) goto error;
  if ( fread(&dataOffset, 4, 1, fd) != 1 ) goto error;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&dataOffset);
#endif

  // Get length of data from the header.
  if ( fread(&fileSize, 4, 1, fd) != 1 ) goto error;
#ifdef __LITTLE_ENDIAN__
  swap32((unsigned char *)&fileSize);
#endif
  fileSize /= 2 * channels;  // Convert to sample frames.
  bufferSize = fileSize;
  if (fileSize > CHUNK_THRESHOLD) {
    chunking = true;
    bufferSize = CHUNK_SIZE;
  }

  byteswap = false;
#ifdef __LITTLE_ENDIAN__
  byteswap = true;
#endif

  return true;

 error:
  sprintf(msg, "WvIn: Error reading SND file (%s).", fileName);
  return false;
}

bool WvIn :: 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
    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
  bufferSize = fileSize;
  if (fileSize > CHUNK_THRESHOLD) {
    chunking = true;
    bufferSize = CHUNK_SIZE;
  }

  // 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 = (MY_FLOAT) mantissa;

  // Set default rate based on file sampling rate.
  rate = (MY_FLOAT) ( fileRate / sampleRate() );

  // 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 == 32 ) dataType = STK_SINT32;
  }
  else {
    if ( fread(&id, 4, 1, fd) != 1 ) goto error;
    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 ) {
    sprintf(msg, "WvIn: %d bits per sample in file %s are not supported.", temp, fileName);
    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
    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:
  sprintf(msg, "WvIn: Error reading AIFF file (%s).", fileName);
  return false;
}

bool WvIn :: getMatInfo( const char *fileName )
{
  // Verify this is a version 5 MAT-file format.
  char head[4];
  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.
  if ( strstr(head, "0") ) {
    sprintf(msg, "WvIn: %s appears to be a Version 4 MAT-file, which is not currently supported.",
            fileName);
    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

  // Check the data element type
  SINT32 datatype;
  if ( fread(&datatype, 4, 1, fd) != 1 ) goto error;
  if ( byteswap ) swap32((unsigned char *)&datatype);
  if (datatype != 14) {
    sprintf(msg, "WvIn: The file does not contain a single Matlab array (or matrix) data element.");
    return false;
  }

  // Determine the array data type.
  SINT32 tmp;
  SINT32 size;
  if ( fseek(fd, 168, SEEK_SET) == -1 ) goto error;
  if ( fread(&tmp, 4, 1, fd) != 1 ) goto error;
  if (byteswap) swap32((unsigned char *)&tmp);
  if (tmp == 1) {  // array name > 4 characters
    if ( fread(&tmp, 4, 1, fd) != 1 ) goto error;  // get array name length
    if (byteswap) swap32((unsigned char *)&tmp);
    size = (SINT32) ceil((float)tmp / 8);
    if ( fseek(fd, size*8, SEEK_CUR) == -1 ) goto error;  // jump over array name
  }
  else { // array name <= 4 characters, compressed data element
    if ( fseek(fd, 4, SEEK_CUR) == -1 ) goto error;
  }
  if ( fread(&tmp, 4, 1, fd) != 1 ) goto error;
  if (byteswap) swap32((unsigned char *)&tmp);
  if ( tmp == 1 ) dataType = STK_SINT8;
  else if ( tmp == 3 ) dataType = STK_SINT16;
  else if ( tmp == 5 ) dataType = STK_SINT32;
  else if ( tmp == 7 ) dataType = STK_FLOAT32;
  else if ( tmp == 9 ) dataType = STK_FLOAT64;
  else {
    sprintf(msg, "WvIn: The MAT-file array data format (%d) is not supported.", tmp);
    return false;
  }

  // Get number of rows from the header.
  SINT32 rows;
  if ( fseek(fd, 160, SEEK_SET) == -1 ) goto error;
  if ( fread(&rows, 4, 1, fd) != 1 ) goto error;
  if (byteswap) swap32((unsigned char *)&rows);

  // Get number of columns from the header.
  SINT32 columns;
  if ( fread(&columns,4, 1, fd) != 1 ) goto error;
  if (byteswap) swap32((unsigned char *)&columns);

  // Assume channels = smaller of rows or columns.
  if (rows < columns) {
    channels = rows;
    fileSize = columns;
  }
  else {
    sprintf(msg, "WvIn: Transpose the MAT-file array so that audio channels fill matrix rows (not columns).");
    return false;
  }
  bufferSize = fileSize;
  if (fileSize > CHUNK_THRESHOLD) {
    chunking = true;
    bufferSize = CHUNK_SIZE;
  }

  // Move read pointer to the data in the file.
  SINT32 headsize;
  if ( fseek(fd, 132, SEEK_SET) == -1 ) goto error;
  if ( fread(&headsize, 4, 1, fd) != 1 ) goto error; // file size from 132nd byte
  if (byteswap) swap32((unsigned char *)&headsize);
  headsize -= fileSize * 8 * channels;
  if ( fseek(fd, headsize, SEEK_CUR) == -1 ) goto error;
  dataOffset = ftell(fd);

  // Assume MAT-files have 44100 Hz sample rate.
  fileRate = 44100.0;

  // Set default rate based on file sampling rate.
  rate = (MY_FLOAT) ( fileRate / sampleRate() );

  return true;

 error:
  sprintf(msg, "WvIn: Error reading MAT-file (%s).", fileName);
  return false;
}

void WvIn :: readData( unsigned long index )
{
  while (index < (unsigned long)chunkPointer) {
    // Negative rate.
    chunkPointer -= CHUNK_SIZE;
    bufferSize = CHUNK_SIZE;
    if (chunkPointer < 0) {
      bufferSize += chunkPointer;
      chunkPointer = 0;
    }
  }
  while (index >= chunkPointer+bufferSize) {
    // Positive rate.
    chunkPointer += CHUNK_SIZE;
    bufferSize = CHUNK_SIZE;
    if ( (unsigned long)chunkPointer+CHUNK_SIZE >= fileSize) {
      bufferSize = fileSize - chunkPointer;
    }
  }

  long i, length = bufferSize;
  bool endfile = (chunkPointer+bufferSize == fileSize);
  if ( !endfile ) length += 1;

  // Read samples into data[].  Use MY_FLOAT data structure
  // to store samples.
  if ( dataType == STK_SINT16 ) {
    SINT16 *buf = (SINT16 *)data;
    if (fseek(fd, dataOffset+(long)(chunkPointer*channels*2), SEEK_SET) == -1) goto error;
    if (fread(buf, length*channels, 2, fd) != 2 ) goto error;
    if ( byteswap ) {
      SINT16 *ptr = buf;
      for (i=length*channels-1; i>=0; i--)
        swap16((unsigned char *)(ptr++));
    }
    for (i=length*channels-1; i>=0; i--)
      data[i] = buf[i];
  }
  else if ( dataType == STK_SINT32 ) {
    SINT32 *buf = (SINT32 *)data;
    if (fseek(fd, dataOffset+(long)(chunkPointer*channels*4), SEEK_SET) == -1) goto error;
    if (fread(buf, length*channels, 4, fd) != 4 ) goto error;
    if ( byteswap ) {
      SINT32 *ptr = buf;
      for (i=length*channels-1; i>=0; i--)
        swap32((unsigned char *)(ptr++));
    }
    for (i=length*channels-1; i>=0; i--)
      data[i] = buf[i];
  }
  else if ( dataType == STK_FLOAT32 ) {
    FLOAT32 *buf = (FLOAT32 *)data;
    if (fseek(fd, dataOffset+(long)(chunkPointer*channels*4), SEEK_SET) == -1) goto error;
    if (fread(buf, length*channels, 4, fd) != 4 ) goto error;
    if ( byteswap ) {
      FLOAT32 *ptr = buf;
      for (i=length*channels-1; i>=0; i--)
        swap32((unsigned char *)(ptr++));
    }
    for (i=length*channels-1; i>=0; i--)
      data[i] = buf[i];
  }
  else if ( dataType == STK_FLOAT64 ) {
    FLOAT64 *buf = (FLOAT64 *)data;
    if (fseek(fd, dataOffset+(long)(chunkPointer*channels*8), SEEK_SET) == -1) goto error;
    if (fread(buf, length*channels, 8, fd) != 8 ) goto error;
    if ( byteswap ) {
      FLOAT64 *ptr = buf;
      for (i=length*channels-1; i>=0; i--)
        swap64((unsigned char *)(ptr++));
    }
    for (i=length*channels-1; i>=0; i--)
      data[i] = buf[i];
  }
  else if ( dataType == STK_SINT8 ) {
    unsigned char *buf = (unsigned char *)data;
    if (fseek(fd, dataOffset+(long)(chunkPointer*channels), SEEK_SET) == -1) goto error;
    if (fread(buf, length*channels, 1, fd) != 1 ) goto error;
    for (i=length*channels-1; i>=0; i--)
      data[i] = buf[i] - 128.0;  // 8-bit WAV data is unsigned!
  }

  // If at end of file, repeat last sample frame for interpolation.
  if ( endfile ) {
    for (unsigned int j=0; j<channels; j++)
      data[bufferSize*channels+j] = data[(bufferSize-1)*channels+j];
  }

  if (!chunking) {
    fclose(fd);
    fd = 0;
  }

  return;

 error:
  sprintf(msg, "WvIn: Error reading file data.");
  handleError(msg, StkError::FILE_ERROR);
}

void WvIn :: reset(void)
{
  time = (MY_FLOAT) 0.0;
  for (unsigned int i=0; i<channels; i++)
    lastOutput[i] = (MY_FLOAT) 0.0;
  finished = false;
}

void WvIn :: normalize(void)
{
  this->normalize((MY_FLOAT) 1.0);
}

// Normalize all channels equally by the greatest magnitude in all of the data.
void WvIn :: normalize(MY_FLOAT peak)
{
  if (chunking) {
    if ( dataType == STK_SINT8 ) gain = peak / 128.0;
    else if ( dataType == STK_SINT16 ) gain = peak / 32768.0;
    else if ( dataType == STK_SINT32 ) gain = peak / 2147483648.0;
    else if ( dataType == STK_FLOAT32 || dataType == STK_FLOAT64 ) gain = peak;

    return;
  }

  unsigned long i;
  MY_FLOAT max = (MY_FLOAT) 0.0;

  for (i=0; i<channels*bufferSize; i++) {
    if (fabs(data[i]) > max)
      max = (MY_FLOAT) fabs((double) data[i]);
  }

  if (max > 0.0) {
    max = (MY_FLOAT) 1.0 / max;
    max *= peak;
    for (i=0;i<=channels*bufferSize;i++)
	    data[i] *= max;
  }
}

unsigned long WvIn :: getSize(void) const
{
  return fileSize;
}

unsigned int WvIn :: getChannels(void) const
{
  return channels;
}

MY_FLOAT WvIn :: getFileRate(void) const
{
  return fileRate;
}

bool WvIn :: isFinished(void) const
{
  return finished;
}

void WvIn :: setRate(MY_FLOAT aRate)
{
  rate = aRate;

  // If negative rate and at beginning of sound, move pointer to end
  // of sound.
  if ( (rate < 0) && (time == 0.0) ) time += rate + fileSize;

  if (fmod(rate, 1.0) != 0.0) interpolate = true;
  else interpolate = false;
}

void WvIn :: addTime(MY_FLOAT aTime)   
{
  // Add an absolute time in samples 
  time += aTime;

  if (time < 0.0) time = 0.0;
  if (time >= fileSize) {
    time = fileSize;
    finished = true;
  }
}

void WvIn :: setInterpolate(bool doInterpolate)
{
  interpolate = doInterpolate;
}

const MY_FLOAT *WvIn :: lastFrame(void) const
{
  return lastOutput;
}

MY_FLOAT WvIn :: lastOut(void) const
{
  if ( channels == 1 )
    return *lastOutput;

  MY_FLOAT output = 0.0;
  for (unsigned int i=0; i<channels; i++ ) {
    output += lastOutput[i];
  }
  return output / channels;
}

MY_FLOAT WvIn :: tick(void)
{
  tickFrame();
  return lastOut();
}

MY_FLOAT *WvIn :: tick(MY_FLOAT *vector, unsigned int vectorSize)
{
  for ( unsigned int i=0; i<vectorSize; i++ )
    vector[i] = tick();

  return vector;
}

const MY_FLOAT *WvIn :: tickFrame(void)
{
  register MY_FLOAT tyme, alpha;
  register unsigned long i, index;

  if (finished) return lastOutput;

  tyme = time;
  if (chunking) {
    // Check the time address vs. our current buffer limits.
    if ( (tyme < chunkPointer) || (tyme >= chunkPointer+bufferSize) )
      this->readData((long) tyme);
    // Adjust index for the current buffer.
    tyme -= chunkPointer;
  }

  // Integer part of time address.
  index = (long) tyme;

  if (interpolate) {
    // Linear interpolation ... fractional part of time address.
    alpha = tyme - (MY_FLOAT) index;
    index *= channels;
    for (i=0; i<channels; i++) {
      lastOutput[i] = data[index];
      lastOutput[i] += (alpha * (data[index+channels] - lastOutput[i]));
      index++;
    }
  }
  else {
    index *= channels;
    for (i=0; i<channels; i++)
      lastOutput[i] = data[index++];
  }

  if (chunking) {
    // Scale outputs by gain.
    for (i=0; i<channels; i++)  lastOutput[i] *= gain;
  }

  // Increment time, which can be negative.
  time += rate;
  if ( time < 0.0 || time >= fileSize ) finished = true;

  return lastOutput;
}

MY_FLOAT *WvIn :: tickFrame(MY_FLOAT *frameVector, unsigned int frames)
{
  unsigned int j;
  for ( unsigned int i=0; i<frames; i++ ) {
    tickFrame();
    for ( j=0; j<channels; j++ )
      frameVector[i*channels+j] = lastOutput[j];
  }

  return frameVector;
}


More information about the Stk mailing list