function samplifyFile(infile, outfile, opt, fmt, delim)
%samplifyFile Compress a signal file using Samplify codec
% samplifyFile(infile,outfile,opt,fmt,delim) parses the file referenced
% by infile, whose format is determined by fmt and delim, samplifies the
% signal using the settings contained in the samplify configuration
% structure opt, and finally stores the compressed data in the Samplify file
% format (.sfy file).
%
% infile can point to a text or binary file. If fmt is 'dec' or 'hex',
% then infile is assumed to reference an ASCII text file with samples
% in decimal or hexadecimal (a preceding '0x' or '0X') format,
% respectively. In the case of a decimal or hexadecimal text file,
% samples are seperated by the delimiter contained in delim. Internally,
% samplifyFile uses DLMREAD to parse text files and if delim isn't specified,
% DLMREAD will guess the delimiter.
%
% If you do not provide a delimiter for text-based parsing of signal files,
% SAMPLIFYFILE will choose amongst one of the following:
%
% 1) tab ('\t')
% 2) comma (',')
% 3) semi-colon (';')
% 4) colon (':')
% 5) pipe ('|')
% 6) newline ('\r' or '\r\n')
%
% You can specify any delimiter, but bear in mind that DESAMPLIFYFILE
% only understands the following delimiters:
%
% 1) tab
% 2) comma
% 3) space
% 4) newline ('\n')
% 5) CRLF ('\r\n')
%
% What this means is that if you specify a delimiter that DESAMPLIFY does
% not about, you will still be able to compress an ASCII text signal
% file, but upon desamplification you will notice that the desamplified
% file (.dsy) will have a different delimiter. If in SAMPLIFYFILE you
% choose an unknown delimiter, the default delimiter in the .dsy file is
% #4, the new-line
%
% opt.InputDataType is mandatory and is used to tell SAMPLIFYFILE how it
% should interpret the samples contained in an ASCII file. That is, if
% fmt is 'dec' and opt.InputDataType is 'int16', the data after being
% read in via DLMREAD will be casted using INT16. Likewise for hex data,
% SAMPLIFYFILE needs know if the data is signed or not, and it uses the
% InputDataType field in the Samplify options struct to determine this
% information. Hexadecimal formatting of floating-point data is not supported.
%
% infile points to a binary file if fmt is 'int8', 'uint8', 'int16', or
% 'uint16', 'float', or 'double'. In the case of a binary file the final
% input argument delim is either ignored or need not be specified. The
% data will be read in using FREAD and then casted to the type specified
% in opt.InputDataType.
%
% Examples
% --------
% % losslessly compress a binary file with signed 16-bit samples
% % (note here it is NOT necessary to provide opt.InputDataType)
% opt = samplifyOptions('Mode','lossless','InputDataType', 'int16');
% infile = 'test_f2_b5_80dB.dat';
% outfile = 'test_f2_b5_80dB.sfy';
% samplifyFile(infile, outfile, opt, 'int16');
%
% % 2:1 fixed-rate compression of 16-bit signed signal in decimal text file
% opt = samplifyOptions('Mode','fixedRate','CompressionParameter',2.0,'InputDataType','int16');
% infile = 'test_f2_b5_80dB.txt';
% outfile = 'test_f2_b5_80dB.sfy';
% samplifyFile(infile, outfile, opt, 'dec');
%
% % same as above, but this time for a CSV (comma separated value)
% % file, with samples stored in hexadecimal format
% opt = samplifyOptions('Mode','fixedRate','CompressionParameter',2.0,'InputDataType','int16');
% infile = 'test_f2_b5_80dB.csv';
% outfile = 'test_f2_b5_80dB.sfy';
% samplifyFile(infile, outfile, opt, 'hex', ',');
%
% See also SAMPLIFYOPTIONS, DESAMPLIFYFILE
% Copyright 2005 Samplify Systems LLC
% Revision: 1.0 (2005/11/23)
% sanity check - # args
if nargin < 4
error('samplifyFile requires at least 4 input arguments.');
end
% sanity check - all args must be of type char
if ~ischar(infile) | ~ischar(outfile) | ~ischar(fmt)
error('All input arguments except samplify options must be of type char.');
end
if 5 == nargin
if ~ischar(delim)
error('All input arguments must be of type char.');
end
else
delim = [];
end
% sanity check - does infile exist?
ifid = fopen(infile, 'r');
if -1 == ifid
error(['Could not open ' infile]);
end
fclose(ifid);
% sanity check - require InputDataType field
if ~isfield(opt, 'InputDataType')
error('samplifyFile requires InputDataType field to be specified in options struct.');
end
% sanity check - floating point & hex incompatible
if strcmpi('hex',fmt) & ...
(strcmpi('float',opt.InputDataType) | strcmpi('double',opt.InputDataType))
error('Cannot read floating-point input data with hexadecimal ASCII files.');
end
[samples, btxt, delim] = read_input_data_from_file(infile, fmt, delim, opt);
[compressed, opt] = samplify(samples, opt);
% tell samplify file numerical format (either decimal, hex, or binary)
if btxt
if strcmpi('dec', fmt)
num_fmt = 0;
else
num_fmt = 1; % hex, see samp_lib.h
end
else
num_fmt = -1; % binary, so key off of class(samples)
end
write_sfy_file(compressed, opt, outfile, length(samples), num_fmt, class(samples), delim);
function [samples, btxt, delim] = read_input_data_from_file(infile, fmt, delim, opt)
%
% READ_INPUT_DATA_FROM_FILE
%
if strcmpi('dec', fmt) | strcmpi('hex', fmt)
[samples, delim] = read_txt_data_from_file(infile, fmt, delim, opt);
btxt = 1;
elseif strcmpi('int8', fmt) | strcmpi('uint8', fmt) | ...
strcmpi('int16', fmt) | strcmpi('uint16', fmt) | ...
strcmpi(fmt, 'float') | strcmpi(fmt, 'double')
samples = read_binary_data_from_file(infile, fmt, opt);
btxt = 0;
delim = [];
else
error(['Unknown format specifier ' fmt]);
end
function [samples, delim] = read_txt_data_from_file(infile, fmt, delim, opt)
%
% READ_INPUT_DATA_FROM_FILE
%
if ~isempty(delim)
if strcmpi(fmt,'dec')
samples = dlmread(infile, delim);
else
samples = read_hex_data_from_file(infile, opt.InputDataType, delim);
end
else
if strcmpi(fmt,'dec')
samples = dlmread(infile);
else
samples = read_hex_data_from_file(infile, opt.InputDataType);
end
% even though the delimiter was inferred, we still need it
% (see delim.m and iofun/private/guessdelim.m)
fid = fopen(infile);
str = fread(fid, 4096,'*char')';
fclose(fid);
delim = guessdelim(str);
end
samples = handle_data_type(samples, opt);
function samples = read_hex_data_from_file(infile, data_type, delim)
if 2 == nargin
records = importdata(infile);
else
records = importdata(infile, delim);
end
% get rid of the '0x' or '0X'
N = length(records);
records = char(records);
records = records(:,3:end);
% cycle thru & convert to decimal representation
for ii=1:N
samples(ii) = hex2dec(records(ii,:));
end
% take care of signed/unsigned if necessary
if 'i'==data_type(1) | 'I'==data_type(1)
[cutoff, minval] = twoscomp(data_type);
inegative = find(samples>cutoff);
samples(inegative) = minval + samples(inegative);
end
function samples = read_binary_data_from_file(infile, fmt, opt)
%
% READ_BINARY_DATA_FROM_FILE
%
fid = fopen(infile, 'rb');
data = fread(fid, inf, fmt);
samples = handle_data_type(data, opt);
fclose(fid);
function delim = guessdelim(str)
%GUESSDELIM Take stab at default delim for this string.
% Copyright 1984-2002 The MathWorks, Inc.
% $Revision: 1.5 $ $Date: 2002/06/17 13:25:28 $
% bail if str is empty
if isempty(str)
delim = '';
return;
end
% count num lines
numLines = length(find(str == sprintf('\n')));
% set of column delimiters to try - ordered by quality as delim
delims = {sprintf('\t'), ',', ';', ':', '|', ' '};
% remove any delims which don't appear at all
% need to rethink based on headers and footers which are plain text
goodDelims = {};
goodDelimCounts = [];
for i = 1:length(delims)
numDelims(i) = length(find(str == sprintf(delims{i})));
if numDelims(i) ~= 0
% this could be a delim
goodDelims{end+1} = delims{i};
end
end
% if no delims were found, return newline
if isempty(goodDelims)
delim = sprintf('\n');
return;
end
% if the num delims is greater or equal to num lines,
% this will be the default (so return)
for i = 1:length(delims)
delim = delims{i};
if numDelims(i) > numLines
return;
end
end
% no delimiter was a clear win from above, choose the first
% in the delimiter list
delim = goodDelims{1};