goto section page goto youngmonkey main page make contact, e-mail


Korg Wavestation Developer Hints/Tips
Advertising Space Available

This document includes a number of comments and explanations to supplement
the developer documentation for the Wavestation series in general, and the
Wavestation SR in particular.

The sections of this document (seperated in the text by a line of stars) are:
Patch_Output parameter
Calculation of the LFO fade-in slope
Calculation of the Mix Envelope Parameters, including Mix_Count1 and Mix_YSlope4
Computing Checksums for the Wavestation Family
Order of System Exclusive messages sent after the ALL Dump
Code for Nibblization of Bytes
Wave Sequence Notes
Sending Individual Performances via System Exclusive
Vector Mix Equations

*   *   *   *   *   *   *   *   *   *   *   *

Patch_Output parameter

The Patch_Output byte is bit encoded (see the figure below). This parameter
determines the FX Bus outputs for each individual Wave, as shown on the Patch
Bus Assignment page. This allows each Wave to be assigned to any combination
of the four FX Buses (and, depending on the effects and effects routing, to
the outputs themselves). Note that this parameter is only used if the Part FX
Bus is set to "Patch;" otherwise, the Part's FX Bus value is used instead.

                      Unused Bits | Wave FX Bus Outputs (0=Off, 1=On)
                                  | D   C   B   A
Patch_Output bits:  7   6   5   4   3   2   1   0


*   *   *   *   *   *   *   *   *   *   *   *

Calculation of the LFO fade-in slope

/*
 *****************************************************************************
 * Function:
 *   update_lfox_fadein
 *
 * Parameters:
 *   indiv   *p   pointer to individual wave structure being edited
 *
 * Returns:
 *   nothing
 *
 * Global variables referenced:
 *   none
 *
 * Description:
 *   Calculate LFO fadein step size.
 *   Called when Lfo1_Fade is edited.
 *
 *****************************************************************************
 */
void    update_lfo1_fadein(indiv *p)
{
   if (Lfo1_Fade == 0)
      Lfo1_Inc = (0x7FFFFF*p->Lfo1_Amt)/127;
   else
      Lfo1_Inc = (0x7FFFFF*p->Lfo1_Amt)/(Rate_Tab[p->Lfo1_Fade]*127);
   return;
}

void    update_lfo2_fadein(indiv *p)
{
   if (Lfo2_Fade == 0)
      Lfo2_Inc = (0x7FFFFF*p->Lfo2_Amt)/127;
   else
      Lfo2_Inc = (0x7FFFFF*p->Lfo2_Amt)/(Rate_Tab[p->Lfo2_Fade]*127);
   return;
}

*   *   *   *   *   *   *   *   *   *   *   *

Calculation of the Mix Envelope Parameters, including Mix_Count1 and
Mix_YSlope4

/*
 *****************************************************************************
 * Function:
 *   update_mix_env
 *
 * Parameters:
 *   none
 *
 * Returns:
 *   nothing
 *
 * Global variables referenced:
 *   none
 *
 * Description:
 *   Calculate mix envelope slopes after rate or level change
 *****************************************************************************
 */
/*   Called when Rate1 is edited   */

