function [y, opt] = samplify(x, opt)
%samplify Compress signal.
% [y, opt] = samplify(x,opt) compresses a vector of samples using the
% settings given in the Samplify configuration structure opt. The input
% signal x to be samplified *must* be a one-dimensional vector (either
% 1xN or Nx1), and can either be a normal MATLAB floating point array or
% of integer type 'int8', 'uint8', 'int16', or 'uint16'.
%
% The following fields in the configuration struct opt are pertinent to
% the operation of this Samplify For MATLAB function:
%
% 1) opt.Display: determines the verbosity of the samplify function,
% either 'off' or 'on'
% 2) opt.Mode: 'lossless', 'fixedRate', 'fixedDynRange', 'NoiseTrak'
% (see user's manual for complete description of these modes)
% 3) opt.CompressionParameter: scalar, whose value is interpreted based
% on the contents of #2.
% This parameter is completely ignored in lossless mode. In fixedRate it
% signifies the desired compression ratio (i.e. opt.CompressionMode = 2.0
% implies 2:1 compression between the samplified data and the
% uncompressed, original data). In fixed dynamic range ('fixedDynRange')
% mode the units of this field are dB, while in NoiseTrak mode the units
% are bits.
% 4) opt.InputDataType: One of 'int8', 'uint8', 'int16', 'uint16',
% 'float', or 'double'. This field gives the data type for how the sample
% values of x should be interpreted. It is not mandatory to specify this,
% and if not provided, it is inferred from the data type of x. That is, if
% x if of type UINT8, then that is the same as if opt.InputDataType='int8'.
%
% This function also modifies opt because depending on the value of
% opt.Mode, certain information potentially needed by DESAMPLIFY is
% returned back to the user inside of opt. In particular, the # of
% samples in x is stored here, as well the Samplify block sizes and
% noise-floor estimates (where appropriate). If floating-point
% compression is being used (i.e. opt.InputDataType='float',
% opt.InputDataType='double', _or_ x's data type is single/double precision
% _and_ opt.InputDataType is not explicitly specified), then opt will
% contain per-bock scale factors.
%
% Examples
% --------
% % losslessly compress a signal vector sig
% [c,opt] = samplify(sig, samplifyOptions('Mode','lossless'));
%
% % Verbose mode, 2:1 fixed-rate compression of 16-bit signed vector sig
% opt = samplifyOptions('Mode','fixedRate','CompressionParameter',2.0,'InputDataType','int16');
% [c,opt] = samplify(sig, opt);
%
% See also SAMPLIFYOPTIONS, DESAMPLIFY
% Copyright 2005 Samplify Systems LLC
% Revision: 1.0 (2005/11/23)
sanity_check_x(x);
sanity_check_options(opt);
if isfield(opt, 'Display')
if strcmpi(opt.Display, 'on')
display_options(opt);
end
end
xprime = handle_data_type(x, opt);
%if isfloat(xprime) MATLAB R12 doesn't have isfloat()
if isa(xprime,'float')
% sanity check, remember that floating-point and lossless compression
% isn't possible!
if strcmpi(opt.Mode, 'lossless')
error('Lossless mode not supported with floating-point input.');
end
% then we need to normalize the input to samplify such that the input
% vector fits nicely into a signed 16-bit array
[xprime, opt] = handle_floating_point_array(xprime, opt);
end
cparam = opt.CompressionParameter;
if strcmpi(opt.Mode, 'lossless')
[y, opt.orig_nsamps, opt.InputDataType, opt.block_sizes, opt.nf_est] = samplify_mex(1, xprime, 0);
elseif strcmpi(opt.Mode, 'fixedRate')
cparam = opt.CompressionParameter;
[y, opt.orig_nsamps, opt.InputDataType, opt.block_sizes, opt.nf_est, opt.dr] = samplify_mex(1, xprime, 1, cparam);
elseif strcmpi(opt.Mode, 'fixedDynRange')
[y, opt.orig_nsamps, opt.InputDataType, opt.block_sizes, opt.nf_est, opt.dr] = samplify_mex(1, xprime, 2, cparam);
elseif strcmpi(opt.Mode, 'NoiseTrak')
[y, opt.orig_nsamps, opt.InputDataType, opt.block_sizes, opt.nf_est, opt.dr] = samplify_mex(1, xprime, 3, cparam);
else
error(['Unknown mode ' opt.Mode]);
end
function sanity_check_x(x)
%
% SANITY_CHECK_X
%
if ~isnumeric(x)
error('Input samples have to be a numeric array.');
end
if ndims(x) > 2
error('Input samples must be a 1xN or Nx1 vector.');
else
if length(x) < 192
error('Input vector too small.');
else
[M N] = size(x);
if 1~=M & 1~=N
error('Input samples must be a 1xN or Nx1 vector.');
end
end
end
function sanity_check_options(options)
%
% SANITY_CHECK_OPTIONS
%
if ~isstruct(options)
error('Malformed samplify options structure.');
end
% leverage samplifyOptions.m, which already does all
% the error checking for us
% first build up the parameter value string
% 'PARAM1',VALUE1,'PARAM2',VALUE2,...
names = fieldnames(options);
params_vals = [];
for ifield = 1:length(names)
value = sprintf('options.%s', names{ifield});
if ~isempty(eval(value))
% User could theoretically pass in a samplify options struct
% containing come of the samplify.m generated fields, like
% options.block_sizes, options.nf_est, and so on. By verifying that
% if it's a numeric element it's length MUST be one we effectively
% filter out alot (not all) of these.
if isnumeric(eval(value))
if 1 == eval(sprintf('length(%s)', value))
params_vals = strcat(params_vals, sprintf(',%c%s%c,%f', 39, names{ifield}, 39, eval(value)));
end
elseif ischar(eval(value))
params_vals = strcat(params_vals, sprintf(',%c%s%c,%c%s%c', 39, names{ifield}, 39, 39, eval(value), 39));
else
error('parameter values must be either scalar or string.');
end
end
end
% this will bomb out with an error in the event of a mal-formed
% samplify options structure
optdummy = eval(sprintf('samplifyOptions(%s)', params_vals(2:end)));
% one can invoke samplifyOptions without specifying all of the
% options - however here I absolutely must have all of them!
if ~isfield(options,'Mode')
error('Samplify requires a mode (lossless, fixedRate, fixedDynRange, NoiseTrak) specification.');
end
if isempty(options.Mode)
error('Samplify requires a mode (lossless, fixedRate, fixedDynRange, NoiseTrak) specification.');
end
if ~strcmpi(options.Mode, 'lossless')
% require compression parameter arg
if ~isfield(options, 'CompressionParameter')
error('Missing compression parameter field.');
end
if isempty(options.CompressionParameter)
error('Missing compression parameter field.');
end
end
function display_options(options)
%
% DISPLAY_OPTIONS
%
disp('Samplify options are:');
disp([' Mode is ' options.Mode]);
if strcmpi(options.Mode, 'fixedRate')
disp(sprintf(' Compression ratio is %.3f', options.CompressionParameter));
elseif strcmpi(options.Mode, 'fixedDynRange')
disp([' Dynamic range is ' options.CompressionParameter ' dB']);
elseif strcmpi(options.Mode, 'NoiseTrak')
disp([' Noise-floor is ' options.CompressionParameter]);
end
if isfield(options, 'InputDataType') & ~isempty(options.InputDataType)
disp([' Data type hint is ' options.InputDataType]);
end
function [xprime, opt] = handle_floating_point_array(xprime, opt)
%
% HANDLE_FLOATING_POINT_ARRAY
%
[nwin, nblocks, istart, iend] = block_info(length(xprime));
for ii=1:nblocks
idx = [istart(ii):iend(ii)];
m = max( abs(xprime(idx)) );
opt.FloatingPointNormalization(ii) = m;
if 0 ~= m
xprime(idx) = (32767/m) .* xprime(idx);
end
end
xprime = int16(xprime);