Shruthi-Synthesizer and my "WAVE 1"


#1121

Hi , i know it is a long time for developement .
The hardware is ready since years. Actual (29.11.2017) we bugfix the software. Rolf get a new job and so there is not too much time to do it.
On the other end - it is not so much to do . All functions are now inbuild. We build in a stepper and remove the ARP . This gives a little memory in the CPU board free .Stepper Video here .



You see , we are hard on the CPU limit. To do are the following things . Bugfix the mod matrix, bugfix the Sample editor , build in MIDI CC’s for an external hardware controller . Build in a glide function and build in the mod wheel function . Than we are ready . Hopefully we finish this jear the software .(oh God yes …).
The hardware /PCB’s are not a big problem. This we can order within one week . Harder is to write the DIY manual …So i hope we finished in the first quarter 2018.
Hopefully

Greets
Andre’


#1122

Hello and good morning…

I have a little sound problem with the “Quad Saw” in DE GENERATOR. In comparison with the
Shruthi Synthesizer sounds the sound in the DE-GENERATOR very static. I have the source code
from the Shruthi for the DE-GENERATOR something adapted. The parameter value is on both
Synthesizers at 0. I shot a little video to show it and hear it.
For the QuadSaw to work, the “phase_spread” must always be> 0.

Youtube: https://youtu.be/G7RaY6ZXLOM

 // ------- Quad saw or pwm (mit aliasing) ------------------------------------

void Oscillator::RenderQuad(uint8_t* buffer) {
uint16_t phase_spread = (
static_cast<uint32_t>(phase_increment_.integral) * parameter_) >> 13;
++phase_spread;
uint16_t phase_increment = phase_increment_.integral;
uint16_t increments[3];
for (uint8_t i = 0; i < 3; ++i) {
phase_increment += phase_spread;
increments[i] = phase_increment;
}

if (shape_ == WAVEFORM_QUAD_SAW_PAD) {
BEGIN_SAMPLE_LOOP
UPDATE_PHASE
data_.qs.phase[0] += increments[0];
data_.qs.phase[1] += increments[1];
data_.qs.phase[2] += increments[2];
uint8_t value = (phase.integral >> 10);
value += (data_.qs.phase[0] >> 10);
value += (data_.qs.phase[1] >> 10);
value += (data_.qs.phase[2] >> 10);
*buffer++ = value;
END_SAMPLE_LOOP
}
else { //WAVEFORM_QUAD_PWM
uint16_t pwm_phase = static_cast<uint16_t>(127 + parameter_) << 8;
BEGIN_SAMPLE_LOOP
UPDATE_PHASE
data_.qs.phase[0] += increments[0];
data_.qs.phase[1] += increments[1];
data_.qs.phase[2] += increments[2];
uint8_t value = phase.integral < pwm_phase ? 0 : 63;
value += data_.qs.phase[0] < pwm_phase ? 0 : 63;
value += data_.qs.phase[1] < pwm_phase ? 0 : 63;
value += data_.qs.phase[2] < pwm_phase ? 0 : 63;
*buffer++ = value;
END_SAMPLE_LOOP
}
}

//*************************************************************************