void    update_mix_rate1(void)
{

   Mix_Count1 = Rate_Tab[Mix_Rate1];
   Mix_Count1B = Rate_Tab[Mix_Rate1];
   if (Mix_X1 >= Mix_X0)
      Mix_XSlope1 =
0x1000000*((ulong)Mix_X1-(ulong)Mix_X0)/Rate_Tab[Mix_Rate1];
   else
      Mix_XSlope1,
-(0x1000000*((ulong)Mix_X0-(ulong)Mix_X1)/Rate_Tab[Mix_Rate1]);
   if (Mix_Y1 >= Mix_Y0)
      Mix_YSlope1 =
0x1000000*((ulong)Mix_Y1-(ulong)Mix_Y0)/Rate_Tab[Mix_Rate1];
   else
      Mix_YSlope1,
-(0x1000000*((ulong)Mix_Y0-(ulong)Mix_Y1)/Rate_Tab[Mix_Rate1]);
   return;
}
void    update_mix_rate2(void)
{

   Mix_Count2 = Rate_Tab[Mix_Rate2];
   Mix_Count2B = Rate_Tab[Mix_Rate2];
   if (Mix_X2 >= Mix_X1)
      Mix_XSlope2 =
0x1000000*((ulong)Mix_X2-(ulong)Mix_X1)/Rate_Tab[Mix_Rate2];
   else
      Mix_XSlope2,
-(0x1000000*((ulong)Mix_X1-(ulong)Mix_X2)/Rate_Tab[Mix_Rate2]);
   if (Mix_Y2 >= Mix_Y0)
      Mix_YSlope2 =
0x1000000*((ulong)Mix_Y2-(ulong)Mix_Y1)/Rate_Tab[Mix_Rate2];
   else
      Mix_YSlope2,
-(0x1000000*((ulong)Mix_Y1-(ulong)Mix_Y2)/Rate_Tab[Mix_Rate2]);
   return;
}
void    update_mix_rate3(void)
{

   Mix_Count3 = Rate_Tab[Mix_Rate3];
   Mix_Count3B = Rate_Tab[Mix_Rate3];
   if (Mix_X3 >= Mix_X2)
      Mix_XSlope3 =
0x1000000*((ulong)Mix_X3-(ulong)Mix_X2)/Rate_Tab[Mix_Rate3];
   else
      Mix_XSlope3,
-(0x1000000*((ulong)Mix_X2-(ulong)Mix_X3)/Rate_Tab[Mix_Rate3]);
   if (Mix_Y1 >= Mix_Y0)
      Mix_YSlope3 =
0x1000000*((ulong)Mix_Y3-(ulong)Mix_Y2)/Rate_Tab[Mix_Rate3];
   else
      Mix_YSlope3,
-(0x1000000*((ulong)Mix_Y2-(ulong)Mix_Y3)/Rate_Tab[Mix_Rate3]);
   return;
}
void    update_mix_rate4(void)
{

   Mix_Count4 = Rate_Tab[Mix_Rate4];
   Mix_Count4B = Rate_Tab[Mix_Rate4];
   if (Mix_X4 >= Mix_X3)
      Mix_XSlope4 =
0x1000000*((ulong)Mix_X4-(ulong)Mix_X3)/Rate_Tab[Mix_Rate4];
   else
      Mix_XSlope4,
-(0x1000000*((ulong)Mix_X3-(ulong)Mix_X4)/Rate_Tab[Mix_Rate4]);
   if (Mix_Y1 >= Mix_Y0)
      Mix_YSlope4 =
0x1000000*((ulong)Mix_Y4-(ulong)Mix_Y3)/Rate_Tab[Mix_Rate4];
   else
      Mix_YSlope4,
-(0x1000000*((ulong)Mix_Y3-(ulong)Mix_Y4)/Rate_Tab[Mix_Rate4]);
   return;
}


/*   Called when point 0 mix is edited.   */

void    update_mix_level0(void)
{
   update_mix_rate1();
   return;
}


/*   Called when point 1 mix is edited.   */

void    update_mix_level1(void)
{
   update_mix_rate1();
   update_mix_rate2();
   return;
}


/*   Called when point 2 mix is edited.   */

void    update_mix_level2(void)
{
   update_mix_rate2();
   update_mix_rate3();
   return;
}


/*   Called when point 3 mix is edited.   */

void    update_mix_level3(void)
{
   update_mix_rate3();
   update_mix_rate4();
   return;
}


/*   Called when point 4 mix is edited.   */

void    update_mix_level4(void)
{
   update_mix_rate4();
   return;
}


*   *   *   *   *   *   *   *   *   *   *   *

Computing Checksums for the Wavestation Family

The header bytes are excluded from the computation of the checksum. These
include:

   11110000 (F0)   System Exclusive status byte
   01000010 (42)   Korg ID
   0011nnnn (3n)   Format ID, n = channel number
   00101000 (28)   Wavestation device ID
   0mmmmmmm        Message type

The checksum is computed by adding up all the bytes of the message, excluding
the
header, and then performing a mod 128 on the sum.


*   *   *   *   *   *   *   *   *   *   *   *

Order of System Exclusive messages sent after the ALL Dump for the
Wavestation SR (the A/D is similar, but without the dumps labeled SR):

Performance Map Dump Expanded
Performance Map Dump SR
Multi Mode Setup Dump Expanded
Multi Mode Setup Dump SR
All Performances (RAM3)
All Patches (RAM3)
All Wave Sequences (RAM3)
System Setup Dump Expanded
System Setup Dump SR

The manual seems to suggest a different order - sorry if that caused
any confusion.

*   *   *   *   *   *   *   *   *   *   *   *

