function y = cl_celp( x )
% function y = cl_celp( x )
%
% Simulation of CELP Codec with Closed-Loop Pitch Predictor 
%
% x: codec input signal (sampled with 8 kHz, 16-bit quantized, i.e range -32768 ... 32767)
% y: codec output signal (sampled with 8 kHz, 16-bit quantized, i.e range -32768 ... 32767)
%   
% Author:  Markus Hauenstein
% Date:    16.01.2002 
% Contact: www.markus-hauenstein.de
%
% This program is copyrighted.

test = 0;

fprintf(1,'\nSimulation of CELP Codec with Closed-Loop Pitch Predictor')
fprintf(1,'\nInitializing')

%
% init parameters identical for both coder and decoder which influence the bitrate
%

% ltp parameters (number of samples)
numberOfSubblocks = 4;     
subblockLength = 40;
minDelay = 20;
maxDelay = 147;

% size of adaptive gain codebook
adapGainCBSize = 64;

% lpc: bits for quantization of predictor coefficients
predQuantBits = 4*[1 1 1 1 1 1 1 1 1 1];

% size of stochastic gain codebook
stochGainCBSize = 64;

% size of stochastic Vector codebook
stochVecCBSize = 1024;


%
% init parameters identical for both coder and decoder which do NOT influence the bitrate
%

blockLength = numberOfSubblocks * subblockLength;

% adaptive Vector codebook
adapVecCBSize = maxDelay-minDelay+1;

% create adaptive gain codebook
minAdapGain = -1024;
maxAdapGain = 1024;
adapGainCBmu = 64;
adapGainCB = crmucb(minAdapGain, maxAdapGain, adapGainCBmu, adapGainCBSize);

% lpc
lpcOrder = length(predQuantBits);
maxLarQ = 2.5;

% create stochastic gain codebook
minStochGain = -1024;
maxStochGain = 1024;
stochGainCBmu = 64;
stochGainCB = crmucb(minStochGain, maxStochGain, stochGainCBmu, stochGainCBSize);

