A 1984 thesis, scanned and brought back to life
In May 1984, Alan Parker (1959–2025) published his doctoral thesis “Linear Predictive Coding with Multi-Pulse Excitation” in Electrical Engineering at North Carolina State University. Forty-two years later, in May 2026, Dr. Parker’s thesis was digitally scanned and remastered in modern LaTeX, restoring all 108 original pages, 197 equations, 30 pen-plotted figures, 15 tables, 28 references, and 140 lines of C that still compiles to this day.
This page is an interactive memorial that celebrates Dr. Parker’s significant contributions to the field of digital speech synthesis. Every digital voice call, whether mobile, internet, or video, is built on pulse-excited linear prediction. Dr. Parker’s thesis helped turn that technique into the codecs running in every phone, app, and video call of the past three decades.
Dr. Parker's voice, digitized through the eras
To understand how digital speech quality has improved over the years, there’s no better way than to listen to it. Use the LPC Analyzer below to play back audio with your choice of 4 different codecs:
- LPC — Linear Predictive Coding. This was state-of-the-art low-bitrate speech synthesis in the 1970s to early 1980s. It has the distinctive robotic, scratchy sound of that era.
- MP-LPC — Multi-Pulse Linear Predictive Coding. The subject of Dr. Parker’s thesis, where multiple excitation pulses are used to enhance the quality of LPC.
- AMR — Adaptive Multi-Rate (ACELP). The narrowband cellular codec that carried nearly every mobile phone call from the late 1990s through the 2000s. It is the direct descendant of Dr. Parker’s work: ACELP excites its filter from an algebraic codebook whose entries are just a handful of signed unit pulses on a fixed grid.
- Opus-FB — Opus Full-band. The modern internet/HD codec (2012), built on LPC-based SILK and MDCT-based CELT. It is the highest-quality, richest sounding audio of today.
Dr. Parker used Multi-Pulse LPC to improve the quality of LPC in the 1980s and helped pave the way for modern codecs like ACELP in the 1990s and Opus in the 2010s. Listen to Dr. Parker himself in the various codecs using the LPC Analyzer. The MP-LPC codec is the 140-line C algorithm Dr. Parker wrote in 1984, applied to a wedding speech he gave 38 years later, in 2022. Toggle the codec back and forth between LPC and MP-LPC to see how Dr. Parker improved upon the algorithm. Then step forward through the eras: AMR is the ACELP codec that carried the cellular calls of the 1990s and 2000s (production-grade proof of the multi-pulse principle), and Opus-FB is the full-band, rich audio of today.
You can also apply the codecs to the voices of Richard Feynman giving a physics lecture on semiconductors (1964) or Neil Armstrong’s famous moon landing speech (1969).
How a voice fits into a computer
Dr. Parker entered NC State’s doctoral program in January 1982 and defended his thesis in May 1984, a time frame in which modern speech coding was rapidly evolving. As Dr. Parker shows in Figure 1.1 of his thesis, speech encoding was done in one of two ways:
- Waveform coding: encode voice as a waveform directly. This requires a large number of bits per second (7.2–200 kbps), used for communications, toll, and broadcast quality.
- Source coding: encode voice in a compressed form by describing the shape using fewer bits per second (0.5-7.2 kbps). Required whenever bandwidth is precious, like in wireless communications.
Below is a demonstration of the more expensive waveform coding. Use the controls to modify both:
- Sample rate frequency: the rate at which points in the waveform are sampled. If the frequency is too low (below the Nyquist rate), the voice is poorly sampled and will not encode well.
- Bit depth: the precision of the encoded amplitude of the voice. At 2-bits, only the sign (positive or negative) and the value 1 or 2 can be encoded, which fails to capture the precise shape of the voice.
Use the sliders to see how the sampling changes. To get a decent quality, you need to retain the higher frequencies by raising the sample rate to 8 kHz and the precision to 8 bits. This requires 64,000 bits per second, which is far too high for effective wireless applications.
Given that waveform coding is not feasible for digitizing voice at low kilobits per second, research went into more efficient digital representations of voice. In 1971, Bishnu Atal and Suzanne Hanauer developed Linear Predictive Coding (LPC) at Bell Laboratories. This kicked off 15 years of LPC development that Dr. Parker extended in his doctoral thesis in 1984 by formalizing multi-pulse LPC. Dr. Parker shows a diagram of how ordinary LPC works in Figure 2.3:
- Speech analysis encodes voice into: (1) the pitch period for voiced segments, (2) a single bit for voiced/unvoiced, (3) gain, and (4) reflection coefficients. This compressed representation of voice is then transmitted to a receiver.
- The receiver decodes the encoded signal to produce synthetic speech. This speech is a best-effort reconstruction of the original speech, but some information is invariably lost, resulting in the robotic-sounding voice on the receiver’s end.
After LPC was formally introduced in 1971, much effort went into improving the quality of LPC output while retaining its low kilobit per second advantage. The 15-year timeline below highlights how Dr. Parker’s multi-pulse LPC work fits into the broader effort to improve LPC quality.
Dr. Parker opens his thesis by naming the defect he set out to fix, citing Atal:
P241
“It is known that by increasing the bit rate for LPC that dramatic improvement of speech quality is not obtained. Atal has suggested that the reason for this is the highly inflexible way present day LPC systems are excited.”
A mathematical model for multi-pulse LPC
Figure 1.2 of Dr. Parker’s thesis shows a diagram for the multi-pulse LPC model that would improve upon the standard LPC model.
The idea of using multiple pulses was not new: Atal & Remde introduced it in 1982, but they utilized a naive, greedy algorithm to find the positions of the pulses. Dr. Parker, who was highly adept at applied mathematics, formalized this approach by solving for the positions optimally and globally. The following quote from Chapter 3 directly calls out how the greedy algorithm does not guarantee an optimal set of pulse positions.
P241
“As mentioned in the first chapter one of the reasons that present day LPC systems sound so mechanical may be because of the lack of a more sophisticated excitation set. One promising approach to the enhancement of low bit rate speech is reformulating the excitation in LPC as multiple impulses per block rather than one impulse per pitch period. Previous researchers have examined the multiple-pulse excitation problem by sequential impulse extraction. That is, over a fixed block of speech the best single impulse excitation location (and amplitude) is found, then the best second impulse excitation is found conditioned on the first ,etc... until the final Nth excitation conditioned on the N – 1 others is found. However, this method does not necessarily result in the optimal set of N impulses (i.e., that which produces the lowest mean square error between the actual and the modelled speech) since the introduction of succeeding excitations could alter the optimal location of the previously calculated impulses.”
The thesis breaks new ground by solving for the pulses and the filter, where earlier work fixed the filter and searched only for pulses. Dr. Parker alternates between the two, finding the optimal pulses for a fixed filter, then the optimal filter (its prediction coefficients uncoupled from the gain) for those pulses. He proves that each step lowers the error and that the alternation converges to a stationary point.
Stripped to its essentials, the method is a short chain of equations, reproduced here exactly as they appear in Chapter 3 of the thesis. It begins by writing down what synthetic speech is: each new sample is predicted from the samples just before it (the vocal-tract filter) plus an excitation that supplies whatever the filter alone cannot:
The classic LPC of the 1970s drove that filter with a blunt excitation: a single impulse per pitch period, or noise. That is the source of its robotic buzz. Multi-pulse LPC replaces it with a small, carefully placed set of pulses: an amplitude dropped at a position :
Dr. Parker scores any choice of filter and pulses by how far the resulting synthetic speech strays from the real recording, squared and summed across the whole block.
Atal & Remde placed their pulses one at a time, greedily, while Dr. Parker proved the optimum has a mathematically closed form. Define the residual as the part of the speech the filter fails to predict. Dr. Parker shows the best pulse positions are simply the points where that residual is largest, with each pulse’s height equal to the residual itself, .
What multi-pulse buys, measured
The demo below runs the multi-pulse algorithm from Dr. Parker’s C code on one real 20 ms frame of voiced speech. At the far left of the pulse count rail the demo shows ordinary LPC: the vocal-tract filter is driven by a single impulse per pitch period and its reconstruction buzzes off the original. Drag the slider right and multi-pulse instead spends its pulses on the peaks of the residual: the amber reconstruction tightens onto the original waveform as the pulses multiply.
The bottom plot shows the normalized error of the speech reconstruction as a function of pulse count. Multi-pulse LPC outperforms standard LPC at all pulse counts . If you go back to the LPC Analyzer and listen to Dr. Parker’s speech again, that difference in audio clarity between LPC and MP-LPC is due to this lower error.
A method that outlived its decade
Dr. Parker summarizes the four things that are new in his dissertation:
P241
“New contributions by this dissertation include: solution of a set of equations for multi-pulse to determine the optimal prediction coefficients for a given set of pulse positions … uncoupled from the gain; a mathematical derivation of the optimal pulse locations; a theoretical proof of convergence for the algorithm; and an algorithm to transmit the pulse locations.”
He was just as candid about the cost. Multi-pulse’s quality came from spending more bits, not from a free lunch:
P241
“Multi-pulse performs better than LPC when allowed to run at 10.6 kBits. As discussed earlier, when the bit rates are the same there is not really a significant improvement over LPC.”
The next decade of work would close that gap (multi-pulse quality without multi-pulse bit rate) by replacing the explicitly transmitted pulses with indices into a shared, pre-trained codebook. The analysis-by-synthesis paradigm he worked in would, within a year, generalize into CELP and go on to carry essentially every cell-phone call on Earth for the next thirty years. The excitation question “what drives the filter?” organizes forty years of codecs into a single family tree displayed below. The amber line traces the multi-pulse algorithm out to ACELP, LPCNet, and the neural codecs of today.
Through his doctoral work, Dr. Parker’s math, engineering, and influence live on for all of human history.
The original dissertation, page for page
Dr. Parker’s original 1984 thesis, digitally scanned in full, is reproduced below. The figure shows a side-by-side comparison of a representative page: the original scan on the left against the modern LaTeX rewrite on the right.
The complete scan of Alan’s original 1984 manuscript:
And the modern, remastered LaTeX edition:
140 lines of C, still compiling
The original C implementation of the multi-pulse algorithm is reproduced verbatim from the 1984 thesis here. It is pre-ANSI K&R C, so the build suppresses implicit-int, no-prototype, and bare-return warnings. You can compile the program with:
/**************************************************************** **** This subroutine calculates the multi-pulse **** parameters for a given set of data. The multi-pulse **** coefficients and pulse positions will be passed back to **** the caller. *****************************************************************/ #include <stdio.h> #include <math.h> multip(n,i20,iter,p,s,nn,dec,pos,alpha,code) float s[ ],alpha[ ]; int n,i20,iter,p,nn,dec,code,pos[ ]; /**************************************************************** **** Meaning of Variables Passed to Subroutine. **** n: the number of data samples in the speech block **** i20: the number of impulses to be used. **** iter: the number of iterations for the algorithm. **** p: the number of predictor coefficients. **** s: the array containing the speech. if s[x] is passed to the algorithm, the algorithm will look for the initial data in samples s[x],s[x+1], ...,s[x+p-1] and the speech data in s[x+p],s[x+p+1], ...,s[x+n+p-1]. **** nn: the number of initial pulse positions. **** dec: the decrement for the number of pulse positions used in for(i=nn;i>=i20;i-=dec) where i denotes the number of pulse positions the algorithm is using at that particular iteration. **** pos: the array containing the positions of the pulses. if pos[y] is passed to the algorithm it will return pulse positions pos[y+1],...,pos[y+i20]. **** alpha the array containing the alphas. If alpha[j] is passed to the algorithm it will return alpha(1) in location alpha[j+1],...,alpha(p) will be in location alpha[j+p]. **** code: if code = 0 then the algorithm calculates the optimal coefficients for the fixed locations passed to it. *****************************************************************/ { #define begin { #define end } #define then float kp[1000],phi[20][20],sum,psi[20],sum1,sum2,sum3; float d[20],v[20][20],y[20],e[1000],b[1000]; int i,j,j3,j4,k,k2; for(i=nn;i>=i20;i-=dec) /*start pulse position loop */ begin for(k2=1;k2<=n;k2++) kp[k2]=1.0; /*initialize correlation select function */ if(code==1) begin for(k2=1;k2<=i20;k2++) /* if code =1 then use pulse positions */ kp[pos[k2]]=0.0; /* passed to the algorithm to define */ end /* new correlation select function */ for(j4=1;j4<=iter;j4++) /* begin iteration loop */ begin for(j3=1;j3<=p;j3++) begin /* begin loop to calculate the multi-pulse */ for(k=1;k<=j3;k++) /* phi matrix. (identical to standard lpc */ begin /* but with kp multiplied in with the sum */ sum=0.0; for(j=1;j<=n;j++) sum+=s[j-j3+p-1]*s[j-k+p-1]*kp[j]; phi[j3][k]=sum; end end for(j3=1;j3<=p;j3++) begin sum=0.0; /* calculate the multi-pulse psi matrix */ for(j=1;j<=n;j++) /* similar to lpc */ sum+=s[j+p-1]*s[j+p-j3-1]*kp[j]; psi[j3]=sum; end d[1]=phi[1][1]; for(j=2;j<=p;j++) v[j][1]=phi[j][1]/d[1]; /*calculate the multi-pulse v matrix */ for(j3=2;j3<=p-1;j3++) begin sum1=0.0; for(k=1;k<=j3-1;k++) sum1+=v[j3][k]*v[j3][k]*d[k]; d[j3]=phi[j3][j3]-sum1; /*calculate the multi-pulse d matrix */ for(j=j3+1;j<=p;j++) begin sum2=0.0; for(k=1;k<=j3-1;k++) sum2+=v[j][k]*d[k]*v[j3][k]; v[j][j3]=(phi[j][j3]-sum2)/d[j3]; end end sum3=0.0; for(j=1;j<=p-1;j++) sum3+=v[p][j]*v[p][j]*d[j]; d[p]=phi[p][p]-sum3; y[1]=psi[1]; for(j3=2;j3<=p;j3++) begin sum1=0.0; for(j=1;j<=j3-1;j++) /*calculate the multi-pulse y matrix */ sum1+=v[j3][j]*y[j]; y[j3]=psi[j3]-sum1; end alpha[p]=y[p]/d[p]; for(j3=p-1;j3>=1;j3--) begin sum2=0.0; for(j=j3+1;j<=p;j++) sum2+=v[j][j3]*alpha[j]; /*alphas for multi-pulse are finally */ alpha[j3]=y[j3]/d[j3]-sum2; /*defined in this loop */ end if(code==1) return; /* if positions were specified you are done */ for(j=1;j<=n;j++) begin sum1=0.0; for(j3=1;j3<=p;j3++) /* loop to calculate error function */ sum1+=alpha[j3]*s[j+p-j3-1]; e[j]=s[j+p-1]-sum1; end for(k2=1;k2<=n;k2++) kp[k2]=1.0; for(j=1;j<=i;j++) begin b[j]=0.0; for(k=1;k<=n;k++) begin if(fabs(*(e+k))>fabs(*(b+j))) then begin /* loop to calculate the largest i values of */ b[j]=e[k]; /* the error function which define the positions */ pos[j]=k; /* to be used in the next iteration */ end end e[pos[j]]=0.0; kp[pos[j]]=0.0; end end end }