DIYstompboxes.com

DIY Stompboxes => Digital & DSP => Topic started by: Ksander on October 27, 2023, 04:33:25 PM

Title: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 27, 2023, 04:33:25 PM
Demo: https://drive.google.com/file/d/1_1SSOmZFlLFsfo6QO0Em526tlwOML5Ju/view?usp=sharing (https://drive.google.com/file/d/1_1SSOmZFlLFsfo6QO0Em526tlwOML5Ju/view?usp=sharing)

I have previously built a pitch shifter using an attiny85, and figured the code might be suitable for a chorus with a few modifications. In the adapted code, the attiny85 can produce a delay up to 14-something ms, and this delay is varied between the max value and 0ms over a period of about 7 seconds; following a triangular waveform. The output is then combined with the (buffered) input signal to yield a chorus effect. It works, and is indeed chorusy! But, there is still some noise -- consider this a first version.

Attached are the code and schematic. Input to improve the code and/or schematic would be much appreciated (also the schematic may be wrong, it is drawn from memory). Considering how complicated chorus effects typically are, I figure this has some serious DIY potential.


/* Attiny85 Chorus by Ksander de Winkel 27th October 2023

   8-Bit Variable delay for audio (guitar/microphone) signals.
   Combining the (scaled down) output with the original input signal yields a chorus effect.
   The maximum delay is 14.3ms. The amount can be scaled by changing the value of Shift.
   The period of the variation can be changed by setting the wrapping value of Counter.
   Note that these variables affect each other.
 
   Based on Attiny85 pitch shifter by David Johnson-Davies - www.technoblogy.com - 11th February 2017
   ATtiny85 @ 8MHz (internal oscillator; BOD disabled)
   
   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license:
   http://creativecommons.org/licenses/by/4.0/

*/

volatile uint8_t Buffer[256];               // Circular buffer
volatile uint8_t WritePtr, LastPtr, New, Ptr, Counter, Shift;
volatile bool Direction = true;
// Everything done by interrupts

// Write to buffer when ADC conversion is complete
ISR (ADC_vect) {

  Buffer[LastPtr] = New;
  New = ADCH + 128;
  Buffer[WritePtr] = (Buffer[WritePtr] + New)>>1;
  LastPtr = WritePtr;
  WritePtr = (WritePtr + 1) & 0xFF;

}

// Read from buffer and output to DAC
ISR (TIMER0_COMPA_vect) {

  Counter = (Counter + 1) & 0xFF;

  if (Counter == 0) {
   
    if (Direction) {
      Shift = (Shift + 1) & 0xFF;
    } else {
      Shift = (Shift - 1) & 0xFF;
    }

    if (Shift == 255 | Shift == 0) Direction = !Direction;
   
  }
 
  Ptr = (LastPtr - Shift) & 0xFF;

  OCR1A = Buffer[Ptr];

}

// Setup

void setup () {
 
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1<<PCKE | 1<<PLLE;     

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                              // Timer interrupts OFF
  TCCR1 = 1<<PWM1A | 2<<COM1A0 | 1<<CS10; // PWM OCR1A, clear on match, 1:1 prescale
  OCR1A = 128;
  pinMode(1, OUTPUT);                     // Enable OC1A PWM output pin

  // Set up Timer/Counter0 to generate 20kHz interrupt
  TCCR0A = 2<<WGM00;                      // CTC mode
  TCCR0B = 2<<CS00;                       // /8 prescaler
  OCR0A = 55;                             // 17.9kHz interrupt (1M/56)
  TIMSK = TIMSK | 1<<OCIE0A;              // Enable interrupt
 
  // Set up ADC
  ADMUX = 6<<REFS0 | 1<<ADLAR | 7<<MUX0;  // Internal 2.56V ref, ADC2 vs ADC3, x20
  // Enable, auto trigger, interrupt, 250kHz ADC clock:
  ADCSRA = 1<<ADEN | 1<<ADSC | 1<<ADATE | 1<<ADIE | 5<<ADPS0; 
  ADCSRB = 1<<7 | 0<<ADTS0;               // Bipolar, free-running

}

void loop () {
}

