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
* * * * * * * * * * * *
|