Update to wavread.m and wavwrite.m

Michael Zeising <[email protected]> Sun, 07 Oct 2007 12:28:08 +0200
Newsgroups gmane.comp.gnu.octave.sources
Message-ID <[email protected]>
This is a multi-part message in MIME format.
--------------060302050900000805070406
Content-Type: text/plain; charset=ISO-8859-15; format=flowed
Content-Transfer-Encoding: 7bit

Hi there,

I've currently noticed that the sample scaling in my WAV functions is 
wrong and causes asymmetries and DC components (thanks to Denis 
Sbragion). I've attached an updated version of them and I hope you can 
include them into the distribution of octave. Thanks in advance!

Regards,
Michael Zeising


--------------060302050900000805070406
Content-Type: text/x-objcsrc;
 name="wavread.m"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="wavread.m"

## Copyright (C) 2007 Michael Zeising
##
## This file is part of Octave.
##
## Octave 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, or (at your option)
## any later version.
##
## Octave 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 Octave; see the file COPYING.  If not, write to the Free
## Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
## 02110-1301, USA.

## -*- texinfo -*-
## @deftypefn {Function File} {@var{y} =} wavread (@var{filename})
## Load the RIFF/WAVE sound file @var{filename}, and return the samples
## in vector @var{y}.  If the file contains multichannel data, then
## @var{y} is a matrix with the channels represented as columns.
##
## @deftypefnx {Function File} {[@var{y}, @var{Fs}, @var{bits}] =} wavread (@var{filename})
## Additionally return the sample rate (@var{fs}) in Hz and the number of bits 
## per sample (@var{bits}).
##
## @deftypefnx {Function File} {[@dots{}] =} wavread (@var{filename}, @var{n})
## Read only the first @var{n} samples from each channel.
##
## @deftypefnx {Function File} {[@dots{}] =} wavread (@var{filename},[@var{n1} @var{n2}])
## Read only samples @var{n1} through @var{n2} from each channel.
##
## @deftypefnx {Function File} {[@var{samples}, @var{channels}] =} wavread (@var{filename}, "size")
## Return the number of samples (@var{n}) and channels (@var{ch})
## instead of the audio data.
## @seealso{wavwrite}
## @end deftypefn

## Author: Michael Zeising <[email protected]>
## Created: 07 October 2007