% create normalized stochastic vector codebook
stochVecCBSize = 1024;
stochVecCBAmp = 16;
randn('seed',0);
stochVecCB = randn( blockLength, stochVecCBSize );
for i=1:stochVecCBSize,
  oneDivRms  = 1/sqrt((stochVecCB(:,i)' * stochVecCB(:,i)) / blockLength);
  stochVecCB(:,i) = oneDivRms * stochVecCB(:,i);
end
stochVecCB = stochVecCBAmp * stochVecCB;


% calculate and display required bitrate
sampleFrequency = 8000;
lpcBitRate = sum( predQuantBits ) * sampleFrequency / blockLength;
adapVecBitRate = ceil( log(adapVecCBSize)/log(2) ) * sampleFrequency / subblockLength;
adapGainBitRate = ceil( log(adapGainCBSize)/log(2) ) * sampleFrequency / subblockLength;
stochVecBitRate = ceil( log(stochVecCBSize)/log(2) ) * sampleFrequency / blockLength;
stochGainBitRate = ceil( log(stochGainCBSize)/log(2) ) * sampleFrequency / blockLength;
bitRate = lpcBitRate + adapVecBitRate + adapGainBitRate + stochVecBitRate + stochGainBitRate;
fprintf(1, '\nBitrate: %d bps', bitRate)


% format Input signal
x=x(:); % make column Vector
inputLength = length(x);
rest = inputLength - blockLength * floor(inputLength/blockLength);
if  rest == 0,
   numberOfBlocks = inputLength / blockLength;
else
   numberOfBlocks = ceil( inputLength / blockLength );
   x = [x; zeros( blockLength-rest, 1 )];
end


% initialize data storage for transmitted parameters 
aChannel = zeros(lpcOrder, numberOfBlocks);
adapGainChannel = zeros(numberOfSubblocks, numberOfBlocks);
adapVecChannel = zeros(numberOfSubblocks, numberOfBlocks);
stochGainChannel = zeros(1, numberOfBlocks);
stochVecChannel = zeros(1, numberOfBlocks);


%
% THIS IS CODER 
%

z = zeros(numberOfBlocks*blockLength,1);


fprintf(1,'\nCoding ')

% initialize coder variables

% ltp 
bestAdapGainIndices = zeros(numberOfSubblocks,1);
bestAdapVecIndices = zeros(numberOfSubblocks,1);
pastLpcExcite = zeros(maxDelay,1);
bestAdapExcite = zeros(blockLength,1);
bestStochExcite = zeros(blockLength,1);
invLpcAdapExcite = zeros(blockLength,1);
exciteBlock = zeros(blockLength,1);

% lpc 
zInvLpc = zeros(lpcOrder,1);
zInvLpc1 = zeros(lpcOrder,1);

% Vector quantization of LPC/LTP residual - helper variables for Vec quantization
synFilterOutSignals = zeros( blockLength, stochVecCBSize );
zeroOutput = zeros(blockLength,1);
zeroInput = zeros(blockLength,1);


% coder main loop
blockIndex = 1 : blockLength;

for i=1:numberOfBlocks,
  fprintf(1,'.')

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % get current block
  xBlock = x( blockIndex );


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % LPC
  lpcInputBlock = xBlock;

  % calculate linear prediction coefficients
  a = linpred( lpcInputBlock, lpcOrder );

  % simulate log-area-ratio quantization of LPC parameters   
  aQ = larpredq( a, maxLarQ, predQuantBits ); 
 

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % Analysis-by-Synthesis: 

  %
  % search for best adaptive excitation 
  %

  % take old filter states into account 
  zeroOutput = filter(1, [1;-aQ], zeroInput, zInvLpc);
  targetAdap = xBlock - zeroOutput; 

  [bestAdapGainIndices, bestAdapVecIndices] = ... 
      clltp( targetAdap, aQ, subblockLength, minDelay, adapGainCB, pastLpcExcite );

  bestAdapExcite = cradapex( subblockLength, ...
      bestAdapGainIndices, bestAdapVecIndices, adapGainCB, pastLpcExcite );

  %
  % search for best stochastic excitation
  %

  % take old filter states and adaptive excitation into account
  invLpcAdapExcite = filter(1, [1;-aQ], bestAdapExcite);
  targetStoch = targetAdap - invLpcAdapExcite; 

  % calculate all possible synthezized speech vectors 
  for l=1:stochVecCBSize,
    synFilterOutSignals(:,l) = filter( 1, [1;-aQ], stochVecCB(:,l) );
  end

  % gain-shape vector quantization of target   
  [bestStochGainIndex, bestStochVecIndex] = gsvecq( targetStoch, stochGainCB, synFilterOutSignals );


  % calculate new lpc filter states for next block 
  bestStochExcite = stochGainCB(bestStochGainIndex) * stochVecCB(:,bestStochVecIndex);
  exciteBlock = bestAdapExcite + bestStochExcite;
  [dummy, zInvLpc] = filter(1, [1;-aQ], exciteBlock, zInvLpc);

if test == 1,
  clg
  plot(xBlock,'g')
  hold
  plot(dummy,'r')
  pause
end

  % calculate new pastExcitation for next block
  pastLpcExciteExt = [pastLpcExcite; exciteBlock];
  pastLpcExcite = pastLpcExciteExt( blockLength+1 : maxDelay+blockLength ); 


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % simulate transmission of parameters via channel to decoder
  aChannel(:,i) = aQ;
  adapGainChannel(:,i) = bestAdapGainIndices;
  adapVecChannel(:,i) = bestAdapVecIndices;
  stochGainChannel(i) = bestStochGainIndex; 
  stochVecChannel(i) = bestStochVecIndex;

  blockIndex = blockIndex + blockLength;
end  % coder main loop


%
% THIS IS DECODER 
%

fprintf(1,'\nDecoding')

% initialize decoder variables
y = zeros(numberOfBlocks*blockLength,1);

% inverse ltp
pastLpcExcite = zeros(maxDelay,1);

% inverse lpc
zInvLpc = zeros(lpcOrder,1);


% decoder main loop
blockIndex = 1 : blockLength;

for i=1:numberOfBlocks,
  fprintf(1,'.')

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % create excitation  

  bestAdapGainIndices = adapGainChannel(:,i);
  bestAdapVecIndices = adapVecChannel(:,i);
  bestAdapExcite = cradapex( subblockLength, ...
      bestAdapGainIndices, bestAdapVecIndices, adapGainCB, pastLpcExcite );

  bestStochGainIndex = stochGainChannel(i);
  bestStochVecIndex = stochVecChannel(i);
  bestStochExcite = stochGainCB(bestStochGainIndex) * stochVecCB(:,bestStochVecIndex);

  exciteBlock = bestAdapExcite + bestStochExcite;


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % Inverse LPC (vocal tract filter)
  a = aChannel(:,i);
  [invLpcOutputBlock, zInvLpc] = filter(1, [1;-a], exciteBlock, zInvLpc);


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % construct output signal
  y( blockIndex ) = invLpcOutputBlock;


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % calculate new pastExcitation for next looping
  pastLpcExciteExt = [pastLpcExcite; exciteBlock];
  pastLpcExcite = pastLpcExciteExt( blockLength+1 : maxDelay+blockLength ); 

  blockIndex = blockIndex + blockLength;
end  % decoder main loop


% format output signal
y = y(1:inputLength);


if test == 1,
  clg
  plot(x,'g')
  hold
  plot(y,'r')
end

fprintf(1,'\nReady\n')
