Hi, as this experiment has come to some end, I post it. Perhaps it might be interesting for somebody?
What is a Sustainer?
A Guitar Sustainer is a setup with a coil acting as an actuator to give feedback to the string to keep it oscillating for ever. A very fine description of such system can be found here: https://scientificguitarist.wixsite.com/home/sustainer-reverse-engineering
My primary and first goal of these experiments was to see, if a normal Humbucker (resistance 7k) pickup could be used as the actuator. These experiments are described here: https://www.musikding.rocks/forum/index.php?thread/437310-sustainer-eigenbau-mit-trafo/ - Result was, that this can work, if you use a 10W 100V.eff audio transformer. About 100V and 3W of power is needed. As I didn't want to ruin my proper neck PU and did not want to have 100V close to my pickup electronics, I made a clamp-on housing which holds the low cost PU "down under" above the strings.
But when I tried to make music with it, I discovered, that you should somehow have more fingers for muting, because the system provides feedback for all six strings. So I asked myself, if it would be possible to have an adaptive filter to block the unwanted feedbacks. The wound strings provide "more flesh" for the magnetic field so their sensivity is always much higher. It is therefore quiet normal, that after some time you end up with oscillations of the low strings. This problem is also tackled by this project.
This second project is not tied to using a transformer and a Humbucker.
As the audio stream for feedback does not need to be high quality, I used a Teensy 3.5 and the onboard adc with dac. A very simple buffer with a selected J201 JFET provides a high impedance input and also the 0,6V mid voltage for the adc range 0....1.2V. There is also a R=10k C=10nF low pass 1500Hz filter after the dac, because there is plenty of HF noise in the dac output signal.
I first tried to use FFT to determine the frequency but frequency resolution is not fine enough. So I was happy to find that "notefreq" can find the frequency of a note within about 80ms. With this information the high pass and low pass filters can be set. The filters are two twins with frequencies set to 0.95*notefreq and 1.05*notefreq. A single pair of filters was not enough. Wider and tighter bands also did not work as well.
Teensy Audio system has some latency. According to some information I found on the forum and also through own measurements this latency is 6.4ms. As cycle time is T= 1000ms/330Hz = 3ms for the high e' string, this latency is very relevant, if you must have the output with a defined phase to the input. So you must add additional delay to the 6.4ms. After some pondering and measurements I found, that as simple possibility you could compose the feedback to always come after 4.8 cycles for my setup. This leads to a formula: delay.additional= -6.4ms*(freq-750)/freq which seems to work quite well.
The audio patch needs about 82 percent peak processor power of T3.5 @ 120MHz.
Many thanks for the Audio Tool and many thanks for the modules in there!
Have fun Christof
What is a Sustainer?
A Guitar Sustainer is a setup with a coil acting as an actuator to give feedback to the string to keep it oscillating for ever. A very fine description of such system can be found here: https://scientificguitarist.wixsite.com/home/sustainer-reverse-engineering
My primary and first goal of these experiments was to see, if a normal Humbucker (resistance 7k) pickup could be used as the actuator. These experiments are described here: https://www.musikding.rocks/forum/index.php?thread/437310-sustainer-eigenbau-mit-trafo/ - Result was, that this can work, if you use a 10W 100V.eff audio transformer. About 100V and 3W of power is needed. As I didn't want to ruin my proper neck PU and did not want to have 100V close to my pickup electronics, I made a clamp-on housing which holds the low cost PU "down under" above the strings.
But when I tried to make music with it, I discovered, that you should somehow have more fingers for muting, because the system provides feedback for all six strings. So I asked myself, if it would be possible to have an adaptive filter to block the unwanted feedbacks. The wound strings provide "more flesh" for the magnetic field so their sensivity is always much higher. It is therefore quiet normal, that after some time you end up with oscillations of the low strings. This problem is also tackled by this project.
This second project is not tied to using a transformer and a Humbucker.
As the audio stream for feedback does not need to be high quality, I used a Teensy 3.5 and the onboard adc with dac. A very simple buffer with a selected J201 JFET provides a high impedance input and also the 0,6V mid voltage for the adc range 0....1.2V. There is also a R=10k C=10nF low pass 1500Hz filter after the dac, because there is plenty of HF noise in the dac output signal.
I first tried to use FFT to determine the frequency but frequency resolution is not fine enough. So I was happy to find that "notefreq" can find the frequency of a note within about 80ms. With this information the high pass and low pass filters can be set. The filters are two twins with frequencies set to 0.95*notefreq and 1.05*notefreq. A single pair of filters was not enough. Wider and tighter bands also did not work as well.
Teensy Audio system has some latency. According to some information I found on the forum and also through own measurements this latency is 6.4ms. As cycle time is T= 1000ms/330Hz = 3ms for the high e' string, this latency is very relevant, if you must have the output with a defined phase to the input. So you must add additional delay to the 6.4ms. After some pondering and measurements I found, that as simple possibility you could compose the feedback to always come after 4.8 cycles for my setup. This leads to a formula: delay.additional= -6.4ms*(freq-750)/freq which seems to work quite well.
The audio patch needs about 82 percent peak processor power of T3.5 @ 120MHz.
C++:
/*
SustA: print FFT
SustB: Funktioniert mit FFT
SustC: notefreq1
SustD: Phasenkorr
SustE: Double Filter
Christof Eberspaecher
*/
// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioInputAnalog adc1; //xy=91.33332824707031,431.3333282470703
AudioFilterStateVariable filter1; //xy=108.33341217041016,535.333402633667
AudioFilterStateVariable filter3; //xy=110,626
AudioAmplifier amp2; //xy=263.3333282470703,207.3333282470703
AudioFilterStateVariable filter4; //xy=263.9999694824219,624.9999389648438
AudioFilterStateVariable filter2; //xy=265.33338165283203,537.3333415985107
AudioAnalyzeRMS rms1; //xy=273.33333587646484,355.3333387374878
AudioAnalyzePeak peak1; //xy=275.3333282470703,320.3333282470703
AudioAnalyzeNoteFrequency notefreq1; //xy=408.3333396911621,207.33336448669434
AudioEffectDelay delay1; //xy=460.33331298828125,542.3333740234375
AudioSynthWaveformSine sine1; //xy=469.33331298828125,674.3333740234375
AudioMixer4 mixer1; //xy=603.333381652832,540.3333702087402
AudioEffectFade fade1; //xy=732,536
AudioOutputAnalog dac1; //xy=857.3334350585938,531.3333740234375
AudioConnection patchCord1(adc1, 0, filter1, 0);
AudioConnection patchCord2(adc1, peak1);
AudioConnection patchCord3(adc1, rms1);
AudioConnection patchCord4(adc1, amp2);
AudioConnection patchCord5(filter1, 0, filter2, 0);
AudioConnection patchCord6(filter3, 0, filter4, 0);
AudioConnection patchCord7(amp2, notefreq1);
AudioConnection patchCord8(filter4, 2, delay1, 0);
AudioConnection patchCord9(filter2, 2, filter3, 0);
AudioConnection patchCord10(delay1, 1, mixer1, 0);
AudioConnection patchCord11(sine1, 0, mixer1, 1);
AudioConnection patchCord12(mixer1, fade1);
AudioConnection patchCord13(fade1, dac1);
// GUItool: end automatically generated code
uint32_t oldDisp;
float del=6.5, amp=10.0, band=1.05, note, prob;
float notes[]= {82.4, 110, 146.8, 196.0, 246.9, 329.6}; // ohne capo
//float notes[]= {392.0, 293.7, 233.1, 174.6, 130.8, 98.0}; // capo III
void setup() {
// put your setup code here, to run once:
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(80);
fade1.fadeOut(10);
delay1.delay(1, 0.0); // <<<<<<<<<<<<<<<<<<<<<<<
mixer1.gain(0,amp);
amp2.gain(4.0);
filter1.frequency(10000.0); // LP
filter2.frequency(max(0.0,0.0)); // HP
notefreq1.begin(0.15);
pinMode(LED_BUILTIN, OUTPUT);
startblinker();
Serial.printf("SustE notefreq \n");
fade1.fadeIn(300);
}
void loop() {
loop_normal();
//findDelay();
}
void loop_normal() {
// put your main code here, to run repeatedly:
//Serial.println(AudioProcessorUsageMax());
//AudioProcessorUsageMaxReset();
if (Serial.available()) {
// read the most recent byte (which will be from 0 to 255):
int ch = Serial.read();
// set the brightness of the LED:
if(ch=='D') {
del+=0.25;
}
if(ch=='d') {
del-=0.25;
}
if(ch=='A') {
amp*=1.259921;
}
if(ch=='a') {
amp/=1.259921;
}
Serial.printf("Delay: %f Amp: %f \n", del, amp);
delay1.delay(1, del);
mixer1.gain(0,amp);
}
if(millis()/3000!=oldDisp) {
oldDisp= millis()/3000;
if (peak1.available()) {
Serial.printf(" Peak: %3.2f ", peak1.read());
}
Serial.printf(" Proc: %f \n",AudioProcessorUsageMax());
Serial.printf(" Note: %3.2f | Probability: %.2f\n", note, prob);
AudioProcessorUsageMaxReset();
}
if (notefreq1.available()) {
note = notefreq1.read();
prob = notefreq1.probability();
//Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob);
filter1.frequency(band*note); // LP
filter2.frequency(max(0.0,note/band)); // HP 43
filter3.frequency(band*note); // LP
filter4.frequency(max(0.0,note/band)); // HP 43
del= -6.4*(note-750)/note; // formula for 4.8 cycles total delay including 6.4ms latency
fade1.fadeOut(10); delay(11);
delay1.delay(1, del);
amp=0.1*map(note, 80, 1200, 80, 120); // give high notes extra gain
mixer1.gain(0,amp);
fade1.fadeIn(10); delay(11);
}
}
void findDelay() {
for(int n=0; n<6; n++) {
note= notes[n]; // 82.4 110 146.8 196.0 246.9 329.6
for(int d=7; d<8; d++) {
//del= d/8.0*1000.0/note;
del= -6.4*(note-750)/note; // formula for 4.8 cycles total delay including 6.4ms latency
delay1.delay(1, del);
sine1.frequency(note);
mixer1.gain(0,0); // feedback off
mixer1.gain(1,1.0); // sine on
delay(5000);
mixer1.gain(1,0.0); // sine off
mixer1.gain(0,amp); // feedback on
filter1.frequency(band*note); // LP
filter2.frequency(max(0.0,note/band)); // HP 43
filter3.frequency(band*note); // LP
filter4.frequency(max(0.0,note/band)); // HP 43
for(int i=0; i<5; i++) {
delay(2000);
Serial.printf("Freq: %5.2f Gain: %3.2f Del: %3.2f Sek: %2d Note: %5.2f RMS: %f \n", \
note, amp, del, 2*i, notefreq1.read(), rms1.read()) ;
}
}
}
Serial.printf(" End \n");
while(1);
}
void startblinker() {
int i;
for(i=10;i>0;i--)
{
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(100); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(100); // wait for a second
}
}
Many thanks for the Audio Tool and many thanks for the modules in there!
Have fun Christof
Last edited: