function y = lpcvocod( x )
% function y = lpcvocod( x )
%
% Simulation of simple LPC Vocoder 
%
% 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:    18.05.2002 
% Contact: www.markus-hauenstein.de
%
% This program is copyrighted.

test = 0;

fprintf(1,'\nSimulation of simple LPC Vocoder');
fprintf(1,'\nInitializing');


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

% general parameters
blockLength = 160;

% gain quantization
ampQuantBits = 5;

% ltp (number of samples)
minDelay = 20;
maxDelay = 147;
akfBlockLength = 40;

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


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

% gain quantization
maxAmp = 32000;

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


% calculate and display required bitrate
sampleFrequency = 8000;
stateBitRate = 2 * sampleFrequency / blockLength; 
ampBitrate = ampQuantBits * sampleFrequency / blockLength;
lpcBitRate = sum( predQuantBits ) * sampleFrequency / blockLength;
ltpDelayBitRate = ceil( log(maxDelay-minDelay)/log(2) ) * sampleFrequency / blockLength;
bitRate = stateBitRate + ampBitrate + lpcBitRate + ltpDelayBitRate;
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 
stateChannel = zeros(1, numberOfBlocks);
ampChannel = zeros(1, numberOfBlocks);
aChannel = zeros( lpcOrder, numberOfBlocks);
ltpDelayChannel = zeros(1, numberOfBlocks);

% initialize classifier parameters
globalMaxAmp = 1024;
silenceMaxLevel = -60;
upperVoicedLimit = 0.3;

% initialize variables for encoding frame types 
silenceFrame = 1;
voicedFrame = 2;
voicelessFrame = 3;

% initialize variables for encoding coder states
silence = 1;
voiced = 2;
voiceless = 3;
wasVoiced = 4;
wasVoiceless = 5;


%
% THIS IS CODER 
%

fprintf(1,'\nCoding'); 
state = silence;

% coder main loop
blockIndex = 1 : blockLength;

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

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

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % silence / voiced / voiceless decision

  % classification of current block

  globalMaxAmp = max( globalMaxAmp, max(abs(xBlock)) );
  xAmp = sqrt( xBlock' * xBlock ) / blockLength;
  level = 20*log10( xAmp / globalMaxAmp );

  if level < silenceMaxLevel
    current = silenceFrame;
  else
    % check if voiced or voiceless frame
    signum = sign( xBlock );
    relZeroCrossRate = ...
        sum( 0.5 * abs( signum(1:blockLength-1) - signum(2:blockLength) ) ) / (blockLength-1);
    if relZeroCrossRate <= upperVoicedLimit 
      current = voicedFrame;
    else
      current = voicelessFrame;
    end
  end

  % state machine which takes history into account  
  % 
  if     state == silence      & current == silenceFrame    state = silence;
  elseif state == silence      & current == voicedFrame     state = voiced;
  elseif state == silence      & current == voicelessFrame  state = voiceless;
  elseif state == voiced       & current == silenceFrame    state = wasVoiced;
  elseif state == voiced       & current == voicedFrame     state = voiced;
  elseif state == voiced       & current == voicelessFrame  state = voiceless;
  elseif state == voiceless    & current == silenceFrame    state = wasVoiceless;
  elseif state == voiceless    & current == voicedFrame     state = voiced;
  elseif state == voiceless    & current == voicelessFrame  state = voiceless;
  elseif state == wasVoiced    & current == silenceFrame    state = silence;
  elseif state == wasVoiced    & current == voicedFrame     state = voiced;
  elseif state == wasVoiced    & current == voicelessFrame  state = voiceless;
  elseif state == wasVoiceless & current == silenceFrame    state = silence;
  elseif state == wasVoiceless & current == voicedFrame     state = voiced;
  elseif state == wasVoiceless & current == voicelessFrame  state = voiceless;
  else disp('error in state machine');
  end       

  %state

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %%%%%%%%%%%%%  Determine now paramenters, depending on state  %%%%%%%%%%%%%%%%
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  if state == silence
      stateChannel(:,i) = silence;
  end


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  if state == voiceless | state == wasVoiceless 

      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % calculate and quantize amplitude of input signal
      %
      xAmp = sqrt( xBlock' * xBlock / blockLength );
      xAmpQ = muquant( xAmp, maxAmp, ampQuantBits );


      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % lpc (inverse vocal tract filter)
      %
      lpcInputBlock = xBlock;

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

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


      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % simulate transmission of parameters via channel to decoder

      stateChannel(:,i) = voiceless;
      ampChannel(:,i) = xAmpQ;
      aChannel(:,i) = aQ;
  end       


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  %
  if state == voiced | state == wasVoiced

      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % calculate and quantize amplitude of input signal
      %
      xAmp = sqrt( xBlock' * xBlock / blockLength );
      xAmpQ = muquant( xAmp, maxAmp, ampQuantBits );


      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % lpc coefficients (inverse vocal tract filter)

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


      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % ltp lag - simple block autocorrelation approach

      blockAkf = conv( xBlock(akfBlockLength:-1:1), xBlock );
      blockAkf = blockAkf( akfBlockLength : length(blockAkf) - akfBlockLength + 1 );     

      searchArea = blockAkf( minDelay + 1 : min(maxDelay, length(blockAkf)) );
      [maximum, maximumIndex] = max( searchArea );
      ltpDelay = maximumIndex(1) + minDelay - 1;


      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      % simulate transmission of parameters via channel to decoder

      stateChannel(:,i) = voiced;
      ampChannel(:,i) = xAmpQ;
      aChannel(:,i) = aQ;
      ltpDelayChannel(:,i) = ltpDelay;
  end

end  % coder main loop



%
% THIS IS DECODER 
%

fprintf(1,'\nDecoding')

% initialize decoder variables
y = zeros(numberOfBlocks*blockLength,1);
randn('seed',0);
inputNoise = randn( blockLength, 1 );
zInvLpc = zeros(1, lpcOrder );
overhang = 0;


% decoder main loop
blockIndex = 1 : blockLength;

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

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % construct excitation signal for vocal tract filter
  
  state = stateChannel(i);

  %%%% if silence frame
  %
  if state == silence
      excitation = zeros( blockLength, 1 );
      overhang = 0;
  end

  %%%% if voiceless frame
  %
  if state == voiceless
      excitation = inputNoise;
      overhang = 0;
  end

  %%%% if voiced frame
  %
  if state == voiced
      excitation = zeros( blockLength, 1 );
      ltpDelay = ltpDelayChannel(i);
      
      if overhang == 0
          position = 1;
      else
          position = max(1, ltpDelay - overhang); 
          % overhang considers last pulse position in last frame
      end     

      while position <= blockLength,
          excitation( position ) = 1; 
          overhang = blockLength - position;
          position = position + ltpDelay;
      end     
  end  

  %plot( excitation )
  %pause


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % inverse lpc (vocal tract model)
  a = aChannel(:, i);     
  [invLpcOutputBlock, zInvLpc] = filter(1, [1;-a], excitation, zInvLpc);


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % scaling
  invLpcPower = invLpcOutputBlock' * invLpcOutputBlock / blockLength;
  if invLpcPower > 0
    gain = sqrt( ampChannel(i) * ampChannel(i) / invLpcPower );
  else
    gain = 0.0; 
  end
  
  yBlock = gain * invLpcOutputBlock;


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


  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % prepare next looping
  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');
