Creating custom audio objects that are more similar to modular synth components.

Status
Not open for further replies.

thedreiskre

Active member
I have been working for some time on a polyphonic synthesizer, however I have been running into a lot of scalability issues due to some very spaghetti code and difficulty interfacing between my sketch and the audio library. A potential solution I have been considering is creating some custom objects for the audio library. I've made some rough diagrams of what I'm thinking of making, but i could do with some advice on the feasibility and difficulty of creating them. Any help is much appreciated.
https://imgur.com/a/SwYSYGc
 
Best way to get started is to delve into the library and learn how the existing things work. The osc you describe could just be the existing osc with some added control inputs.

Here's a really simple effect. It's a basic full wave lossless rectifier. Combine it with a biquad, you got an envelope follower. This is what the library is all about :)
 

Attachments

  • effect_rectifier.h
    1.8 KB · Views: 93
  • effect_rectifier.cpp
    2.3 KB · Views: 112
Should I merge this rectifier effect into the audio library?

Any chance you might be willing to contribute an example program using it with the filter?
 
There's a bug in the rectifier code. It appears to work for all input values except 0x8000. In this case it should return 0x7fff (maximum positive value) but it returns 0x8000.
I used a rectifier effect in some code I wrote a few years back. I vaguely remember getting the code from Paul somehow. Anyway, it does the rectify like this, where the variable 's' is declared int16_t:
Code:
    if (s < 0) {
      if (s == -32768) {
        s = 32767;
      } else {
        s = -s;
      }
    }
I suspect that this could be made more efficient in assembler code. If I figure it out, I'll post it here.

@ thedreiskre
For your oscillator, start with the audio library code for synth_sine.cpp and/or synth_waveform.cpp to see how the existing code generates those waveforms and then tweak the code to do what you want.

Pete
 
Should I merge this rectifier effect into the audio library?

Any chance you might be willing to contribute an example program using it with the filter?

I'd be flattered if you would merge it into the library. It would save me having to edit my copy and paste code from the gui to make my own rectifier links.

Here's a vocoder I made based on another sketch. Very basic but it's functional and demonstrates how to use the object. Of course, right now to copy and paste it back into the gui you'd need to change the "AudioEffectRectifier" to AudioEffectFade (that's the stub I use) so the gui can construct the full diagram.
 

Attachments

  • BoxxyVocoder.ino
    62.3 KB · Views: 75
Scratch that, that's a weeks old file. Here's a basic demo that uses a built in noise source to get things started.
 

Attachments

  • MyVocoder19bandDemo.ino
    32.2 KB · Views: 75
And here's an updated c module minus the bug pointed out by el_supremo
 

Attachments

  • effect_rectifier.cpp
    2.4 KB · Views: 85
How about mixers with 8 and 16 inputs? This would make some designs a lot simpler.

Thanks,
Michael
 
About the original question...

E0U1NWj.png

Like with any software project, a key first step is to start with a clear idea of the code will actually do. This picture probably means something specific to you, but I'm confused. I don't really understand why it has "Exp Frequency" and also "Exp Frequency adjust". It also has 2 similarly named inputs for "Linear Frequency". That seems like 4 separate inputs which all do the same thing, in slightly different ways.

While you could probably build such code, what will it do if signals are connected to both the Linear and Exp inputs? These are important details when it comes to actually writing the code....
 
I was thinking to create an audio object to neaten up my code and audio graph. To implement all the types of synthesis my design needs would take a pretty complex node setup and program. For example, when making an 8 voice polysynth I pretty much had to make everything 8 times, the resulting graph looked like this https://imgur.com/a/RuPcej8.

The project I'm now working on is monophonic, however I am looking to implement multiple different generation algorithms. If i was working purely with 1 arduino sketch and the audio node editor, this would become a very complex system (and that is without adding in filters, multiple oscillators, lfos etc.

While implementing each one of these algorithms using separate objects is relatively simple, I dont see how they could be condensed into a single object, let alone changing the algorithm with a value and having the inputs then control different settings.

What would be the best way to go about coding a single object which has multiple oscillation algorithms within it , and can take inputs via the audio graph?

revised diagram: https://imgur.com/a/jVIrMYh
 
Can we delete attachments? I now have two of effect_rectifier.cpp files in my attachments manager.

Anyway, this one is elegant and works great.
 

Attachments

  • effect_rectifier.cpp
    2.3 KB · Views: 83
@ boxxofrobots
I don't see how your latest version is any better. The problem is that you are copying an int16_t into an int32_t and apparently assuming that the 16-bit integer is copied into the high order 16 bits of the int32_t. But if block->data is 0x8000 then after this:
Code:
  sampleRect = block->data[i];
sampleRect will be 0xFFFF8000, not 0x80000000. There is also the problem that when sampleRect is assigned to be 0x7fffffff, and the following statement is executed:
Code:
   	block->data[i] = sampleRect;
this will set block->data to 0xffff (-1), not 0x7fff.
You would be better off declaring sampleRect to be int16_t and use code similar to what I posted in #4.

Pete
 
Interesting. I see your point, but the api seems to be doing the heavy lifting because it works as specified. Here's a proof.

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSineHires sine_hires1; //xy=773,891
AudioEffectRectifier rect1; //xy=1096,933
AudioOutputI2S i2s1; //xy=1271,810
AudioOutputUSB usb1; //xy=1283,1057
AudioConnection patchCord1(sine_hires1, 0, i2s1, 0);
AudioConnection patchCord2(sine_hires1, 0, usb1, 1);
AudioConnection patchCord3(sine_hires1, 0, rect1, 0);
AudioConnection patchCord4(rect1, 0, usb1, 0);
AudioConnection patchCord5(rect1, 0, i2s1, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=992,1058
// GUItool: end automatically generated code



void setup() {
// put your setup code here, to run once:
AudioMemory(10);
sgtl5000_1.enable();
sgtl5000_1.volume(1);
// noise1.amplitude(0.5);
// waveform1.begin(WAVEFORM_SINE);
sine_hires1.amplitude(1);
sine_hires1.frequency(1000);

}

void loop() {
// put your main code here, to run repeatedly:
Serial.println("Nothing");
}
 
While testing your code I realized that what you've implemented is a half-wave rectifier. I had needed a full-wave rectifier and got confused. Sorry about that.
However, if you are doing a half wave rectifier wouldn't this be a lot simpler?
Code:
  for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
    if(block->data[i] < 0)block->data[i] = 0;
  }

Pete
 
file.png

Code:
#include <Audio.h>

AudioSynthWaveformSine sine;
AudioEffectRectifier rect;
AudioOutputAnalog dac;
AudioConnection p1(sine, 0, rect, 0);
AudioConnection p2(rect, 0, dac, 0);

void setup() {
  AudioMemory(10);
  sine.amplitude(0.9);
  sine.frequency(440);
}

void loop() {
}
 
I was thinking to create an audio object to neaten up my code and audio graph. To implement all the types of synthesis my design needs would take a pretty complex node setup and program. For example, when making an 8 voice polysynth I pretty much had to make everything 8 times

I believe this problem would be best solved in the design tool. Someday I hope we can support hierarchical designs, where you can create objects which are actually entire designed in themselves. The export process would flatten the whole thing to a long list of instances to copy into Arduino.

A custom object with new features might also be useful for other reasons. But consider that processing input streams is expensive in terms of CPU performance, as you do more and more math for every audio sample, 44100 times per second.
 
What about supporting naming conventions like osc[1], adsr[4], etc? Presently a copy and paste of such names results in a mess like osc[1]1, adsr[4]1, etc.
 
Status
Not open for further replies.
Back
Top