Audio Library

Status
Not open for further replies.
According to the Teensy 3.1 breakout, pins 26 and 31 underneath the Teensy are alternate pins for Serial2. I don't know if Paul has a way switch to use those pins in the current library.
 
Is there a way to either remap some of the audio library pins to free serial port 2 or 3 or even remap the serial ports?

Yes, there is a way. You'll need to write directly to the port config registers. It's been posted previously on another thread.
 
I've changed the API for both the FFT objects. Sorry, it's an incompatible change. Anyone currently using the FFT objects will need to update their code. See the Analyze > FFT example for details. The FFT objects are now also documented in the GUI.

Over the next several days, I intend to make several more API changes in preparation for a 1.0 release. The idea is the keep the API stable after 1.0. This week will probably be the last opportunity to clean up the API, to remove cruft and make things consistent.

Since I started really working on this library about 1 year ago (as opposed to merely talking and dreaming about it... for a LOT longer), a tremendous amount has changed. I've learned a lot. There were many false starts and many ideas that turned out to not be so great. There were several really great contributions, especially from Pete & Rob, before decisions were made about a consistent API. However, most of the cruft and mess is entirely my fault. Architecturally, this library is one of the most ambitious software projects I've ever attempted, and honestly some of it has been quite a learning experience along the way.

The change in these FFT objects, other than simply adding functions, involves eliminating parameters in their constructors. Originally (late 2013), I had imagined most objects taking configuration parameters, with only a small number of functions to use for control. In hindsight, constructor parameters have made this library more difficult to understand. Descriptive functions used in setup() are usually self-documenting and they're a style more familiar to most Arduino users. Before 1.0, my plan is to completely eliminate constructor parameters, so all objects are instantiated with only the class name and instance name, without any parameters. Only a few objects still have them, but anyone using those will need to update their code.

I'd like to thank everyone who's endured using this "beta" library. If you're thinking about giving it a try, I know waiting for a stable release is easiest, but I could really use your feedback now!
 
