Abstract:
A DSP-based electronic carillon system is disclosed. The system comprises a digital signal processor (DSP), memory for storing program code for controlling the operation of the DSP in carrying out pre-programmed algorithms, and an output circuit for converting the output of the DSP into audible sound. DSP algorithms are also disclosed.

Description:
FIELD OF THE INVENTION 
     The present invention relates generally to electronic carillons, and more particularly to a digital signal processor (DSP) algorithm for use in a DSP-based electronic carillon system. 
     BACKGROUND OF THE INVENTION 
     An electronic carillon system is a system that synthesizes or reproduces bell sounds. Such a system is capable of synthesizing or reproducing the sound of a single bell strike, a single swinging bell, a number of bells swinging in or out of sync with one another, and musical compositions. Electronic carillon systems are most often found in churches, but they also can be advantageously employed in government buildings, universities, department stores, etc. Typical applications include announcing the time of day and playing music. Further background information relating to electronic carillons may be found in U.S. Pat. No. 5,471,006, Nov. 28, 1995, &#34;Electronic Carillon System and Sequencer Module Therefor,&#34; and in U.S. Pat. No. 4,805,511, Feb. 21, 1989, &#34;Electronic Bell-Tone Generating System,&#34; both of which are assigned to Schulmerich Carillons, Inc., the assignee of the present invention. 
     SUMMARY OF THE INVENTION 
     Objects of the present invention are to provide an improved electronic carillon system having greater versatility than the prior tone generator-based system, and to provide algorithms for operating a DSP-based electronic carillon system. An electronic carillon system in accordance with the present invention comprises a DSP, memory means for memorizing program code for controlling the operation of the DSP in carrying out pre-programmed algorithms, and output means for converting the output of the DSP into audible sound. In presently preferred embodiments of the invention, the output means comprises a codec and at least one loudspeaker. Moreover, the DSP operates, in accordance with the pre-programmed algorithms, so as to perform the functions of receiving input data from the memory means and calculating pitch-shifted output data on the basis of the input data. In addition, the DSP may be programmed to scale the output data for volume and velocity as described below. 
     Other features and advantages of the invention are described below. 
    
    
     BRIEF DESCRIPTION OF THE DRAWINGS 
     FIG. 1 is a block diagram of a preferred embodiment of an electronic carillon system in accordance with the present invention. 
     The remaining drawings depict flowcharts of various program elements of the presently preferred embodiment. In particular: 
     FIGS. 2A and 2B depict a &#34;main()&#34; subroutine. 
     FIG. 3 depicts an &#34; --  irq2 --  asserted()&#34; subroutine. 
     FIG. 4 depicts an &#34;spt0 --  asserted()&#34; subroutine. 
     FIGS. 5 and 5A-5K depict a &#34;parse --  Rx()&#34; subroutine. 
     FIG. 6 depicts an &#34;error --  handler()&#34; subroutine. 
     FIGS. 7 and 7A-7E depict an &#34;assign --  toll --  note()&#34; subroutine. 
     FIG. 8 and 8A-8F depict &#34;assign --  swing --  note()&#34;. 
     FIG. 9 depicts &#34;assign --  rt --  voice()&#34;. 
     FIG. 10 depicts a &#34;transfer --  channels&#34; subroutine. 
     FIGS. 11 and 11-11G depict a &#34;misc --  funcs&#34; subroutine. 
     FIG. 12 depicts a &#34;czero --  cross()&#34; subroutine. 
     FIGS. 13A and 13B depict &#34;init --  card()&#34;. 
     FIG. 14 depicts &#34; --  uart --  transmit()&#34;. 
     FIGS. 15A and 15B depict &#34;update --  toll --  chans()&#34;. 
     FIGS. 16 and 16A-16H depict an &#34;update --  swing --  chans&#34; subroutine. 
     FIGS. 17A-17C depict an &#34;update --  real --  time&#34; subroutine. 
    
    
     DETAILED DESCRIPTION OF PREFERRED EMBODIMENTS 
     Overview 
     FIG. 1 is a block diagram of a presently preferred embodiment of a DSP-based electronic carillon system in accordance with the present invention. This embodiment of the invention comprises the following components: a serial input/output circuit, or I/O port, 10, which includes a universal asynchronous receiver transmitter (UART, not shown); one or more memory cards 12; a boot memory 14; a DSP 16 (e.g., a model ADSP21062 digital signal processor, available from Analog Devices, Inc.); a codec (coder-decoder) (e.g., model AD1847-PLCC) and audio I/O circuitry 18 (in practice, the codec may be separate from the audio I/O circuitry); an inside amplifier 20a and outside amplifier 20b, and an inside loudspeaker 22a and outside loudspeaker 22b (see the above-cited U.S. Pat. No. 5,471,006 for further information about the use of &#34;inside&#34; and &#34;outside&#34; speakers); a data buss 24a and an address buss 24b, each of which is connected to the serial I/O port 10, memory cards 12, boot memory 14 and DSP 16; a first clock 26 (33.3 MHZ) and a second clock 26b (22.579 MHZ) coupled to the DSP and codec, respectively; and an optional MIDI input circuit 28, which may be connected to the UART (not shown) of the serial I/O port 10. 
     A principal difference between the presently preferred, DSP-based system and the tone generator-based system described in detail in U.S. Pat. No. 5,471,006 is the use of a DSP and its associated algorithms instead of the tone generator (block 12 of FIG. 1A of the &#39;006 patent) of the earlier embodiment. It has been discovered that the DSP can be exploited to create unexpectedly improved sound quality and versatility. Therefore, the DSP algorithms will be the primary focus of this disclosure. 
     The main job of the DSP is to generate digital audio output signals on the basis of memorized (stored) samples of sounds emitted by bells, rods, or the like, or real-time samples, and commands fed to the DSP via the address and data busses 24a, 24b. The DSP utilizes interpolation to construct discrete sound (e.g., bell strike) samples on the basis of a limited number of samples. 
     In the preferred embodiment, the DSP uses interpolation to generate up to five octaves of bell notes based on the limited number of samples. 
     In addition, the sample data may be scaled such that the &#34;volume&#34; of the output data, i.e., the audio output of the speakers 22a, 22b, is loudest for the longest and shortest rods, or biggest and smallest bells (or the ends of a keyboard coupled to the serial input port). Further, the audio output of the outside loudspeaker can be made louder than that of the inside loudspeaker, although in the preferred embodiment, the inside and tower volumes are totally adjustable (the perceived loudness of either is based on the installed amplifier and speaker complement, hence no difference exists between the two circuits as viewed from the DSP itself). The data can also be scaled for velocity, such that, if a keyboard is employed to provide input commands, the output data will be adjusted in accordance with the velocity of the struck key. Such data scaling may be implemented by storing appropriate information in a header file associated with the sample data. 
     The overall DSP operation can be summarized as follows: (1) receive input data from memory or serial I/O port; (2) calculate and accumulate audio output data based on up to 24 simultaneous active (ringing) bell notes; and (3) pass the output data and any volume change or mute commands to the codec. Note that velocity is relevant only at the start or assignment of a particular tolling note--it is basically a starting volume. 
     The codec 18 performs several functions: it operates as a stereo D/A converter and A/D converter; it allows the user to attenuate individually the left and right (tower and inside) outputs; it provides internal circuitry to digitally control gain attenuation on the analog audio inputs; and it provides internal circuitry to digitally mix these audio inputs with the digital output. 
     A feature of the DSP-based system is the &#34;Interpolated Fractional Address&#34; algorithm. A scenario to illustrate this algorithm is as follows: a 1 kHz sine wave is digitally recorded at a 44.1 kHz sampling rate. The sampling period is 1/44.1 k, or 22.6 μsec. To play back this sample at its original frequency of 1 kHz, one could output consecutive samples once every 22.6 μsec. To play back the same sample at twice its audio frequency, one could output consecutive samples every 11.3 μsec or output only alternate samples every 22.6 μsec. To play back the sample at a frequency between 1 and 2 kHz, one could output consecutive samples at a periodic interval between 11.3 and 22.6 μsec, or interpolate between corresponding points of the 1 and 2 kHz waveforms and output the result every 22.6 μsec (i.e., at the original 44.1 kHz sample rate). 
     To explain how this is done, we will consider one active bell channel. Every 22.6 μs, this channel is updated to output data at the 44.1 kHz sampling rate. Each channel is comprised of several parameters, which are initialized at note start time, that pertain directly to this explanation. 
     These parameters include: 
     Integer address: current location in sample data; 
     Fractional address: current fractional value--used to interpolate between two read data values; 
     Integer increment: value added to integer address each update period; 
     Fractional increment: value added to fractional address each update period; 
     Volume: scale factor to scale interpolated output data to be mixed with remaining channels; 
     Data 1 and Data 2: two consecutive digital audio data points at current integer address; 
     Decay: scaling factor which scales volume every time a loop end is reached; 
     Loop end address: address at which integer address is reset to loop start address; 
     Loop start address: address at which loop starts. 
     Assume that the note to play is a G2, and that the raw sample to be used to generate this note is a C2. Based on the table below, the channel&#39;s new integer increment will be 1 and the new fractional increment will be the 12th root of 2 to the power of 7 (i.e., 2 7/12 ). At the start, the channel&#39;s integer address is initialized to the C2 sample start address and the fractional address is initialized to 0. During each sample update, the following occurs: the current fractional address is multiplied by the difference between the two data values, Data 1 and Data 2; this result is then added to the Data 1 value, hence interpolating between Data 1 and Data 2 based on the current fractional address; the interpolated output is scaled by the channel&#39;s volume and accumulated with the other channels&#39; data; the channel&#39;s integer and fractional addresses are updated with their respective integer and fractional increments; the channel&#39;s current address is compared to the loop end address--if past, the current integer address is reset to the loop start address and the volume is scaled down by the decay value; if the channel&#39;s volume falls below a minimum, the channel is disabled; means for getting the next update period Data 1 and Data 2 values are employed. It should be noted that this is the scheme used for sounding tolling bells, and these operations and more are performed for swinging bells. 
     
         ______________________________________Note           Ratio of frequencies______________________________________C              1C.sub.#        (12th root of 2)D              (12th root of 2) to power of 2D#             (12th root of 2) to power of 3E              (12th root of 2) to power of 4F              (12th root of 2) to power of 5F#             (12th root of 2) to power of 6G              (12th root of 2) to power of 7G#             (12th root of 2) to power of 8A              (12th root of 2) to power of 9A#             (12th root of 2) to power of 10B              (12th root of 2) to power of 11C.sup.1        (12th root of 2) to power of 12______________________________________ 
    
     From this we separate the ratios into integer and fractional values. The following lookup table is used at channel assignment time. The lookup values are based upon the interval or musical distance between the note to be played and the raw sample this note uses. It provides the integer and fractional values to be used by the channel to update the integer and fractional address values each update period: 
     
         ______________________________________# Semi-tonesFrom Raw Sample       Integer Increment                     Fractional Increment______________________________________0           1             01           1             (.05946) to power of 12           1             (.05946) to power of 23           1             (.05946) to power of 34           1             (.05946) to power of 45           1             (.05946) to power of 56           1             (.05946) to power of 67           1             (.05946) to power of 78           1             (.05946) to power of 89           1             (.05946) to power of 910          1             (.05946) to power of 1011          1             (.05946) to power of 1112          2             0______________________________________ 
    
     The following program descriptions and accompanying flowcharts will enable a person of ordinary skill in the art of programming electronic carillon systems to make and use the present invention. 
     Internal Memory Structure and Access 
     The memory of the DSP 16 is divided into program and data memory. Program memory is divided into three spaces: a 4 k×48-bit interrupt and initialization segment, a 12 k×48-bit code segment, and an 8k×32-bit program memory data segment. This last data segment is used in combination with the data memory for parallel data accesses in updating channel routines. The 32 k×32-bit data memory space is divided into 24 k data memory, 4 k of heap, and 4 k of runtime stack. The 24 k chunk holds C and assembly language variables and headers of existing bell voice PC cards. 
     External Memory Structure and Access 
     The DSP 16 accesses four external memory areas: PCMCIA card slots 1 and 2 (i.e., memory cards 12 in FIG. 1), the UART of I/O circuit 10, and the 128 k×8-bit boot flash memory 14. The first three are chip selected via memory bank select pins and the last by the DSP&#39;s boot memory select pin. Memory banks 1 and 2 access the two 16-bit cartridge slots and are 32 Mbytes in length. They are set up to read memory with 4 internally generated wait states. Bank 0 is used to access the UART, with an address range of 8×8-bit words. Wait states are set at 6. The boot memory 14 is used only for booting at power-up or reset. In the present embodiment, all bell data saved on the memory cards 12 is 16-bit signed 2&#39;s complement data, whereas real-time samples are 8-bit samples. 
     Other DSP I/O 
     The DSP 16 has three other modes of external communication: the codec 18, flag lines which are part of the data buss 24a, and a JTAG port (not shown). The JTAG port is used for board testing and emulation purposes. It is a serial test access port corresponding to the IEEE 1149.1 specification. This treats all I/O pins as one large shift register giving access via the serial scan path to read or write to any pin of the DSP. The codec to DSP interface comprises a serial port of the DSP 16. The DSP acts as the slave in this dual line operation with separate Tx (transmit) and Rx (receive) lines. The codec 18 generates clock and frame sync for the serial port timing. Two flag lines are used to sense the existence of the memory cards. Another flag line is used to signal running status to a state code input of a watchdog timer (not shown). 
     Initialization 
     At power-up, the DSP 16 first boots from boot memory 14, and configures and initializes itself, the UART (in I/O circuit 10), and the coded 18. It also evaluates its playing capabilities based on the available voices stored in the memory cards 12. 
     In an idle operating mode (audio output muted), the system sits in a short C loop waiting for the UART flag bit. This main loop is interrupted by a serial port transmit completion flag. The subroutine  --  spt0 --  asserted (FIG. 4) updates any active mode channels and signals UART interrupts to the main loop via the UART flag bit. The UART servicing routine is called, and it empties the UART hold register (hr) contents to a circular buffer,  --  uart --  buf. If an FF is received, the MSG --  DONE flag is set. This flag signals the main C loop to parse the UART message via the subroutine parse --  Rx (FIG. 5), which acts on the received message by either processing a control code or engaging a particular mode: TOLL, SWING, or REAL --  TIME. Active channels are initialized via the subroutines assign --  toll --  note (FIG. 7), assign swing --  note (FIG. 8), and assign --  rt --  voice (FIG. 9). 
     The parse --  Rx routine returns to the main loop when all messages are processed. At this time, the C code jumps to the assembly transmit routine,  --  uart --  transmit (FIG. 14), if a flag bit (TXTIME) is set by parse --  Rx. If there is no error, the subroutine misc --  funcs (FIG. 11) is called, and it arbitrates card status changes, sends new codec commands, if any, and disables active mode when all appropriate channels modes are 0. The subroutine  --  error --  handler (FIG. 6) is called if the code fails or reset related commands arrive. The flag output for the watchdog timer is toggled every loop iteration. The main loop and all other code execution terminate if an error remains unresolved at loop bottom. 
     The initialization routines are &#34;init --  21k&#34;, &#34;init --  uart&#34;, and &#34;setup --  audio&#34;. These subroutines are not described in detail in this specification. 
     main() subroutine 
     FIGS. 2A-2B depict the main() subroutine. The functions performed by this subroutine are described below. 
     Initialization: Steps 200-209 are directed to the performance of various initialization procedures. 
     Step 200: initializes tx --  buf 3! to zero. 
     Step 201: initializes uart --  ptr to start of uart --  buf and clears uart --  buf. 
     Steps 202-206: clear channel --  tablea, ch --  tableb, sw --  tablea, and sw --  tableb. 
     Step 207: enable spt0 (serial port 0 to codec) interrupt. 
     Step 208: set MUTE and VOL --  CHANGE flag bits. 
     Step 209: initialize error to zero. 
     Main loop operation, executed while no errors exist (see While step 210): 
     Step 212: toggle state code FLAG1 output to watchdog timer. 
     Steps 213-214: if UART flag bit, call irq2 --  asserted. 
     Steps 215-216: if MSG --  DONE flag bit, call parse --  Rx. 
     Steps 217-218: if TXTIME flag-bit set, call uart --  transmit. 
     Steps 219-220: if no error call misc --  funcs. 
     Steps 221-222: if error, call error --  handler. 
     Step 211: if error not resolved terminate loop and execution, otherwise loop. 
       --  irq2 --  asserted() interrupt routine 
     The irq2 --  asserted() subroutine is summarized below with reference to FIG. 3. This routine is flagged in the spt0 --  asserted subroutine, and can be interrupted if necessary. It reads the UART status register and checks whether the read value is data or an address. If it is an address, the UART receiver is enabled or disabled, depending on the address received as indicated in the flowchart. 
     If the read value is not an address, it is added to the circular buffer,  --  uart --  buf, which is indexed by the i0, m0, l0, b0 primary index register set. If FF is read, the UART receiver is placed in &#34;sleep&#34; mode (disabled), and the MSG --  DONE flag is set. If data is read, the  --  ubuf --  tail pointer is advanced and the UART flag is cleared. 
     spt0 --  asserted() interrupt routine 
     FIG. 4 depicts the spt0 --  asserted() subroutine, which is an interrupt routine used to transmit information through the serial port. In normal operation, this is the only real-time enabled interrupt in the system besides the external reset. When playing bells, the DSP 16 spends most of its time here. This routine uses the DSP&#39;s alternate sets of working and index registers. It uses the &#34;flags&#34; variable to determine which mode is active, by checking which mode bit, TOLL, SWING, or REAL --  TIME, is set. It then executes the appropriate channel updating code. When complete, the interrupt routine polls the irptl register for the UART to determine whether the UART interrupt is asserted. If an interrupt is present, it sets the UART flag-bit. 
     parse --  Rx() subroutine 
     FIGS. 5A-5K depict the parse --  Rx() subroutine. FIG. 5 illustrates how FIGS. 5A-5P may be arranged to form a complete flowchart. 
     The parse --  Rx routine is called from the main loop if the MSG --  DONE flag-bit is set by the irq2 --  asserted subroutine. As shown in FIG. 5F, and returns an integer error value (0 if no error) as shown in FIG. 5F. 
     In operation, this subroutine reads the first four UART data values in uart --  buf starting from the  --  ubuf --  ptr address (in this embodiment, no message should ever be longer than 4 bytes). The subroutine then checks byte1 to see whether the message is a control code. If so, the control code is processed (see FIG. 5E). 
     Control Code Processing 
     If the control code message relates to the upper or lower stops, the received stop is ANDed with  --  current --  voices to determine whether it is a playable voice. If so, the upper --  voice or lower --  voice values are set. 
     If the message relates to tower or inside volume, the indicated volume change is checked for validity and the volume value is used with the lookup table, volume --  table 100!, to determine the actual volume value to send to the codec 18. The inside --  volume or tower --  volume value is loaded and the CVOL --  CHANGE flag bit is set. 
     If the control code message is TRANSPOSE, the new transpose value is checked for validity and, if valid, centered around zero and then saved to the transpose variable. &#34;Transpose&#34; is a feature of some keyboard systems which allows the user to shift the keyboard&#39;s outputted note value up or down by up to six notes. For example, if the transpose knob on the console is in the +1 position, and the C2 key is pressed, the C#2 note will play. 
     If the message is KEYSWITCH --  STATUS, the following occurs: if the message is keyswitch &#34;ON&#34;, then the CCLOCK --  MANUAL and CVOL --  CHANGE flags are set and the CMUTE flag is cleared; if the message is keyswitch &#34;OFF&#34;, then the CCLOCK --  MANUAL flag is cleared. In addition, if all active modes are disabled, then the CVOL --  CHANGE and CMUTE flags are set. 
     If the message is RT --  SAMPLE --  START (real time sample start), the corresponding channel and mode flags are cleared, the subroutine assign --  rt --  voice (assign real time voice) is called, the REAL --  TIME flag bit is set, and the transmit buffer is loaded and the transmit flag is set. 
     If the message is OUTPUT --  STATUS --  REQ and the RELAY flag-bit is set, then the subroutine responds with the tower status based on the TOWER --  ON flag bit. If the message is STATUS --  REQUEST, the subroutine responds with a status OK message. 
     If the message is DEVICE --  RESET, an error=1 (all function stop) is returned. 
     If the message is TOWER --  RELAY and the RELAY flag-bit is set, the tower relay command is completed and the TOWER --  ON bit is updated. 
     All command parsing is followed by flushing of the uart --  buf. 
     Note Processing 
     If the control code comprises note data, the note value is checked to determine whether it is between 1 and 122 (1&lt;=note&lt;=122(61*2)). The next byte is then checked to ascertain whether the message is swing or toll. If the message is swing, the active mode is checked. If TOLL mode is active, all toll channels are disabled. The SWING flag is set and the assign --  swing --  note subroutine is called. If the message is toll, the mode is checked; if it is not swing, the TOLL flag is enabled and the assign --  toll --  note subroutine is called. The next two bytes are checked to determine whether the notes are valid and, if so, the assign --  toll --  note subroutine is called. If the swing mode is active, the transfer --  channels subroutine is used to determine if all swing channels are on last strike, i.e., the SW --  STRIKE --  INH flag-bit is set. If still ringing, the subroutine ignores the new toll note; otherwise, the TOLL flag is enabled and assign --  toll --  note is called. The next two bytes are checked to determine whether the notes are valid and, if so, the assign --  toll --  note subroutine is called. 
     All command parsing is followed by flushing of the UART buffer (uart --  buf). 
     error --  handler() subroutine 
     FIG. 6 depicts the error --  handler() subroutine. This subroutine is called only from the main routine; it accepts an error as input and returns an error=0 if the error is resolved. 
     assign --  toll --  note() subroutine 
     FIGS. 7A-7E depict the assign --  toll --  note() subroutine. FIG. 7 shows how the flowchart sections depicted in FIGS. 7A-7E may be arranged to form the complete flowchart. 
     The assign --  toll --  note subroutine is called only by the parse --  Rx subroutine. It accepts an input note and velocity, and operates as follows: First, it determines whether the input note is upper or lower manual, and then it adds the transpose value and determines whether the note is still in a playable range. An appropriate upper/lower --  voice is assigned to new --  voice. The subroutine returns if new --  voice is zero. It advances into the voice table to find the voice ID and address of the voice header in memory for each available voice. All new note parameters from the header table are obtained by incrementing into the header by new --  voicebase+(new --  note*16). Each note header is 16 bytes long, and all fractional note parameters are shifted up to left justify them in a 32-bit data value (values on card are 24-bit values). Next, the subroutine looks for the same note and voice already playing in the channel tables. This routine will allow up to two channels to have the same note and voice. If a channel is not selected according to the above rules, the code looks for an open channel (MODE=0) and grabs the open channel if one is found. If no open channel is found, the channel which has been running the longest is selected. The subroutine gives less weight to the lower octave channels, to keep the low bells ringing (since it sounds unnatural when low notes are cut off). 
     assign --  swing --  note() subroutine 
     FIGS. 8 and 8A-8F depict the assign --  swing --  note() subroutine. This subroutine is called by parse --  Rx, and accepts an input note and swing command. All active swing note values are stored in the swing --  notes array. 
     The subroutine first checks the new note against existing notes in swing --  notes. If a match is found, then this is a command for an existing bell. The MODE of the existing bell is then checked. 
     If the command is swing down and the mode is SW --  DOWN --  UP, the SW --  DOWN --  UP flag is cleared for that channel, causing the bell to fully swing down. If the command is swing down and the mode is SW --  UP, SW --  UP --  DOWN is set. Otherwise, the bell is swinging and SW --  DOWN and SW --  MODE --  CHANGE are set. 
     If the command is swing up and the bell is on its last strike, the channel is reassigned. If the command is swing up and the mode is SW --  UP --  DOWN, the SW --  UP --  DOWN flag is cleared for that channel, causing the bell to fully swing up and not swing down afterward. If mode is SW --  DOWN, SWDOWN --  UP is set and the subroutine returns. 
     If no match is found for the new note, the subroutine looks for open channels (SW --  MODE=0). If none is found, it returns and no channel is assigned. If an open channel is found, that channel is used. The new note is assigned to a correct spot in the voice table. All new --  parameters are loaded, as are swing --  notes with the new note value. If the MUTE flag-bit is set, MUTE is cleared and VOL --  CHANGE is set to enact. The new parameters are assigned to sw --  tablea and sw --  tableb, and the channel is enabled by setting the proper channel MODE values. The subroutine then returns. 
     assign --  rt --  voice() subroutine 
     FIG. 9 depicts the assign --  rt --  voice() subroutine. This subroutine is called by parse --  Rx and accepts an input voice. In operation, it searches through rt --  table VOICE values to see if there is a match. If not, it returns. If the MUTE flag-bit is set, MUTE is cleared and VOL --  CHANGE is set to enact. All new parameters are assigned to rt --  table. 
     transfer --  channels subroutine 
     FIG. 10 depicts the transfer --  channels subroutine. This subroutine is called only by parse --  Rx. It takes no input but returns status as follows: 
     0: all swing channel had SW --  STRIKE --  INH-bit in mode set=&gt;all swing channels are on last strike; 
     1: some bells still swinging--normal DOWN or UP. 
     If it finds a last struck bell, it transfers the parameters to an open toll channel. If all channels are off or transferred, 0 is returned. Otherwise, 1 is returned. 
     misc --  funcs subroutine 
     FIGS. 11 and 11A-11K depict the misc --  funcs subroutine. This subroutine is called by the main loop and takes no inputs but returns an error, if found. It performs three tasks: (1) check card status change; (2) check for volume change; and (3) check for all active mode channels off. 
     Check Card Status Change 
     This function uses FLAG0 and FLAG2 external pins, and CARD1 and CARD2 flag bits to determine a change in card status. If a card has been removed, it clears that card&#39;s voice from the voice table and current --  voices. If upper voice was on that card, it zeros the MODE flag for the toll and swing channels using the upper voice. If lower voice was on that card, it zeros MODE for the toll and swing channels using the lower voice. If operating in real-time mode, that card&#39;s voice is cleared from rt --  table and MODE is cleared. 
     If a card has been added, error=2 is returned. 
     Check for Volume Change 
     Volume changes are passed to the codec 18 via the array tx --  buf. Muting is accomplished via the high-bit of the volume byte sent to the codec. The codec output is muted (with the MUTE flag) whenever no channels are active and the clock is not in manual mode (the CLOCK --  MANUAL flag bit is not set). This code only executes when the VOL --  CHANGE flag-bit is set. In operation, it loads cmds --  1847 with 0×8600+tower --  volume+MUTE-bit. It loads cmds --  1847 with 0×8700+inside --  volume+MUTE bit. If the MUTE flag is set, it sits idle until spt0 --  asserted interrupts and returns. The first volume command is copied to the control --  out variable. It then sits idle until spt0 --  asserted interrupts and returns again. It then copies the second volume command to the control --  out variable. The VOL --  CHANGE flag bit is disabled. 
     Check For All Active Mode Channels Off 
     This routine is used to disable an active mode flag (TOLL, SWING or REAL --  TIME) if all channels in that mode have their MODE register equal to 0. The CVOL --  CHANGE and MUTE flags are set if the active mode has been disabled and the CCLOCKMANUAL flag bit is not set. 
     czero --  cross() subroutine 
     FIG. 12 depicts the czero --  cross() subroutine. This subroutine is called by the assign --  toll --  channel subroutine if the channel to be assigned over has a non-zero MODE parameter (it is running). This routine searches for a zero-crossing in the channel&#39;s digital audio data, and will execute until a zero-crossing is found. In addition, it gets rid of clicks and pops which are created when a channels output dramatically changes in value. It accepts a channel number and returns nothing. 
     init --  card() assembly subroutine 
     FIGS. 13A and 13B depict the init --  card() subroutine. This subroutine uses alternate working and index registers. It is called from the error --  handler subroutine. It uses i0 as a voice table index, i1 as internal header index, i3 as a real-time voice table index, r1 as an index to the start of cart, and r0 as a scratch register. In addition, it uses DMA channel 7 to obtain card header information. The flag flag0 is employed to sense the existence of card 1, and flag2 is used to sense the existence of card 2. Appropriate CARD1 --  EXIST and CARD2 --  EXIST flag bits are set in the  --  flags register. 
     In operation, the subroutine first loads 16 32-bit words (voice id and the next voice address and swing parameters). A voice id greater than 0×80 indicates a real-time sample. A next voice address equal to zero indicates that this is the last voice header on this card. Bell voice id words are ORed with the variable  --  current --  voices, and the bell voice id and internal header start pointer are saved in the array --  voice. Voice note header information is loaded into the internal memory array  --  header. A voice&#39;s id word has its high-bit set to indicate that it is located in the second card slot. Real-time voice parameters are directly loaded into the array  --  rt --  table. When this is all complete, the DSP 16 is fully aware of the number and type of voices that it can play. 
       --  uart --  transmit() assembly subroutine 
     FIG. 14 depicts the  --  uart --  transmit() subroutine. This subroutine is called from the main loop when the CTXTIME flag-bit is set. In operation, it checks for buss availability via UARTimr. If the buss is unavailable, the routine exits. If the buss is available, it follows standard UART transmit routines. Address and data are read from the array  --  tx --  message  5!. When 0×FF is stored in  --  tx --  message, this signals an end of message. The UART is then set for receive, and the TXTIME flag-bit in the  --  flags register is reset. 
     update --  toll --  chans() interrupt routine 
     FIGS. 15A and 15B depict the update --  toll --  chans() subroutine. This subroutine can process 24 simultaneous channels in one 44.1 μsec update period. It uses one index register for each table parameter--each index gets post modified at read time by the length of a channel table so that it is pointing to the next channel&#39;s set of parameters. Two channel tables (ch --  tablea and ch --  tableb) in separate memory areas are utilized to allow dual memory accesses during updating. The previous interrupt calculations saved in  --  dataout are passed to the serial port output array tx --  buf. A channel with a non-zero MODE is active, and a linear interpolation between DATA1 and DATA2 is performed based on the value of the fractional address FADDR. This value multiplied by VOLUME is accumulated in MRF. New integer (IADDR) and fractional (FADDR) addresses are calculated, by adding increments IINC and FINC to them, respectively, and then saved. The current address is checked to see if it is past the loop end address LEADDR. If so, the current address IADDR is set equal to the loopstart address LSADDR. Additionally, the volume is multiplied by the factor DECAY and saved. A DMA chain, toll --  tcb, is added to for each active channel&#39;s data acquisition for the next iteration. When all channels are updated, the DMA chain for card data is terminated and the DMA channel 6 chain pointer register,CP6, is loaded with the last address of the first tcb in toll --  tcb. This starts fetching data from an external card to the active channel tables&#39; data registers. This DMA chain is still running even when the next update&#39;s interrupt occurs. 
     update --  swing --  chans interrupt routine 
     FIGS. 16 and 16A-16H depict the update --  swing --  chans subroutine. This subroutine currently processes up to 12 simultaneous channels (6 swinging bells) in one 44.1 μsec update period. It uses the same structure as the toll update routine (update  --  toll --  chans) but adds inner loop control processing. This routine stores values in sw --  tablea and sw --  tableb. Each swinging bell is made up of two active channels: (1) a main channel, which controls the overall operation of both channels, and (2) a doppler channel, which adds processing to create a doppler shifting effect on the bell. The MODE register of each channel not only signals activity but completely defines the running status of the channel: 
     
         ______________________________________CSW.sub.-- STATUS  = channel activeCSW.sub.-- DIRECTION              = swingingforward/backwardCSW.sub.-- DOPPLER = main/doppler channelCSW.sub.-- ZERO.sub.-- CROSS              = time to find zerocrossingCSW.sub.-- UP      = swinging upCSW.sub.-- DOWN    = swinging downCSW.sub.-- STRIKE.sub.-- INH              = swung down - last tollCSW.sub.-- COUNT   = count-bit for swingingup and downCSW.sub.-- WOW     = signals when FINC ofdoppler channel is modifiedCSW.sub.-- MODE.sub.-- CHANGE = time to change swing modeCSW.sub.-- UP.sub.-- DOWN              = swinging up - have to              swing down when doneCSW.sub.-- DOWN.sub.-- UP              = swinging down - have              to swing up when done______________________________________ 
    
     Main channel controls include: enable/disable WOW-bit of doppler channel; check for end of swing, if so, set ZERO --  CROSS; check for zero crossing of main channel(minimum value); when minimum found, look for MODE --  CHANGE; if none perform normal channel reassign &#34;swingin&#34;; only perform mode change on front strike--else &#34;swingin&#34;; if SWING --  UP or SWING --  DOWN set, use &#34;swingin up&#34; and &#34;swingin down&#34; reassign routines; each swing up/down is made up of two sets of front and back strikes, each time the doppler value, swing length and volume is changed to simulate the up/down swinging. 
     The doppler channel only has to generate its own effect, which is made by modifying the finc only during the WOW period. On the front swing, the doppler channel rises in pitch, and on the back swing drops in pitch. The WOW period is active from the 1/4 point to the 3/4 point in the swing period. This varying finc causes the bell to sound as if it is changing velocity. 
     update --  real --  time interrupt routine 
     FIG. 17A-17C depict the update --  real --  time subroutine. In the present embodiment, this subroutine is able to accommodate 2 sample formats: 22050 8 bit and 11025 8-bit. It does not change codec sample rates. The subroutine numerically calculates the values. 8-bit data is unsigned, and only one real time sample is active at a time. The active position in  --  rt --  table is determined by  --  rt --  table --  ptr, which is assigned in the assign --  rt --  voice subroutine. Voice parameters are read from rt --  table; each sample reserves 16 bytes for initialization and run-time data. The sample rate is determined by the parameter RT --  MAX --  COUNT: 
     2=&gt;44100 
     4=&gt;22050 
     8=&gt;11025 
     This implies how many steps the code must go thru to process one full 16-bit data value. These particular values apply only for 8-bit data, i.e., 22050 implies that it will (1) use the first 8-bit data value, (2) interpolate between first and second values, (3) use the second 8-bit data value, or (4) interpolate between the second value and the next word&#39;s first value. Thus, a four step process is performed for one 16-bit word (the DSP 16 only reads 16-bit words from the cards). Both swing and toll updates look for channel minimum volumes. If found, the channel is disabled by zeroing CH --  MODE or SW --  MODE. 
     In sum, the present invention as presently implemented is controlled by an Analog Devices ADSP21062 running at 33 MHz. Program code is loaded at power-up and reset from a 128 k×8 Flash memory, the 28F010. The DSP receives commands via an RS-485 serial interface which is arbitrated by a programmable UART, the Intersil 26C91. Bell and real-time sample data are stored on up to two PMCCIA 68 pin Flash memory cards for DSP access. Audio data is passed in the digital domain via a bi-directional serial link to an Analog Devices AD1847 Stereo Codec which digitizes analog audio, converts digital data into the analog domain, and performs input and output mixing and volume control. Analog Devices OP213&#39;s and SSM2142 op-amps are used for audio input and output mixing and buffering. The current version supplies two line-level audio outputs to external mixing or amplification. 
     The current version has the following capabilities and characteristics: 
     1) Up to 24 channels of sample data can be played simultaneously. 
     2) Up to 6 channels of a swinging realism effect on samples can be produced simultaneously. 
     3) other audio playback program such as File volley followed by &#34;Taps&#34;, or horns, whistles and other sound effects are stored as real-time (no pitch shifting) samples and can be in the following sample formats: 8 bit unsigned 11025 kHz and 8 bit unsigned 22050 kHz. 
     4) A tower control circuit is available to actuate a tower relay. 
     5) A MIDI input port is available providing access from an external MIDI controller such as a keyboard or sequencer. 
     It should be noted that the true scope of the present invention is not limited to the specific hardware and software elements described above, and thus many variations of the examples described above will fall within the scope of protection of the following claims. For example, modifications of the presently preferred embodiment include but are not limited to: 
     1) Using some different type of either DSP or microprocessor. 
     2) Implementing D/A and/or A/D conversion and audio attenuation and mixing via some other available audio codec or discrete component set. 
     3) Interface to any other type of serial buss with or without different protocols. 
     4) Implementing storage of either program or sample data in combination or separate in any other type of static or dynamic memory device. 
     5) Any number of simultaneous channels of either normal or swinging sample playing can be implemented. 
     6) Any other real-time samples of any existing standard digital audio sample formats can be implemented. 
     7) The tower control can be used as a general purpose I/O pin. 
     8) The codec which also digitizes audio can be used to process in real-time external audio signals and either store or output them.