function [y, samples_per_sec, bits_per_sample] = wavread (filename, param)

  FORMAT_PCM        = 0x0001;   # PCM (8/16/32 bit)
  FORMAT_IEEE_FLOAT = 0x0003;   # IEEE float (32/64 bit)
  BYTEORDER         = "ieee-le";

  if (nargin < 1 || nargin > 2)
    print_usage ();
  endif

  if (! ischar (filename))
    error ("wavwrite: expecting filename to be a character string");
  endif

  # open file for binary reading
  [fid, msg] = fopen (filename, "rb");
  if (fid < 0)
    error ("wavread: %s", msg);
  endif
  
  ## check for RIFF/WAVE header
  ck_id = char (fread (fid, 4))';
  fseek (fid, 4, SEEK_CUR);
  wave_id = char (fread (fid, 4))';
  if (ck_id != "RIFF" || wave_id != "WAVE")
    fclose (fid);
    error ("wavread: file contains no RIFF/WAVE signature");
  endif
  
  ## find format chunk within the next 256 (4*64) bytes
  i = 1;
  while (true)
    if (char (fread (fid, 4))' == "fmt ");
      break;
    endif
    if (i++ == 64)
      fclose (fid);
      error ("wavread: file contains no format chunk");
    endif
  endwhile

  ## format chunk size
  ck_size = fread (fid, 1, "uint32", 0, BYTEORDER);         
  
  ## sample format code
  format_tag = fread (fid, 1, "uint16", 0, BYTEORDER);
  if (format_tag != FORMAT_PCM && format_tag != FORMAT_IEEE_FLOAT)
    fclose (fid);
    error ("wavread: sample format %#x is not supported", format_tag);
  endif

  ## number of interleaved channels  
  channels = fread (fid, 1, "uint16", 0, BYTEORDER);

  ## sample rate
  samples_per_sec = fread (fid, 1, "uint32", 0, BYTEORDER);

  ## bits per sample
  fseek (fid, 6, SEEK_CUR);
  bits_per_sample = fread (fid, 1, "uint16", 0, BYTEORDER);

  ## ignore the rest of the chunk
  fseek (fid, ck_size-16, SEEK_CUR);
  
  ## find data chunk
  i = 1;
  while (true)
    if (char (fread (fid, 4))' == "data")
      break;
    endif
    if (i++ == 64)
      fclose (fid);
      error ("wavread: file contains no data chunk");
    endif
  end

  ## data chunk size
  ck_size = fread (fid, 1, "uint32", 0, BYTEORDER);
  
  ## determine sample data type
  if (format_tag == FORMAT_PCM)
    switch (bits_per_sample)
      case 8
        format = "uint8";
      case 16 
        format = "int16";
      case 24
	format = "uint8";
      case 32 
        format = "int32";
      otherwise
        fclose (fid);
        error ("wavread: %d bits sample resolution is not supported with PCM",
	       bits_per_sample);
    endswitch
  else
    switch (bits_per_sample)
      case 32 
        format = "float32";
      case 64 
        format = "float64";
      otherwise
        fclose (fid);
        error ("wavread: %d bits sample resolution is not supported with IEEE float",
	       bits_per_sample);
    endswitch
  endif
  
  ## parse arguments
  if (nargin == 1)
    length = inf;
  else
    if (size (param, 2) == 1)
      ## number of samples is given
      length = param * channels;
    elseif (size (param, 2) == 2)
      ## sample range is given
      if (fseek (fid, (param(1)-1) * channels * (bits_per_sample/8), SEEK_CUR) < 0)
        warning ("wavread: seeking failed");
      endif
      length = (param(2)-param(1)+1) * channels;
    elseif (size (param, 2) == 4 && char (param) == "size")
      ## size of the file is requested
      fclose (fid);
      y = [ck_size/channels/(bits_per_sample/8), channels];
      return
    else
      fclose (fid);
      error ("wavread: invalid argument 2");
    endif
  endif

  ## read samples and close file
  if (bits_per_sample == 24)
    length *= 3;
  endif
  [yi, n] = fread (fid, length, format, 0, BYTEORDER);
  fclose (fid);

  ## check data
  if (mod (numel (yi), channels) != 0)
    error ("wavread: data in %s doesn't match the number of channels",
	   filename);
  endif

  if (bits_per_sample == 24)
    yi = reshape (yi, 3, rows(yi)/3)';
    yi(yi(:,3) >= 128, 3) -= 256;
    yi = yi * [1; 256; 65536];
  end
  if (format_tag == FORMAT_PCM)
    ## normalize samples
    switch (bits_per_sample)
      case 8
        yi = (yi - 128)/127;
      case 16
        yi /= 32767;
      case 24
		yi /= 8388607;
      case 32
        yi /= 2147483647;
    endswitch
  endif
  
  ## deinterleave
  nr = numel (yi) / channels;
  y = reshape (yi, channels, nr)';
  
endfunction


--------------060302050900000805070406
Content-Type: text/x-objcsrc;
 name="wavwrite.m"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="wavwrite.m"

## Copyright (C) 2007 Michael Zeising
##
## This file is part of Octave.
##
## Octave 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, or (at your option)
## any later version.
##
## Octave 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 Octave; see the file COPYING.  If not, write to the Free
## Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
## 02110-1301, USA.

## -*- texinfo -*-
## @deftypefn {Function File} {} wavwrite (@var{filename}, @var{y})
## Write @var{y} to the canonical RIFF/WAVE sound file @var{filename}. A sample 
## rate of 8000 Hz and 16-bit samples are assumed. Each column of the data 
## represents a separate channel.
##
## @deftypefnx {Function File} {} wavwrite (@var{filename}, @var{y}, @var{fs})
## Set the sample rate to @var{fs} Hz.
##
## @deftypefnx {Function File} {} wavwrite (@var{filename}, @var{y}, @var{fs}, @var{bits})
## Set the sample rate to @var{fs} Hz and resolution to @var{bits} bits.
## @seealso{wavread}
## @end deftypefn

## Author: Michael Zeising <[email protected]>
## Created: 07 October 2007

function wavwrite (filename, y, samples_per_sec, bits_per_sample)

  BYTEORDER = "ieee-le";
  
  if (nargin < 2 || nargin > 4)
    print_usage ();
  endif

  ## test arguments
  if (columns (y) < 1)
    error ("wavwrite: Y must have at least one column");
  endif
  if (columns (y) > 2^15-1)
    error ("wavwrite: Y has more than 32767 columns (too many for a WAV-file)");
  endif

  ## parse arguments
  if (nargin < 3)
    samples_per_sec = 8000;
  endif

  if (nargin < 4)
    bits_per_sample = 16;
  endif

  ## determine sample format
  switch (bits_per_sample)
    case 8  
      format = "uint8";
    case 16 
      format = "int16";
    case 32 
      format = "int32";
    otherwise
      error ("wavwrite: sample resolution not supported");
  endswitch
  
  ## calculate filesize
  [n, channels] = size(y);

  ## size of data chunk
  ck_size = n*channels*(bits_per_sample/8);
  
  ## open file for writing binary

  if (! ischar (filename))
    error ("wavwrite: expecting filename to be a character string");
  endif
    
  [fid, msg] = fopen (filename, "wb");
  if (fid < 0)
    error ("wavwrite: %s", msg)
  endif
  
  ## write RIFF/WAVE header
  c = 0;
  c += fwrite (fid, "RIFF", "uchar");

  ## file size - 8
  c += fwrite (fid, ck_size + 36, "uint32", 0, BYTEORDER);
  c += fwrite (fid, "WAVEfmt ", "uchar");

  ## size of fmt chunk
  c += fwrite (fid, 16, "uint32", 0, BYTEORDER);

  ## sample format code (PCM)
  c += fwrite (fid, 1, "uint16", 0, BYTEORDER);

  ## channels
  c += fwrite (fid, channels, "uint16", 0, BYTEORDER);

  ## sample rate
  c += fwrite (fid, samples_per_sec, "uint32", 0, BYTEORDER);

  ## bytes per second
  bps = samples_per_sec*channels*bits_per_sample/8;
  c += fwrite (fid, bps, "uint32", 0, BYTEORDER);

  ## block align
  c += fwrite (fid, channels*bits_per_sample/8, "uint16", 0, BYTEORDER);

  c += fwrite (fid, bits_per_sample, "uint16", 0, BYTEORDER);   
  c += fwrite (fid, "data", "uchar");
  c += fwrite (fid, ck_size, "uint32", 0, BYTEORDER);
  
  if (c < 25)
    fclose (fid);
    error ("wavwrite: writing to file failed");
  endif
  
  ## interleave samples
  yi = reshape (y', n*channels, 1);
  
  ## scale samples
  switch (bits_per_sample)
    case 8
      yi = round (yi*127 + 128);
    case 16
      yi = round (yi*32767);
    case 32
      yi = round (yi*2147483647);
  endswitch
  
  ## write to file
  c = fwrite (fid, yi, format, 0, BYTEORDER);
  
  fclose (fid);
  
endfunction


--------------060302050900000805070406
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Octave-sources mailing list
[email protected]
https://www.cae.wisc.edu/mailman/listinfo/octave-sources

--------------060302050900000805070406--