this is going off into a somewhat different (ie non virtual analog) direction, but one basic thing that seems be missing at this point (please correct me if i'm wrong) is some kind of more flexible sample-stream/buffer object. i'm having in mind something like the [tabread4~] object in puredata (basically: sample array+interpolation), which can be fairly useful (eg for granular synthesis type things). somewhat like a cross between play_memory/play_sd_wav and synth_waveform/synth_sine.

i've tried to come up with something but the result didn't sound very nice.
 
There's 2 options.

The waveform object recently got an option for arbitrary waveform. It's documented in the GUI. Click "waveform" in the synth section. The object will play your buffer repetitively, advancing through it with linear interpolation. Currently there are no examples. Maybe you'll write the first one?

The "queue" object gives you low-level stream access. In a nutshell, you call getBuffer(), then fill the buffer it gives you, and call playBuffer() to send that buffer into the audio system. It's up to you to do whatever math you want to come up with the samples to fill the buffer.
 
oh, i hadn't noticed the queue - object. well, i'll have to look into it some more/again, i haven't thought about it in a while (that was still with the old i2s library). but looks doable, i guess.

and well, i'm not the kind of person who should supply example code, but fwiw, here's some code using arbitrary waveforms; it just randomly picks one of 10 wavetables (i hope) and plays a little sequence (it does) (edit: see below for the cleaner/nicer example (example #2). i should add that the struct below has more members in real life. it's not needed here).

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>

// AUDIO lib objects:

AudioSynthWaveform       someWaveform;      
AudioOutputAnalog        DAC;           
AudioConnection          patchCord1(someWaveform, DAC);

#define MAX_FREQ 600    // max frequency < - > arbitraryWaveform(array, maxFreq); 

/* include some wavetables: the files need to contain arrays [256] of type const short int wt1[] = { ... } */

#include </path/to/wt1.h>
#include </path/to/wt2.h>
...
etc
...
#include </path/to/wt10.h>


// the following presumably is needlessly complicated: 

struct WaveTable {
  int16_t const* table; 
};  

#define TABLE_NUM 10 // 

WaveTable waveTables[TABLE_NUM] {
  wt1,
  wt2,
  wt3,
  wt4,
  wt5,
  wt6,
  wt7,
  wt8,
  wt9,
  wt10
}; 

const uint16_t sequence[] = {220, 440, 261, 587}; // a little sequence
#define SEQ_L 3        // sequence length 
#define SEQ_SPEED 400  // sequence speed (ms)
uint8_t waveselect=3, pos;  // variable to select wavetable, and position in sequence



void setup() {
  
  AudioMemory(12);
  
  /* // basic usage would be: 
  someWaveform.arbitraryWaveform(wt1, MAX_FREQ); 
  someWaveform.begin(0.5, 440, WAVEFORM_ARBITRARY);
  */
  // here we want to randomly select from the 10 wavetables above, so:
  const struct WaveTable* waveTable  = &waveTables[waveselect];
  int16_t* wave = const_cast<short int*>(waveTable->table);
  someWaveform.arbitraryWaveform(wave, MAX_FREQ);
  someWaveform.begin(0.5, 440, WAVEFORM_ARBITRARY);

}

void loop() {
  
 delay(SEQ_SPEED);
 
 if (pos > SEQ_L) {
     pos = 0; // reset sequence
     waveselect = random(10);  // and pick a new wavetable
     const struct WaveTable* waveTable  = &waveTables[waveselect];
     int16_t* wave = const_cast<short int*>(waveTable->table);
     someWaveform.arbitraryWaveform(wave, MAX_FREQ);
 }
 
 // play next note in sequence
 someWaveform.frequency(sequence[pos++]);
  
  
}
 
Last edited:
Examples of using arbitrary waveforms / wave tables

Thanks for the arbitrary waveform example. Here is a modified version that includes several waveforms from Adventure Kid (thanks Nantonos!)
zip file: https://github.com/mihow/audio-patches/blob/master/Teensy/CustomWavetable.zip?raw=true
browse code: https://github.com/mihow/audio-patches/tree/master/Teensy/CustomWavetable

Here is another example that is a copy of the PlaySynthMusic music, but plays the William Tell Overture with an Electronic Organ sound. You can hear some aliasing.
zip file: https://github.com/mihow/audio-patc.../PlaySynthMusicArbitraryWaveform.zip?raw=true
browse code: https://github.com/mihow/audio-patches/tree/master/Teensy/PlaySynthMusicArbitraryWaveform
 
Last edited:
Yes, there is a way. You'll need to write directly to the port config registers. It's been posted previously on another thread.

LINK TO REMAP SERIAL2 port
To connect these, the following registers need to be set (have not tried yet, but I'm sure it will be fine)
PORTE_PCR0 = 0x00000300 (mux 3) - connects alternative function UART1_TX to pin PTE0
PORTE_PCR1 = 0x00000300 (mux 3) - connects alternative function UART1_RX to pin PTE1
or
CORE_PIN26_CONFIG = PORT_PCR_MUX(3);
CORE_PIN31_CONFIG = PORT_PCR_MUX(3);

Thank you sir, I'm sure you do not get thanked enough for your work. Keep on with the amazing product. I hope to buy a vast number of bootloader chips to help fund your projects after we get the new prototypes built up in 2-3 months.
 
LINK TO REMAP SERIAL2 port
To connect these, the following registers need to be set (have not tried yet, but I'm sure it will be fine)
PORTE_PCR0 = 0x00000300 (mux 3) - connects alternative function UART1_TX to pin PTE0
PORTE_PCR1 = 0x00000300 (mux 3) - connects alternative function UART1_RX to pin PTE1
or
CORE_PIN26_CONFIG = PORT_PCR_MUX(3);
CORE_PIN31_CONFIG = PORT_PCR_MUX(3);

Thank you sir, I'm sure you do not get thanked enough for your work. Keep on with the amazing product. I hope to buy a vast number of bootloader chips to help fund your projects after we get the new prototypes built up in 2-3 months.

Code:
  //CORE_PIN26_CONFIG = PORT_PCR_MUX(3);
  //CORE_PIN31_CONFIG = PORT_PCR_MUX(3);
  PORTE_PCR0 = 0x00000300;// (mux 3) - connects alternative function UART1_TX to pin PTE0
  PORTE_PCR1 = 0x00000300;// (mux 3) - connects alternative function UART1_RX to pin PTE1
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600); //moved slave/android to pins that were used by audio

Hey, the audio will not play after I initialize serial2 even after I remapped it. The serial port works by itself after remapped, but not together with the audio library. As soon as I comment out Serial2.begin, it will play audio again!?

Is there a way to free up either pins 7/8/RX3/TX3 or 9/10/RX2/TX2? If not, what's the work around to have two hardware serial ports while still having audio (not soft serial, too slow).
 
Last edited:
As soon as I comment out Serial2.begin, it will play audio again!?

I'll investigate, but only if you post a complete program that demonstrates the problem.

If you've watched the forum for any length of time, or read the forum rules, the one main rule around here is THOU SHALT POST COMPLETE CODE TO REPRODUCE THE PROBLEM.
 
I'll investigate, but only if you post a complete program that demonstrates the problem.

If you've watched the forum for any length of time, or read the forum rules, the one main rule around here is THOU SHALT POST COMPLETE CODE TO REPRODUCE THE PROBLEM.

Thanks for the quick reply, here is a modified version of the SD card example. I really need access to another serial port.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>

// Create the Audio components.  These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioPlaySdWav     wav;
AudioOutputI2S     dac;

// Create Audio connections between the components
//
AudioConnection c1(wav, 0, dac, 0);
AudioConnection c2(wav, 1, dac, 1);

// Create an object to control the audio shield.
// 
AudioControlSGTL5000 audioShield;


void setup() 
{
  //CORE_PIN26_CONFIG = PORT_PCR_MUX(3);  //alternate means to toggle serial port 2 to different pins
  // CORE_PIN31_CONFIG = PORT_PCR_MUX(3); //alternate means to toggle serial port 2 to different pins
  PORTE_PCR0 = 0x00000300;// (mux 3) - connects alternative function UART1_TX to pin PTE0
  PORTE_PCR1 = 0x00000300;// (mux 3) - connects alternative function UART1_RX to pin PTE1
  Serial2.begin(9600);  //comment this out and code works fine, put it back in and you can use pins 26 and 31 to talk to Serial2
  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(50);

  audioShield.enable();
  audioShield.volume(0.7);

  SPI.setMOSI(7);
  SPI.setSCK(14);
  if (SD.begin(10)) {
  wav.play("290MSG.WAV");
  //wav.play("01_16M.WAV");
  }
}

void loop() 
{
}
 
I know the variable filter object is brand new, but... is there a way to change the bandwidth of the bandpass output? I am playing with formants to make vowel sounds (sets of 3-5 bandpass filters) and would love to use the variable filter to transition between formants.

Also what's a reasonable way to generate a pulse wave with a variable duty cycle? That would be my ideal sound source for the formants. Variable PWM is a way to make some really great sounds on other synths as well.

Here are example settings for 5 bandpass filters to make an "ah" vowel from the CSound manual:

freq (Hz) 800 1150 2800 3500 4950
amp (dB) 0 -4 -20 -36 -60
bwidth (Hz) 80 90 120 130 140

Thanks for all your great work Paul!
 
Last edited:
Vowel test

Okay here is a quick and grimy patch that cycles through some vowel-like formant sounds.


Teensy-Formant-Flow-Example.png

Code:
/* Formant values for vowels taken from CSound manual appendix:
http://csound.github.io/docs/manual/MiscFormants.html
*/

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

// GUItool: begin automatically generated code
AudioSynthNoisePink      noise1;         //xy=65,218
AudioSynthWaveform       waveform1;      //xy=65,264
AudioSynthWaveform       waveform2;      //xy=65,305
AudioEffectMultiply      multiply1;      //xy=152,361
AudioMixer4              mixer2;         //xy=229,233
AudioFilterStateVariable filter2;        //xy=388,263
AudioFilterStateVariable filter3;        //xy=389,323
AudioFilterStateVariable filter1;        //xy=390,200
AudioFilterStateVariable filter4;        //xy=393,384
AudioMixer4              mixer1;         //xy=584,253
AudioEffectEnvelope      envelope1;      //xy=724,254
AudioOutputI2S           i2s1;           //xy=749,374
AudioConnection          patchCord1(noise1, 0, mixer2, 0);
AudioConnection          patchCord2(waveform1, 0, multiply1, 0);
AudioConnection          patchCord3(waveform2, 0, multiply1, 1);
AudioConnection          patchCord4(multiply1, 0, mixer2, 1);
AudioConnection          patchCord5(mixer2, 0, filter1, 0);
AudioConnection          patchCord6(mixer2, 0, filter2, 0);
AudioConnection          patchCord7(mixer2, 0, filter3, 0);
AudioConnection          patchCord8(mixer2, 0, filter4, 0);
AudioConnection          patchCord9(filter1, 1, mixer1, 0);
AudioConnection          patchCord10(filter2, 1, mixer1, 1);
AudioConnection          patchCord11(filter3, 1, mixer1, 2);
AudioConnection          patchCord12(filter4, 1, mixer1, 3);
AudioConnection          patchCord13(mixer1, envelope1);
AudioConnection          patchCord14(envelope1, 0, i2s1, 0);
AudioConnection          patchCord15(envelope1, 0, i2s1, 1);
// GUItool: end automatically generated code

AudioControlSGTL5000 audioShield;



void setup(void)
{

  // Set up
  AudioMemory(16);
  audioShield.enable();
  audioShield.volume(0.45);


  // Sound source
  waveform1.begin(0.4, 30, WAVEFORM_SQUARE);
  waveform2.begin(0.4, 30.2, WAVEFORM_SQUARE); // A little variation
  waveform2.phase(80); // Kind of PWM? 2 sq waves out of phase, multiplied to gether
  noise1.amplitude(0.1); // Some breathiness

  // Volume of each formant
  mixer1.gain(0, 0.9);
  mixer1.gain(1, 0.8);
  mixer1.gain(2, 0.7);
  mixer1.gain(3, 0.4); 

  // Q of each formant.
  filter1.resonance(5);
  filter2.resonance(4.8);
  filter3.resonance(4.6);
  filter4.resonance(4.2);

  // Envelope of each utterance
  envelope1.attack(20);
  envelope1.hold(200);
  envelope1.decay(50);
  envelope1.release(200);


}

void loop(void)
{

  // Table D.8. bass “i”
  filter1.frequency(250);
  filter2.frequency(1750);
  filter3.frequency(2600);
  filter4.frequency(3050);
  
  envelope1.noteOn();
  delay(600);
  envelope1.noteOff();
  delay(400);


  // Table D.7. bass “e”
  filter1.frequency(400);
  filter2.frequency(1620);
  filter3.frequency(2400);
  filter4.frequency(2800);
  
  envelope1.noteOn();
  delay(600);
  envelope1.noteOff();
  delay(400);


  // Table D.9. bass “o”
  filter1.frequency(400);
  filter2.frequency(750);
  filter3.frequency(2400);
  filter4.frequency(2600);
  
  envelope1.noteOn();
  delay(700);
  envelope1.noteOff();
  delay(500);
  

  // Table D.10. bass “u”
  filter1.frequency(350);
  filter2.frequency(600);
  filter3.frequency(2400);
  filter4.frequency(2675);
  
  envelope1.noteOn();
  delay(800);
  envelope1.noteOff();
  delay(600);

}
 
Last edited:
hey guys - is it possible to do variable speed sample playback with this library?
Thanks
Zach

no, see my post #856 above. (ie, if "variable speed" is referring to resampling (or "varispeed", as in tape players), but for even that to sound nice, linear interpolation probably won't do; if "time stretching", that'll be an entirely different story, and probably not doable - i don't know).
 
Thanks for the quick reply, here is a modified version of the SD card example. I really need access to another serial port.

Try this:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>

AudioPlaySdWav     wav;
AudioOutputI2S     dac;

AudioConnection c1(wav, 0, dac, 0);
AudioConnection c2(wav, 1, dac, 1);

AudioControlSGTL5000 audioShield;

void setup() 
{
  Serial2.begin(9600);
  CORE_PIN9_CONFIG = PORT_PCR_MUX(6);
  CORE_PIN10_CONFIG = PORT_PCR_MUX(1);
  CORE_PIN26_CONFIG = PORT_PCR_MUX(3);
  CORE_PIN31_CONFIG = PORT_PCR_MUX(3);
  
  AudioMemory(50);
  audioShield.enable();
  audioShield.volume(0.7);

  SPI.setMOSI(7);
  SPI.setSCK(14);
  if (SD.begin(10)) {
    //wav.play("290MSG.WAV");
    wav.play("01_16M.WAV");
  }
}

void loop() 
{
}
 
no, see my post #856 above. (ie, if "variable speed" is referring to resampling (or "varispeed", as in tape players), but for even that to sound nice, linear interpolation probably won't do; if "time stretching", that'll be an entirely different story, and probably not doable - i don't know).

Currently none of the play objects supports this.

If you or someone else (not me) wanted to work on this, slowing playback ought to be fairly simple. Just take the output data and repeat samples or use linear interpolation to lengthen the data size. Each update sends 128 samples, so you'll have to either read the correct number to be scaled up to 128, or keep some in a buffer between updates. It'll take some work coding, but not impossible. There's already linear interpolation code in several places within the library.

Speeding things up is harder. The trouble with dropping samples or skipping past data with linear interpolation is the high frequencies can alias down into the main audio. But most music has very little energy above 15 kHz, and the aliasing effect starts at the high frequencies and grows downward. For small speedup ratios with "normal" music and speech, aliasing might not be too terrible. The proper way to skip samples is to first filter the data to remove high frequencies. Probably the Biquad filter code could be reused. That would all have to be done within a play object, with more than 128 samples input to the filter per block, so the output could be downsampled to 128 samples on each update. Again, not impossible, but it'll take quite a bit of work to do correctly.

At the moment, I have a long list of very mundane little improvements, fixes and cleanups... all in preparation for the 1.0 release. I'm not going to work on any major new functionality until after an 1.0 (stable API) release is published.
 
I know the variable filter object is brand new, but... is there a way to change the bandwidth of the bandpass output? I am playing with formants to make vowel sounds (sets of 3-5 bandpass filters) and would love to use the variable filter to transition between formants.
Yes, you can change the Q of the SVF.

Also what's a reasonable way to generate a pulse wave with a variable duty cycle? That would be my ideal sound source for the formants. Variable PWM is a way to make some really great sounds on other synths as well.
This is missing functionality, agreed. Need to have a pulse wave (variable mark/space ratio) , and also a pulse wave with an audio PWM input.

Square wave is a rather special case of a pulse; at exactly 50% mark/space it has no even harmonics. The other pulse types sound rather different. Audio frequency PWM is also a standard sound for synths.

Ideally there would also be a frequency modulation input on the waveform object as well.
 
Last edited:
I've updated the Github issue tracker with all the known bugs to be fixed before the 1.0 release.

https://github.com/PaulStoffregen/Audio/issues

If anyone knows of a bug, or any API that needs to change, which isn't already on the tracker, please add another issue. My main concern is missing any significant known bugs before 1.0, so if there's anything missing, please create a new issue, so it's not forgotten.

Feature requests are ok too, but please use the "enhancement" label. The only new objects I'm considering for 1.0 are SPI flash play/record, analyze volume, and a "queue" record for passing data from the library to sketch.
 
Currently none of the play objects supports this.

If you or someone else (not me) wanted to work on this, slowing playback ought to be fairly simple. Just take the output data and repeat samples or use linear interpolation to lengthen the data size. Each update sends 128 samples, so you'll have to either read the correct number to be scaled up to 128, or keep some in a buffer between updates. It'll take some work coding, but not impossible. There's already linear interpolation code in several places within the library.

my idea for a naive approach was to just the synth_sine object and feed it the 512 blocks from the sd card through a ping pong buffer. as far as i can see that should yield some similar functionality to tabread4~, only that is using some 4 point interpolation scheme (floating point, of course):

https://github.com/soundcloud/pd-zexy/blob/master/src/tabread4~~.c

i'll report, but at the end of the day someone knowledgable will have to do this.
 
Try this:

Code:
#include <Audio.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>

AudioPlaySdWav     wav;
AudioOutputI2S     dac;

AudioConnection c1(wav, 0, dac, 0);
AudioConnection c2(wav, 1, dac, 1);

AudioControlSGTL5000 audioShield;

void setup() 
{
  Serial2.begin(9600);
  CORE_PIN9_CONFIG = PORT_PCR_MUX(6);
  CORE_PIN10_CONFIG = PORT_PCR_MUX(1);
  CORE_PIN26_CONFIG = PORT_PCR_MUX(3);
  CORE_PIN31_CONFIG = PORT_PCR_MUX(3);
  
  AudioMemory(50);
  audioShield.enable();
  audioShield.volume(0.7);

  SPI.setMOSI(7);
  SPI.setSCK(14);
  if (SD.begin(10)) {
    //wav.play("290MSG.WAV");
    wav.play("01_16M.WAV");
  }
}

void loop() 
{
}
OH HO! Tested and working, excellent customer service. Guys at work couldn't believe I got a response so quick or how fast the prototype is coming along. So much more powerful than all the Arduino chips, including DUE! I've used almost every chip in Arduino lineup quite extensively and made many custom boards. ARM w/ Teensy IDE is the way to go for a project like this, love it.
 
This is missing functionality, agreed. Need to have a pulse wave (variable mark/space ratio) , and also a pulse wave with an audio PWM input.

Square wave is a rather special case of a pulse; at exactly 50% mark/space it has no even harmonics. The other pulse types sound rather different. Audio frequency PWM is also a standard sound for synths.

Ideally there would also be a frequency modulation input on the waveform object as well.

I saw you added these as feature requests in GitHub, thank you. Paul, I believe only you or official contributors can add the "enhancement" label in GitHub.
 
I've committed a bunch of changes to the SGTL5000 control object, and updates to its documentation, hopefully to simplify things.

Can any help test the mic input and new micGain() function? I wrecked the 2 mics I bought earlier. Will get more on order today....

I dropped the signal routing stuff. I spent a couple hours today trying to write documentation for it. I kept writing about the concept there's only 3 useful ways to route the signals, the default (no audio processing), pre-processing before the data goes to Teensy, and post-processing after Teensy sends audio. Finally, I decided to just get rid of the ability to configure the hardware so directly, and I put the only useful ways into the processor enable/disable functions.
 
Status
Not open for further replies.
Back
Top