// OscRender: Quad saw or pwm (mit aliasing)
//*************************************************************************
void OscRender_PAD(uint8_t osc_nr)
{
uint8_t block_size = 40;
uint8_t shape = Osc.shape[osc_nr];
uint8_t parameter = Osc.prm[osc_nr];
uint8_t *buffer; // pointer name
uint8_t i = 0;

if (osc_nr == 0)
{
	if (Voice.buffer_nr == 0)
	{
		buffer = Voice.Buffer2a;
	}
	else {buffer = Voice.Buffer1a;}
}
else
{
	if (Voice.buffer_nr == 0)
	{
		buffer = Voice.Buffer2b;
	}
	else {buffer = Voice.Buffer1b;}
}

uint16_t phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
++phase_spread;

uint16_t increments[3];
for (uint8_t i = 0; i < 3; ++i) {
	phase_increment_ += phase_spread;
	increments[i] = phase_increment_;
}

//BEGIN_SAMPLE_LOOP ---------------------------------------------------
if (shape == OSC_SAW_PAD) {
	do
	{
		// update Phase
		Osc.phase[osc_nr] += Osc.phase_increment[osc_nr];
		
		if (SREG & 0b00000001) // test overflow
		{
			Osc.phase[osc_nr] = 0;
		}
			
		uint16_t phase_intergral = Osc.phase[osc_nr] >> 8;
		uint8_t value = (phase_intergral >> 10);
		
		// Osc1
		if (osc_nr == 0)
		{
			Osc1.data_qs_phase[0] += increments[0];
			Osc1.data_qs_phase[1] += increments[1];
			Osc1.data_qs_phase[2] += increments[2];
			value += (Osc1.data_qs_phase[0] >> 10);
			value += (Osc1.data_qs_phase[1] >> 10);
			value += (Osc1.data_qs_phase[2] >> 10);
		}
		else // Osc2
		{
			Osc2.data_qs_phase[0] += increments[0];
			Osc2.data_qs_phase[1] += increments[1];
			Osc2.data_qs_phase[2] += increments[2];
			value += (Osc2.data_qs_phase[0] >> 10);
			value += (Osc2.data_qs_phase[1] >> 10);
			value += (Osc2.data_qs_phase[2] >> 10);
		}
		
		// write sample in sound-buffer
		buffer[i++] = ~value;
		
	} while (--block_size);
}

}

Maybe someone has an idea how I can improve that in DE-GENERATOR. Thanks… :slight_smile:


#1123

I have now integrated a Vowel sound from Shruthi in my DE-GENERATOR: https://soundcloud.com/rolf-degen/vowel-sound-from-de-generator


#1124

I experimented a little and added a small random value to the phase_spread. Sounds better but just as different as in Shruthi.

My code
.
//*************************************************************************
// OscRender: Quad saw or pwm (mit aliasing)
//*************************************************************************
void OscRender_PAD(uint8_t osc_nr)
{
uint8_t block_size = 40;
uint8_t shape = Osc.shape[osc_nr];
uint8_t parameter = Osc.prm[osc_nr];
uint8_t *buffer; // pointer name
uint8_t i = 0;

if (osc_nr == 0)
{
    if (Voice.buffer_nr == 0)
    {
        buffer = Voice.Buffer2a;
    }
    else {buffer = Voice.Buffer1a;}
}
else
{
    if (Voice.buffer_nr == 0)
    {
        buffer = Voice.Buffer2b;
    }
    else {buffer = Voice.Buffer1b;}
}

uint16_t phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;

// Test phase_spread with/without random value
if (Osc.Test_flag == 0)
{
    ++phase_spread;
}
else {phase_spread += (Noise.sample >> 7);}


uint16_t increments[3];
for (uint8_t i = 0; i < 3; ++i) {
    phase_increment_ += phase_spread;
    increments[i] = phase_increment_;
}

//BEGIN_SAMPLE_LOOP ---------------------------------------------------
if (shape == OSC_SAW_PAD) {
    do
    {
        // update Phase
        Osc.phase[osc_nr] += Osc.phase_increment[osc_nr];
       
        if (SREG & 0b00000001) // test overflow
        {
            Osc.phase[osc_nr] = 0;
        }
           
        uint16_t phase_intergral = Osc.phase[osc_nr] >> 8;
        uint8_t value = (phase_intergral >> 10);
       
        // Osc1
        if (osc_nr == 0)
        {
            Osc1.data_qs_phase[0] += increments[0];
            Osc1.data_qs_phase[1] += increments[1];
            Osc1.data_qs_phase[2] += increments[2];
            value += (Osc1.data_qs_phase[0] >> 10);
            value += (Osc1.data_qs_phase[1] >> 10);
            value += (Osc1.data_qs_phase[2] >> 10);
        }
        else // Osc2
        {
            Osc2.data_qs_phase[0] += increments[0];
            Osc2.data_qs_phase[1] += increments[1];
            Osc2.data_qs_phase[2] += increments[2];
            value += (Osc2.data_qs_phase[0] >> 10);
            value += (Osc2.data_qs_phase[1] >> 10);
            value += (Osc2.data_qs_phase[2] >> 10);
        }
       
        // write sample in sound-buffer
        buffer[i++] = ~value;
       
    } while (--block_size);
}

}

In the video I switch between an addition with random value and without.

Youtube: https://www.youtube.com/watch?v=klz7gUTZ9xs


#1125

Hello everyone and Happy New Year :rofl:

Have the code now as in Shruthi Synth adapted (Osc.phase_increment and buffer size). But same problem :disappointed_relieved:

When increasing the parameter value (0-127), the sound is different from the Shruthi.

Sound: https://soundcloud.com/rolf-degen/quadsaw-4

Up 21 sec (parameter value = 103) you can hear the error. A value is overflow ??

I checked everything. I see no mistake. The only difference in my code is the cast in Shruthi code:

uint16_t phase_spread = (
static_cast<uint32_t>(phase_increment_.integral) * parameter_) >> 13;
++phase_spread;
uint16_t phase_increment = phase_increment_.integral;
uint16_t increments[3];

My code:

//*************************************************************************
// OscRender: Quad saw or pwm (mit aliasing)
//*************************************************************************
void OscRender_PAD(uint8_t osc_nr)
{
if (osc_nr == 1)
{
return;
}

uint8_t block_size = 128;
uint8_t shape = Osc.shape[osc_nr];
uint8_t parameter = Osc.prm[osc_nr];
uint8_t *buffer;	// pointer name
uint16_t increments[3];

// Double buffering	
if (Voice.buffer_nr == 0)
{
	buffer = Voice.Buffer2a;
}
else {buffer = Voice.Buffer1a;}	

uint16_t phase_increment_ = (Osc.phase_increment[osc_nr] >> 8);
uint16_t phase_spread = (uint32_t)(phase_increment_ * parameter) >> 13;
++phase_spread;

for (uint8_t i = 0; i < 3; ++i) {
	phase_increment_ += phase_spread;
	increments[i] = phase_increment_;
}	


//BEGIN_SAMPLE_LOOP ---------------------------------------------------
do
{
	// update Phase
	Osc.phase[osc_nr] += Osc.phase_increment[osc_nr];
	
	if (SREG & 0b00000001) // test overflow
	{
		Osc.phase[osc_nr] = 0;
	}
	
	uint16_t phase_intergral = Osc.phase[osc_nr] >> 8;
	uint8_t value = (phase_intergral >> 10);
	
	Osc1.data_qs_phase[0] += increments[0];
	Osc1.data_qs_phase[1] += increments[1];
	Osc1.data_qs_phase[2] += increments[2];
	value += (Osc1.data_qs_phase[0] >> 10);
	value += (Osc1.data_qs_phase[1] >> 10);
	value += (Osc1.data_qs_phase[2] >> 10);
	
	// write sample in sound-buffer
	*buffer++ = ~value;
	
} while (--block_size);	

}

Thank for help. Greetings from Germany


#1126

Ok. result is init “phase_increment_” to 24Bit. But sound is not compare with shruthi. DE-GENERATOR Its more static sound.

Soundcloud: https://soundcloud.com/rolf-degen/quadsound-5


#1127

My best sound result is an addition of a random value (8bit) to phase_spread.

My Code:

uint24 phase_increment = (Osc.phase_increment[osc_nr] >> 8);
uint16_t phase_spread = (uint32_t)(phase_increment
* parameter) >> 13;
//++phase_spread;
phase_spread += (Noise.sample >> 7);

for (uint8_t i = 0; i < 3; ++i) {
    phase_increment_ += phase_spread;
    increments[i] = phase_increment_;
}

Sound example: https://soundcloud.com/rolf-degen/quadsaw-test

Greetings Rolf


#1128

My new “Workplace” and a place for the delicious cookies are still available :yum:

Greetings Rolf


#1129

Hallo…

How calculate the increments for shruthi