Code for Nibblization of Bytes

rcv_nybbles:
   mov.l   @sp+,@rcv_nybbles_pc:16          ; Address we called from
   sub.l   #1,loop_cnt1                     ; SCB goes until -1 so compensate
nybbles_loop:
   ;repeat
      wait_for_interrupt                    ; Wait for byte at MIDI IN port
      bset.b   #MIDI_LED,@led_status:16     ; Turn on MIDI LED....
      mov.b   @led_status,@LED_OUTPUT_PORT  ; Write it out to light LED
      set_device_timer      midi_in,#2000   ; Keep it lit for 2 seconds.
      mov.b   current_byte,@nybble_data:16  ; Save received data
      add.b   current_byte,@check_sum:16    ; Add received data to checksum
      wait_for_interrupt                    ; Wait for next MIDI IN byte
      add.b   current_byte,@check_sum:16    ; Add received data to checksum
      shll.b   #4,current_byte              ; Shift data left 4 bits
      or.b   @nybble_data:16,current_byte   ; Or 2 halves of data together
      mov.b   current_byte,@destin_ptr+     ; Save in dest and increment
      scb/f   loop_cnt1,nybbles_loop        ; Subtract from count and branch
   ;until eq -1;                            ; until count is -1
   wait_for_interrupt                       ; Get another byte
   add.b   current_byte,@check_sum:16       ; Add it to checksum
   wait_for_interrupt                       ; Get another byte
   add.b   current_byte,@check_sum:16       ; Add it to checksum
   mov.l   @rcv_nybbles_pc:16,r0            ; Get return address
   jmp   @r0                                ; Return

*   *   *   *   *   *   *   *   *   *   *   *

Wave Sequence Notes

In the early design stages of the Wavestation, the designers were concerned
that there be no arbitrary limits on the number of steps in a Wave Sequence.
To allow for maximum flexibility in this regard, individual Wave Sequences are
not stored discreetly. The waveseq data contains the basic information about
the wave sequence, including the starting step, the loop, modulation settings,
and so on, but the step data (including PCM wave, tuning, duration, crossfade,
etc.) for all 32 Wave Sequences in a Bank are stored together in a single,
massive block of data, the wavestep_block, and indexed by their number
within that block. The waveseq data stores the number of the starting step,
and the individual wavestep entries store links to both the step before and
the step after their position in the sequence.

Most of the entries in the Wave Sequence Data Structure (as described in the
Wavestation System Exclusive implementation, included in the Reference Guide
for all Wavestation models) are reasonably self-explanitory, but several
subjects require more comments:

1. The first wavstep entry is special; it is used as the stop step,
   entered at the end of every Wave Sequence.

2. Differences between WS_Link,WS_Slink and WS_Start_Step.

   The WS_Link is an index to the very first step in a
   Wave Sequence. All wavesteps after this one are linked
   with the WS_Flink and WS_Blink.

   The WS_Slink is the index of the start wavestep in the
   Wave Sequence (which does not have to be the first step).

   The WS_Start_Step is the step number of the start wavestep
   relative to the start of the Wave Sequence, whereas the WS_Slink
   is an index relative to the start of wavestep memory.


3. WS_Flink WS_Blink

   These are the links that link all the steps in a Wave Sequence.
   WS_Flink is the wavestep index of the wavestep following
   this one. If it is 0 then this is last step. WS_Blink is
   the wavestep index of the wavestep preceeding this one.
   An empty step has WS_Flink and WS_Blink set to 0xffff.


4. WS_Llink

   This is set to 0xffff except for the last step of a Wave Sequence.
   If it is set to 0xfffe it means that looping is off and the
   Wave Sequence should terminate. Otherwise if WS_Llink is set to any
   other value it is the wavestep index of the start of the loop.

5. WS_Mod_Index

   This is used for Dynamic Modulation of the Wave Sequence; it is a
   pre-calculated using the waveseq block's WS_Mod_Amt parameter (as
   ahown below), allowing faster realtime response. Basically it
   breaks the Wave Sequence down into a +/- 64 range of modulation values
   with the Wave Sequence start step located at 0. The WS_Mod_Index value
   is the modulation value which causes this wavestep to sound.
   The WS_Mod_Index of the wavstep following this one is the modulation
   value that causes this step to stop sounding or to start xfading
   away. Below is a code fragment used in calculating this value.


