MacroMachines
Well-known member
Not sure what #1071 refers to? I was responding to Roels entry, he said he uses 1024 word tables. Can't use quote feature on my phone for some reason :/
In flash_spi.cpp are only functions to read block-wise. It is not necessary to read blockwise.
Here's a little extension, I use it in my codec-lib:
Code:void readserflash(uint8_t* buffer, const size_t position, const size_t bytes) {//flash_spi.h has no such function. digitalWriteFast(SERFLASH_CS, LOW); SPI.transfer(0x0b);//CMD_READ_HIGH_SPEED SPI.transfer((position >> 16) & 0xff); SPI.transfer((position >> 8) & 0xff); SPI.transfer(position & 0xff); SPI.transfer(0); for(unsigned i = 0;i < bytes;i++) { *buffer++ = SPI.transfer(0); } digitalWriteFast(SERFLASH_CS, HIGH); }
have Fun
Regards,
Frank
@nantonos
About the point of band limiting:
My teensy project happens to be a virtual analog synthesizer.
about wavetables: I use 14 band limited wavetables, with 1024 samples each (it fills the teensy quite fast!)
Each wavetables I covers half an octave, which gives around 7 octaves. This is almost, but not yet enough.
Still, the lowerest octaves can be relatively harmonically rich with 500 harmonics.
The higher notes use less aliases.
I can't hear the switching of wavetables, so that doesn't give any problems.
a possible optimization might be to use smaller wavetables for higher octaves with less harmonics.
I might implement it if I run out of memory.
Another optimization is that the pulse oscillator is made by subtracting 2 phase shifted saws.
shifting one of those saws changes the duty cycle of the pulse.
Perhaps my code can help, it can be found at my github
I also added hard sync, but haven't anti-aliased that part yet.
Is there an audible difference between hard and soft sync?
I couldn't find a good comparison.
You need flash_init() and #define SERFLASH_CS 6.Do you need to add anything to the .h file to use this?
I am checking out your code, does it work in tandem with the teensy audio library?
Also very curious about your DigitalPotFilter Class.. is this for removing zipper noise on a digital pot or is it an actual analog filter circuit being controlled with a digital pot?
I'd be curious if you are willing to share the schematic if it is the latter.
How were you able to use 1024 samples in the wave table? It says they are limited to 256? Or did I misunderstand the notes in the function on the audio config tool?
int16_t LinearSample(const int16_t *pWaveTable, uint32_t ph, int TableSizeBits, int32_t magnitude)
{
uint32_t index,scale;
int32_t val1, val2;
index = ph >> 22;
val1 = pWaveTable[index];
val2 = pWaveTable[index+1];
scale = (ph >> 6) & 0xFFFF;
val2 *= scale;
val1 *= 0xFFFF - scale;
return multiply_32x32_rshift32(val1 + val2, magnitude);
}
index = ph >> 22;
//original value was >>24
scale = (ph >> 6) & 0xFFFF;
//original value was >>8
Well, about the DigitalPotFilter class... it was used to control the filter cutoff and resonance of an analog filter.
I used a schematic from wikipedia.
However, it didn't work too well with digipots.
The digipot has divided in 256 steps, which were divided linearly.
The 256 steps weren't divided linearly for frequency, with small steps in frequency for a low cutoff.
When the cutoff increased, one digipot-step could mean a difference of 1000 hertz or more.
This... was audible and ugly.
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
// GUItool: begin automatically generated code
AudioInputAnalog adc1; //xy=55,98
AudioEffectDelay delay1; //xy=223,207
AudioMixer4 mixer1; //xy=380,146
AudioOutputAnalog dac1; //xy=564,155
AudioConnection patchCord1(adc1, 0, mixer1, 0);
AudioConnection patchCord2(adc1, delay1);
AudioConnection patchCord3(delay1, 0, mixer1, 1);
AudioConnection patchCord4(mixer1, dac1);
// GUItool: end automatically generated code
void setup()
{
// allocate storage
AudioMemory(120);
// 300 ms of delay
delay1.delay(0, 300.0);
// wet/dry mix at 0.8
mixer1.gain(0, 0.2);
mixer1.gain(1, 0.8);
}
void loop()
{
// empty
}
Audio memory simply assigns RAM to the audio engine, I don't think that is your problem. But then I can't see an issue anywhere else in your code. I've not used analog in or DAC output before so maybe someone else can comment.
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
// GUItool: begin automatically generated code
AudioInputAnalog adc1; //xy=113,154
AudioMixer4 mixer1; //xy=335,362
AudioEffectDelay delay1; //xy=352,183
AudioOutputAnalog dac1; //xy=592,204
AudioConnection patchCord1(adc1, 0, mixer1, 0);
AudioConnection patchCord2(mixer1, delay1);
AudioConnection patchCord3(delay1, 0, dac1, 0);
AudioConnection patchCord4(delay1, 0, mixer1, 1);
// GUItool: end automatically generated code
void setup()
{
// allocate storage
AudioMemory(120);
// 300 ms of delay
delay1.delay(0, 300.0);
// feedback at 0.8
mixer1.gain(0, 1.0);
mixer1.gain(1, 0.8);
}
void loop()
{
// empty
}
If using the ADC, make sure you have 96 MHz selected. It doesn't work at 72 MHz. Yes, that's a known bug.
This quest resulted in some new code which might be useful to others, thus I've submitted a pull request of some things:
- an AnalyzeRMS object that calculates the RMS of an audio block
- 2 additional SGTL5000 functions to control the DAC ramping.
- a fix of AnalyzePeak, which returned the maximum distance between the positive and negative peak of the audio, while (in my opinion) normally it should return the maximum absolute distance to DC.
// Demonstrate a resonant state-variable filter whose filter frequency
// is controlled by an LFO and envelope. A second encelope is used
// to control the dynamics of each note. This example is a simple,
// monophonic synth.
//
// Accepts MIDI in over USB. Set USB type to MIDI in the Tools menu.
//
// This example code is in the public domain.
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
// GUItool: begin automatically generated code
AudioSynthWaveformSine sine1; //xy=86,359
AudioSynthWaveform waveform2; //xy=89,302
AudioSynthWaveformSine sine2; //xy=90,419
AudioEffectMultiply multiply1; //xy=229,389
AudioEffectEnvelope envelope1; //xy=379,388
AudioFilterStateVariable filter1; //xy=383,309
AudioEffectEnvelope envelope2; //xy=547,295
AudioOutputI2S i2s1; //xy=727,300
AudioConnection patchCord1(sine1, 0, multiply1, 0);
AudioConnection patchCord2(waveform2, 0, filter1, 0);
AudioConnection patchCord3(sine2, 0, multiply1, 1);
AudioConnection patchCord4(multiply1, envelope1);
AudioConnection patchCord5(envelope1, 0, filter1, 1);
AudioConnection patchCord6(filter1, 0, envelope2, 0);
AudioConnection patchCord7(envelope2, 0, i2s1, 0);
AudioConnection patchCord8(envelope2, 0, i2s1, 1);
AudioControlSGTL5000 audioShield; //xy=720,223
// GUItool: end automatically generated code
byte currentNote = 255; // MIDI note number of currently playing note, 0 - 127 or 255 for no note
void setup(void)
{
//set up a basic subtractive synth patch
AudioMemory(8);
audioShield.enable();
audioShield.volume(0.45); // headphone volume (line-out muted)
// modulation
sine1.frequency(5); // 5Hz LFO
sine1.amplitude(0.7); // controls modulation depth
sine2.frequency(23); // 23Hz second LFO
sine1.amplitude(0.9); // controls modulation depth
envelope1.attack(140); // fairly slow attack and decay
envelope1.hold(180);
envelope1.sustain(0.3);
envelope1.decay(140);
envelope1.release(40);
// saw oscillator
waveform2.begin(0.2, 220, WAVEFORM_SAWTOOTH); // 220Hz saw wave oscillator
// quiet level of 0.4 as resonant filter adds gain
// resonant filter, low-pass mode (output 0 of filter is LP)
filter1.resonance(18);
filter1.octaveControl(1.5); // modulation signal shifts resonant frequency by +/- 1.5 octaves
filter1.frequency(200); // start below the resonant peak
//envelope for note -on and -off dynamics
envelope2.attack(10);
envelope2.decay(20);
envelope2.release(80);
// now respond to MIDI over USB. In this example, only note-on and note-off used and velocity ignored
usbMIDI.setHandleNoteOff(OnNoteOff);
usbMIDI.setHandleNoteOn(OnNoteOn);
}
void loop() {
usbMIDI.read(); // USB MIDI receive
}
void OnNoteOn(byte channel, byte note, byte velocity) {
// accept input on any channel, and ignore velocity
// check for a currently held note and brutally kill it
// this sounds bad so only play one note at once, retro style
if (currentNote <= 127) {
envelope2.release(2);
envelope2.noteOff();
delay(3); // briefest period for killed note to stop sounding
envelope2.release(80);
}
currentNote = note;
// MIDI note 69 == A4 == 440Hz
float freq = ((float)note - 69) / 12.0; // semitones above or below A4
freq = 440.0 * powf(2.0, freq);
sine1.phase(0); // reset LFOs phases
sine2.phase(0);
envelope1.noteOn(); // start modulation
waveform2.frequency(freq);
waveform2.phase(0);
filter1.frequency(1.2 * freq); // resonance above played note
envelope2.noteOn();
}
void OnNoteOff(byte channel, byte note, byte velocity) {
// accept any channel, ignore off-velocity as most things do
// we only care about the currently playing note
if (note == currentNote) {
envelope2.noteOff();
envelope1.noteOff();
currentNote = 255;
}
}
Has anyone got it running at faster speed but playing back at 44Khz properly?
Also added an example which is a toy monophonic synthesizer.
Having a crack at itneed to get fiddling with my dusty knobs.
To that end, I have 2 questions:
1) Earlier I've submitted a pull request with other functionality. Would you (Paul) prefer if I push these 2 extra effects once tested to my master and thus add upon that pull request, or branch and make a new pull request (i'm no git pro yet, so I'd prefer the first)?
2) I had to add a few DSP functions, being "signed_subtract_16_and_16()", "signed_halving_add_16_and_16()" and "signed_halving_subtract_16_and_16()". Can these just be added to "utility/dspinst.h"?
Please don't use branches. I'm not a github pro either!