First value: 16Bit * note_frequenz / sample_rate
16.777.215 * 25,956544 (G#0) / 39215 Hz = 11104

second values… I don’t know ???

This increments 39.250 Hz
const prog_uint16_t lut_res_oscillator_increments[] PROGMEM = {
11104, 11185, 11266, 11347, 11430, 11512, 11596, 11680,
11765, 11850, 11936, 12022, 12109, 12197, 12285, 12374,
12464, 12554, 12645, 12737, 12829, 12922, 13016, 13110,
13205, 13301, 13397, 13494, 13592, 13691, 13790, 13890,
13991, 14092, 14194, 14297, 14401, 14505, 14610, 14716,
14822, 14930, 15038, 15147, 15257, 15367, 15479, 15591,
15704, 15818, 15932, 16048, 16164, 16281, 16399, 16518,
16638, 16758, 16880, 17002, 17125, 17249, 17374, 17500,
17627, 17755, 17884, 18013, 18144, 18275, 18408, 18541,
18675, 18811, 18947, 19084, 19223, 19362, 19502, 19643,
19786, 19929, 20074, 20219, 20366, 20513, 20662, 20812,
20962, 21114, 21267, 21421, 21577, 21733, 21890, 22049,
22209,
};

Thanks for help :))


#1130

16 bit is 65536 and the first note in the table is MIDI note 116.

65536 * (440 * 2 ** ((116 - 69)/ 12.0) / 39215)

Then it goes up by increment of 0.125 (8 entries per semitone).

65536 * (440 * 2 ** ((116.125 - 69)/ 12.0) / 39215)

Also, I remind you that the tables in resources.cc are generated by python code that is also made public.


#1131

Hallo Oliver

Great sanks for your tips.

Greetings from germany. Rolf


#1132

The singing DE-GENERATOR

Parameter
Osc1: Vowel
Osc2: Vowel -7 semitones
Env1: CUTOFF + Osc1+2 vowel parameter
Env2: VCA
LFO1: Osc1+2 fine tune
Portamento: 14
Delay: Time 114 / Feedback 79 / Volume 127

Link: https://soundcloud.com/rolf-degen/singing-de-generator


#1133

I found a bug in RenderVowel code from shruthi.

// Interpolate formant amplitudes.
for (uint8_t i = 0; i < 4; ++i) {
uint8_t amplitude_a = ResourcesManager::Lookup<uint8_t, uint8_t>(
wav_res_vowel_data,
offset_1 + 3 + i);
uint8_t amplitude_b = ResourcesManager::Lookup<uint8_t, uint8_t>(
wav_res_vowel_data,
offset_2 + 3 + i);
data_.vw.formant_amplitude[i] = U8U4MixU8(
amplitude_a,
amplitude_b, balance);
}