void     update_ws_mod(void)
{
    register long   seqstep;        /* Step array element number */
    register long   steptotal = 1;
    register long   stepcount = 0;
    ubyte   startstep;
    byte    modamount;
    word    temp;
    wavesequence    *waveseq_ptr = Cur_Waveseq_Ptr();
    wavestep        *wavestep_mem_ptr;


    if ((seqstep = &waveseq_ptr->WS_Link) == 0)
        return;
    startstep = waveseq_ptr->WS_Start_Step;
    modamount = waveseq_ptr->WS_Mod_Amt;
    if (modamount < 0)
        modamount = -modamount;

/* Calculate number of steps in wave sequence */
    while ((seqstep = &Cur_Wavestep_Mem_Ptr(seqstep)->WS_Flink) !=0)
    {
        steptotal +=1;
    }

/* Are we doing static or dynamic style of modulation */
    if ((127 & waveseq_ptr->WS_Mod_Src) <= 3)
    {
        seqstep = &waveseq_ptr->WS_Link;
        while (seqstep != 0)
        {
            wavestep_mem_ptr = Cur_Wavestep_Mem_Ptr(seqstep);

            if (modamount == 0)
                wavestep_mem_ptr->WS_Mod_Index = 127;
            else
            {
                if (stepcount >= startstep)
                {
                    temp =((8191/modamount) *(stepcount - startstep)
                         /(steptotal-startstep));
                    if (temp > 127)
                        temp = 127;
                    wavestep_mem_ptr->WS_Mod_Index = temp;
                }
                else
                {
                    temp =  (8191/modamount)*(startstep-stepcount)/(startstep);
                    if (temp > 127)
                        temp = 127;
                    wavestep_mem_ptr->WS_Mod_Index = temp;
                }
            }
            stepcount++;
            seqstep = &wavestep_mem_ptr->WS_Flink;
        }
    }
}


*   *   *   *   *   *   *   *   *   *   *   *

Sending Individual Performances via System Exclusive

When sending a Performance and its Patches, it's best to send the
Performance itself last. When a Patch is received via MIDI SysEx, the
current Part is always changed to reference that new Patch, to make
auditioning individual Patches easy; this means, however, that the Performance
is altered. To ensure that the Performance remains the same, sent it after you
send its Patches.

*   *   *   *   *   *   *   *   *   *   *   *

Vector Mix Equations

{
    switch (sys.mix_env_point)
    {
     case 0:
         xp = patch_ptr->Mix_X0 + patch_ptr->Mix_Y0 - 255;
         yp = patch_ptr->Mix_Y0 - patch_ptr->Mix_X0;
         break;
     case 1:
         xp = patch_ptr->Mix_X1 + patch_ptr->Mix_Y1 - 255;
         yp = patch_ptr->Mix_Y1 - patch_ptr->Mix_X1;
         break;
     case 2:
         xp = patch_ptr->Mix_X2 + patch_ptr->Mix_Y2 - 255;
        yp = patch_ptr->Mix_Y2 - patch_ptr->Mix_X2;
         break;
     case 3:
         xp = patch_ptr->Mix_X3 + patch_ptr->Mix_Y3 - 255;
         yp = patch_ptr->Mix_Y3 - patch_ptr->Mix_X3;
         break;
     case 4:
         xp = patch_ptr->Mix_X4 + patch_ptr->Mix_Y4 - 255;
         yp = patch_ptr->Mix_Y4 - patch_ptr->Mix_X4;
         break;
    }

The code below limits the joystick data to a diamond-shaped range:

                    C
            _________________
           | clip   *   clip |
           |      *   *      |
           |    *       *    |
           |  *           *  |
         A |*               *| B
           |  *           *  |
           |    *       *    |
           | clip *   * clip |
           |________*________|

                    D


   /* Now calculate the A,B,C, and D % from the x,y coordinates */

    if (xp > 127)   /* Limit range to -128 - 127 */
        xp = 127;
    else if (xp < -128)
        xp = -128;

    if (yp > 127)   /* Limit range to -128 - 127 */
        yp = 127;
    else if (yp < -128)
        yp = -128;

    xp += 128;
    yp += 128;

    d = xp*yp/645;   /* Calculate individual wave % */
    c = xp*(255 - yp)/645; /* 645=(255^2/100)*127/128 */
    b = (255-xp)*(255-yp)/645;
    a = 100 - b - c - d;
}
Vector Mix Equations

*   *   *   *   *   *   *   *   *   *   *   *