updated schematic:
(https://i.postimg.cc/0rQsKwMy/Tiny-Chorus-schem.png) (https://postimg.cc/0rQsKwMy)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 28, 2023, 04:36:42 AM
Tiny improvement: add

pinMode(#,INPUT_PULLUP);
with #=0 and another line with #=2, to the setup. This defines the levels on pin pb0 and pb2, and might reduce some noise as well(?).
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on October 28, 2023, 06:43:20 AM
This is a very interesting bit of work, Ksander. Nice one!

Looking at the hardware for a minute, one reason it's so simple is because it leaves out any filtering. A BBD-based chorus (or *any* sampling system, really) would usually include a filter ahead of the delay to eliminate any frequencies that would cause aliasing when sampled. There would also be another filter after the delay to smooth the output and turn the digital output back into an analogue signal.
Both of these filters are designed to remove noise of certain type - whether that's what your circuit has a problem with, I can't say. With a PWM output, proper filtering to remove the PWM clock would be especially important.

I've got some questions too.

It looks from the code like the sample rate is 17.9KHz. And since everything seems to be done with bytes, it looks like we've got 8 bit ADC input? Is that right?
The output is then done with PWM module. What frequency is this? again, it's 8-bit presumably?

There's a couple of things I don't get in the code too.

The ADC set up says "bipolar" which I take to mean "signed -128 to +127", but then the data we use everywhere is unsigned 0-255, and at one point we even add an offset to remove the negative values (is that what it's doing?):

  New = ADCH + 128;
Would it be better to set the ADC up for unipolar values instead?

This line in the ADC ISR doesn't seem to do anything:

  Buffer[LastPtr] = New;
because it is followed by this:

  New = ADCH + 128;
Finally, the way the LFO is produced is interesting. First it uses the counter variable to effectively divide the samaple rate by 256, so 17900/256 = 69.92Hz. It then uses this slower rate to count up from 0 to 255 and then back down from 255 to 0 again. That's 512 steps, and 69.92/512 = 0.1365 Hz = 7.3seconds per wave.
One improvement would be to use the 8 bits of the Counter variable to produce interpolation between one sample and the next. At the moment the code outputs the same sample 256 times before then jumping to the next one. That abrupt jump could be smoothed by doing a linear interpolation across the 256 samples instead - crossfading from one to the other, essentially. This would reduce some noise.

There's a theoretical noise floor due to the 8-bit sampling which isn't very low, so we're never going to be able to do better than that, but it might be nice to get as close as we can!

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 28, 2023, 07:58:08 AM
Thanks for the elaborate reply!

Actually, there is (or should be) some filtering; there is an 100pF cap to ground after the 220k resistor of the output voltage divider, which is intended as a ~ 8kHz LP filter, and then there is a 2.2nF cap at the junction of the buffered input signal and attiny output, which both have a 10kOhm resistor in front of them; also intended as an 8kHz LP filter. I'm however not quite certain whether it is implemented correctly, so suggestions are also very welcome in this respect!

EDIT: and there is a LP filter before the Attiny85 input with Fc ~ 16kHz (10k/1nF), which I forgot to draw (now updated in the schematic). I guess the Fc could be lowered.

To answer your questions (as best I can):
* The PWM output is updated at the same rate as the ADC; the IC runs at 8mHz, there is a factor 8 prescaler and then a division by 56.

* Considering the implementation of the ADC - I'm not sure about the bipolar part either. This comes from the original code from technoblogy. The ADC uses two pins, one is connected to ground, the other is the signal. Maybe that is what is bipolar. Further processing is indeed done on unsigned chars, and the +128 indeed gets the signal in that range. I have previously tried using just a single ADC input (unipolar?) with an internal reference, but this was a lot more noisy.

*The Buffer[LastPtr] = New; is also directly from the old code. I believe it is functional however; the way it is explained on technoblogy indicates that the code is to average two consecutive samples.

*The LFO is my contribution. It indeed works as you describe. I'm not sure what you mean by linear interpolation however; the LFO gives varies the delay by changing the read-index relative to the write-index. Since the buffer is only 256 samples, smaller steps than 1 (once every 256 iterations of the ISR) are not possible?
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: mzy12 on October 28, 2023, 10:17:14 AM
This is very interesting! Something I wonder about on a lot of digital choruses/delays - I wonder if it could be improved with companding and emphasis/de-emphasis filters? In general, the chips people use for guitar effects don't have a lot of headroom and although they are (or at least can be) a lot less noisy than the old BBD chips, I still think there's something to be said for the methods those pedals used to get more out those chips. If you look at boss schematics, their old digital delays used companders and they sounded excellent. I'm pretty sure their modern digital delay line CE-5 and CH-1 pedals still use the companding and emphasis & de-emphasis filters of the older BBD equiqed revisions and they still sound great for it.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 28, 2023, 10:23:42 AM
Quote from: mzy12 on October 28, 2023, 10:17:14 AMThis is very interesting! Something I wonder about on a lot of digital choruses/delays - I wonder if it could be improved with companding and emphasis/de-emphasis filters? In general, the chips people use for guitar effects don't have a lot of headroom and although they are (or at least can be) a lot less noisy than the old BBD chips, I still think there's something to be said for the methods those pedals used to get more out those chips. If you look at boss schematics, their old digital delays used companders and they sounded excellent. I'm pretty sure their modern digital delay line CE-5 and CH-1 pedals still use the companding and emphasis & de-emphasis filters of the older BBD equiqed revisions and they still sound great for it.

A BBD chorus was the inspiration to try this. however, those IC's are too expensive imo. Attiny's cost only a fraction of the price!

Regarding the companding, I'm not sure how to do that; at least not in a simple way. One thing I thought would reduce noise is that the signal is amplified in the ADC by a factor 20, and then the Attiny85's output is scaled down back to input signal level using a voltage divider. I figured this would also reduce noise by a factor 20. Not sure if the thinking is correct though...
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on October 28, 2023, 12:13:24 PM
Quote from: ElectricDruid on October 28, 2023, 06:43:20 AM[...]

The ADC set up says "bipolar" which I take to mean "signed -128 to +127", but then the data we use everywhere is unsigned 0-255, and at one point we even add an offset to remove the negative values (is that what it's doing?):

  New = ADCH + 128;
Would it be better to set the ADC up for unipolar values instead?

This line in the ADC ISR doesn't seem to do anything:

  Buffer[LastPtr] = New;
because it is followed by this:

  New = ADCH + 128;
[...]
I tihnk the ADC inside the ATTiny85 is 10-bit so I'm not sure what this +128 is about. Is that because you're using 1.1V as Vref? I'm not super familiar with the ADC inside the Tiny but what I would suggest is a unipolar operation with 5V Vref so you get values between 0 and 1024 but then you truncate the 2 LSBs to fit it inside the buffer, makes it a bit more easy to understand I'd say.

What you could also try (if memory allows) is to raise the buffer type to uint16_t and use a Dual PWM output (where one port outputs the least significant bits and the other the most):
(https://farm8.staticflickr.com/7458/10657009473_26c1f478de.jpg)
Should raise the output resolution to about 14-bits and effectively reduce the noise-floor somewhat.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: mzy12 on October 28, 2023, 12:20:30 PM
Quote from: Ksander on October 28, 2023, 10:23:42 AMRegarding the companding, I'm not sure how to do that; at least not in a simple way. One thing I thought would reduce noise is that the signal is amplified in the ADC by a factor 20, and then the Attiny85's output is scaled down back to input signal level using a voltage divider. I figured this would also reduce noise by a factor 20. Not sure if the thinking is correct though...
The problem here is that there is no way that the Attiny85 has enough headroom to deal with that. A typical guitar pickup has, at it's most extreme, a peak to peak voltage of around 2V. Scaling that up by a factor of 20 would mean that the Attiny85 would have to have the capability of handling a 40V peak to peak signal at it's ADC and also have a >40Vdc supply, which it doesn't and you definitely can't respectively. A compander could allow you to limit the incoming signal THEN bring it up to max Vin of the ADC (which I can't actually find any definitive information on, but the peak to peak of said signal physically can't be more than the supply voltage.), which would give you a better S/N ratio. Don't forget the expanding part of the equation!

Will be following your progress on this  :D
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 28, 2023, 12:46:41 PM
Quote from: mzy12 on October 28, 2023, 12:20:30 PM
Quote from: Ksander on October 28, 2023, 10:23:42 AMRegarding the companding, I'm not sure how to do that; at least not in a simple way. One thing I thought would reduce noise is that the signal is amplified in the ADC by a factor 20, and then the Attiny85's output is scaled down back to input signal level using a voltage divider. I figured this would also reduce noise by a factor 20. Not sure if the thinking is correct though...
The problem here is that there is no way that the Attiny85 has enough headroom to deal with that. A typical guitar pickup has, at it's most extreme, a peak to peak voltage of around 2V. Scaling that up by a factor of 20 would mean that the Attiny85 would have to have the capability of handling a 40V peak to peak signal at it's ADC and also have a >40Vdc supply, which it doesn't and you definitely can't respectively. A compander could allow you to limit the incoming signal THEN bring it up to max Vin of the ADC (which I can't actually find any definitive information on, but the peak to peak of said signal physically can't be more than the supply voltage.), which would give you a better S/N ratio. Don't forget the expanding part of the equation!

Will be following your progress on this  :D

That explains the clipping when I strum hard!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: mzy12 on October 28, 2023, 01:01:45 PM
Yep, that would do it! I think the Attiny ADC max Vin is about half the supply voltage from what I understand?
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 28, 2023, 03:34:47 PM
Quote from: niektb on October 28, 2023, 12:13:24 PM[...]

I tihnk the ADC inside the ATTiny85 is 10-bit so I'm not sure what this +128 is about. Is that because you're using 1.1V as Vref? I'm not super familiar with the ADC inside the Tiny but what I would suggest is a unipolar operation with 5V Vref so you get values between 0 and 1024 but then you truncate the 2 LSBs to fit it inside the buffer, makes it a bit more easy to understand I'd say.

What you could also try (if memory allows) is to raise the buffer type to uint16_t and use a Dual PWM output (where one port outputs the least significant bits and the other the most):
(https://farm8.staticflickr.com/7458/10657009473_26c1f478de.jpg)
Should raise the output resolution to about 14-bits and effectively reduce the noise-floor somewhat.


The ADC is indeed 10 bits, but only the 8 most significant bits are read out when calling ADCH. I have tried to work with a 10 bit reading in the pitch shifter, but gave up. It is interesting though - maybe it can work with two PWM outputs, but I don't know if there are enough PWM pins and also not if there are enough timers available for the ADC, and two PWM. Can two PWM run on the same timer? Let's dive back into the datasheet...

EDIT: it looks like PB0 and PB1 can be both run from timer0, but I realized that there is not enough memory for a 256 sample 16-bit buffer. Maybe there is another way - storing part of a sample in the 6 unused bits of a 16 bit variable? This gets complicated!
EDIT 2: The waveform is probably not a triangle... I need to reconsider the code!
EDIT 3: The code has been updated in the opening post for the triangle waveform and to set the ADC reference to 2.56V.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: cordobes on October 28, 2023, 05:06:48 PM
You have to use oversampling and decimation. I have tried taking the ADC to 1MHz, and it already compromises the third least significant bit. The best compromise solution is 500 kHz, take the ADH data and store it, then gain 2 bits of dynamic range through 16 samples. You would have a bandwidth of 15.6 kHz, and 10 effective bits, which you could take to a dual PWM DAC scheme.
Because a chorus does not do too much processing, with a 64 MHz clock it is perfectly feasible to do between samples.
There are other solutions to gain more dynamic range, but they involve other techniques and different hardware, which this little micro unfortunately does not have.
I hope you find it useful
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on October 28, 2023, 05:51:28 PM
I was thinking similar as Cordobas, what you could do is run your buffer at a lower sample rate but at 16-bit. You could for example sum 2 adjacent adc samples together (the sum of 2 10-bit numbers fit inside 16-bit) and store that single number in the buffer. On the output you use interpolation to bump up to the higher sample rate.

Not sure if lower sample-rate storage but higher resolution improves the end result compared to 8-bit all the way :)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: cordobes on October 28, 2023, 06:27:46 PM
Definitely any improvement is always welcome, the result of gaining 2 bits of dynamic range by adding 16 samples (oversampling) and corresponding right shift (decimation), is appreciable, and the implementation is trivial. And it is what makes the difference with other kind of developments with basic HW.
If you add only 2 samples you get half a bit.
One thing that has not been brought into play in this design is the use of pre and de-emphasis.
And another issue is that it does not make sense, as has been discussed before, is to use 2's complement or other data handling like IQ, when you are only going to move through a circular buffer, and you are modulating the pointers.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: mzy12 on October 28, 2023, 07:08:29 PM
Quote from: cordobes on October 28, 2023, 06:27:46 PMOne thing that has not been brought into play in this design is the use of pre and de-emphasis.
How much would that affect the ADC input of an Attiny85? Does it perform better at some frequencies than others? Maybe I'm on the barking up the wrong tree here, but I do think companding would be a sure fire way to increase S/N ratio. If pre and de-emphasis filters also help, that would be great  :)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: cordobes on October 28, 2023, 07:56:59 PM
I haven't tried it, but I find no reason to replace a 1024 stage BBD with an attiny processing z^-1024 in a circuit like a ce-2, and not take advantage of its analog design. It's different headroom, but nothing that a gain recalculation can't optimize.
Companding would add additional stages, but it shouldn't be ruled out either.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: mzy12 on October 28, 2023, 08:11:37 PM
I must admit my knowledge here mostly ends at the analogue domain. I've not tried programming a microcontroller like this for a pedal, and my suggestions come from understanding how the operate in the realm of analogue voltages. You sound like you know what you're doing in relation to the digital stuff so I most definitely will defer to you on that end!

One question, when you say this

Quote from: cordobes on October 28, 2023, 07:56:59 PMand not take advantage of its analog design

are you referring to your own pre/de-emphasis filter suggestion here? Or some other aspect of the Attiny85?
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on October 28, 2023, 11:57:04 PM
Honestly, I think compansion somewhat defeats the purpose of this pedal because of the added complexity. At that point I would first invest in a 'bigger' MCU which has the memory for higher resolution and Sample rate (you know 16-bit and >32kHz) and maybe even a true DAC pin.

Another thing I would try is try sticking a capacitance multiplier in front of the LDO, and add a 100nF as close as possible to VCC pin of the Attiny.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 29, 2023, 08:06:34 AM
Quote from: cordobes on October 28, 2023, 06:27:46 PMDefinitely any improvement is always welcome, the result of gaining 2 bits of dynamic range by adding 16 samples (oversampling) and corresponding right shift (decimation), is appreciable, and the implementation is trivial. And it is what makes the difference with other kind of developments with basic HW.
If you add only 2 samples you get half a bit.
One thing that has not been brought into play in this design is the use of pre and de-emphasis.
And another issue is that it does not make sense, as has been discussed before, is to use 2's complement or other data handling like IQ, when you are only going to move through a circular buffer, and you are modulating the pointers.

I'm sorry, but it is not trivial to me - I don't know much about programming nor hardware and generally take the empirical approach when building stuff  :icon_wink:

As I understand, writing to and reading from the buffer have to be done at the same speed (otherwise you get pitch shifting; what the original code was for). Provided that these speeds can both be reduced to 15.6kHz, the buffer could still not be much larger than 128 samples when it has to hold 16 bit values, which works out to some 8ms of delay. Or do you mean something along the lines of sampling at a higher rate, but averaging multiple samples before writing them to a buffer? Could you provide some example code?
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on October 29, 2023, 10:34:31 AM
I'm not super well-versed with the Attiny register nor do I have the setup to test it so here is some pseudo-code. I think something like this could work.
// Pseudo-code, not functional!

volatile uint16_t intermediateSum = 0;
volatile uint16_t New;
volatile uint8_t adc_counter = 0;
volatile k = 0;

#define BUFFER_SIZE 256  // Adjust as needed
#define INTERPOLATION_FACTOR 4

/*
We know the ADC input is 10-bits. We can sum together 64 10-bit values in a 16-bit variable before it overflows.
Therefore we want to run the ADC at 64 times the speed of the main flow but only write to the buffer every 64th time,
storing the sum of the input values. Then we reset the intermediate values at 0 and start over.
Say the sample rate of the buffer is 8kHz, then the ADC sample rate should be 512kHz.
*/
ISR(ADC_vect) {
  intermediateSum = intermediateSum + New;
  adc_counter++;

  if (adc_counter == 64) {
    Buffer[write_pointer] = intermediateSum;
    intermediateSum = 0;
    adc_counter = 0;
    write_pointer = (write_pointer + 1) % BUFFER_SIZE;
  }
}

/*
Mow because the buffer is fairly slow, we want to output DAC values faster to move the PWM carrier out of the audible range.
For example 4 times. We use linear interpolation to determine the intermediate samples.
*/

ISR(TIMER0_COMPA_vect) {

  /* do the LFO-thingy to determine the new read pointer position */

  // k keeps track of the location between samples. so we can use it to determine the weights of this sample and the next sample. We can probably write a more efficient form of the equation below.
  uint16_t outputVal = (Buffer[read_pointer] / INTERPOLATION_FACTOR) * (INTERPOLATION_FACTOR - k) + (Buffer[(read_pointer + 1) % BUFFER_SIZE] / INTERPOLATION_FACTOR) * (k);
  // i'm not sure if it should be read_pointer + 1 or read_pointer - 1, I'm inclined to think the first. Maybe we need to use floats for the intermediate values and then cast to int to avoid losing data.

  /* Set OutputVal to ports */

  k++; // modulo operator or something similar is probably more elegant
  if (k == INTERPOLATION_FACTOR) {
    k = 0;
  }
}
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on October 29, 2023, 03:44:59 PM
The sample output rate and the PWM rate don't have to be the same. You can increase the PWM rate, but only update it occasionally. What will prove most limiting is that as you push the PWM rate higher, the resolution gets smaller. So with a given clock, a desired resolution will have a maximum speed.
Say we're using the 8MHz system clock for the PWM. 10-bit output is 1024 values, so 8MHz/1024 = 7812Hz maximum PWM output rate. Not very good.
I don't know the ATTiny chip very well, but on the PICs I was able to get this up to 31.25KHz by running with a 32MHz system clock. For 8-bit output, you can get much higher PWM frequencies (x4 higher, clearly) which helps a lot.

TBH, if there's a NCO peripheral on this chip, pulse density modulation (PDM) produces a much better output than PWM for "cheap DAC" applications like this.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 29, 2023, 03:53:15 PM
Quote from: niektb on October 29, 2023, 10:34:31 AMI'm not super well-versed with the Attiny register nor do I have the setup to test it so here is some pseudo-code. I think something like this could work.
...


Thanks. I get the idea now and will try in the coming days!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on October 29, 2023, 06:27:12 PM
Quote from: ElectricDruid on October 29, 2023, 03:44:59 PMThe sample output rate and the PWM rate don't have to be the same. You can increase the PWM rate, but only update it occasionally. What will prove most limiting is that as you push the PWM rate higher, the resolution gets smaller. So with a given clock, a desired resolution will have a maximum speed.
Say we're using the 8MHz system clock for the PWM. 10-bit output is 1024 values, so 8MHz/1024 = 7812Hz maximum PWM output rate. Not very good.
I don't know the ATTiny chip very well, but on the PICs I was able to get this up to 31.25KHz by running with a 32MHz system clock. For 8-bit output, you can get much higher PWM frequencies (x4 higher, clearly) which helps a lot.

TBH, if there's a NCO peripheral on this chip, pulse density modulation (PDM) produces a much better output than PWM for "cheap DAC" applications like this.

I think what you're describing is in fact a form of interpolation, namely zero order hold interpolation. I think it's by definition that the pwm rate is equal to the output rate, even if you decide to output the same value for multiple pwm time periods. (But I need to read some more to understand what different interpolation methods do to signal quality.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on October 29, 2023, 07:05:57 PM
Quote from: niektb on October 29, 2023, 06:27:12 PMI think what you're describing is in fact a form of interpolation, namely zero order hold interpolation. I think it's by definition that the pwm rate is equal to the output rate, even if you decide to output the same value for multiple pwm time periods. (But I need to read some more to understand what different interpolation methods do to signal quality.
Yes, outputting the same value for multiple PWM cycles would be zero order hold. A simple linear interp would be better if there's time to do it. But my point is really that we need to get the PWM frequency much higher if we're to have any chance of filtering it effectively.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 31, 2023, 03:40:24 AM
Also, there are some parts in the circuit I'd like to ask about:

In purple, there is a feedback path? Is this a concern?
In blue, the signal is split after the buffer using two resistors. In other schematics, I instead see a signal split before being split by being sent to two op-amps. Any considerations on this?

(https://i.postimg.cc/Wqg5rWfh/Tiny-Schem-Paths.png) (https://postimg.cc/Wqg5rWfh)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on October 31, 2023, 07:20:21 AM
There's various things I'd do differently on the schematic.

The two-stage output filter, for example: R3/C4, R8/C6. This currently has 220K/100p, 10K/2n2 values. The usual rule-of-thumb for cascading passive filters like this is that the second stage should have a resistor ten times larger than the first to avoid loading it down too much. So these stages would be better the other way around.
R2/10K is there to make a divider to reduce the output level (because it was boosted in the firmware) if I understood correctly. It also acts to make that first filter into a lowpass shelf rather than a simple lowpass, so it's reducing it's effectiveness. It should go.
Using a larger resistor in the R8 position wouuld reduce the amount of delyed signal in the mix, so it's unnecessary. And a larger R8 is what you'll have if you swap the two filter stages over.
The signal splitting is ok. You've got a low impedance output from the input buffer, which is then taken to the R9/C10 input filter and via resistor R1 to the output. Not much to argue with there.
The signal mixing is a simple passive mix. Will there be some feedback around the loop you show in magenta? Possibly, yes. If it's a problem, the output op-amp could be turned into a proper inverting mixer, at the minor cost of an inverting-overall signal path. For bonus points, it could be turned into a wet/dry blender (example here: https://electricdruid.net/two-stomplfo-projects-ptwobble/ (https://electricdruid.net/two-stomplfo-projects-ptwobble/) )
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on October 31, 2023, 09:26:37 AM
I'm also a little concerned on DC-biasing. You have C2 to remove the DC-bias but then it seems you try to use R7 to bias the voltage up again but this won't work (properly) because you have R2 and R8 pulling the voltage down?
@ElectricDruid: don't you need R2 to create a HPF in conjunction with C3 though? (although I don't think this HPF works very effective with the 220k in series).


Edit: In fact, I thought I'd simulate it but because the output signal is still more or less centered around zero it gets clipped away by the TL072 (which is not rail-to-rail). Are you sure the schematic is reflecting the status of your prototype?
(https://i.postimg.cc/WdBz9zKm/2023-10-31-15-09-13-Noname-Schematic-Editor.png) (https://postimg.cc/WdBz9zKm)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 31, 2023, 10:22:56 AM
Thanks, both of you. I'll check the schematic against the prototype. It seems there should be at least a capacitor between the signals going into the second op-amp and its bias source...

EDIT: and there was: a 100nF electrolytic. I couldn't edit the opening post anymore, so here is the revised schematic (as it is now)

(https://i.postimg.cc/SXQpmy0s/Tiny-Chorus-schem-v1-3.png) (https://postimg.cc/SXQpmy0s)

And a photo of the (working) prototype
(https://i.postimg.cc/NKkchKzS/IMG-20231031-153202.jpg) (https://postimg.cc/NKkchKzS)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on October 31, 2023, 12:18:12 PM
Quote from: niektb on October 28, 2023, 05:51:28 PM...
You could for example sum 2 adjacent adc samples together (the sum of 2 10-bit numbers fit inside 16-bit) and store that single number in the buffer
...

I have also tried to implement the proposed 'oversampling', but by adding 8 bit samples to a 16 bit variable (to keep things simple). In the original code, sampling happens at 8MHz/32, and with each ADC conversion taking 14 clock cycles, this works out to 17.9kHz. In the first attempt at oversampling, sampling was done at 8MHz/16; 2 samples were summed and, after two samples were collected, divided by 2 and cast to an 8-bit variable for the PWM. This worked, but was more noisy than before. 8MHz/8 with 4 samples also worked, but is also more noisy. With higher speeds, the Attiny didn't produce output anymore.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on November 01, 2023, 04:53:00 AM
oversampling is a way to increase the bit depth so there is not really a point in it if you throw the added bits away at the output :)
On the other hand though I wouldn't expect it worsen, I'm not really sure what's going on? One thing I could think of, is that you don't have any bypass capacitors at the AREF and VCC of the ATTiny?

Maybe we should take a step back first and reduce noise at the hardware level first. Put also a decoupling capacitor at the Opamp VCC and increasing the size of the decoupling at the LDO (I would make the output 10uF, input 100nF is probably fine with a battery) The output stage with the big volume reduction isn't quite helping either (as ElectricDruid pointed out previously)

Do you have any way to quantify the amount of noise you have? (lets say a sine wave generator and an audio interface + spectrum analyzer program on your computer)

I have a Atmega32U4 board here where I wanted to test it on but alas I discovered I need to pay attention to which timer the pin is connected to when designing the board :icon_mrgreen:
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 01, 2023, 12:03:14 PM
Quote from: niektb on November 01, 2023, 04:53:00 AMoversampling is a way to increase the bit depth so there is not really a point in it if you throw the added bits away at the output :)
On the other hand though I wouldn't expect it worsen, I'm not really sure what's going on? One thing I could think of, is that you don't have any bypass capacitors at the AREF and VCC of the ATTiny?

Maybe we should take a step back first and reduce noise at the hardware level first. Put also a decoupling capacitor at the Opamp VCC and increasing the size of the decoupling at the LDO (I would make the output 10uF, input 100nF is probably fine with a battery) The output stage with the big volume reduction isn't quite helping either (as ElectricDruid pointed out previously)

Do you have any way to quantify the amount of noise you have? (lets say a sine wave generator and an audio interface + spectrum analyzer program on your computer)

I have a Atmega32U4 board here where I wanted to test it on but alas I discovered I need to pay attention to which timer the pin is connected to when designing the board :icon_mrgreen:

Sure! The LDO is a 78L05. The datasheet specifies a 330nF capacitor between input and GND, and a minimum 10nF capacitor between output and GND (I put 100nF there). I take it you suggest to increase the 330nF to 10uF?

I have also tried placing 100nF capacitors directly between Vcc and GND pins of both IC's. This didn't make any audible difference. Note - there is not that much noise, but I do think there is some digital/squealing present. I'm not sure the AREF pin is used, but will consult the datasheet.

EDIT: You can choose to use a bypass capacitor by setting REFS0 = 7 instead of 6 (both values set an internal 2.56v reference voltage). The datasheet doesn't specify a value for the bypass capacitor, but I tried 100nF. This indeed reduces the noise, at least together with 100nF bypass caps across Vcc and GND of both ICs. In fact, I think the only remaining noise is due to clipping. I need to so some more testing, and:

I will also try do collect some data on the noise!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on November 02, 2023, 10:08:36 AM
Aah glad to header that the decoupling caps helped a big deal! Noo I would think the capacitor on the input of the LDO is fine (I would make it bigger if you used a DC adapter but a battery is already considered low-noise). I would increase the LDO  output capacitance to at least 10uF (but bigger doesn't hurt)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 02, 2023, 03:55:18 PM
Taking the suggestions together: I have swapped the stages of the filter and got rid of the first, added 100nF capacitors from Vcc to GND for both IC's (the TL072 doesn't have Vcc/GND pins in Fritzing, so those are not shown in the schematic) and added a 100nF decoupling capacitor for AREF. The voltage divider also had to be changed and the value of the filter cap now is 220pF for a 3.5kHz LP filter. I am quite happy with the result.

This is the schematic now:

(https://i.postimg.cc/HcWmYxj3/Tiny-Chorus-schem-v1-4.png) (https://postimg.cc/HcWmYxj3)

And this is the code:


/* Attiny85 Chorus by Ksander N. de Winkel

   8-Bit Variable delay for audio (guitar/microphone) signals.
   Combining the (scaled down) output with the original input signal yields a chorus effect.
   The maximum delay is 14.3ms. The amount can be scaled by changing the value of Shift.
   The period of the variation can be changed by setting the wrapping value of Counter.
   Note that these variables affect each other.
 
   Based on Attiny85 pitch shifter by David Johnson-Davies - www.technoblogy.com - 11th February 2017
   ATtiny85 @ 8MHz (internal oscillator; BOD disabled)
   
   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license:
   http://creativecommons.org/licenses/by/4.0/

*/

volatile uint8_t Buffer[256];               // Circular buffer
volatile uint8_t WritePtr, LastPtr, New, Ptr, Counter, Shift;
volatile bool Direction = true;
// Everything done by interrupts

// Write to buffer when ADC conversion is complete
ISR (ADC_vect) {

  Buffer[LastPtr] = New;
  New = ADCH + 128;
  Buffer[WritePtr] = (Buffer[WritePtr] + New)>>1;
  LastPtr = WritePtr;
  WritePtr = (WritePtr + 1) & 0xFF;

}

// Read from buffer and output to DAC
ISR (TIMER0_COMPA_vect) {

  Counter = (Counter + 1) & 0xFF;

  if (Counter == 0) {
   
    if (Direction) {
      Shift = (Shift + 1) & 0xFF;
    } else {
      Shift = (Shift - 1) & 0xFF;
    }

    if (Shift == 255 | Shift == 0) Direction = !Direction;
   
  }
 
  Ptr = (LastPtr - Shift) & 0xFF;

  OCR1A = Buffer[Ptr];

}

// Setup

void setup () {
 
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1<<PCKE | 1<<PLLE;     

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                              // Timer interrupts OFF
  TCCR1 = 1<<PWM1A | 2<<COM1A0 | 1<<CS10; // PWM OCR1A, clear on match, 1:1 prescale
  OCR1A = 128;
  pinMode(1, OUTPUT);                     // Enable OC1A PWM output pin

  // Set up Timer/Counter0 to generate 20kHz interrupt
  TCCR0A = 2<<WGM00;                      // CTC mode
  TCCR0B = 2<<CS00;                       // /8 prescaler
  OCR0A = 55;                             // 17.9kHz interrupt (1M/56)
  TIMSK = TIMSK | 1<<OCIE0A;              // Enable interrupt
 
  // Set up ADC - REFS0 = 6 w/o ext. bypass cap; REFS0 = 7 w ext. bypass capacitor.
  ADMUX = 7<<REFS0 | 1<<ADLAR | 7<<MUX0;  // Internal 2.56V ref, ADC2 vs ADC3, (6<<MUX = gain 1; 7<<MUX0 = gain 20);
  // Enable, auto trigger, interrupt, 250kHz ADC clock:
  ADCSRA = 1<<ADEN | 1<<ADSC | 1<<ADATE | 1<<ADIE | 5<<ADPS0; 
  ADCSRB = 1<<7 | 0<<ADTS0;               // Bipolar, free-running

  pinMode(2, INPUT_PULLUP);

}

void loop () {
}

I did not continue with the oversampling, as my efforts seemed to worsen noise. There is some clipping due to the gain of 20 in the Attiny. This can probably be resolved by instead using the first opamp to amplify the signal and set the gain to 1 in the code, but this will probably also require other changes to the schematic to tune the input/delated signal levels to each other.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 02, 2023, 04:23:35 PM
Quote from: Ksander on November 02, 2023, 03:55:18 PMThere is some clipping due to the gain of 20 in the Attiny. This can probably be resolved by instead using the first opamp to amplify the signal and set the gain to 1 in the code, but this will probably also require other changes to the schematic to tune the input/delated signal levels to each other.
In order to get the best performance out of the limited resolution of the ADC, you need the input signal to push it as close to full-scale as you dare (but not over!). So the gain should definitely be in the op-amp, in my view. You can then see how much you need to reduce the signal coming out, but since the amount of delayed signal to mix back in is often a variable in a chorus pedal, I don't think that's such a big deal. Put a pot on it it and leave it to the user.

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 08, 2023, 02:02:18 PM
As per ElectricDruid's suggestion, I moved the gain to the op-amp, and changed the cascade of RC filters on the output. There are also two LEDs to clip the signal in case it reaches too high voltages.

(https://i.postimg.cc/VSrNx9Wp/Tiny-Chorus-v4-schem.png) (https://postimg.cc/VSrNx9Wp)

In the code, this line has to be changed to set the gain to 1

  // Set up ADC - REFS0 = 6 w/o ext. bypass cap; REFS0 = 7 w ext. bypass capacitor.
  ADMUX = 7<<REFS0 | 1<<ADLAR | 6<<MUX0;  // Internal 2.56V ref, ADC2 vs ADC3, (6<<MUX = gain 1; 7<<MUX0 = gain 20);

and the lines with INPUT_PULLUP should be omitted.

With the pot, the effect can be varied between no notable effect at all, subtle chorus to fat flanging-like, which is nice. Despite the 3rd order filter, there is still some carrier frequency present, which I don't know how to get rid of...
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: niektb on November 08, 2023, 05:21:41 PM
Don't know man, the >100k output impedance for the wet signal feels a bit fishy... The schematic isn't very clear if I might add. The way it is drawn suggests that you have an amplifier before the signal goes into the Attiny so it would affect both dry and wet?
A third order filter with an fc of 1.5kHz should filter quite hard but I'm not sure if that still works if you have to gain it up at your amp because you lose a bunch of signal
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 08, 2023, 06:35:05 PM
Sorry, I agree. That schematic is confused.

What's the second op-amp actually doing?

Can we start with input buffer on the left, move to ATTiny delay in the centre, and then out via filter to output on the right?

Where does the second op-amp come into that scheme? Are the clipping diodes supposed to be before the delay or after? (before would make more sense to me)

It'd be clearer if you could separate some stuff out. Can you put the power supply and Vref off to one side, linked to "+9V", and "Vref" labels, for example, and then just use those labels in the rest of the diagram? Can Fritzing do that sort of stuff? If not, it's limiting you, since it's making it harder for you to see what's going on.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 08, 2023, 11:20:30 PM
Quote from: ElectricDruid on November 08, 2023, 06:35:05 PMSorry, I agree. That schematic is confused.

...

It'd be clearer if you could separate some stuff out. Can you put the power supply and Vref off to one side, linked to "+9V", and "Vref" labels, for example, and then just use those labels in the rest of the diagram? Can Fritzing do that sort of stuff? If not, it's limiting you, since it's making it harder for you to see what's going on.


The clipping stages indeed go before the delay, but I can see that the schematic is confusing. I'll try if Fritzing can work with labels. I've also downloaded LTSpice but find it very difficult to work with  :(
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: cordobes on November 09, 2023, 04:36:35 AM
You have the initiative, which is the important thing.
I agree with Tom, from the beginning you have to be very clear about the scheme and the steps.
Check how the big companies make their schematics (at least in effects pedals), there is plenty of material.
Build functional blocks, at each stage, don't try to implement any improvements until you have something solid, and work from there.
I would recommend that you start with just filters on the input and output, and a simple SW that reads the ADC and updates PWM, at the rate you decide.
As for building the HW, there's also tons of information on ground wiring, separating analog and digital signals, etc.
Get organized, you can get help, but you must have perfectly located where you are having problems.

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 09, 2023, 05:27:46 AM
Quote from: Ksander on November 08, 2023, 11:20:30 PMI've also downloaded LTSpice but find it very difficult to work with  :(

Yeah, LTSpice wouldn't have been my recommendation for a schematic-drawing package either!

Fritzing is a diagramming tool really, good for drawing pictures of breadboards with parts on them, not so hot for a more abstract schematic. You want something that is actually designed for drawing schematics.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ben N on November 09, 2023, 06:04:52 AM
A thought: Along with chorus, this seems like it might be a fruitful approach for ADT effects, which many readily available digital delay chips can't manage because their delay time won't go short enough.

Now carry on with the fascinating filtering/noise reduction discussion.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 12, 2023, 03:53:58 PM
Just an update: after some more fiddling, I now got Vcc set as ADC reference with external bias of the input signal (at half the supply to the Attiny), and changed the ADC to single-ended input. This frees up pins (depth/rate?) and gets rid of the '+128' that was in the code. It works, but does not improve, nor  does it worsen, the noise. I also got kicad, which is much nicer to draw circuits with than LTSpice. 
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 12, 2023, 05:19:26 PM
Nice work, sounds like progress.

As far as noise goes, this is a digital system, so we know pretty well where the noise comes from, and what you have to do to get rid of it. Of course, on an Attiny some options may well not be available.

1) Aliasing - too many high frequencies above Nyquist going in. Improve the input filtering.
Aliasing is easy to identify because if you input a smoothly rising tone, you hear a *falling* tone at the output.
2) Quantisation distortion - not enough bitdepth to accurately represent the signal. Increase the bit depth. The rough rule-of-thumb is that S/N for an n-bit signal is 2 + (6*n) dB. 8-bit gives you 50dB, 10-bit gives you 62dB, 12-bit gives you 74dB. Note that we need 12-bit signals to match the 73dB of a MN3207 BBD!
3) Imaging - output signal not getting properly turned back into an analogue signal. Improve the output filtering.

Of course, there's other stuff can go wrong during the digital processing that can introduce noise, but that's a maths problem.

I suspect that here the biggest issue is (2), and I doubt there's much more we can do about that. What's the fastest the ADC can run, and how many bits can it generate at that speed?

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 13, 2023, 03:22:55 PM
I'll go over your reply later, but I wanted to quickly share the schematic made with kicad. I think it is accurate...

The circuit is powered using a 9v battery. This source is used to power the op-amp IC, and to create a 4.5v reference to bias the input to the op-amp (middle left). The 9v battery also powers the 78L05, which has a voltage divider attached to it to bias the buffered signal at half the Vcc for the Attiny (5v and 2.5v, top left).

The input signal is first buffered using one of the op-amps (middle left, buffered signal). It is then split; one part goes to the output 'mixer' (top right), the other part goes to the other op-amp to amplify the signal (below the first op-amp). The amplified signal is LP filtered and sent to the Attiny, with clipping diodes in front of itto protect the Attiny. It is biased at 1/2 the Attiny Vcc, which is used as the ADC reference voltage --I think this makes the most of the ADC resolution. The signal is then mo-delay-ted using the Attiny, and the output is sent to the output 'mixer' (modulated signal), where it is also filtered and scaled down to get closer to the input signal.

There are 10k pull-down resistors attached to unused pins on the Attiny (bottom right). These may be used for additional inputs.

(https://i.postimg.cc/ZWWRxsN5/Tiny-Ensemble.png) (https://postimg.cc/ZWWRxsN5)

In the code, the ADC setup must be changed accordingly:

  // Set up ADC
  ADMUX = 0<<REFS0 | 1<<ADLAR | 2<<MUX0;  // Vcc as ref, left align, ADC2 (PB4) single ended input;
  ADCSRA = 1<<ADEN | 1<<ADSC | 1<<ADATE | 1<<ADIE | 5<<ADPS0; // Enable, auto trigger, interrupt, 250kHz ADC clock:
  ADCSRB = 0<<7 | 0<<ADTS0;               // unipolar, free-running

Technoblogy has a post showing how to get 10-bit PWM from the Attiny. I've attempted to implement this, but so far without success...

I'd like to hear if anyone tries this circuit
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 13, 2023, 03:45:14 PM
Have you got "Output" flags on your schematic flipped around so they look like inputs? Is that why they all have a line through the middle?

I'd expect the connection to be to the flat end, and the pointy end to point to the right.

(*Least* important thing about the schematic, but the *first* thing I noticed!)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 13, 2023, 03:55:32 PM
Quote from: ElectricDruid on November 13, 2023, 03:45:14 PMHave you got "Output" flags on your schematic flipped around so they look like inputs? Is that why they all have a line through the middle?

I'd expect the connection to be to the flat end, and the pointy end to point to the right.

(*Least* important thing about the schematic, but the *first* thing I noticed!)

 ;D yes, probably!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: antonis on November 13, 2023, 04:47:27 PM
U1B might exhibit a DC gain from unity up to x4.5 [200k/(47k+10k) +1].. :icon_wink:
Connect R5 to GND with a series cap..
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 18, 2023, 03:46:23 PM
Quote from: ElectricDruid on November 12, 2023, 05:19:26 PM...

I suspect that here the biggest issue is (2), and I doubt there's much more we can do about that. What's the fastest the ADC can run, and how many bits can it generate at that speed?


Regarding the ADC speed, the datasheet says that best ADC resolution (10-bit) can be attained between 50-200kHz, but that when less resolution suffices, speeds up to 1MHz can be used. I've seen a forum post somewhere that it works up to 2MHz, but that quality get progressively more shitty when going higher. I've done a few things since the last post:

1. soldered the circuit on a prototyping board. This proved to be quite an improvement over the breadboard version, since now -when I don't play the guitar- there is no audible noise at all, meaning that the 3rd order filter does its job. However, when I do play, there is some noise, which I think is quantization noise. It sounds like there is a mild bit crushing going on in the modulated signal.
2. I also experimented with increasing the ADC speed from 250kHz to 500kHz and then averaging two readings before writing it to the buffer, and similarly, reading at 1MHz and averaging four readings before writing to the buffer. Neither made any difference in the sound quality though; the same kind of quantization noise can be heard. From reading up on ADC's some more, it appears that differential ADC is less noisy than single-ended (as in the original circuit). So I also tried this, but now referencing the negative ADC channel to Vcc/2, where also the amplified guitar signal positive ADC channel is biased at. This did indeed reduce more noise.

However, when I compare the recording I posted in the opening post to what I have before me right now, unfortunately these efforts do not appear to have improved the sound quality :-[ . Mostly, I think this means that setting the gain outside the attiny doesn't really improve things. It is a pity, because I really like this IC, but maybe I have to accept that the attiny just doesn't have enough oomph for good audio processing.

One thing that I want to explore still is using two (now unused) digital pins to read a rotary encoder, and use this set the modulation-oscillation speed. And then, hopefully, come up with some code to store the set speed in EEPROM when the circuit powers down (brown-out detection?)...
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 18, 2023, 05:58:56 PM
10-bit ADC resolution is your best case, and that's a theoretical limit of 62dB S/N right there. You can't get away from that. You can maybe *mitigate* it, by using companding or something, but you're not going to get close to BBD quality with such a chip. It's just not possible.

If you pushed the sample rate up to 200KHz at 10-bit (if that's possible while maintaining accuracy) then perhaps you could over-sample at 50KHz and gain most of a couple of extra bits. That gets you close to 12-bit, 74dB S/N, and like that you *are* in the 1970s BBD ballpark.
But then you have to deal with 12-bit samples. The simplest way is two bytes, but then that halves your memory. The more complicated way is 1.5 bytes, which gives you 2/3rds the delay you have now, but implies a lot more overhead getting bits from a nibble here and a nibble there, etc etc.

For these low end chips, I think really we have to be amazed by what they *can* do rather than shocked at what they can't. Doing *any* sort of real-time audio on a little 8-bit processor like this is frankly astounding. Super-duper audio quality would be nice, but it's not going to happen. The thing about a dog riding a bicycle is not that the dog rides well, but that it rides at all!

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 19, 2023, 03:45:10 PM
Reading with a higher bitrate is relatively straightforward, and a shorter buffer may still prove sufficient for a modulation type effect. I think the bigger challenge is getting 10 bits out. Niektb has suggested combining two PWM pins, and scaling their outputs externally with resistors. Here the problem is that resistors aren't sufficiently precise. Another method may be writing at 4 times the speed, and dividing a 10 bit variable over 4 8-bit cycles. This works because one of the timers can generate continuously high or low values. Unfortunately I can't get this to work - it justgives me a squealing sound.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 19, 2023, 06:03:56 PM
Could you do something similar to what you'Ve done on the way in? So use a faster output sample rate, and then send samples that average to the right level?

For example, say we want 9-bits at 32KHz. We could output 8-bit data at 64KHz. For the final bit, we would have to look at what individual *pairs* of samples are doing. Two outputs of (for example) 72 gives us a final value of 72. One output of 72 and one output of 73 gives us an effective value of 72.5 across the pair. So we've gained a half-bit inbetween our bits by using a sample rate twice as fast as we need.
This can be extended to get two extra bits. You need a sample rate four times faster than your final output. Then you have four samples to send, so (to use the same example) we could send 72,72,72,72 or 73,72,72,72 or 73,72,73,72, or 73,73,73,72 which gives us value of 72.0, 72.25, 72.5, 72.75. Then start on 73 the same way!

I haven't tried this ever, so I don't know how well it works.

One thing that keeps coming back to me is the old DSP-G1 project by Jan Ostman - a decade ago already. That was based on some little 8-pin chip and I'd love to know what it was because he apparently got a lot out of it. It might be a better choice for something like this. <goes digging>

Oh, more details in this thread:
https://modwiggler.com/forum/viewtopic.php?t=120304 (https://modwiggler.com/forum/viewtopic.php?t=120304)
The chip was the NXP LPC-810 ARM Cortex M0+ MCU. Don't know anything about it, and he apparently did some hacking to create the output DAC. Ah well...
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 20, 2023, 03:11:54 AM
LPC83x? Looks interesting!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 20, 2023, 02:52:45 PM
It's alive! I got the 10-bit version working, and it is sufficient to get rid of the quantization noise that was audible in the 8-bit version, while also providing a nice chorus effect!

It works by merging some tricks that can be found at technoblogy, and also suggested by ElectricDruid.
Specifically, the code takes 10-bit ADC readings at 250kHz, which, with a 14 clock cycle conversion time, works out to 17.857kHz sampling rate, stores these in a 128 value 16-bit buffer. This gives about 8ms of audio in the buffer. Values are subsequently written to the 8-bit PWM pin at 4 times the sampling rate, but while dividing the 10-bit reading over four 8-bit chunks (which gives the same read/write speeds).
The modulation works in a similar way as before, but my head is a bit fuzzy and I'm not exactly sure what the period of the modulation is - I think about 3.7s.

There is some memory still available, so the delay time might still be increased, and I switched back to differential ADC at some point, but this may now also be unnecessary.

For anyone interested, here is the code:


/* Attiny85 Chorus by Ksander N. de Winkel

   10-Bit modulated delay for audio (guitar/microphone) signals.
   Combining the (scaled down) output with the original input signal yields a chorus effect.
   The maximum delay is about 8ms, and the period of modulation is 3.67s.
   The period of the modulation can be changed by setting the wrapping value of Counter.
   
   Based on Attiny85 pitch shifter by David Johnson-Davies - www.technoblogy.com - 11th February 2017
   ATtiny85 @ 8MHz (internal oscillator; BOD disabled)
   
   CC BY 4.0
   Licensed under a Creative Commons Attribution 4.0 International license:
   http://creativecommons.org/licenses/by/4.0/

*/

// differential ADC, external biasing
volatile uint16_t Buffer[128];               // Circular buffer
volatile uint16_t Sample;
volatile uint8_t Cycle, WritePtr, LastPtr, Ptr, Counter, Index, Shift;

// Everything done by interrupts

// Write sample to buffer when ADC conversion is complete
ISR (ADC_vect) {

    Buffer[WritePtr] = ADC;
    LastPtr = WritePtr;
    WritePtr = (WritePtr + 1) & 0x7F;

}

// Read from buffer and output to DAC
ISR (TIMER0_COMPA_vect) {

  static int remain;

  if (Cycle == 0) {
   
    if (Counter == 0) {
      Index = (Index + 1) & 0xFF;
      Shift = abs(Index - 127);
    }
   
    Ptr = (LastPtr - Shift) & 0x7F;   
    remain = (Buffer[Ptr]+Sample) >> 1;
    Sample = remain; 
    Counter = (Counter + 1) & 0xFF;
   
  }
 
  if (remain >= 256) { OCR1A = 255; remain = remain - 256; }
  else { OCR1A = remain; remain = 0; }
  Cycle = (Cycle + 1) & 0x03;
 
}

// Setup
void setup () {

  pinMode(0, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
 
  // Enable 64 MHz PLL and use as source for Timer1
  PLLCSR = 1<<PCKE | 1<<PLLE;     

  // Set up Timer/Counter1 for PWM output
  TIMSK = 0;                              // Timer interrupts OFF
  TCCR1 = 1<<PWM1A | 2<<COM1A0 | 1<<CS10; // PWM OCR1A | clear on match | /1 prescaler
  OCR1A = 128;                            // Initial value
  pinMode(1, OUTPUT);                     // Enable OC1A PWM output pin

  // Set up Timer/Counter0 to generate write to PWM interrupt
  TCCR0A = 2<<WGM00;                      // CTC mode
  TCCR0B = 2<<CS00;                       // /8 clock prescaler
  OCR0A = 13;                             // 1M/14 = 71.4kHz interrupt
  TIMSK = TIMSK | 1<<OCIE0A;              // Enable interrupt
 
  // Set up ADC
  ADMUX = 0<<REFS0 | 0<<ADLAR | 6<<MUX0;  // Vcc as ref | Right align | ADC2 vs ADC3 (PB4, PB3) differential input
  ADCSRA = 1<<ADEN | 1<<ADSC | 1<<ADATE | 1<<ADIE | 5<<ADPS0; // Enable | auto trigger | interrupt | /32 prescaler - 250kHz ADC clock
  ADCSRB = 1<<7 | 0<<ADTS0;               // differential ADC | free-running

}

void loop () {
}


The cycling dog is getting somewhere.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 20, 2023, 03:33:54 PM
Quote from: Ksander on November 20, 2023, 02:52:45 PMThe cycling dog is getting somewhere.

Lol, fantastic! Next stop, the yellow jersey in the Tour De France?
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 21, 2023, 04:05:42 PM
Some artwork for the box (work in progress)


(https://i.postimg.cc/HVc0mWg5/Doggy-Ensemble.jpg) (https://postimg.cc/HVc0mWg5)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 29, 2023, 09:36:43 AM
Quote from: niektb on October 28, 2023, 12:13:24 PMWhat you could also try (if memory allows) is to raise the buffer type to uint16_t and use a Dual PWM output (where one port outputs the least significant bits and the other the most):
(https://farm8.staticflickr.com/7458/10657009473_26c1f478de.jpg)
Should raise the output resolution to about 14-bits and effectively reduce the noise-floor somewhat.


Could you please elaborate? I assume that to do this, I would need to:

I further assume that the 4n7 capacitor is for filtering, and that there should also be a decoupling cap inserted in the schematic?

How sensitive is this to the precision of the resistors? I have implemented what is described above and it appears to be proportional to the input signal somehow, but that is about it...
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: FiveseveN on November 29, 2023, 09:59:08 AM
Has this been mentioned yet? Take a look at how the pedalSHIELD UNO does it: https://electrosmash.com/pedalshield-uno
I think this is the earliest discussion of the dual DAC (goes into some depth): http://www.openmusiclabs.com/learning/digital/pwm-dac/
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 29, 2023, 10:33:10 AM
If you're going for a dual-PWM output, it would be best to have the same number of bits for each output. The reason being that that will give you the highest-possible PWM frequency. So if we want a 12-bit output, two 6-bit PWM channels gives better performance than a 4-bit channel and an 8-bit channel (the 8-bit channel will be slower).

The resistor values have to match the relationship of the bits they represent. So for my 12-bit example, one channel does the six least significant bits, and the other does the six most significant bits. That means the high bits are x64 larger than the low bits. The mixing needs to be in the same ratio, so the low bits get a resistor x64 larger than than the high bits to reduce them to 1/64th of the level. If you want an accurate output, the error in the high bits' resistor should be smaller than 1/2LSB - that's 1/2 of /64, 1/128th - So 1% resistors would be *close but not quite* for this job, and 0.1% tolerance would be perfect. Or hand-pick a few from some 1% resistors.

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 29, 2023, 03:55:26 PM
Thanks for the input, both. Those links have really nice explanations. Still, I'm a bit confused. Taking your example and making it even a bit more simple, is this understanding correct:

Say I want a 10-bit output, I would use two 5-bit channels. To get 5-bit PWM, should I set the PWM timer counter to wrap when it reaches 31 (OCR1C set at 31 and CTC1 bit set for clear on match)? Then there would be 0:31 = 32 = 2^5 values.

Next, I would take the 10 bit sample, and send the lower 5 bits to one PWM pin, and the upper 5 bits to another, like so:

 
OCR1A = (Buffer[ReadPtr] >> 5) & 0x1F;
OCR1B = (Buffer[ReadPtr]) & 0x1F;

Finally, I would scale the outputs using resistors: 4k7 and 150k should be close?

I have tried the above approach, and get some noise until I a play a note. As soon as I do, there is no output anymore. The IC seems to freeze.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 30, 2023, 03:56:14 AM
Quote from: Ksander on November 29, 2023, 03:55:26 PMThanks for the input, both. Those links have really nice explanations. Still, I'm a bit confused. Taking your example and making it even a bit more simple, is this understanding correct:

Say I want a 10-bit output, I would use two 5-bit channels. To get 5-bit PWM, should I set the PWM timer counter to wrap when it reaches 31 (OCR1C set at 31 and CTC1 bit set for clear on match)? Then there would be 0:31 = 32 = 2^5 values.
I don't know the ATTiny specifically, but generally that's how it works, yes. You should do some experiments to check you've got the PWM working how you think you have. The output frequency will be the PWM module clock / 32, so check that. Another simple test it to feed the output a simple incrementing count. Then you can see the pulse width getting wider and wider and then snapping back to zero on the 'scope.

QuoteNext, I would take the 10 bit sample, and send the lower 5 bits to one PWM pin, and the upper 5 bits to another, like so:

 
OCR1A = (Buffer[ReadPtr] >> 5) & 0x1F;
OCR1B = (Buffer[ReadPtr]) & 0x1F;
Yes.

QuoteFinally, I would scale the outputs using resistors: 4k7 and 150k should be close?
Yes, close enough for starters.

QuoteI have tried the above approach, and get some noise until I a play a note. As soon as I do, there is no output anymore. The IC seems to freeze.
That sounds a lot like the chip is crashing. Experiment with the PWM output until you're sure you've got two working 5-bit PWM channels. Then you can combine them and see if you can get a 10-bit output. If it's crashing, you'll have to take stuff out until it's simple enough that it works, and then put things back in until it breaks again, and then have a close look at whatever that last thing you put in was!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: cordobes on November 30, 2023, 07:59:13 AM
In most 8-bit uC's you have 8-bit pwm channels, you cannot control the bits per channel, so the frequency is defined as a function of the system frequency / 2^8.
There is an improvement in using a 16-bit data, but you must be careful how to align the bits in each channel, and in the case of wanting a "centered" data, or 5 bits per PWM channel, you must perform a shift of both high and low bytes. To start, use 8 bits in pwm_low and 2 bits in pwm_high. The weighting of the resistors should be done based on the total bits of the channel, in the case of the attiny, 8 bits, and not on the data to be loaded.

Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 30, 2023, 08:49:33 AM
Quote from: cordobes on November 30, 2023, 07:59:13 AMIn most 8-bit uC's you have 8-bit pwm channels, you cannot control the bits per channel, so the frequency is defined as a function of the system frequency / 2^8.

Sorry, but that's just not true. Many/most chips offer a way to control the count length as well as the compare value. Including this one.

Ksander suggested how in their last post; you have to use an OCR to clear the timer when it gets to 31 (Clear Timer on Compare Match (CTC) Mode) and then you'd use another OCR to generate 5-bit PWM.
You can do this on both Timer0 and Timer1, so you've got two 5-bit PWMs available. This is important because (system frequency / 2^5) is eight times higher than (system frequency / 2^8) so our filtering improves massively and the sidebands get further from our signal.

Ksander, it occurred to me that one reason your output might go silent would be if the OCR value got *higher* than 31. In that case, the waveform would never switch on or off, so you'd get a solid DC level and no audio output.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 30, 2023, 10:51:02 AM
This is what I gather from the datasheet (may be incorrect):

There are two timers in the Attiny85; Timer0 and Timer1. Timer 1 has an OCR1A register which sets behavior for pin OC1A (PB1), and OCR1B which sets behavior on pin OC1B (PB4), and then there is an OCR1C register which sets the TOP value for the counter. With setting bit CTC1 in the Timer 1 Counter Control Register TCCR1, you can clear the counter on a match with OCR1C. So I set OCR1C to 31, and then OCR1A/OCR1B to put out the five high or five low bits. As a test, per ElectricDruid's suggestion, I let both pins count up from 0 to 31, and monitored the output with my oscilloscope. OC1B nicely keeps counting up from 0 to 31, and OC1A does so as well for a second or two, and then the output becomes constant. I don't know why --yet. 

As an aside, the earlier version, where the 10-bit sample is spread out over four cycles works, but not in the Pitch Shifter (other topic), which can't keep up when the writing frequency becomes too high, so for that effect I want to explore this way of getting a higher bit rate.

Anyway, it looks like with your help, I'm getting closer :)
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: cordobes on November 30, 2023, 11:27:46 AM
What I wanted to say is that generally you cannot use the ducty cycle OCRA and OCRB registers for both PWM signals, the low byte and the high byte, as the OP posted, precisely because one of the comparisons must be done to modify the timer from 8 to 5 bits.
But I see that you have the OCRC register, so there is the solution.
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 30, 2023, 12:34:32 PM
Quote from: cordobes on November 30, 2023, 11:27:46 AMWhat I wanted to say is that generally you cannot use the ducty cycle OCRA and OCRB registers for both PWM signals, the low byte and the high byte, as the OP posted, precisely because one of the comparisons must be done to modify the timer from 8 to 5 bits.
Ah, I see. Yes, that's true.

QuoteBut I see that you have the OCRC register, so there is the solution.
Yes, it looks like it. Loads of Output Compare Registers on that timer!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 30, 2023, 12:59:53 PM
Quote from: ElectricDruid on November 30, 2023, 03:56:14 AM...
If it's crashing, you'll have to take stuff out until it's simple enough that it works, and then put things back in until it breaks again, and then have a close look at whatever that last thing you put in was!


Taking this approach, I've taken out everything but the ISR to change the value written to the pins and the settings for getting 5-bit PWM's. I let OCR1B count down, and OCR1A count up. Lo and behold! It works, without crashing. So now it is time to add stuff bit by bit (as a figure of speech).
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 30, 2023, 01:54:16 PM
Quote from: Ksander on November 30, 2023, 12:59:53 PMLo and behold! It works, without crashing.

Now *that* is a step forward!

Believe me, I know how tedious and time-consuming this sort of low-level debugging can be. I've spent/wasted days of my life sometimes on things that turned out to be something really stupid, or something really obscure.

There's no substitute for being careful, logical, and methodical. If you take it step by step and make sure you're damn sure that what you think is happening is what's happening before you move to the next step, you can always track this stuff down. It's only a dumb chip, and we're smart monkeys!
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on November 30, 2023, 02:15:57 PM
I'm a dumb monkey too, but luckily quite persistent :)

Now I have a 16 bit variable count up to 1023 and then wrap to 0; divide the value over 5 high and 5 low bits, write these to the respective pins and sum them with resistors (along with a little filtering cap to ground). On the oscilloscope I see a beautifully smooth sloping line. This is so exciting  ;D
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: ElectricDruid on November 30, 2023, 07:05:26 PM
Hey hey hey!!! 10-bit audio here we come!  :icon_cool:
Title: Re: Tiny Ensemble - an Attiny85 powered chorus effect
Post by: Ksander on December 01, 2023, 09:14:35 AM
I added the ADC sampling and outputting 10-bit samples to the two PWM pins - it works (yay), in that decent audio comes out. However, the output is now flat: with a 440Hz test signal input, I measure about 403Hz at the output. The sampling and output frequency settings are identical to what they were before. That means, their frequencies should match (17.9kHz). I could of course correct for this by changing the output interrupt frequency OCR0A, but it doesn't make sense to me. Any idea why this might happen?

Edit: in the datasheet, it says that the phase locked loop/Fast PWM mode has an 'asynchronous' clock, so I tried disabling that, which means that the main clock is used for the PWM 8MHz instead of 64MHz.  This seems to work much better. But then I wonder. What is the PLL locked to exactly?

Edit 2: I also added using a rotary encoder to change the read and output to PWM rate (i.e. a 10-bit pitch shifter), and this works too without the IC freezing, although still the setting where in- and output frequencies should match doesn't... And the error doesn't scale linearly with OCR0A setting. Maybe I should move this stuff to the pitch shifter topic :)