False: “for (uint8_t i = 0; i < 4; ++i) {”

Right: “for (uint8_t i = 0; i < 3; ++i) {”

uint8_t formant_amplitude[3];

//--------------------------------------------------------------------------------------------
Another mistake ??

int16_t phase_noise = int8_t(Random::state_msb()) * int8_t(data_.vw.noise_modulation);
if ((phase.integral + phase_noise) < phase_increment_.integral) {

There is no datas for data_.vw.noise_modulation. noise_modulation is always zero,


Vowels OSC artifacts
#1134

I see what you mean. I wonder what this means in behaviour though:

When the for loop (erroneously?) access formant_amplitude[3] it will actually set the value noise_modulation if I’m not mistaken. This means noise_modulation isn’t 0 after all.

B


#1135

That depends on the memory alignment. It may well be that the compiler adds another byte for padding after formant_amplitude[3]

Edit: Sorry my bad. It’s an 8bit processor, there is no need fro alignment. Consequently, there seems to be no padding on avr-gcc (unless soemthing is stored in flash - then there’s a aligment to even numbered adresses)


#1136

Hmmm, I don’t understand why that would happen for a consecutive number of bytes in a C struct.
Anyway, I only think it’s Olivier that can comment if this is a deliberate “hack” or not.
Cheers


#1137

Not a bug.

There are 7 bytes of data per phoneme: 3 formant frequencies, 3 formant amplitudes, and an additional noise amplitude.

The 3 amplitudes and the noise are all represented by a 4-bit number, so the same code is used to crossfade the 3 amplitudes and the noise. It’s normal for the loop to count to 4, and to affect the variable that follows the last entry in the formant_amplitude[] array - noise_modulation


#1138

Add with phase_noise makes interference above parameters value 96

vowel with phase_noise from Shruthi: https://soundcloud.com/rolf-degen/vowel-sound-from-shruthi

vowel without phase_noise from DE-GENERATOR: https://soundcloud.com/rolf-degen/vowel-sound-from-de-generator-1


#1139

Now… my code is function without noise_amplitudes and without interference:

//*************************************************************************
// OscRender: VOWEL
//*************************************************************************
void OscRender_Vowel(uint8_t osc_nr)
{
uint8_t block_size = 127;
uint8_t parameter;
uint8_t *buffer;

if (osc_nr == 0)
{
	parameter = Osc.prm[0];
	if (Voice.buffer_nr == 0)
	{
		buffer = Voice.Buffer2a;
	}
	else {buffer = Voice.Buffer1a;}
	
	Osc.vw_update[0] = (Osc.vw_update[0] + 1) & 3;
	
	if (!Osc.vw_update[0])
	{
		uint8_t offset_1 = U8ShiftRight4(parameter);
		offset_1 = (uint8_t)(U8U8Mul(offset_1, 7));
		uint8_t offset_2 = offset_1 + 7;
		uint8_t balance = parameter & 15;
		
		// Interpolate formant frequencies.
		for (uint8_t i = 0; i < 3; ++i) {
			Osc.vw1_formant_increment[i] = U8U4MixU12(
			pgm_read_byte (&(wav_res_vowel_data[offset_1 + i])),
			pgm_read_byte (&(wav_res_vowel_data[offset_2 + i])),balance);
			Osc.vw1_formant_increment[i] <<= 3;}
		
		// Interpolate formant amplitudes.
		for (uint8_t i = 0; i < 3; ++i) {
			uint8_t amplitude_a = pgm_read_byte (&(wav_res_vowel_data[offset_1 + 3 + i]));
			uint8_t amplitude_b = pgm_read_byte (&(wav_res_vowel_data[offset_2 + 3 + i]));
			Osc.vw1_formant_amplitude[i] = U8U4MixU8(amplitude_a,amplitude_b, balance);}
	}
	
	uint16_t phase_integral = Osc.phase[0] >> 8;
	uint16_t phase_increment_integral = Osc.phase_increment[0] >> 8;
	
	
	// sample loop --------------------------------------------------------
	do
	{
		int8_t result = 0;
		
		for (uint8_t i = 0; i < 3; ++i) {
			Osc.vw1_formant_phase[i] += Osc.vw1_formant_increment[i];
			result += pgm_read_byte (&((i == 2 ? wav_res_formant_square : wav_res_formant_sine)
			[((Osc.vw1_formant_phase[i] >> 8) & 0xf0) | Osc.vw1_formant_amplitude[i]]));}
		
		result = S8U8MulShift8(result, ~(phase_integral >> 8));
		phase_integral += phase_increment_integral;
		
		if (phase_integral < phase_increment_integral) {
			Osc.vw1_formant_phase[0] = 0;
			Osc.vw1_formant_phase[1] = 0;
			Osc.vw1_formant_phase[2] = 0;
		}
		uint8_t x = S16ClipS8(4 * result) + 128;
		*buffer++ = x;
		*buffer++ = x;
		block_size--;			
	} while (block_size--);
	
	// update phase
	Osc.phase[0] = (__uint24)phase_integral << 8;
}

Vowel sound from DE-GENERATOR: https://soundcloud.com/rolf-degen/vowel2

two vowel voices: https://soundcloud.com/rolf-degen/vowel3


#1140

I love vowel sounds :heart_eyes: