PDA

View Full Version : Audio Library



Pages : [1] 2 3 4 5 6

PaulStoffregen
01-05-2014, 02:37 AM
Edit: This URL is now the official Audio library page:

http://www.pjrc.com/teensy/td_libs_Audio.html

Much of the info on this thread is old, pre-1.0 stuff.

-----------------------------------------

Edit Again: This huge thread spans the pre-1.0 "beta" development of the Teensy Audio Library. Nearly all the issues mentioned were fixed before the 1.0 release. Most of the feature requests not implemented in 1.0 were put on the future development roadmap (http://www.pjrc.com/teensy/td_libs_AudioRoadmap.html).

This old thread will be closed, now that the stable 1.0 release has been made.

-----------------------------------------

Here is the initial beta release of the audio library!

https://github.com/PaulStoffregen/Audio

Please understand this is *beta* status code. It works well, but many features are missing or incomplete, and it's very likely to get incompatible changes as features develop.

Currently the only "documentation" is the examples, and of course the source code itself.

If you discover any bugs, please try to post a reproducible bug report with complete code and enough detail to reproduce the problem.

The Teensy3 Audio Board (http://www.pjrc.com/store/teensy3_audio.html), available and in stock as of Jan 3, 2014, is the primary hardware intended to be used with this library. It's also possible to use PWM output and ADC input.

http://www.pjrc.com/store/teensy3_audio_front.jpg http://www.pjrc.com/store/teensy3_audio_back.jpg

merlin
01-05-2014, 04:51 AM
Hi Paul

Thanks for all the hard work to make this available.

I cant find the "MemoryAndCpuUsage example" referred to in the examples code.

If the input is from the micro analog pin. it says...
// When AudioInputAnalog is running, analogRead() must NOT be used.

Also is it possible to stop the Audio library reading, to scan other ADC inputs, then restart the audio again?


Thanks

pburgess
01-05-2014, 06:40 AM
OMG OMG *awesomesplode*

nlecaude
01-05-2014, 09:56 AM
Ahh looks delicious.
I'm on tour for a month in Germany with a circus, wish I could play with it now... Is there a German distributor that will have it before the end of the month ?

Nantonos
01-05-2014, 10:50 AM
Here are a couple of introductions to the windowing functions (Hanning, BlackmanHarris, etc) used in FFT:
* Windowing: Optimizing FFTs Using Window Functions (http://www.ni.com/white-paper/4844/en/)
* Window function on Wikipedia (http://en.wikipedia.org/wiki/Window_function)
I found those useful while reading audio.h

mxxx
01-05-2014, 02:11 PM
i was just giving it a quick try (the SD wav example), the file opens alright but can't seem to get it outputting sound. i have a working setup (wm8731) with the old i2s/DMA library, the only difference being i have I2S0_TX_FS on pin 4 and I2S0_TXD0 on pin 3.

in the example sketch, i've changed audioShield to:


AudioControlWM8731 audioShield;

and in audio.cpp, i've changed, in AudioOutputI2S::begin :


//CORE_PIN22_CONFIG = PORT_PCR_MUX(6); // pin 22, PTC1, I2S0_TXD0

CORE_PIN3_CONFIG = PORT_PCR_MUX(6); // pin 3, PTC1, I2S0_TXD0

and in AudioOutputI2S::config_i2s :


//CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
//CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
//CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK

CORE_PIN4_CONFIG = PORT_PCR_MUX(6); // pin 4, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK

but that should be it, no?

thanks!

scswift
01-06-2014, 09:13 AM
Is it possible to use I2C0 to control another chip at the same time as you're playing audio without interfering with it?

I'd like to drive some leds while playing audio using this IC, without having to resort to breaking out the pins for the second I2C port on the underside of the teensy:
http://www.adafruit.com/products/1427

PaulStoffregen
01-06-2014, 09:48 AM
Yes, I2C (and lots of other common Arduino stuff) works great while audio is playing.

scswift
01-06-2014, 11:39 AM
Oh, cool, I see now... you used the Wire library. I've never used I2C so I didn't know it was a standard Arduino lib and I was concerned if I tried to write something to to the bus it would collide with data being sent in some interrupt.

How about SPI? Are there any precautions that need to be taken if you wish to use that? With the WaveHC lib I had to disable interrupts before I attempted to access the card to see if the next file I wanted to play existed before stopping the current one. I'm sure that particular issue doesn't exist with your library, but what if I were to connect another SPI device to the bus? Could I safely transfer data to it? It appears the Due added some new SPI functions for handling multiple slaves but does that all just work when an interrupt calls it while you're in the middle of a transfer?

MichaelMeissner
01-06-2014, 01:20 PM
Speaking of I2C and the Audio add-on shield, does the add-on board have the 4.7K pull-up resistors for I2C? If so, if I have other I2C devices, would I still need my own pull-up resistors? If it doesn't have the resistors, I assume we would need to add them to talk to the audio components?

PaulStoffregen
01-06-2014, 05:07 PM
Speaking of I2C and the Audio add-on shield, does the add-on board have the 4.7K pull-up resistors for I2C?

Yes, it has 2K pullup resistors on SDA and SCL.



If so, if I have other I2C devices, would I still need my own pull-up resistors?


The 2K resistors on the audio board ought to be plenty if you connect other I2C devices.


I should mention the actual audio data is sent using I2S, which is entirely different than I2C, even though the first 2 letters are the same.

(edited, typo fixed)

I2C uses 2 signals: SDA and SCL.

I2S uses 5 signals: 3 clocks (LRCLK, BCLK, MCLK) and 2 data pins (TX, RX).


The I2C and I2S ports are completely independent and can work simultaneously.

PaulStoffregen
01-06-2014, 05:12 PM
With the WaveHC lib I had to disable interrupts before I attempted to access the card to see if the next file I wanted to play existed before stopping the current one. I'm sure that particular issue doesn't exist with your library, but what if I were to connect another SPI device to the bus?

Actually, this same issue exists when you are playing a .WAV file. The library is regularly using the SPI port to talk to the SD card.

You can use the SPI port while audio is played from other sources, like streaming the input to output (possibly through other objects that modify the data). The audio library doesn't use the SPI port for normal operations, but it does need it for accessing the SD card.




Could I safely transfer data to it? It appears the Due added some new SPI functions for handling multiple slaves but does that all just work when an interrupt calls it while you're in the middle of a transfer?

Perhaps a future version will provide a way to negotiate access to the SPI while playing from the SD card. But that is quite tricky. If the audio library needs to obtain another block of audio from the SD card, it really can't wait.

PaulStoffregen
01-06-2014, 05:14 PM
i was just giving it a quick try (the SD wav example), the file opens alright but can't seem to get it outputting sound.

You might check your .wav file. Currently the library only supports 16 bit, 44.1 kHz MONO wav files. I'll add support for stereo format and upconverting some of the lower sample rates. But in this first beta release, only the simplest format is supported.

mxxx
01-06-2014, 06:22 PM
You might check your .wav file. Currently the library only supports 16 bit, 44.1 kHz MONO wav files. I'll add support for stereo format and upconverting some of the lower sample rates. But in this first beta release, only the simplest format is supported.

i think it must be something else, the 440 hz sine example (miditone) doesn't work either, unfortunately. i'm trying to figure out what's going on, but can't seem to figure out what "AudioConnection" is doing, or where it's coming from?

MuShoo
01-07-2014, 07:26 AM
I2S uses 2 signals: SDA and SCL. I2S uses 3 clocks (LRCLK, BCLK, MCLK) and 2 data pins (TX, RX). The I2C and I2S ports are completely independent and can work simultaneously.

Normally I wouldn't point out minor mistakes like this, but I've never used I2S or I2C before and I'm not sure which is which from this...

scswift
01-07-2014, 09:08 AM
How does the audio mixing work? I see an audio mix function that appears to mix up to four channels. The output right now is mono, correct? So that would be four mono 44,100hz wav files you can play at once?
What happens if you attempt to play another file if four are already playing? Does it simply not play?

I presume for now, the volume of each channel is simply divided by 4 before being output? It would be nice eventually to have automatic gain control. Compression would be nice too.

scswift
01-07-2014, 09:10 AM
Normally I wouldn't point out minor mistakes like this, but I've never used I2S or I2C before and I'm not sure which is which from this...

What he meant to say was:
"I2C uses 2 signals: SDA and SCL. I2S uses 3 clocks (LRCLK, BCLK, MCLK) and 2 data pins (TX, RX)."

PaulStoffregen
01-07-2014, 09:47 AM
Opps, yeah, bad typo. I edited the post above.

I2C is a fairly slow bus (100 or 400 kbps) used to control chips. The audio board uses it to let you control settings, like the output volume, which input is selected (line in vs mic), and lots of other optional features. I2C is a "bus", meaning many different chips can be connected to the same 2 wires. You can connect other I2C chips, as long as they don't use the same address. Communication on I2C is in bursts, typically short messages, with the 2 wires idle between messages.

I2S is a fairly fast (1.4 Mbit/sec) digital audio protocol. Unlike I2C, I2S is meant as a point-to-point protocol. The BCLK (bit clock) signal indicates when each TX and RX signals has a new bit of data. The LRCLK (left-right clock) signal indicates which bits are the actual 16 bit words. When LRCLK is high, the left channel is sending, and when it's low, the right is sending (actually, there's a 1 clock delay.... minor details). MCLK (master clock) technically isn't part of I2S. It's simply a clock at 256 times the speed of LRCLK, or 11.29 MHz. Almost all audio chips need MCLK.

PaulStoffregen
01-07-2014, 10:50 AM
How does the audio mixing work? I see an audio mix function that appears to mix up to four channels.


Yup, pretty much just like that, it accepts up to 4 inputs. The streams are simply added together and output.



The output right now is mono, correct?


The WAV file player currently only parses mono WAV files. I'll add stereo parsing soon.

But the library itself supports any number of streams, within CPU and memory limits.



So that would be four mono 44,100hz wav files you can play at once?


Yes, if your SD card (and the SD library) can keep up.

In 1.18-rc2, I added a speedup to the SD library. So far, I haven't retested its performance with the audio stuff. But before the speedup, I was seeing about 40% CPU usage for reading a single mono stream from the SD card, mostly due to slowness in the SD library.



What happens if you attempt to play another file if four are already playing? Does it simply not play?


Each instance of the WAV player is independent. If you create 4 WAV player objects and decide you want to play a 5th file, how you do that is up to you. If your code has only created 4 of those objects, obviously you're going to need to stop one of them when you want to start playing a 5th file. The reason the limit is 4 is only because you created 4 independent objects.

Of course, you could just create 5 WAV player objects. The mixer supports only 4 inputs, but don't let that stop you! Just create 2 mixes and feed the output of one into the input of another, and now you can mix 7 channels!



I presume for now, the volume of each channel is simply divided by 4 before being output?


Oh, looks like I commented out the gain function. I'll put that back in. Remember, this stuff is very "beta" right now.....

The mixer does support variable gain on each channel. It defaults to a gain of 1 on each channel. If your mix exceeds the maximum level, the output clips, just like real audio gear (well, except this is digital audio, so there's a hard clipping limit rather than a lot of extra headroom like you'd find on analog gear).



It would be nice eventually to have automatic gain control. Compression would be nice too.

Pull requests are welcome!

In a week or two, I'll write up a how-to guide about creating your own audio effects. But here's a start.....

Start by finding a similar object in Audio.h and copy its definition, and of course change the name. All objects much inherit from AudioStream. If your object has inputs, you must have "audio_block_t *inputQueueArray[num]" in the private section, where "num" is the number of inputs. Your constructor must initialize AudioStream with the number of inputs and that array. You also must have the virtual update() function in your public section. Everything else is optional... just add private variables as needed for whatever you want to do, and add public functions for whatever stuff you want to be accessible from the Arduino sketch. The simplest way to get this "boilerplate" stuff right is to copy an object definition that already works. Maybe I ought to publish a template example?

In your actual code, the only function you must implement is update(). If you don't, the compiler will give a rather unhelpful vtable error.

If your object gets input, in update() you'll call receiveReadOnly(channel) or receiveWritable(channel) to acquire any incoming audio. The library allows shared memory, so receiveReadOnly() is more efficient in if the same audio is fed to other objects. Each input can only source 1 block, so you only need to call either of these one for each input channel. As the name suggests, you choose which function depending on whether you will modify the contents of the audio data. The mixer object, for example, calls receiveWritable() for the first block it acquires, and then receiveReadOnly() for any others. These functions can and do return NULL if there is no input, so you must check for NULL and treat the input as silent.

If you need to create audio, or you just need another block to fill, call allocate(). It too can return NULL, so you must check.

When you obtain audio from allocate(), receiveReadOnly() or receiveWritable(), you own it. You must call release() to free the memory. If you need to buffer audio, you can store block pointers in your object's private variables and use them on future update() runs, but this consumes the precious audio memory, so only keep blocks allocated if you really must.

The pointers you get, type "audio_block_t *" are a struct with 1 member you're mean to access: "data", which is an array of 128 int16_t's with the actual audio data. The struct has a couple other members which you should not touch. If you obtained the block with receiveReadOnly(), do not write to the data[] array. But you still must call release() when you're done with that block.

Once you've got your input blocks, plus any new blocks you need, do whatever your object will do. If you're modifying audio, you'll probably obtain the inputs as writeable blocks and change their data[] arrays directly. If you're synthesizing something, you'll probably get new blocks with allocate() and fill them up. Of course, you can do anything you want, within the limits of your programming skill and the available memory and CPU power.

If your object has outputs, call transmit(block, channel) to send audio out. It's ok to transmit the same block to multiple outputs. You still own that block, even after transmit. You must always call release() for every block you obtain with receiveReadOnly(), receiveWritable() or allocate().

For example, if you wanted to create automatic gain control, you might have 1 input and 1 output. You'd call receiveWritable(), then perhaps compute an average by just summing up the samples (inverting the negative ones, of course). Or maybe you'd square them and add them all up? Then you might adjust your gain slightly if the average is above or below the target, which of course would be a private member variable, and multiply all 128 samples by the gain setting. Then just transmit and release the block.

Every objects gets simple CPU usage tracking (but I need to publish an example), so you can test how much CPU you're using. The Cortex-M4 DSP optimizations can really help. In this AGC example, to compute the average, you could fetch the input samples 2 at a time using a 32 bit pointer. The M4 optimizes multiple reads in a row, where a normal 32 bit read takes 2 cycles, but subsequent back-to-back reads take only 1 more cycle. So you could fetch 8 inputs in only 5 cycles. The DSP instructions feature a dual 16x16 multiply-and-accumulate (allowing inputs from separate halves of 32 bit registers), so you could square and sum each pair of inputs in just 1 cycle. The average could be computed in just 144 cycles without looping overhead. To keep things in perspective, ALL the update functions for all audio objects must complete in under 278528 cycles. The CPU usage functions can tell you what fraction of that total you're using up.

Likewise, the AudioMemory() function at the beginning of setup() creates the pool of memory which provides all audio_block_t data. There are functions to query the current and worst case usage, so you can tell if you've got memory issues. But if you receive or allocate audio blocks and fail to release them, you'll quickly run out of memory and the entire system will go silent, so memory leaks are pretty obvious.

Objects that actually move data on or off the chip have some other requirements, which are what causes every update() function to actually run every 2.9 ms. But you don't need to worry about those if your object runs entirely on-chip, using only receiveReadOnly(), receiveWritable(), allocate(), transmit() and release().

PaulStoffregen
01-07-2014, 02:22 PM
I've added support for stereo format WAV files. They must be 16 bit, 44.1 kHz, but now either mono or stereo should play.

The 1.18-rc2 SD library speedup is working. WAV file playing now consumes approx 12% CPU, down from 40% before the speedup.

scswift
01-07-2014, 03:18 PM
4x speedup! Not bad.

Is that for a single wav file? Mono or stereo?

mxxx
01-07-2014, 06:20 PM
further re using this with wm8731.

i've got the SD card example working now. strangely, it plays the file only if/once i open the serial monitor? the sine example still doesn't work for me.

as an aside, using AudioControlWM8731 gives me:

error: cannot declare variable 'codec' to be of abstract type 'AudioControlWM8731'

so i added


bool disable(void) { return false; }

in class AudioControlWM8731 : public AudioControl , which seemed to be missing?

PaulStoffregen
01-07-2014, 07:22 PM
Sorry, that WM8731 code probably has a lot of bugs. I switched to the SGTL5000 fairly early in the library's development and haven't tested with the WM8731 since.

Pull requests are welcome....

PaulStoffregen
01-07-2014, 07:34 PM
4x speedup! Not bad.

Is that for a single wav file? Mono or stereo?

Yes, that's a single WAV file.

The library runs every 2.9 ms, which is 128 audio samples. While playing a mono WAV file, it ends up using 12% every other update and only 1% on the other half. While playing stereo, it uses 12% every time.

It would be ideal if the usage were 6% every time on mono mode, but that's simply not how the SD library works. It reads the SD card 512 bytes at a time, since the card has 512 byte sectors. In mono mode, one 512 byte sector provides 256 audio samples, which is enough for 2 update cycles, so the WAV player object calls the SD library every other update. In stereo mode, every update consumes an entire 512 byte sector.

mxxx
01-07-2014, 08:24 PM
Sorry, that WM8731 code probably has a lot of bugs. I switched to the SGTL5000 fairly early in the library's development and haven't tested with the WM8731 since.

Pull requests are welcome....

no worries, i don't think it's anything essential. sounding very nice, in fact. thank you very much.

i'm still puzzling over SD card example though, and must be missing something obvious? i'm simply trying to loop a file but it's only ever playing once. then things stop entirely:


void loop() {

Serial.println("play");
wav.play("1.wav");
delay(4000);
wav.stop();
Serial.println("stop");
delay(20);
}

the rest is essentially playwavfromsdcard

PaulStoffregen
01-07-2014, 08:59 PM
i'm simply trying to loop a file but it's only ever playing once. then things stop entirely:

Opps, that's a bug. I just pushed a fix to github.

https://github.com/PaulStoffregen/Audio

PaulStoffregen
01-08-2014, 01:06 AM
I've just committed a change to add an AudioSynthWaveform object, which can synthesize any arbitrary waveform. The library defines 4 waveforms: sine, square, triangle and sawtooth. This replaces AudioSineWave, which could only do sine waves.

You can use the object like this:



AudioSynthWaveform myTone(AudioWaveformSine);


This creates an object with 1 output, which you can feed into mixers, directly to the I2S or PWM, or any other object that accepts input.

The parameter "AudioWaveformSine" is an array of 257 integers. The waveform is 256 integers long, plus a duplicate of the first value. This 257th number is needed because AudioSynthWaveform uses DDS with linear interpolation between samples. You can pass any array of 257 int16_t's, representing 1 period of the waveform, and AudioWaveformSine will synthesize the waveform at any frequency. Sub-audible frequencies are possible, so this can be used to create control waveforms... for a number of interesting objects that don't exist yet, but they're coming soon.

These objects have 2 functions you can call, amplitude(value) and frequency(value). The amplitude expects a float between 0 to 1.0, and frequency expects a number between 0 to 22050.

adrianfreed
01-08-2014, 02:15 AM
For those tempted to use these waveshapes for sound synthesis, you will notice aliasing problems at high frequencies. Solutions are well known but unfortunately
rather complicated: http://www.musicdsp.org/files/bandlimited.pdf

PaulStoffregen
01-08-2014, 09:02 AM
I just checked the waveforms on a scope, and yes indeed, there are problems at higher frequency. Visually, it looks like the amplitude is somewhat unstable, but if I get the triggering just right, it's clearly some low frequency content superimposed on the waveform. :(

Looks like I have quite a bit more work to do on this......

Edit: Maybe instead of a fixed table, these objects will need to take a reference to an object that's a collection of tables to be used in different frequency ranges.

mxxx
01-08-2014, 09:20 AM
I just checked the waveforms on a scope, and yes indeed, there are problems at higher frequency. Visually, it looks like the amplitude is somewhat unstable, but if I get the triggering just right, it's clearly some low frequency content superimposed on the waveform. :(

Looks like I have quite a bit more work to do on this......

Edit: Maybe instead of a fixed table, these objects will need to take a reference to an object that's a collection of tables to be used in different frequency ranges.

yep, this question came up on the old audio thread. i was wondering too whether, say, a bandlimited saw or pulse is worth the trouble when the purpose of AudioSynthWaveform is to do arbitrary waveforms? so some reduced harmonics/wavetable-per-octave scheme seems more practical/less complicated?

Nantonos
01-08-2014, 09:32 AM
.
Edit: Maybe instead of a fixed table, these objects will need to take a reference to an object that's a collection of tables to be used in different frequency ranges.

That is the usual way to handle this, for example one table per octave (and is probably easier than taking one largest table and then computing the bandlimited subtables in the setup function). The tables become progressively smaller for higher frequencies so the total space needed for table storage is around twice that of a single, non-bandlimited table.

While this improves the aliasing, there is still a discontinuity on a frequency sweep on passing from one table to the next. The next refinement is therefore to interpolate between a pair of tables (taking progressively more from the 'upper octave' table as the note moves through the octave).

Nantonos
01-08-2014, 10:31 AM
Some additional resources that may be useful:
* 'Adventure Kid' single-cycle waveforms (http://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/). These use a CC-BY-3.0 (Creative Commons) license.
* Cross-platform morphing Wavetable maker (zip file) (http://mungo.com.au/Doc/Wavetable.zip) requires SciLab (http://www.scilab.org/download/) to run.
* Band-Limited Sound Synthesis (http://slack.net/~ant/bl-synth/) (lecture notes and sample C++ code)
* Stilson & Smith Alias-Free Digital Synthesis of Classic Analog Waveforms (https://ccrma.stanford.edu/~stilti/papers/blit.pdf) (pdf)
* Bandlimited Synthesis (http://www.music.mcgill.ca/~gary/307/week5/bandlimited.html) (lecture notes from McGill University)
The tablemaker is intended for the Mungo g0 (http://mungo.com.au/euro.html) wavetable oscillator but saves the morphed wavetable as a mono 16bit .wav file. The lecture notes are specific to bandlimited square wave synthesis, and seem to advocate a buffer of sample differences rather than a buffer of samples. Stilson & Smith argue for bandlimited impulse trains (BLIT) which is also where the McGill lecture notes end up.

mxxx
01-08-2014, 11:14 AM
Some additional resources that may be useful:

* Cross-platform morphing Wavetable maker (zip file) (http://mungo.com.au/Doc/Wavetable.zip) requires SciLab (http://www.scilab.org/download/) to run.


what's the mungo scilab thing outputting? just "morphing" wavetables? or bandlimited ones?

at any rate, if this was going down that route, some "mip map" wavetable maker would come in handy. fwiw, when playing around with the old dma/i2s library, i created wavetables using audioterm http://dl.dropbox.com/s/1y0d88ts8zy1um7/Audio-Term.zip, which lets you edit+export single cycle wave forms as a .wav and then ran a processing sketch to generate the band-limited versions. audio-term is windows only unfortunately, but runs ok in wine.

Nantonos
01-08-2014, 08:53 PM
European folks, Pieter Floris has the audio board in stock at 17.50 as of 20 minutes ago.
http://floris.cc/shop/en/8-teensy
Also the 14-pin double height insulator things which I didn't notice in the rush to order the board :)

Nantonos
01-08-2014, 08:56 PM
mxxx I don't know, I stopped at the 'will need to install SciLab, do that later' step. I don't actually have any of his modules though I hear they are good.
Got a link to the processing sketch that does band-limiting? (Presumably it makes a continuous version then re-samples?)

cartere
01-08-2014, 10:41 PM
I have been looking a the doc trying to figure out the best way to mute an input and not having much luck. Will the mixer at some point have a gain control?

adrianfreed
01-09-2014, 04:42 AM
I just checked the waveforms on a scope, and yes indeed, there are problems at higher frequency. Visually, it looks like the amplitude is somewhat unstable, but if I get the triggering just right, it's clearly some low frequency content superimposed on the waveform

What is happening is high frequencies harmonics are being folded down in the wrong place in the spectrum. If your scope has an FFT mode it will guide an intuition about this.

The "arbitrary waveform generator" is never quite in reach. We always have bandwidth and noise limitations. A true sawtooth and square wave alas
are impossible to make.

mxxx
01-09-2014, 10:23 AM
mxxx I don't know, I stopped at the 'will need to install SciLab, do that later' step. I don't actually have any of his modules though I hear they are good.
Got a link to the processing sketch that does band-limiting? (Presumably it makes a continuous version then re-samples?)

i see. neither do i (for obvious reasons. not that i would mind owning a bunch), so i couldn't be bothered either. as to the sketch, i can pm it, it's just something i hacked up though. it takes a single cycle .wav runs an fft, removes the upper partials, then ifft. and so on, recursively.

Tomsim
01-09-2014, 10:50 AM
Hi Paul,

I tried your instruction on creating my own audio stream worker object but for some reason, I'm not getting any update call in the audio stream. Attached is my code. Right now it doesn't do much. I just want to see if I can get some audio sample data.

Any help is greatly appreciated.
Tomsim
1274

PaulStoffregen
01-09-2014, 11:05 AM
Opps, looks like there's a bug in AudioInputAnalog. All input and output objects are supposed cause the updates. To be honest, I've never tested AudioInputAnalog alone... every test I've done so far used it together with at least one other input or output object, so I never caught this issue.

I'll work on a fix soon.

In the meantime, a simple workaround is to just add an unused PWM output object, like this:



AudioOutputPWM pwmOutput;


You don't need to make any connection to it. Just having it will cause updates to happen as they should.

I'll fix AudioInputAnalog, but at the moment I'm working on converting WAV and playing clips from flash, so it might be a few days.

Also, you might want to make outputflag volatile. It may not matter now, but it's a possible issue (and something future docs ought to mention) since the updates occur at low priority interrupt context.

Nantonos
01-09-2014, 12:56 PM
The "arbitrary waveform generator" is never quite in reach. We always have bandwidth and noise limitations. A true sawtooth and square wave alas
are impossible to make.

True, in theory they require infinite bandwidth up to radio frequencies. In practice, "very good" and "indistinguishable for all practical purposes from mathematically ideal" square, triangle and saw are perfectly possible when limited to the audio bandwidth.

Nantonos
01-09-2014, 12:59 PM
it takes a single cycle .wav runs an fft, removes the upper partials, then ifft. and so on, recursively.
I'm wondering, ARM math has fft does it have ifft too? Maybe possible to do that processing on the teensy itself (not in real time, but it could write the results to flash or SD for later use).

PaulStoffregen
01-09-2014, 01:13 PM
What's the best way to convert from a lower sample rate to 44.1 kHz? Or what's the fastest, or best trade-off?

I'm working on playing slower sample rate WAV files, like 8 kHz sample rate. Right now, I'm working on simply playing some samples 5 times, others 6 times, to at least get _something_ working.

Surely there must be better ways. A couple quick searches have turned up a lot of stuff about fairly complex filtering....

mxxx
01-09-2014, 01:37 PM
as to resampling, afaik, this is popular and fixed point*: http://www.mega-nerd.com/SRC/index.html

and, though i know that's not exactly what you're after, there's a few pd objects which can deal with arbitrary sample rates and have some interpolations going on (personally, i was wondering whether the AudioPlaySDcardWAV couldn't be expanded in the direction)

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

susloop~
https://github.com/pd-l2ork/pd/blob/master/externals/bsaylor/susloop~.c

xgroove~
https://github.com/pd-l2ork/pd/blob/master/externals/grill/trunk/xsample/source/groove.cpp

*edit: no it's not. resample_1.8 is, though: https://github.com/S25RTTR-Aux/s25rttr/tree/master/s-c/resample-1.8.1

scswift
01-09-2014, 02:27 PM
What's the best way to convert from a lower sample rate to 44.1 kHz? Or what's the fastest, or best trade-off?

I'm working on playing slower sample rate WAV files, like 8 kHz sample rate. Right now, I'm working on simply playing some samples 5 times, others 6 times, to at least get _something_ working.

Surely there must be better ways. A couple quick searches have turned up a lot of stuff about fairly complex filtering....


I would go with linear filtering. Nearest neighbor as you are suggesting is unlikely to sound good.

Tween = (DestSample / 44khz) * SourceSampleRate

So for a 22khz file, sample 0 of the destination would be sample 0 of the source. Sample 1 would be sample 0.5. Sample 2 would be sample 1. Sample 3, sample 1.5...

If you can do this with fixed point math then you should be able to toss out the integral part of the number and keep the fraction easily and then use that to interpolate between samples 0 and 1 of the source to calculate sample 1 in the destination.

Ie:

A = Source sample 0
B = Source sample 1
DestSample = (B-A)*TweenFraction

This won't work if you want to scale something down from above 44khz of course. Then you'd have to average multiple samples together, weighting them if some are partially outside the window the destination sample covers.

(There's probably a better way to do the math above that doesn't need to store the integral part of your result. Perhaps you can take advantage of overflow to leave behind only the last 8 or 16 bits of the result and use that to do your calculation.)

scswift
01-09-2014, 02:37 PM
I found this question was asked on Stackoverflow:
http://stackoverflow.com/questions/1125666/how-do-you-do-bicubic-or-other-non-linear-interpolation-of-re-sampled-audio-da

I don't know what sinc-interpolation is but one person suggested it would sound better than linear and they have an implementation of it here in resample.h:
http://www.cockos.com/wdl/

scswift
01-09-2014, 02:40 PM
Found this page explaining sinc:
http://www.st-andrews.ac.uk/~www_pa/Scots_Guide/iandm/part7/page3.html

Nantonos
01-09-2014, 04:54 PM
What's the best way to convert from a lower sample rate to 44.1 kHz? Or what's the fastest, or best trade-off?

I'm working on playing slower sample rate WAV files, like 8 kHz sample rate. Right now, I'm working on simply playing some samples 5 times, others 6 times, to at least get _something_ working.

That will work, but badly. Its the audio equivalent of nearest-neighbour interpolation for images, so gives ugly blocky results with increased noise and with aliasing caused by the non-bandlimited, regularly-spaced square steps you are introducing.

Simple linear interpolation will give much better results. Not correct, since the peicewise linear representation has sharp transitions where the edges join, but way better than just repeating samples.


Surely there must be better ways. A couple quick searches have turned up a lot of stuff about fairly complex filtering....
Yes, the proper way resamples it to the analog domain as a continuous waveform then resamples it.

PaulStoffregen
01-09-2014, 07:59 PM
I'm trying for linear interpolation today, pretty much like this at the moment.



} else if (rate == 22050) {
int16_t s0, s1, s2, s3, s4;
s0 = prior;
if (bits == 8) {
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 8) {
tmp32 = *in++;
s1 = (int8_t)((tmp32 >> 0) & 255) << 8;
s2 = (int8_t)((tmp32 >> 8) & 255) << 8;
s3 = (int8_t)((tmp32 >> 16) & 255) << 8;
s4 = (int8_t)((tmp32 >> 24) & 255) << 8;
*out++ = (s0 + s1) / 2;
*out++ = s1;
*out++ = (s1 + s2) / 2;
*out++ = s2;
*out++ = (s2 + s3) / 2;
*out++ = s3;
*out++ = (s3 + s4) / 2;
*out++ = s4;
s0 = s4;
}


This approach requires keeping only 1 prior sample, but 2 or 3 prior samples could be kept. Those 8 output lines could become simple polynomials implementing a short FIR filter. But what equations to use? Adrian, any suggestions?

I'm also still open to any suggestions for playing 8 kHz sampled files....

jstsch
01-09-2014, 09:04 PM
You're creating quite the audio library :) I would be happy already with only the capability to read samples from flash, audio in and pushing samples out... if you get into the territory of synthesis using BLIT's, SINC-interpolation and filters you could be diving into a new life's work :)

As for upsampling from low sample rates, a simple linear interpolation would suffice (very similar as you would need in a wavetable). The lower the sample rate of the source file, the less blocks you would need available. It does mean that when using linear interpolation samples repeat, but they slowly 'fade' from the previous to the next. I've never thought about it in the context of low-memory devices and arbitrarily large sound files, but I think it would mean that it would be beneficial for the the routine fetching blocks from disk to know the sample rate of the material.

Cheers! My audio board should arrive tomorrow... very exciting! :)

MickMad
01-10-2014, 12:31 AM
Hey there, I've just tested the audio lib with the Mikroe proto board and the new PlayMidiTones sketch (which actually just plays sinewaves).

While reading through the code I discovered that the lib assumes that the ADC/DAC/CODEC on the I2S channel is in Slave mode, so I've added a #define and some #if blocks in the AudioI2S*put classes code to check wether the MCLK signal is external or internal and to set the appropriate registers to signal that.

in Audio.h, line 3

#define MCLK_IS_EXTERNAL 1

in Audio.h, line 337

bool disable(void) { return false; }

in Audio.cpp, line 631


void AudioOutputI2S::config_i2s(void)
{
SIM_SCGC6 |= SIM_SCGC6_I2S;
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;

// if either transmitter or receiver is enabled, do nothing
if (I2S0_TCSR & I2S_TCSR_TE) return;
if (I2S0_RCSR & I2S_RCSR_RE) return;
#if MCLK_IS_EXTERNAL
// Select input clock 0
// Configure to input the bit-clock from pin, bypasses the MCLK divider
I2S0_MCR = I2S_MCR_MICS(0);
I2S0_MDR = 0;
#else
// enable MCLK output
I2S0_MCR = I2S_MCR_MICS(3) | I2S_MCR_MOE;
I2S0_MDR = I2S_MDR_FRACT(1) | I2S_MDR_DIVIDE(16);
#endif
// configure transmitter
I2S0_TMR = 0;
I2S0_TCR1 = I2S_TCR1_TFW(1); // watermark at half fifo size
I2S0_TCR2 = I2S_TCR2_SYNC(0) | I2S_TCR2_BCP;
#if !MCLK_IS_EXTERNAL
I2S0_TCR2 |= I2S_TCR2_MSEL(1) | I2S_TCR2_BCD | I2S_TCR2_DIV(3);
#endif
I2S0_TCR3 = I2S_TCR3_TCE;
I2S0_TCR4 = I2S_TCR4_FRSZ(1) | I2S_TCR4_SYWD(15) | I2S_TCR4_MF
| I2S_TCR4_FSE | I2S_TCR4_FSP;
#if !MCLK_IS_EXTERNAL
I2S0_TCR4 |= I2S_TCR4_FSD;
#endif
I2S0_TCR5 = I2S_TCR5_WNW(15) | I2S_TCR5_W0W(15) | I2S_TCR5_FBT(15);

// configure receiver (sync'd to transmitter clocks)
I2S0_RMR = 0;
I2S0_RCR1 = I2S_RCR1_RFW(1);
I2S0_RCR2 = I2S_RCR2_SYNC(1) | I2S_TCR2_BCP;
#if !MCLK_IS_EXTERNAL
I2S0_RCR2 |= I2S_RCR2_MSEL(1) | I2S_RCR2_BCD | I2S_RCR2_DIV(3);
#endif
I2S0_RCR3 = I2S_RCR3_RCE;
I2S0_RCR4 = I2S_RCR4_FRSZ(1) | I2S_RCR4_SYWD(15) | I2S_RCR4_MF
| I2S_RCR4_FSE | I2S_RCR4_FSP | I2S_RCR4_FSD;
#if !MCLK_IS_EXTERNAL
I2S0_RCR4 |= I2S_RCR4_FSD;
#endif
I2S0_RCR5 = I2S_RCR5_WNW(15) | I2S_RCR5_W0W(15) | I2S_RCR5_FBT(15);

// configure pin mux for 3 clock signals
CORE_PIN23_CONFIG = PORT_PCR_MUX(6); // pin 23, PTC2, I2S0_TX_FS (LRCLK)
CORE_PIN9_CONFIG = PORT_PCR_MUX(6); // pin 9, PTC3, I2S0_TX_BCLK
CORE_PIN11_CONFIG = PORT_PCR_MUX(6); // pin 11, PTC6, I2S0_MCLK
}

in Audio.cpp, line 1684


bool AudioControlWM8731::enable(void)
{
Wire.begin();
delay(5);
//write(WM8731_REG_RESET, 0);
#if MCLK_IS_EXTERNAL
write(WM8731_REG_INTERFACE, 0x42); // I2S, 16 bit, MCLK master
#else
write(WM8731_REG_INTERFACE, 0x02); // I2S, 16 bit, MCLK slave
#endif
write(WM8731_REG_SAMPLING, 0x20); // 256*Fs, 44.1 kHz, MCLK/1


in Audio.cpp, line 1702

write(WM8731_REG_POWERDOWN, 0x00); // codec powerdown

This last line is important, without it the WM8731 wouldn't start properly when activated.

It works, though there was a missing mysine.amplitude() call in the demo sketch and I lost half an hour just to find that :D The pins are exactly the same as the Audio Adaptor, minus MCLK which is not connected:

Audio Codec PROTO SCK -> Teensy 9 (I2S0_BCLK)

Audio Codec PROTO ADCL -> Teensy 12 (I2S0_RX_FS)
Audio Codec PROTO MISO -> Teensy 13 (I2S0_RXD0)

Audio Codec PROTO MOSI -> Teensy 22 (I2S0_TXD0)
Audio Codec PROTO DACL -> Teensy 23 (I2S0_TX_FS)

I've also noticed that the output frequency is slightly higher than the 440 Hz it should output.

I still gotta test the codec in input, will let you know.

I will probably make a pull request tomorrow, is it okay? :D

PaulStoffregen
01-10-2014, 01:27 AM
The trouble with MCLK as an input is the timing won't necessarily be synchronous with the CPU. It's probably not a big deal if you don't use another other input or output objects that move audio on/off chip. But if you do, things can drift out of sync. I'm not even sure what will happen... if the result might be as benign as infrequent 3ms dropouts, or if the whole thing might crash hard?

PaulStoffregen
01-10-2014, 02:07 AM
I've been working on converting 11025 and 22050 Hz samples by linear interpolation. It seems to be working pretty well, but admittedly I've only been listening pretty casually, not comparing to the 44100 version (yet). I have one minor bug to fix in the utility that converts WAV to C arrays you can drop into your sketch, and then I'm going to commit this to github.

8000 Hz is the real problem. Lots of WAV files are 8 kHz sampled. Especially for embedded them into the extra on-chip flash memory, you get a lot more playback time, but of course with reduced bandwidth.

This is probably going to be a bit controversial, but my current plan is to convert every 2 samples at 8000 Hz to 11 samples at 44000 Hz (using linear interpolation), and then feed that wrong-rate 44000 Hz data out at 44100 Hz. That's a 0.2% error. 8000 Hz files (stored in flash) will actually play at 8018 Hz. It's not technically correct, but this is a trade-off in programming time, to let me publish something that will work ok for most people who just want to play sound effects with easy conversion from already-made WAV files, and let me move on to other parts of the library.

Anyone who feels strongly about correct pitch can convert their data to 8018 Hz. Or if they _really_ feel strongly, of course well designed pull requests are always welcome.

PaulStoffregen
01-10-2014, 02:09 AM
Currently I've been working with 6 small WAV files, which will be embedded in a new example program.

If anyone has any favorite (short) audio clips that are freely sharable, please post them. My goal is to keep the entire collection of WAVs in the example to about 100K, so the code can compile for either Teensy 3.0 or 3.1.

Tomsim
01-10-2014, 08:23 AM
Thanks Paul. I added unused PWM output object per your suggestion and I can get audio sample data now using AudioInputAnalog. Next, I'll try AudioInputAnalog with your FFT example.

mxxx
01-10-2014, 11:48 AM
I'm trying for linear interpolation today, pretty much like this at the moment.



} else if (rate == 22050) {
int16_t s0, s1, s2, s3, s4;
s0 = prior;
if (bits == 8) {
for (i=0; i < AUDIO_BLOCK_SAMPLES; i += 8) {
tmp32 = *in++;
s1 = (int8_t)((tmp32 >> 0) & 255) << 8;
s2 = (int8_t)((tmp32 >> 8) & 255) << 8;
s3 = (int8_t)((tmp32 >> 16) & 255) << 8;
s4 = (int8_t)((tmp32 >> 24) & 255) << 8;
*out++ = (s0 + s1) / 2;
*out++ = s1;
*out++ = (s1 + s2) / 2;
*out++ = s2;
*out++ = (s2 + s3) / 2;
*out++ = s3;
*out++ = (s3 + s4) / 2;
*out++ = s4;
s0 = s4;
}


This approach requires keeping only 1 prior sample, but 2 or 3 prior samples could be kept. Those 8 output lines could become simple polynomials implementing a short FIR filter. But what equations to use?

pd tabread4~, for example, defaults to 4 point polynomial / Lagrange (-1, 0 ,1 , 2), so something like this might be worth considering then (ie, if 3 samples could be kept)? will "rate" be an user-supplied argument or is it deriving from the file header?

PaulStoffregen
01-10-2014, 05:39 PM
At the moment I'm parsing the WAV file's header and packing the audio into the array with the first 8 bytes as a header, indicating the sample rate, number of bits, and length.

Today I'm reconsidering the number of bits. I've been doing more listening tests with quality headphones, and I'm just not happy with 8 bits. It sounds terrible on my PC and on Teensy3. Some clips encode ok, but many end up with considerable "hiss" noise.

This morning I tried 8 but mu-law encoding, so far only on my PC, and it sounds a LOT better. Since the files get translated with code I'm writing, I'm considering switching from linear 8 bit to mu-law. It will add another day of coding, but in the end I believe it will really help make sound clip playing projects sound much better with the limited 256K flash.

scswift
01-10-2014, 08:37 PM
What's this 8 bit stuff about? I thought the library played 16 bit wavs. Does it downconvert them to 8 bit or are you just talking about a way to get more out of 8 bit clips stored in flash?

PaulStoffregen
01-11-2014, 03:05 PM
I've uploaded the latest Audio library code to Github.

https://github.com/PaulStoffregen/Audio

There's now a PlayFromSketch example with 6 sound clips. It's capable of playing all 6 simultaneously (6 voices).

The audio library is still fully 16 bits. The new AudioPlayMemory object internally converts from 8 to 16 bits as it reads from the on-chip memory, if the stored copy is 8 bit. This is only to allow longer clips to be stored in the limited (approx 220K) on-chip memory.

In the PlayFromSketch example's directory is a wav2sketch program that converts 16 bit WAV files to the data arrays for use with the AudioPlayMemory. It can convert 2 different ways, either original 16 bit PCM, or 8 bit u-law encoding. The default is 8 bit u-law, which sounds pretty good for many files, but it does add distortion that's audible with some types of sounds. Just run "wav2sketch -16" to convert in 16 bit mode. This obviously uses twice as much flash, but you get a perfect lossless copy of the original WAV file. The audio library can play either type. I put a mix of them in the example.

Only sample rates 44100, 22050 and 11025 are supported. 22050 and 11025 are upconverted to 44100 using simple linear interpolation. @mxxx, how would that 4 point polynomial fit into this code? Certainly more than just "s0" could be kept between runs, if needed.

mxxx
01-11-2014, 06:13 PM
Only sample rates 44100, 22050 and 11025 are supported. 22050 and 11025 are upconverted to 44100 using simple linear interpolation. @mxxx, how would that 4 point polynomial fit into this code? Certainly more than just "s0" could be kept between runs, if needed.

i wish i knew enough of this stuff to be of any help. i think it's mostly about using more expensive forms of interpolation. this (or similar) is how interpolation is or used to be done in said pd object:





cminusb = c-b;
*out++ = b + frac * (
cminusb - 0.1666667f * (1.-frac) * (
(d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)
)
);

where a-d are points -1, 0, 1, and 2; frac the fractional part of an index into the sample array. i'm not sure whether it would be worth the trouble though, considering AudioPlayMemory is (i'd guess) going to used only for lo-fi stuff anyways. it might be more relevant for some kind of AudioPlaySDcardWAV::varispeedplay(...) type thing.

PaulStoffregen
01-11-2014, 08:12 PM
I am planning to bring this upsampling code into to AudioPlaySDcardWAV, so files at those slower sample rates can play from SD cards. I'm also planning to work on another function to begin playing with an optional delay, where the library will use the delay to attempt to schedule multiple instances of AudioPlaySDcardWAV to interleave their reading from the SD card. 44.1 kHz 16 bit stereo requires reading a sector every update, but mono and slower rates don't need a read every time. If interleaved properly, more simultaneous streams ought to be able to play.

For now, I think the linear interpolation sounds pretty good. Even with good quality headphones, I can't hear much difference between the Teensy output and my PC's output, but then my PC might be using a less than ideal upsampling algorithm too. For now, I'm moving on to other stuff, like an object to output to Teensy 3.1's on-chip DAC. I'll revisit this if one the gurus like Adrian Freed chimes in with a compelling algorithm....

Nantonos
01-11-2014, 11:57 PM
8000 Hz is the real problem. Lots of WAV files are 8 kHz sampled. Especially for embedded them into the extra on-chip flash memory, you get a lot more playback time, but of course with reduced bandwidth.

This is probably going to be a bit controversial, but my current plan is to convert every 2 samples at 8000 Hz to 11 samples at 44000 Hz (using linear interpolation), and then feed that wrong-rate 44000 Hz data out at 44100 Hz. That's a 0.2% error. 8000 Hz files (stored in flash) will actually play at 8018 Hz.

That seems totally fine to me. They are slightly pitch shifted but not by much: 3.9 cents sharp (there are 100 cents in a semitone). For a format that is prioritising space over fidelity and is likely going to be used for sound effects and so on rather than music, that seems totally fine.

If someone wanted better conversion, they could do it on a computer (and if they wanted better quality they wouldn't be starting with 8k WAV files).

PaulStoffregen
01-12-2014, 04:49 AM
Today I've been working on supporting Teensy 3.1's built-in DAC in the audio library. I currently have it running with interrupts and I hope to convert to DMA soon.

I have a bit of excellent news to share. It turns out the DAC's bandwidth is much better than Freescale's datasheet specifies: 15 us typical, 30 us worst case.

But if you check out this scope capture, the rise time is approx 0.6 us. While this measurement isn't precise enough for checking settling to 0.8 mV, visually it looks like the waveform setting pretty well in about 1 to 2 us.

1281
(click for larger)

As you can see, it's easily capable of creating a 22 kHz square wave. The 30 us worst case was a bit slow, but it turns out the hardware is at about 10X faster than the datasheet says, so full bandwidth audio won't be any problem at all.

PaulStoffregen
01-12-2014, 05:48 AM
I got the DAC working! Code is now on Github.

https://github.com/PaulStoffregen/Audio

To use it, you'll also need to update mk20dx128.h for the extra DMA channel definitions. The file is here:

https://github.com/PaulStoffregen/cores/blob/master/teensy3/mk20dx128.h


Edit: The update scheduling doesn't yet work on the DAC object, so you MUST have one of the I2S objects or the PWM output objects. I'll work on this bug soon...

PaulStoffregen
01-12-2014, 01:14 PM
While reading through the code I discovered that the lib assumes that the ADC/DAC/CODEC on the I2S channel is in Slave mode, so I've added a #define and some #if blocks in the AudioI2S*put classes code to check wether the MCLK signal is external or internal
.....
I will probably make a pull request tomorrow, is it okay? :D

If you're still working on this, I would like a pull request, but done a bit differently.

Instead of a #define that needs to be edited within the library source, I'd like to see 2 new objects with slightly different names. Perhaps AudioOutputI2Sslave and AudioInputI2Sslave? They should share the same dma_ch0_isr() and dma_ch1_isr() and reuse all the same static variables and buffers.

Perhaps the cleanest way to do this might be to have those 2 new classes inherit from the 2 existing ones? You'd probably only need to implement a different begin() and config_i2s(), and let the C++ inheritance reuse everything else. It's fine to change any of the I2S private members to protected, if necessary.

If that doesn't work out, the new slave-mode classes could just be normal audio objects that inherit from AudioStream. You'd need to add friend class lines into the existing I2S objects to allow these new ones to access their private variables.

I'd like to see it done this way, as 2 new classes, so casual Arduino users can simply add the extra word onto the end of the object name to use your slave-mode version. They shouldn't need to edit a #define within the library.

MickMad
01-12-2014, 05:16 PM
If you're still working on this, I would like a pull request, but done a bit differently.
I'd like to see it done this way, as 2 new classes, so casual Arduino users can simply add the extra word onto the end of the object name to use your slave-mode version. They shouldn't need to edit a #define within the library.

That is exactly what I want to avoid too :) I'm working on the mods right now.

daperl
01-12-2014, 07:19 PM
@Paul

In the PassThroughAnalog example you don't declare audioInput.

scswift
01-12-2014, 08:26 PM
// slowly ramp up to DC voltage, approx 1/4 second
for (int16_t i=0; i<128; i++) {
analogWrite(A14, i);
delay(2);
}


The DAC is 12bit, so why are you ramping up to 127 instead of 2047 here? You're not giving up 4 bits of accuracy are you?

Hm, I see 2047 in some other functions after that but I don't see analogWrite or analogWriteResolution anywhere else... I guess you're setting the resolution after that somehow and outputting the data to the DAC at 12 bit accuracy using some register somewhere.

PaulStoffregen
01-12-2014, 08:56 PM
The DAC is 12bit, so why are you ramping up to 127 instead of 2047 here?


Because analogWrite defaults to 8 bit



You're not giving up 4 bits of accuracy are you?


Only during the initial ramp up to the DC offset.



Hm, I see 2047 in some other functions after that but I don't see analogWrite or analogWriteResolution anywhere else... I guess you're setting the resolution after that somehow and outputting the data to the DAC at 12 bit accuracy using some register somewhere.

Don't worry. Once it starts running, the DMA transfers move 12 bit data.

In fact, this part of the code translates the 16 bit audio from the library to the 12 bits needed by the DAC.



block = AudioOutputAnalog::block_left_1st;
if (block) {
src = &block->data[offset];
do {
// TODO: this should probably dither
*dest++ = ((*src++) + 32767) >> 4;
} while (dest < end);


You can see the conversion from the library's signed integers to the DAC's unsigned range, and the 4 bit shift that converts from 16 to 12 bits.

Someday this code might get proper dithering, as the comment indicates. For now, the conversion is done by simply discarding the 4 lowest bits.

PaulStoffregen
01-13-2014, 03:19 PM
The AudioOutputAnalog update scheduling bug is fixed. You can now use Teensy 3.1's DAC without I2S or any other input or output objects.

I also just fixed the mixer gain function. You can set the gain on each mixer channel, using myMixer.gain(channel, gain). The gain range is 0.0 to 32767.0 (floating point), so you can also use it to amplify signals. The default gain is 1.0.

scswift
01-13-2014, 04:22 PM
You've got the DAC set up to use the 3.3v internal regulator as a reference, right?

I was reading this page, and came across the following:
https://pjrc.com/teensy/teensy31.html

The output is created by the stable reference voltage, so it's doesn't vary if your power supply voltage changes slightly.

It got me wondering.

If the DAC uses 3.3v as its reference, what happens when one decides to power their device from an external 3.3v regulator thus bypassing the internal regulator, and said regulator is supplying other power hungry devices? The 3.3v output would no longer be stable, would it not? For that matter, isn't the power hungry SD card powered from the internal 3.3v regulator anyway? Wouldn't that be introducing a lot of noise on the output?

It seems like it would be better to use the internal 1.2v reference for the DAC when playing audio though it. That would remain stable even if the 3.3v line itself isn't particularly stable, right? It's also close to consumer line level which is .894v peak to peak. One could potentially get away without using a voltage divider.

I only mention this because on my own boards I had major issues with the audio output due to attaching a couple TLC5947 LED drivers to it. They were introducing so much noise into my audio system that I had to use a ground loop isolator when attaching my board to an amplifier. My problem may have been an issue with noise on my system's ground rather than noise on the 5v reference for my DAC though. I was told that if I referenced my voltage divider to the amplifier ground instead of grounding it to both my board and the amplifier that that would fix it and cancel the noise out. I don't know for sure though as I have yet to test it.

PaulStoffregen
01-13-2014, 04:50 PM
It seems like it would be better to use the internal 1.2v reference for the DAC when playing audio though it.

Yes, that's a good point.

I've added an analogReference() function to the AudioOutputAnalog object. You can use it like this:



dac.analogReference(INTERNAL);


It does result in a sudden jump on the DC level when changing reference voltage. At some point I'll add code to deal with that issue.....

cartere
01-13-2014, 09:24 PM
The AudioOutputAnalog update scheduling bug is fixed. You can now use Teensy 3.1's DAC without I2S or any other input or output objects.

I also just fixed the mixer gain function. You can set the gain on each mixer channel, using myMixer.gain(channel, gain). The gain range is 0.0 to 32767.0 (floating point), so you can also use it to amplify signals. The default gain is 1.0.


Thanks Paul, I have a proof of concept running with a kludge this will clean things up :)

psxpetey
01-14-2014, 05:01 AM
teensy you are the coolest

mattomatto
01-14-2014, 07:58 PM
Hi there,

I have already developed audio code that I would like to just output samples through the teensy audio shield. Is the best way to do this by creating my own audio object in the library and using the following format?:


void audioObjectTemplate::update(void)
{
if (magnitude > 0 && (block = allocate()) != NULL) {
// Declare some variables
for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
//Generate and process audio samples
}
//update variables
transmit(block);
release(block);
} else {
//What should/can I do in here?
}
}



Thanks

PaulStoffregen
01-14-2014, 09:12 PM
Is the best way to do this by creating my own audio object in the library and using the following format?:

Yes, exactly.

It doesn't have to be inside Audio.h and Audio.cpp. Just use #include "AudioStream.h" and you can put it anywhere.



} else {
//What should/can I do in here?


Usually you would just return without doing anything. You're not required to transmit audio. Other objects that receive are supposed to treat a lack of data as if it were incoming silence. The 3 objects that actually move output data off-chip actually do substitute a block of silent output for any update() when data doesn't arrive.

Nantonos
01-15-2014, 04:29 AM
In Audio.h, a comment explaining the magic numerical value would be welcome

phase_increment = (freq / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f;
or, if it is used in multiple places, perhaps define it as a named constant and put the comment there.

MichaelMeissner
01-15-2014, 08:20 PM
Paul, if you ever respin the PCBs for the Audio board and the WIZ820io & Micro SD board, it might be helpful to some to have the pin numbers on the bottom of the shield like you do on the bottom of the Teensy 3.0's and 3.1's. While I need to reorganize things, my current soldering station is on a different floor than the computer, so I will have to be careful to solder my audio board in the correct orientation when I get enough time to solder it up.

Also, in posting this, I wonder if the SD card slots on the Audio and WIZ820io boards would interfere with each other if you decided to mount both boards on the same Teensy.

zeevj
01-16-2014, 12:16 AM
Hi Paul and everyone
I just got the Teensy 3.1 and the audioShield and was able to do a full roundtrip (mic->device->headphone), :o
This thread talk allot about SD read but what about SD write?
Do you think that the Teensy+audioShield is capable of writing 44.1khz mono wav/mp3 file from the analog input?

PaulStoffregen
01-16-2014, 07:26 AM
I've added an AudioFilterBiquad object to the library. The only documentation at this point is a new Filter example, from File > Examples > Audio > Filter.

https://github.com/PaulStoffregen/Audio

if you want to give it a try with a different filter response that the 800 Hz low pass in the example, here's the part you edit:



// each filter requires a set up parameters
int myFilterParameters[] = { // lowpass, Fc=800 Hz, Q=0.707
3224322, 6448644, 3224322, 1974735214, -913890679, 0, 0, 0};


These first five numbers define the filter response. This website has the easiest calculator for the numbers:

http://www.earlevel.com/main/2013/10/13/biquad-calculator-v2/

Take the 5 numbers from that web page and multiply by 1073741824 (which happens to be 2^30). On the last 2 numbers, also multiply by -1 (negative becomes positive and vise-versa).

mattomatto
01-16-2014, 10:42 AM
In Audio.h, a comment explaining the magic numerical value would be welcome

phase_increment = (freq / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f;
or, if it is used in multiple places, perhaps define it as a named constant and put the comment there.



Nantonos, the magic number is (2<<31). This would make sense if the phase depth is 32 bit.

PaulStoffregen
01-16-2014, 12:15 PM
Yes, 4294967296 = 2^32.

scswift
01-17-2014, 02:17 AM
I noticed your diagram here is missing AGND:
http://www.pjrc.com/store/teensy3_audio.html

You might want to include that in case someone is only populating the pins that are shared between the boards and wants to use the volume knob.

cartere
01-17-2014, 03:56 PM
Trying to implement a notch filter using the BiQuad filter. The notch works but clips badly, suspect because the coefficients are large. I need about a 30db notch and I believe the Q was about 3. Any thought on getting more dynamic range out of the filter?




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

//const int myInput = AUDIO_INPUT_LINEIN;
const int myInput = AUDIO_INPUT_MIC;

// each filter requires a set up parameters
int myFilterParameters[] = { // notch, Fc=2175 Hz
896456695, -1707514503, 896456695, 1707514503, -719171567, 0, 0, 0};

// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioInputI2S audioInput; // audio shield: mic or line-in
AudioFilterBiquad myFilter(myFilterParameters);
AudioOutputI2S audioOutput; // audio shield: headphones & line-out

// Create Audio connections between the components
//
AudioConnection c1(audioInput, 0, audioOutput, 0);
AudioConnection c2(audioInput, 0, myFilter, 0);
AudioConnection c3(myFilter, 0, audioOutput, 1);

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


void setup() {
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(12);

// Enable the audio shield and set the output volume.
audioShield.enable();
audioShield.inputSelect(myInput);
audioShield.volume(60);
}

elapsedMillis volmsec=0;

void loop() {
// every 50 ms, adjust the volume
if (volmsec > 50) {
float vol = analogRead(15);
vol = vol / 10.24;
audioShield.volume(vol);
volmsec = 0;
}
}

}

MickMad
01-17-2014, 06:04 PM
I pulled the request on the Github repository to add the objects needed to use the Audio library with the Mikroe Audio Codec PROTO board (WM8731 in master mode, I2S module in slave mode).
I followed Paul's guidelines :

Perhaps the cleanest way to do this might be to have those 2 new classes inherit from the 2 existing ones? You'd probably only need to implement a different begin() and config_i2s(), and let the C++ inheritance reuse everything else. It's fine to change any of the I2S private members to protected, if necessary.

I added two objects, AudioInputI2Sslave and AudioOutputI2Sslave that inherit from the existing Audio*putI2S classes; to avoid configuring twice the DMA and the I2S module (which will lead to bugs, I assure that :D ), I overloaded the constructor in both superclasses adding a protected one that bypasses the modules initialization and lets the subclasses do their own initialization.

I also added the AudioControlWM8731master object, to set the WM8731 codec in master mode, and an example that plays a sinewave on the codec's output.

As I stated in some previous comment, the sinewave frequency is slightly higher than it should be, and as Paul said:

The trouble with MCLK as an input is the timing won't necessarily be synchronous with the CPU. It's probably not a big deal if you don't use another other input or output objects that move audio on/off chip. But if you do, things can drift out of sync. I'm not even sure what will happen... if the result might be as benign as infrequent 3ms dropouts, or if the whole thing might crash hard?
I still gotta try these objects with the I2S input, will let you know of the effects.

In the meanwhile, you can check the pull request summary here: https://github.com/PaulStoffregen/Audio/pull/1 (I avoided to repeat these informations on the pull request's summary because the discussion about the library is here)

PaulStoffregen
01-17-2014, 07:48 PM
Trying to implement a notch filter using the BiQuad filter. The notch works but clips badly, suspect because the coefficients are large.

I tried it just now, but couldn't reproduce the problem. Well, at least not with the filter part. It's easy to get clipping by feeding in a waveform larger than the input range, or by setting the output volume over about 83 (if your digital signal is full scale).

Here's the code I used for testing. Instead of taking I2S input, I fed a full scale sine wave from AudioSynthWaveform into the filter. With the output volume at 82, I'm seeing a 3 Vp-p sine wave output. I tried several frequencies. Sure enough, around 2200 the output is filtered to almost nothing. I got large sine waves without clipping when testing low and high frequencies.



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

int myFilterParameters[] = { // notch, approx 2.2 kHz
896456695, -1707514503, 896456695, 1707514503, -719171567, 0, 0, 0};

// Create the Audio components. These should be created in the
// order data flows, inputs/sources -> processing -> outputs
//
AudioSynthWaveform sine(AudioWaveformSine);
AudioFilterBiquad myFilter(myFilterParameters);
AudioOutputI2S audioOutput;

// Create Audio connections between the components
//
AudioConnection c1(sine, 0, myFilter, 0);
AudioConnection c2(myFilter, 0, audioOutput, 1);

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


void setup() {
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(12);

// Enable the audio shield and set the output volume.
audioShield.enable();
audioShield.volume(82);

sine.frequency(150);
sine.amplitude(1.0);
}

void loop() {
}

cartere
01-17-2014, 08:16 PM
WOW! About 20Hz bandwidth and good notch depth. I will have to dig and find out where the clipping is coming from. I have a mic hooked up to the mic input that sounds good in the pass through demo but is clipped to the point of non intelligibility when I put the filter in the path.

cartere
01-17-2014, 08:40 PM
Looks like the problem was a (in my head/perception ) issue, with the volume around 80 I was unconsciously backing off the mic, when the level was around 60 I found myself talking much closer to the mic and louder resulting in clipping................go figure :)

jstsch
01-18-2014, 11:47 PM
Hurray! :) soldered up the Audio Board (also put in a W25Q128FV) and it's happily playing random sines now :)

jstsch
01-18-2014, 11:59 PM
BTW, no W25Q128FV example code yet, am I correct?

cartere
01-19-2014, 12:03 AM
Paul,
I have been trying to figure out what myFFT.output[i]in the FFTexample actually represents. Is it real^2+imag^2 or has the sqrt been taken? I am trying to come up with a db meter.

el_supremo
01-19-2014, 12:21 AM
The audio adaptor is described as sampling at 44.1kHz. The audio chip, SGTL5000, can do several sampling rates including 48000Hz. Can the adaptor do 48000Hz?

Pete

PaulStoffregen
01-19-2014, 01:39 AM
Correct, so far there's absolutely no support for the W25Q128FV chip (other than a location to solder it).

The FFT object is magnitude only. Even then, it's currently buggy. There seems to be some sort of strange numerical range or precision problem. So far every attempt I've made (admittedly only a couple) has led back to the ARM math library. I don't want to claim there's definitely a bug in that library. At least not yet. This still needs more investigation. It's one of the many reasons the library is still considered "beta".



Can the adaptor do 48000Hz?


No, not really.

The main issue is the MK20 chip can't create a low-jitter MCLK needed for 48 kHz mode. If I2S_MDR_FRACT is set to anything other than 0 or 1, the MCLK output is terrible. Here's an old post from a few months ago where I tested this:

http://forum.pjrc.com/threads/24078-Audio-For-Teensy3-What-Features-Would-You-Want?p=34652&viewfull=1#post34652

Luckily, the MK20's PLL runs at 96 MHz. MCLK is created with 96 * 2 / 17 = 11.294 MHz. When divided by 256, the audio sample rate is 44117.647 Hz.

However, if you use an external MCLK, and configure the codec chip in master mode, you can use a different sample rate. MickMad submitted a patch that's now in the library for I2S slave mode. In that mode, the codec provides the clocks and the audio library runs at whatever speed the external hardware wants. Normally the library lets you use any combination of objects, but if you're using the I2S slave objects, you must not use the other input or output objects.

Nantonos
01-19-2014, 03:38 AM
Can the adaptor do 48000Hz?
[/code]

No, not really.

The main issue is the MK20 chip can't create a low-jitter MCLK needed for 48 kHz mode. If I2S_MDR_FRACT is set to anything other than 0 or 1, the MCLK output is terrible. Here's an old post from a few months ago where I tested this:

http://forum.pjrc.com/threads/24078-Audio-For-Teensy3-What-Features-Would-You-Want?p=34652&viewfull=1#post34652

Luckily, the MK20's PLL runs at 96 MHz. MCLK is created with 96 * 2 / 17 = 11.294 MHz. When divided by 256, the audio sample rate is 44117.647 Hz.

Maybe this is a dumb comment but isn't 96 * 1/2 = 48 MHz. When divided by 1024, the audio sample rate is 48kHz. What am I missing here?

PaulStoffregen
01-19-2014, 04:43 AM
MCLK needs to be 256 times the sample rate, or 12.288 MHz for 48 kHz audio. For low jitter, the hardware can do integer division of 192 MHz.

Also, 48e6 / 1024 = 46875, which isn't 48000. But if 46875 were used, the hardware could create a 12 MHz MCLK with 192 MHz / 16.

PaulStoffregen
01-19-2014, 01:14 PM
I've added an AudioAnalyzeToneDetect object to the library. A DTMF (dial tone) decoder example is coming soon....

PaulStoffregen
01-20-2014, 09:38 AM
I've added the DTMF dial tone decoder example to the library.

https://github.com/PaulStoffregen/Audio/blob/master/examples/DialTone_DTMF/DialTone_DTMF.ino

cartere
01-20-2014, 10:48 PM
Paul I am confused with the DTMF demo. How does the output of the audio synth objects get to the input of the tone decoder objects? I only see a connection between sine1, sine2 and the mixer and from the mixer to the output.

PaulStoffregen
01-20-2014, 11:10 PM
Paul I am confused with the DTMF demo. How does the output of the audio synth objects get to the input of the tone decoder objects?


It doesn't. The decoding is done on a real audio input.

For testing, I connected an actual telephone, using a 12V supply, resistor, capacitor and opamp to couple the signal to the audio input.




I only see a connection between sine1, sine2 and the mixer and from the mixer to the output.

Yup, that's for generating DTMF output, which is sent to the audio out.

robsoles
01-21-2014, 02:15 AM
I've started looking at the existing stuff, in Paul's audio library, for SGTL5000. This post is primarily to subscribe to this thread but I may as well announce my intentions at least a bit.

I'm interested in fleshing out the AudioControlSGTL5000 object as much as I can, based on having learned a fair deal about the SGTL5000 for my employer's purposes, I think I can offer it a fair bit.

My focus will be on things the codec can do without using I2S, I will post a list of what I propose to add or change in the next couple/few days and see where who points what from there I guess.

cartere
01-21-2014, 08:35 PM
I've started looking at the existing stuff, in Paul's audio library, for SGTL5000. This post is primarily to subscribe to this thread but I may as well announce my intentions at least a bit.

I'm interested in fleshing out the AudioControlSGTL5000 object as much as I can, based on having learned a fair deal about the SGTL5000 for my employer's purposes, I think I can offer it a fair bit.

My focus will be on things the codec can do without using I2S, I will post a list of what I propose to add or change in the next couple/few days and see where who points what from there I guess.



A function to change the mic gain would be good, I have been editing the header file which is not convenient.

robsoles
01-22-2014, 12:54 AM
A function to change the mic gain would be good, I have been editing the header file which is not convenient.If you are in that much a hurry for it you might like my attachment, provided of course that it compiles - I didn't try it yet, please give me feedback (as verbose as you like) if you try it.

Please see 'CHIP_MIC_CTRL' on page 40 of http://www.pjrc.com/teensy/SGTL5000.pdf and note that I have added ::micGain(unsigned int n) to a copy of the library which only manipulates bits 1:0 of that register so actual gain applied should be 10 + (n * 10) dB unless n = 0 in which case it will be 0dB

I am not sure how long it will take me to make the list I was envisaging when I made yesterday's post, I might piece-meal it after all - perhaps my next post to this thread will be to try to start a discussion on how best to add the IIR filter & PEQ stuff to the library.


@Paul: Can I turn the read, write & modify functions public for the AudioControlSGTL5000 without annoying anyone do you think? I worry that in so doing it could cause confusion for some because they are not like .read & .write for other objects like Serial. etc etc but I think they might be terribly useful to even only slightly more advanced users, maybe they could suffer name changes like regWrite, regRead & regModify without being too annoying?

edit: I removed the attachment because it contained an error, I will reattach to a post a couple of posts down without the error, sorry.

cartere
01-22-2014, 03:07 AM
If you are in that much a hurry for it you might like my attachment, provided of course that it compiles - I didn't try it yet, please give me feedback (as verbose as you like) if you try it.

Please see 'CHIP_MIC_CTRL' on page 40 of http://www.pjrc.com/teensy/SGTL5000.pdf and note that I have added ::micGain(unsigned int n) to a copy of the library which only manipulates bits 1:0 of that register so actual gain applied should be 10 + (n * 10) dB unless n = 0 in which case it will be 0dB

I am not sure how long it will take me to make the list I was envisaging when I made yesterday's post, I might piece-meal it after all - perhaps my next post to this thread will be to try to start a discussion on how best to add the IIR filter & PEQ stuff to the library.


@Paul: Can I turn the read, write & modify functions public for the AudioControlSGTL5000 without annoying anyone do you think? I worry that in so doing it could cause confusion for some because they are not like .read & .write for other objects like Serial. etc etc but I think they might be terribly useful to even only slightly more advanced users, maybe they could suffer name changes like regWrite, regRead & regModify without being too annoying?


Several thing going on, I get the following error:

This report would have more information with
"Show verbose output during compilation"
enabled in File > Preferences.
Arduino: 1.0.5 (Windows 7), Board: "Teensy 3.1"
In file included from Toneremote.ino:1:0:
C:\Users\Turner\Documents\Arduino\libraries\Audio/Audio.h: In member function 'unsigned int AudioControlSGTL5000::micGain(unsigned int)':
C:\Users\Turner\Documents\Arduino\libraries\Audio/Audio.h:509:55: error: 'CHIP_MIC_CTRL' was not declared in this scope

When I replace CHIP_MIC_CTRL WITH 0X002a the sketch compiles but no change in mic gain.

robsoles
01-22-2014, 03:31 AM
...
C:\Users\Turner\Documents\Arduino\libraries\Audio/Audio.h:509:55: error: 'CHIP_MIC_CTRL' was not declared in this scope

When I replace CHIP_MIC_CTRL WITH 0X002a the sketch compiles but no change in mic gain.

Sorry about that, my mistake thinking the definitions in the .cpp file were available by the time the compiler sees that in the .h file, your fix should have made it OK but the pedant in me wants me to ask you if you can make it '0x002A' instead of '0X...' :)

When you call the function does the return value look reasonable to you? By this I mean that the return value I expect is 0x017n, so 0x0170 for n=0 and 0x0171 for n=1 etc etc. Maybe my modify routine isn't as good as it looks to me ?

I should probably wait till I have the Teensy 3.1 and Audio Adaptor, rather than posting rubbish I haven't verified myself yet :o

cartere
01-22-2014, 04:18 AM
Sorry about the X finger got stuck:) Return values look right.

robsoles
01-22-2014, 04:47 AM
Ok, so, weird that it does not seem to change gain setting for you - I wonder just how arduous a task it will be for me to try to test that part of the library using the awful freescale tower nonsense I have with my favored Teensy++ 2.0

Just in case anybody else wants to try what I managed to give cartere, I removed the one with the *obvious* error in it from the post above and attach the remedied one to this post...

PaulStoffregen
01-22-2014, 12:23 PM
@Paul: Can I turn the read, write & modify functions public for the AudioControlSGTL5000 without annoying anyone do you think?


Would "protected" be enough?

robsoles
01-23-2014, 12:36 PM
They are already protected. Never mind about those for now.

Please look at http://forum.pjrc.com/threads/24578-Controlling-SGTL5000-(TWR-AUDIO-SGTL)-using-Teensy-2-0?p=40354&viewfull=1#post40354 for reference to PEQcalc and loadPEQx if interested and you haven't already.

I'm thinking of reworking PEQcalc so that it can quantize for both the SGTL5000 PEQ and the AudioFilterBiquad object, rename it filterCalc and ask for suggestions as to where to stick it - if not for AudioFilterBiquad I would just put it in AudioControlSGTL5000 (practically but not quite as is, sans Serial.prints of course) but I think it merits the question; where would it be best placed?

the loadPEQx routine can almost just become a member of AudioControlSGTL5000 as is but I think I will rework it to blend in a little more.

To make it worth having any form of the loadPEQx routine there has to be opportunity to route audio data via the DAP so I will try to post soon with a few models of functions I propose to add at the same time a more reasonable simile of laodPEQx is added.

scswift
01-23-2014, 08:50 PM
I'm having trouble with my audio board. I finally got my Teensy hooked up last night and successfully got it to play a wav file through my headphones. However, thus far I have been able to get a peep out of my amplifier, with or without the headphones plugged in.

The amplifier is connected to the Line Out port on the audio module via a 3.5mm plug. I have attached the pins for said plug to the three closest to the Teensy leaving the two near the edge of the board unconnected, just as the stencil indicates. I have tested the continuity of the cable, and the center pin goes to the sleeve, and the outside ones go to the tip and ring. So the issue isn't with the cable.

I have also tested the amplifier using my PC. The amplifier works fine.

The amplifier receives power from a 12v wall adapter. I have two speakers connected to it just in case one channel starts working and the other doesn't. This 12v supply goes to a 3.3v regulator that I use to supply power to the Teensy through its 3.3v port. The grounds are connected. All pins of the Teensy are connected to the Audio module. I have no other boards attached to the Teensy. With this setup, the headphone jack on the Teeensy works fine, I can hear the music. But I hear nothing from my amp. I have tested both with and without headphones attached just in case there is a headphone detect built in that would shut off the line out.

I have even attempted to disconnect the 3.3v regulator from the Teensy and power it from the USB instead while the amplifier is powered by the 12v source. Headphones still work fine, but no audio on the Line Out.

It occurred to me just now that there may be some setting I need to enable in the audio library to get sound on the line out pins, since they're not attached to the same pins on the codec as the headphone jack and so may be independently controlled. Is there?

cartere
01-23-2014, 09:21 PM
I'm having trouble with my audio board. I finally got my Teensy hooked up last night and successfully got it to play a wav file through my headphones. However, thus far I have been able to get a peep out of my amplifier, with or without the headphones plugged in.

The amplifier is connected to the Line Out port on the audio module via a 3.5mm plug. I have attached the pins for said plug to the three closest to the Teensy leaving the two near the edge of the board unconnected, just as the stencil indicates. I have tested the continuity of the cable, and the center pin goes to the sleeve, and the outside ones go to the tip and ring. So the issue isn't with the cable.

I have also tested the amplifier using my PC. The amplifier works fine.

The amplifier receives power from a 12v wall adapter. I have two speakers connected to it just in case one channel starts working and the other doesn't. This 12v supply goes to a 3.3v regulator that I use to supply power to the Teensy through its 3.3v port. The grounds are connected. All pins of the Teensy are connected to the Audio module. I have no other boards attached to the Teensy. With this setup, the headphone jack on the Teeensy works fine, I can hear the music. But I hear nothing from my amp. I have tested both with and without headphones attached just in case there is a headphone detect built in that would shut off the line out.

I have even attempted to disconnect the 3.3v regulator from the Teensy and power it from the USB instead while the amplifier is powered by the 12v source. Headphones still work fine, but no audio on the Line Out.

It occurred to me just now that there may be some setting I need to enable in the audio library to get sound on the line out pins, since they're not attached to the same pins on the codec as the headphone jack and so may be independently controlled. Is there?



This is what I did:

audioShield.unmuteLineout();

scswift
01-23-2014, 09:59 PM
That seems to have fixed it. Though I seem to be getting a pop after my audio file ends and I just double checked that the file does not have a pop in it. I hear this pop when using the headphones as well.

I have not changed the example much:


void setup() {
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(5);

audioShield.enable();
audioShield.volume(50);
audioShield.unmuteLineout();

SPI.setMOSI(7);
SPI.setSCK(14);
if (SD.begin(10)) {
wav.play("001.WAV");
}
}

void loop() {
//float vol = analogRead(15);
//vol = vol / 10.24;
//audioShield.volume(vol);
delay(20);
}



Btw, vol = vol / 10.24 looked like a bug to me, but I guess it's not since volume takes 0..100 for input instead of 0..1, which is an odd choice, but anyway, shouldn't that be 10.23? Analogread doesn't go up to 1024.

PaulStoffregen
01-23-2014, 11:40 PM
Though I seem to be getting a pop after my audio file ends and I just double checked that the file does not have a pop in it. I hear this pop when using the headphones as well.


I'm pretty sure it's related to this part of the code:



// end of file reached
if (block_offset > 0) {
// TODO: fill remainder of last block with zero and transmit
}
state = STATE_STOP;
return false;

robsoles
01-24-2014, 03:04 AM
Paul,

I have made a version of 'filterCalc' which appears to output usable coefficients for both SGTL5000 built in stuff & your AudioFilterBiquad object, I tested it by making a sketch for Teensy++ 2.0 and having it output values to serial monitor and I think its output is OK.
// void filterCalc(unsigned char filtertype, float fC, float dB_Gain, float Q, unsigned long quantization_unit, unsigned long fS, unsigned long coef[]);

// usage for SGTL5000 built in filtering
unsigned long coefs[5];
filterCalc(PEQ_LPF,800,0,0.707,524288,44100,coefs) ;


// usage for AudioFilterBiquad
long myFilterParameters[8];
filterCalc(PEQ_LPF,800,0,0.707,2147483648,44100,(u nsigned long*)myFilterParameters);The resultant output of my test was
SGTL5000 Filter info;
b0=0x00313
b1=0x00626
b2=0x00313
a1=0x75B3C
a2=0xC9878

AudioFilterBiquad Filter info;
a0=3224303
a1=6448607
a2=3224303
b1=1974714496
b2=-913869824Mind you this was done on Teensy++ 2.0 where sizeof(int)==2 and sizeof(long)==4 - I've the impression that if compiling for Teensy 3.x the sizeof(int)==4 and sizeof(long)==?; I am hoping the ?==4 as well in that case.

I may or may not be quantizing well for AudioFilterBiquad, I expect I am but I see I come out with slightly different figures than derived the way you derived yours - I expect it amounts to very very similar filtering but I haven't means to test it properly yet.


It looks to me like AudioFilterBiquad does not make a copy of 'myFilterParameters' but instead refers to the original array giving me the impression it could be changed on the fly, please correct me if I misinterpreted that.

If I am right about that I expect I am probably right that before changing them it would be best to 'cli()', change them, re-zero the other three elements of the array and finally 'sei()' again - am I very wrong here or not?

If nobody thinks I'm very wrong about those details then I can whip up a test-sketch I would be pleased if somebody would volunteer to try for me, if I am only wrong about sizeof(long) on Teensy 3.x or necessity to stop interrupts or re-zero other elements of myFilterParameters[] and somebody is verbose enough about how wrong exactly then I can nearly as quickly whip up a test sketch anyway...

MichaelMeissner
01-24-2014, 01:54 PM
Mind you this was done on Teensy++ 2.0 where sizeof(int)==2 and sizeof(long)==4 - I've the impression that if compiling for Teensy 3.x the sizeof(int)==4 and sizeof(long)==?; I am hoping the ?==4 as well in that case.

On arm chips like the Teensy, sizeof (short) == 2, sizeof (int) == 4, sizeof (long) == 4, sizeof (long long) == 8. The include file limits.h gives you the min/max for each type, and you can use that in #if type statements to adjust types.

Derangedgamer123
01-24-2014, 04:44 PM
The audio jack is it the output?

PaulStoffregen
01-24-2014, 04:49 PM
For programming inside the library, it's best to use the these types: int64_t, uint32_t, int32_t, uint16_t, int16_t, uint8_t, int8_t. Even though this library is very specific to the Cortex-M4 processor, it's still good to use those names to specify exactly how many bits you want.

One minor complexity (well, perhaps not so minor) is using the M4's packed format. Two int16_t's can be packed into a single 32 bit variable, which is usually specified as uint32_t type. Generally it's best to write code first using ordinary variables and types and get it working and well tested, before piling on this extra complexity. But the packed format really helps speed things up, partly because you can bring 2 int16_t's into the processor as a single uint32_t read from memory, which is twice as fast as reading them separately.




I may or may not be quantizing well for AudioFilterBiquad, I expect I am but I see I come out with slightly different figures than derived the way you derived yours - I expect it amounts to very very similar filtering but I haven't means to test it properly yet.


When your board arrives, it's best to test the quantizing problems using a low pass filter with a low corner frequency.



It looks to me like AudioFilterBiquad does not make a copy of 'myFilterParameters' but instead refers to the original array giving me the impression it could be changed on the fly, please correct me if I misinterpreted that.


Yes, the filter coefficients can be changed on the fly.



If I am right about that I expect I am probably right that before changing them it would be best to 'cli()', change them, re-zero the other three elements of the array and finally 'sei()' again - am I very wrong here or not?


Yes. If interrupts are enabled, and your code is running in the main program, it's possible the update() function will run after you've changed some but not all the coefficients. That could produce 128 samples of very wrongly processed audio.

Within the library, use __disable_irq() and __enable_irq().

In the example sketches, normally you should use AudioNoInterrupts() and AudioInterrupts(). Users will copy the examples, so even if you know these aren't truly necessary for your specific example, they should still always be used in the example sketches.

The idea behind AudioNoInterrupts() is to allow the sketch to suspend audio updates, even when it calls to library functions that have __disable_irq() and __enable_irq(). The library's function can guarantee correct operation with __disable_irq() and then use __enable_irq(), but the audio library will remain disabled until the sketch completes a sequence of several such library calls and finally enables the library with AudioInterrupts().

scswift
01-24-2014, 07:40 PM
The audio jack is it the output?

The audio jack is a headphone output. Don't attach it to an amplifier, or it may damage the chip. The line out pins are a separate output which it is safe to attach an amp to.

robsoles
01-25-2014, 12:39 AM
Thanks very much Michael & Paul. I will review my example/test sketch a bunch and post it later, my family want some attention from me too so 'later' could be a day or something.

robsoles
01-25-2014, 12:24 PM
Attached should be suspiciously similar to a bass control, it compiles for Teensy 3.1 and I'll be very pleased to hear if someone can try it and it just works but grateful no less to hear how it fails if that is the case. It is probably not well done centered at 220Hz and being the width 'Q=0.2' makes it.

cartere
01-25-2014, 11:22 PM
Attached should be suspiciously similar to a bass control, it compiles for Teensy 3.1 and I'll be very pleased to hear if someone can try it and it just works but grateful no less to hear how it fails if that is the case. It is probably not well done centered at 220Hz and being the width 'Q=0.2' makes it.

Seems to work, the update every 50ms is very audible.

robsoles
01-26-2014, 12:12 AM
Seems to work, the update every 50ms is very audible.Thanks for trying it cartere.

It must sound awful, I imagine. Here is a replacement loop which should, at minimum, reduce that noise hopefully as much as drastically; There will be a cleaner way to update those coefficients than I am here and if nobody makes an example of how before I get my parcel I will definitely be looking for it.


void loop() {
// every 50 ms, check for adjustment the bass
if (volmsec > 250) { // every quarter of a second instead perhaps.
float vol = analogRead(15);
vol=((vol-512)/512)*60;
vol=(int)vol;
vol=vol/10; // only changing for 0.1dB steps
if(lastvol!=vol) {
AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
AudioInterrupts();
lastvol=vol;
}
/* vol = vol / 10.24;
audioShield.volume(vol); */
volmsec = 0;
}
}

cartere
01-26-2014, 12:43 AM
Thanks for trying it cartere.

It must sound awful, I imagine. Here is a replacement loop which should, at minimum, reduce that noise hopefully as much as drastically; There will be a cleaner way to update those coefficients than I am here and if nobody makes an example of how before I get my parcel I will definitely be looking for it.


void loop() {
// every 50 ms, check for adjustment the bass
if (volmsec > 250) { // every quarter of a second instead perhaps.
float vol = analogRead(15);
vol=((vol-512)/512)*60;
vol=(int)vol;
vol=vol/10; // only changing for 0.1dB steps
if(lastvol!=vol) {
AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
AudioInterrupts();
lastvol=vol;
}
/* vol = vol / 10.24;
audioShield.volume(vol); */
volmsec = 0;
}
}

/code
if(fabs(lastvol-vol)>02.0) {
vol=((vol-512)/512)*6;
//AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
//AudioInterrupts();
lastvol=vol;
/code

This helped in the center range of the pot, still noisy towards both ends. No change with AudioInterrupts enabled or disabled.

robsoles
01-26-2014, 01:14 AM
/code
if(fabs(lastvol-vol)>02.0) {
vol=((vol-512)/512)*6;
//AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
//AudioInterrupts();
lastvol=vol;
/code

This helped in the center range of the pot, still noisy towards both ends. No change with AudioInterrupts enabled or disabled.

If you change that 02.0 to 9 or maybe 10

if(fabs(lastvol-vol)>9) {
vol=((vol-512)/512)*6;
//AudioNoInterrupts();
filterCalc(FILTER_PEAKEQ, 220, vol, 0.2, 2147483648, 44100, (unsigned long*)myFilterParameters);
myFilterParameters[5]=myFilterParameters[6]=myFilterParameters[7]=0;
//AudioInterrupts();
lastvol=vol;yours would be a near enough simile of my proposed fix (0.1dB steps) and it would defeat even more of the jitter/noise coming from the pot (my guess, at least.)

scswift
01-27-2014, 01:36 AM
So I've gotten to the point where I need to know if a wav file is still playing. I assume I can just do this:



if (wav::state != STATE_STOP) {}


But how can I find out exactly how long a particular wav file is in case I want to begin doing something a second before it finishes playing? I see that some states list the bit depth and whether the file is stereo or mono, and I assume the sample rate is available somewhere as well, but I don't see were I can get the amount of data after the header to calculate the length, and using the state to calculate it seems like a big kludge anyway because I'd have to wait until it gets into one of those states, seeing as how there are one or two before it gets into that state where it's looking at the header.

Is the only solution here to look at the wav file parsing code and make my own function to parse the wav file? But how should I do that? You said accessing the SD card while wav files are playing isn't supported, so do I have to do all that before I start playing files or do it between playing files? And how does your own code handle starting the playback of a second file anyway if you can't access the SD card while a wav file is playing? Or does the existence of AudioNoInterrupts() now mean I can safely access the card for brief periods of time while playing back audio?

robsoles
01-27-2014, 06:30 AM
...

To make it worth having any form of the loadPEQx routine there has to be opportunity to route audio data via the DAP so I will try to post soon with a few models of functions I propose to add at the same time a more reasonable simile of loadPEQx is added.

I suggest these all as public members of AudioControlSGTL5000, mostly returning the contents of the register they are effecting
// CHIP_SSS_CTRL
uint16_t dap_mix_lrswap(uint8_t n); // 1=true/enable
uint16_t dap_lrswap(uint8_t n);
uint16_t dac_lrswap(uint8_t n);
uint16_t i2s_lrswap(uint8_t n);
uint16_t dap_mix_select(uint8_t n); // 0=ADC, 1=I2S_IN
uint16_t dap_select(uint8_t n);
uint16_t dac_select(uint8_t n); // 3=DAP
uint16_t i2s_select(uint8_t n);

// CHIP_DAC_VOL
uint16_t dac_vol_right(float n); // by percentage 0-100
uint16_t dac_vol_left(float n);
uint16_t dac_vol(float n); // set both directly

// DAP_CONTROL
uint16_t dap_mix_enable(uint8_t n);
uint16_t dap_enable(uint8_t n);

// DAP_PEQ
uint16_t dap_peqs(uint8_t n); // valid to n&7, 0 thru 7 filters enabled.

// DAP_AUDIO_EQ
uint16_t dap_audio_eq(uint8_t n); // 0=NONE, 1=PEQ (7 IIR Biquad filters), 2=TONE (tone), 3=GEQ (5 band EQ)

// DAP_AUDIO_EQ_BASS_BAND0 & DAP_AUDIO_EQ_BAND1 & DAP_AUDIO_EQ_BAND2 etc etc
uint8_t dap_audio_eq_band(uint8_t bandNum, float n); // by signed percentage -100/+100; dap_audio_eq(3);
void dap_audio_eq_geq(float bass, float mid_bass, float midrange, float mid_treble, float treble);
uint8_t dap_audio_eq_tone(float bass, float tone); // dap_audio_eq(2);

// SGTL5000 PEQ Coefficient loader
void load_peq(uint8_t filterNum, uint32_t filterParameters[]);

// a route selection routine to simplify a little
void route(uint8_t input, uint8_t output, uint8_t via_i2s, uint8_t via_dap);Please question/comment/advise :)

PaulStoffregen
01-27-2014, 02:27 PM
So I've gotten to the point where I need to know if a wav file is still playing.


Yup, you're running into the limitations of the very minimal API I created merely to get it working.



But how can I find out exactly how long a particular wav file is in case I want to begin doing something a second before it finishes playing?


I do plan to add more functions. My general strategy has been to just publish a minimal API and wait for feedback exactly like this. Well, sort-of like this, but maybe giving me a better picture of what you're trying to accomplish?

I do want to add this stuff, for you and for all sorts of other projects. If you can take a few moments to give me a better picture of what you're doing, it would really help me. I like to think about creating APIs in terms of actual projects. Thinking about them from the perspective of underlying tech usually ends up with extremely efficient but difficult to use libraries.




I see that some states list the bit depth and whether the file is stereo or mono, and I assume the sample rate is available somewhere as well, but I don't see were I can get the amount of data after the header to calculate the length, and using the state to calculate it seems like a big kludge anyway because I'd have to wait until it gets into one of those states, seeing as how there are one or two before it gets into that state where it's looking at the header.


Yes, all that stuff is in there. You can writing functions to access it, if you want to get into changing the wav player object.

I'm willing to do this. In fact, I've planned to fill in the API... but I'm waiting for feedback from real projects like yours. I'm interested in understanding project needs and crafting functions that are easy and intuitive, not just raw access to low-level details.



Is the only solution here to look at the wav file parsing code and make my own function to parse the wav file?


Well, if you don't want to wait for me (or someone else) to add it, of course the code is open source and you can hack it any way you like.



And how does your own code handle starting the playback of a second file anyway if you can't access the SD card while a wav file is playing?


The update() functions are called 1 at a time, so they can't interrupt each other.



Or does the existence of AudioNoInterrupts() now mean I can safely access the card for brief periods of time while playing back audio?

Yes, it should be safe to access the SD library if you've used AudioNoInterrupts(). But if you spend too long, you could cause an audio drop out.

scswift
01-27-2014, 08:35 PM
I do plan to add more functions. My general strategy has been to just publish a minimal API and wait for feedback exactly like this. Well, sort-of like this, but maybe giving me a better picture of what you're trying to accomplish?

I am making a circuit to to control a kinetic art piece. The customer wants the piece to play music, and between each track I need to smoothly transition some RGB leds between colors, and adjust the speed of a motor. I could script it all, but since the music isn't finalized and the customer wants to be able to sell microSd cards with new songs on it to his customers, I'd like it to handle changes automatically.

As I don't want there to be a second of silence between each track, I thought the best way to do it would be to determine how long the song is, and how much time remains, and begin my color fade a second before the song ends.

But if there's no easy way to do that, I guess I'll have to hard code it for now since I need to have the prototype ready tomorrow. I have a more pressing issue at the moment that I need to focus on anyway; that popping noise at the end of each song.

(Since I'm going to script everything, I suppose I can quickly fix that by ending the song before it hits the end of the file.)

scswift
01-27-2014, 09:32 PM
I do want to add this stuff, for you and for all sorts of other projects. If you can take a few moments to give me a better picture of what you're doing, it would really help me. I like to think about creating APIs in terms of actual projects. Thinking about them from the perspective of underlying tech usually ends up with extremely efficient but difficult to use libraries.


Well, here's another project that I've been working on makes extensive use of sound:

http://www.youtube.com/watch?v=mLGnVd03MxM

It doesn't use a Teensy; it uses a board of my own design based on the Arduino, but with an ATMega1284, 12 bit DAC, and 3W amplifier on board; but I may eventually transition it over to the Teensy because I'd like to be able to mix multiple sound effects.

Anyway, for that kit I modified the WaveHC lib to allow the automatic looping of a sound file, and though I'd have liked perfectly seamless transitions between sound effects, polling in a loop to see when a sound had finished playing so I could begin the next ended up being good enough in this case.

The sound effects generally fall into one of three categories: head, body, or tail. A firing sound effect might have one sound for the head which transitions into a seamlessly looping body, and then multiple tails based on how long the user was firing. Sometimes however, as in the case of the powerup sound, I have a head that plays first, and a body which I queue up when the head begins to play and then the body automatically begins to loop once the head is finished playing.

(This is all something I would not expect your library to handle as there's too many different things you might want to do. I might want to randomly choose which head to play based on how long you'd previously been firing for example.)

Other times I will have a sound effect like the hum playing and the user will trigger another sound effect, but that sound effect does not exist because they deleted it for whatever reason. In that case I don't want to stop the looping hum if there's no new sound to play but the WaveHC lib doesn't allow for that, so I have to access the SD card to check to see if the file exists before I issue the command to play it. I see your library suffers from the same issue:



bool AudioPlaySDcardWAV::play(const char *filename)
{
stop();
wavfile = SD.open(filename);
if (!wavfile) return false;


Of course there's no right way to handle this, but if you changed it to only stop the wav if the file exists then the programmer could check to see if the play() failed and stop it himself if that's the behavior he wanted, whereas with this setup I have to halt the audio playback before checking to see if the wav exists...

...or do I? I can call that function at any time to play a new wav file, and it's trying to open the file, and returning failure if it fails to do so, but it's not stopping the audio interrupts when it does this. So is that safe to do without stopping the interrupts? Or should this function be stopping the audio interrupts?


Anyway, the basic things I need are:

1. The ability to seamlessly loop a sound. Right now with the pops in there when it reaches the end of a file, even if manually restarting it turns out to be good enough I wouldn't be able to loop an effect.
2. The ability to seamlessly transition from one sound to another. Again, those pops would get in the way of this.
3. The ability to check to see if a sound exists before I play it in case I don't want the previous sound to stop playing if it doesn't. It seems like I can do this already. And it's less of an issue when I can mix sound effects.

And I think that's pretty much it, for this particular kit anyway. The rest of the stuff I mentioned is stuff I can implement in my own API as long as the basic functionality is there.

This is my sound code. I forgot to mention that when playing a sound I also have the ability to clear any queued sounds, and I have the ability to specify if a sound that is queued will need to loop. And as you can see I modified WaveHC to accept a "loop" parameter, so when it reaches the end of the file in its interrupt it automatically begins the next file so the transition is seamless. This may be necessary when moving from the head to the body in my hum sound effect to avoid any pops, I'm not sure, but when firing the transition is so abrupt I don't need it to be perfectly seamless:



/*

This function plays a sound file.
Unless otherwise specified, playing a sound file with play() will clear the queue of pending sounds.
Normally, only updateSound() needs to call play without it clearing the queue.

If the file does not exist, play() will not halt any currently playing sound, or clear the queue.

*/
int play(char* filename, boolean loopsound = false, boolean emptyQueue = true, boolean logerror = false) {

FatReader tmp;

//if (!FatReader::exists(root2, filename)) return 0; // Check to see if file exists before doing anything.

cli(); // Disable interrupts
if (!tmp.open(root, filename)) { sei(); return 0; }
sei(); // Enable interrupts

wave.stop(); // Halt playback of sound currently playing.

if (emptyQueue) { clearQueue(); }

if (!file.open(root, filename)) { // "root" and "file" are global variables.
if (logerror) { Serial1.println(F("Failed to open WAV file!")); halt(4); }
return 0; // Fail!
}

if (!wave.create(file)) {
if (logerror) { Serial1.println(F("Failed to create WAV object!")); halt(5); }
return 0; // Fail!
}

setVolume(1.0); // Must be set after call to wave.create()
wave.play(loopsound);

return 1; // Success!

}


/*
This function queues up a file to be played after the current one ends. It's used when you want to play a sound effect which has a head and a loop.
*/
void queue(char* filename, boolean loopsound = false, boolean logerror = false) {

queuedFile = filename;
queuedLoop = loopsound;

}


/*
This function begins playing the queued file when the current one ends.
*/
void updateSound() {
if (!wave.isplaying) {
if (queuedFile != NULL) { play(queuedFile, queuedLoop); }
}
}


/*
This function stops any sounds in the queue from being played back.
*/
void clearQueue() {
queuedFile = NULL;
}


/*
This function sets the current playback volume.
newVolume = New volume level. Valid range: [0..1]
Currently, volume must be reset after every new wave file is loaded, but I should probably edit the wave file library to just use a global value.
*/
void setVolume(float newVolume) {
wave.volume = newVolume * 4095.0;
}

PaulStoffregen
01-27-2014, 09:42 PM
I have a more pressing issue at the moment that I need to focus on anyway; that popping noise at the end of each song.


I believe the pop is happening because the last small chunk of the file isn't playing. If the waveform isn't at zero, there's an abrupt transition back to zero.

Try adding a tenth of a second of silence onto the end of your file as a quick fix.

For a demo tomorrow, definitely hard code song lengths. This description really helps. I'll look into improving the library over the next several days.

cartere
01-27-2014, 10:28 PM
I am currently working on a portable, battery powered tester for use in Land Mobile Radio. Control point "remotes" communicate with base radios over leased twisted pair telephone circuits. The protocol is a high level guard tone (2175Hz for 120ms at 10db) followed by a function tone ( 1950, 1850, 1750........for 40ms at 0db) that sets transmit frequency in the base radio) followed by low level guard tone (2175Hz at -20db). Voice audio is mixed with the low level guard tone and the base radio continues to transmit as long as low level guard tone is detected.

I have basic functionality working for both the transmit and receive functions ( tone generation, mic audio mixing, timing) on tx, on rx (tone detection of high level guard tone, function tone, low level guard tone, notch filtering of low level guard tone from tx audio to base radio).

The transmit audio to the base station will be buffered using a small audio power amp driving a 600 ohm line transformer. Rx audio (after processing) will be feed ( through form C sections on a PTT switch???) to a 4d Systems ULCD-32PTU which will do speaker audio and the UI.

Things I would like to see included in the library:
Level control of the DAC

Sensitivity control of Mic IN ( using Mixer4 object now to get gain control)

A AGC object that could be set like AudioConnection c01(audioIn, 0, myAGC, 0);
AudioConnection c02(myAGC, 0, audioOut, 0);
and expose a method i.e. myAGC.Level( agc level in db?)

Or someway of turning on AGC in the SGTL5000. Which could also handle DAC level control and for that matter some of the filtering.

A level object that would look like AudioConnection c03(audioIn, 0, myLevel, 0);
with a property level_in_db=myLevel.level(), thinking something as simple as sqrt of sum of squares of an input sample block? Some books suggest sqrt of sum of bins squared in an FFT but that seems time consuming.

A frequency object that would do a period based frequency count of a input block. Goertzel is working well for what I need now but I will need higher resolution if I try to do two tone sequential decoding audio range from about 200hz to 2000hz.

I am very pleased with the current library and wish to thank Paul for all his hard work. And if Paul would work up some documentation on the library and some templates I would be happy to try to do some of this myself. Background is in C, I am finding C++ code in the library hard going :)

scswift
01-27-2014, 11:00 PM
I believe the pop is happening because the last small chunk of the file isn't playing. If the waveform isn't at zero, there's an abrupt transition back to zero.

Try adding a tenth of a second of silence onto the end of your file as a quick fix.

Ah, that's easy enough. I thought the problem was that when it played the final block at the end there was some garbage data. My files do fade out though... I'm skeptical that a second of silence will help, but I'll give it a shot and let you know if it worked.

But even if adding a second of silence does work, wouldn't the following also cause a pop?



void AudioPlaySDcardWAV::stop(void)
{
__disable_irq();
if (state != STATE_STOP) {
state = STATE_STOP;
__enable_irq();
wavfile.close();
} else {
__enable_irq();
}
}


Stopping the file ought to cause just as abrupt a change as cutting the wav file off just before the end.

el_supremo
01-28-2014, 12:45 AM
I've attached a sketch I wrote to process stereo samples from the line-in, change them a bit and then output them to the headphones but I get no output at all. I am not familiar with C++ so I've had some fun sorting out how the examples work. I thought I had them figured out but obviously not.
Would some kind soul take a look at my attached sketch and show me what's wrong?

Thanks
Pete

el_supremo
01-28-2014, 01:12 AM
P.S. I forgot to mention that the code does not even start up properly because it doesn't print "A" and "setup done".

Pete

robsoles
01-28-2014, 02:28 AM
...

Things I would like to see included in the library:
Level control of the DAC

...

Or someway of turning on AGC in the SGTL5000. Which could also handle DAC level control and for that matter some of the filtering.

...AVC appears to be SGTL5000 documented equivalent of AGC. Would SGTL5000 AVC like;
...
AudioControlSGTL5000 sgtl;
...
void setup()
{
sgtl.init();
... // commands to enable DAP etc etc (AGC is part of DAP in SGTL5000)...
sgtl.avc(enable,maxGain,lbiResponse,hardLimitEnabl e,threshold,attack,decay);
} be any good? Of course with examples and explanations etc etc.

Would DAC level control like;
void loop()
{
...
sgtl.dacVol((float)n); // n=0 to 100...
...
sgtl.dacVolRight((float)n);
sgtl.dacVolLeft((float)n);
...
} be ok too?

el_supremo
01-28-2014, 02:46 AM
I've found the problem. The beginning of the update function should look like this:

l_block = receiveWritable(0);
r_block = receiveWritable(1);

if(!l_block || !r_block) {
if(l_block)release(l_block);
if(r_block)release(r_block);
return;
}

lp = l_block->data;
rp = r_block->data;

Pete

cartere
01-28-2014, 03:11 AM
AVC appears to be SGTL5000 documented equivalent of AGC. Would SGTL5000 AVC like;
...
AudioControlSGTL5000 sgtl;
...
void setup()
{
sgtl.init();
... // commands to enable DAP etc etc (AGC is part of DAP in SGTL5000)...
sgtl.avc(enable,maxGain,lbiResponse,hardLimitEnabl e,threshold,attack,decay);
} be any good? Of course with examples and explanations etc etc.

Would DAC level control like;
void loop()
{
...
sgtl.dacVol((float)n); // n=0 to 100...
...
sgtl.dacVolRight((float)n);
sgtl.dacVolLeft((float)n);
...
} be ok too?

That would be great. Have you received your teensy 3.1 and audio shield yet?

robsoles
01-28-2014, 03:59 AM
No, but that wouldn't stop me posting you temporary solutions in the next couple of days, if you want 'em quicker...

I found (well, accidentally noticed) an error in the 'filterCalc' routine, part of the last .ino I posted, so I post correction to the error and implementation of (hopefully) even better way of updating the 'live' parameters in the loop();

scswift
01-28-2014, 07:09 AM
Well, I added a second of silence to the end of the audio file, and that seems to have done the trick, so thanks for suggesting that.

I can't say I'm not perplexed by this behavior though. As far as I can tell you're reading 512 samples at a time from the SD card, but you only process 128 samples at a time in the WAV reader, so the most that should ever be thrown away is 128.

Even assuming 512 samples are tossed though, at a sample rate of 44100hz, that's only 1/100th of a second of audio. But the last 3/100th's second of my audio file are silent already.

scswift
01-28-2014, 09:46 AM
I'm not sure if this is already in there, but a nice feature I just thought of would be the ability to calculate the volume of the audio at a particular point in time. Since a true moving average is probably not possible with only a small part o the waveform in memory at once, I was thinking you could do a weighted average, adding absolute value of the current sample to the previous average, multiplied by some user specified weight, say 99, and then divide the result by that plus one to calculate the new average. A loud section would then not immediately affect the average but if it continued for a while, it would pull it up.

This could be useful for a vu-meter, or for adjusting the brightness of an led, or the strength of a vibration motor to match the loudness of a sound.

mxxx
01-28-2014, 10:19 AM
1. The ability to seamlessly loop a sound.



that sounds like a useful feature. i didn't get round to spending a lot of time on the API, but from what i saw, indeed it won't be possible without coming up with a dedicated function that interleaves the loop. ideally, one would be able to set the loop points (in samples or milliseconds).


void AudioPlaySDcardWAV::play_loop(uint32_t loop_start, uint32_t loop_end)

one thing i was wondering about -- perhaps at some point it could be extended to adjust the playback speed, assuming the arm chip can pull that off in realtime?
(i've seen el_supremo came up with a resampling sketch, but i figure that's meant to resample the entire file)

PaulStoffregen
01-28-2014, 12:38 PM
I'm not sure if this is already in there, but a nice feature I just thought of would be the ability to calculate the volume of the audio at a particular point in time.
......
This could be useful for a vu-meter, or for adjusting the brightness of an led, or the strength of a vibration motor to match the loudness of a sound.

I actually started working on that several days ago, but put it aside for a while after I ran into some troubles with response times and numerical precision. Eventually I'll get this added to the library.

Likewise, I'm considering adding fading and panning objects. Those should really help with applications where you want to have multiple sources with smooth transitions between them.

Derangedgamer123
01-28-2014, 03:06 PM
Ooops I attached this to some powered CPU speakers.... I hope its still good :-(


The audio jack is a headphone output. Don't attach it to an amplifier, or it may damage the chip. The line out pins are a separate output which it is safe to attach an amp to.

linuxgeek
01-28-2014, 05:48 PM
I don't see an example to do an audio recording. With the new audio library, what is the fastest sample rate that can be sampled and written to an sdcard? Are there are any issues with using DMA for ADC, as well as the sdcard?

linuxgeek
01-28-2014, 09:00 PM
I have a more pressing issue at the moment that I need to focus on anyway; that popping noise at the end of each song.


I think the easiest thing to do is to truncate the audio stream at the last zero-crossing. It will likely be only a few millisecond at most.

robsoles
01-28-2014, 10:13 PM
Ooops I attached this to some powered CPU speakers.... I hope its still good :-(fwiw I work in the electronics industry and will not, as I have not, hesitate to connect anything with a headphone jack to powered amplifiers using it - I haven't yet seen a fully qualified electronics engineer hesitate to connect power amplifiers to headphone jacks on a variety of devices either.

ipods (blech) & mp3 players (and modern phones etc) would all lose a fair degree of lustre if playing them through amplifiers via their headphone sockets harmed them.

I'd make a point of the volume control being set on the source device to output lower than 'line level' and compensate with the volume control on the powered amp to err on the side of caution but that is about all the caution I would bother with.

PaulStoffregen
01-28-2014, 10:39 PM
The headphone output uses a "virtual ground", which is really about 1.5 volts DC. There's a caution message about this on the bottom side of the PCB.

If you connect the headphone output to something that has a floating ground, like amplified computer speakers powered from a wall wart or a battery, it's perfectly fine. Likewise, if Teensy is powered from a battery or floating supply, it's fine too.

Where things can go badly is if Teensy is grounded, which is usually the case with a USB cable connected to a computer running from AC mains power, AND if the amplifier is grounded. Then the 1.5V DC from the headphone VGND connects to GND. The chip has over-current detection and shut down, so the odds of damage are slim. But it won't work, and it does stress the chip slightly, and the current limit is around 100mA which is at about the max current Teensy's built-in 3.3V regulator can provide.

cartere
01-29-2014, 12:42 AM
No, but that wouldn't stop me posting you temporary solutions in the next couple of days, if you want 'em quicker...

I found (well, accidentally noticed) an error in the 'filterCalc' routine, part of the last .ino I posted, so I post correction to the error and implementation of (hopefully) even better way of updating the 'live' parameters in the loop();

Better, still some radom clicks and you hear clicks when the pot is rotated, but much less noticeable.

zeevj
01-29-2014, 10:11 AM
I don't see an example to do an audio recording. With the new audio library, what is the fastest sample rate that can be sampled and written to an sdcard? Are there are any issues with using DMA for ADC, as well as the sdcard?

Same here, i'm also trying to answer this question.
meanwhile i'm trying to write an audio block to an SD card every ::update, was able to write something (random data, not audio).

Any information regarding SD card file recording will be great :)

scswift
01-29-2014, 11:40 AM
I believe what Paul is saying is, when using the headphone jack to tie the two together (but not the line out, that's fine), if the Teensy and amplifier are connected to the SAME ground that's bad.

IE:
1. Amplifier connected to same battery as Teensy - BAD
2. Amplifier connected to a separate battery, but with a common ground - BAD
3. Amplifier connected to separate battery, and with no common ground - GOOD
4. Amplifier powered by wall wart, Teensy powered from same wall wart - BAD
5. Amplifier powered by wall wart, Teensy powered by USB with laptop NOT plugged in - GOOD
6. Amplifier powered by wall wart, Teensy powered by USB with laptop plugged in - BAD, if wall wart and laptop both have a ground pin on the plug?
7. Amplifier powered by wall wart, Teensy powered by a second wall wart - BAD, if both wall warts have ground pin on the plugs?

Again note, these only apply to using the headphone jack to tie the two together. Any of the above combos is fine with the Line Out pins.

cartere
01-29-2014, 02:31 PM
Just a thought, have not tried it, bridge the two grounds with a large(ish) cap and reference everything to GND.

scswift
01-29-2014, 06:34 PM
I'm getting some popping noises in my audio, and I think I've tracked it down to something being done in the library to drive this:
http://www.adafruit.com/products/1455

The library is here:
https://github.com/adafruit/Adafruit_TLC59711

It's using a form of software SPI, and there are some delayMicroseconds() commands, and a call to cli() in the update function. Could one of these be affecting the audio library? I thought cli() didn't even work on the Teensy 3.1? I guess I can try removing those, I don't think it will affect the function...

PaulStoffregen
01-29-2014, 06:56 PM
Could one of these be affecting the audio library?


Yes.



I thought cli() didn't even work on the Teensy 3.1?


It does indeed work on Teensy 3.1.



I guess I can try removing those, I don't think it will affect the function...

Yes, I would try that first, since it's so easy to just comment out that cli() line.

If you're playing from the SD card, you must use the slower bit-bash constructor (the one where you give it all 3 pin numbers), and of course use 3 pins that aren't used by the audio shield or SD card.

scswift
01-29-2014, 07:02 PM
Yes.
Yes, I would try that first, since it's so easy to just comment out that cli() line.


So you think it's cli() causing the issue then and not delaymicroseconds()?



If you're playing from the SD card, you must use the slower bit-bash constructor (the one where you give it all 3 pin numbers), and of course use 3 pins that aren't used by the audio shield or SD card.


Yep, I'm using that. Other than the pops I was getting with certain songs, everything works great.

scswift
01-29-2014, 07:12 PM
Well I think that did the trick, but I seem to have another bug where sometimes sounds fail to play. I need to do some more testing though to see what's going on. I don't think it's my code (it's pretty simple) but it could be.

scswift
01-29-2014, 07:15 PM
Do I need to call SD.begin(10); every time I play a wav file, or just once at the start of my code? I only call it once right now.

scswift
01-29-2014, 07:27 PM
I'm doing PWM on pin 21 for motor control if that matters:
analogWriteFrequency(21, 46875);

scswift
01-29-2014, 07:40 PM
Well this isn't good, I put SD.begin(10); before each call to wav.play() and it's still doing the same thing. It plays 4 or 5 files and then craps out. I assume calling wav.play() with the same object uses single sound channel so I don't need to stop it before playing the next file. It was working fine until very recently. And my own code is still running as expected. It's not the sound files at fault either, because when the cycle ends it repeats but the sound never comes back on. Besides, I haven't modified them.

scswift
01-29-2014, 07:50 PM
I removed SD.begin() and put a wav.stop() before each wav.play() but I'm still losing my audio. No idea what's causing this but I wonder if it has something to do with the change to the LED code. I guess I'll disable that and see if the problem goes away.

scswift
01-29-2014, 08:10 PM
Argh! I've disabled both LEDs and motor speed control now. I'm doing no PWM and acessing nothing, just running a state loop that waits a specified period of time and then tells the board to play the next song, and it's still failing to start the next song after the 5th one.

PaulStoffregen
01-29-2014, 08:18 PM
There may be a bug in the WAV player object.

Does it still fail if you trim this to just a small amount of code, similar to the examples that come with the library?

Can you get it to fail using small WAV files? Like small enough to put into a single ZIP file that fits within the forum file upload limits?

I'm working on a fader object now.... so if you can save me some time by trimming this down to the smallest example that locks up, using small WAV files, well, that will maximize the time I spend writing a nice fader object that ought to really benefit your project!

scswift
01-29-2014, 08:35 PM
I'll post some simplified code for you shortly. Testing now to see if it still fails.

scswift
01-29-2014, 08:49 PM
Still fails. Here's the simplified code. I'll PM you a link to the sound files.




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

// Audio:

// Create the Audio components. These should be created in the order data flows, inputs/sources -> processing -> outputs
AudioPlaySDcardWAV 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;


// ----------
// Timing:

unsigned long time; // Current global time in miliseconds.
unsigned long lastTime; // Previous global time.
unsigned long timeDelta; // Time since last loop.
float timeDeltaSec; // Time since last loop in seconds.


void setup() {

// Initialize audio module:

AudioMemory(5); // Audio connections require memory to work. For more detailed information, see the MemoryAndCpuUsage example

audioShield.enable();
audioShield.volume(50); // 0..100
audioShield.unmuteLineout();

SPI.setMOSI(7);
SPI.setSCK(14);

SD.begin(10); // Init SD card for audio lib.

// Record start time.
time = millis();

}

void loop()
{

// Timing:

lastTime = time; // Store start time of last update.
time = millis(); // Get current system time.
timeDelta = time - lastTime; // Calculate how long last update took, in milliseconds.
timeDeltaSec = float(timeDelta) / 1000.0; // Convert last update period into seconds.

updateState();


}

void updateState() {

// Statics: (These are initialized once and their value is maintained on each subsequent loop.)

static int state = 0;

static unsigned long songStart; // Song start time
static byte song = 0;

const unsigned long songTime[] = {63656, 63708, 67522, 63708, 63708, 63708, 63447, 180100};
//const unsigned long songTime[] = {10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000};

const char *songFile[] = {"001.wav", "002.wav", "003.wav", "004.wav", "005.wav", "006.wav", "007.wav", "008.wav"};

switch (state) {

case 0: // Reset

song = 0;
state = 1;

break;


case 1:

songStart = time;
wav.play(songFile[song]);
state = 2;

break;


case 2: // Wait for song to end.

if (time > (songStart + songTime[song])) {

song++; // Next song.
if (song > 7) { song = 0; }

state = 1;

}

break;

}

}

scswift
01-29-2014, 08:55 PM
PaulStoffregen has exceeded their stored private messages quota and cannot accept further messages until they clear some space.

Email it is then!

el_supremo
01-30-2014, 08:52 PM
I've written an addition to the Audio library which computes an FIR filter with user-supplied coefficients using ARM's CMSIS Q15 FIR function. I have mailed it to Paul but it wouldn't hurt if some of you tried it out just in case you find a problem.
It requires that you cut and paste the new code into audio.cpp and Audio.h
I've included a sketch with two filters - a low pass and band pass - to demo the code. The sketch uses two pins, one to select which filter is being used and the other pin selects passthru so that you can easily hear the effect of the filter.
1359
Pete

PaulStoffregen
01-31-2014, 12:37 AM
I've added AudioEffectFade to the library. It works like the other library objects. Create an instance like this:



AudioEffectFade fader;


Then connect audio through it with AudioConnection objects.

It has 2 functions, fadeIn(milliseconds) and fadeOut(milliseconds). For example:



Serial.println("Audio Test");
toneLow.frequency(440);
toneLow.amplitude(0.9);
delay(2500);
fader.fadeOut(1020);
delay(2500);
fader.fadeIn(600);
delay(2500);
fader.fadeOut(2520);
delay(2500);
fader.fadeIn(2000);
delay(2500);
fader.fadeOut(350);

cartere
01-31-2014, 12:44 AM
I've written an addition to the Audio library which computes an FIR filter with user-supplied coefficients using ARM's CMSIS Q15 FIR function. I have mailed it to Paul but it wouldn't hurt if some of you tried it out just in case you find a problem.
It requires that you cut and paste the new code into audio.cpp and Audio.h
I've included a sketch with two filters - a low pass and band pass - to demo the code. The sketch uses two pins, one to select which filter is being used and the other pin selects passthru so that you can easily hear the effect of the filter.
1359
Pete

Pete did you use an online tool to calculate the coefficients ?

PaulStoffregen
01-31-2014, 12:48 AM
I tried your instruction on creating my own audio stream worker object but for some reason, I'm not getting any update call in the audio stream. Attached is my code. Right now it doesn't do much. I just want to see if I can get some audio sample data.


Just a quick followup. This bug was fixed a couple days ago. It's now possible to use AudioInputAnalog without any other input or output objects.

el_supremo
01-31-2014, 01:19 AM
The coefficients were calculated with a program I wrote many years ago. I converted the original Parks-McClellan FORTRAN code to C and then added a graphical interface to run on my Amiga. When I changed to a PC, I updated the interface again.

Pete

PaulStoffregen
01-31-2014, 01:40 AM
Still fails. Here's the simplified code. I'll PM you a link to the sound files.

I believe I found the bug. It's memory leaks.

I just pushed a fix to github. Please give it a try and let me know how this works for you?

https://github.com/PaulStoffregen/Audio

scswift
01-31-2014, 09:39 AM
Paul:
The fix seems to work great, thanks!

scswift
01-31-2014, 10:50 AM
Hey Paul, how's this app for calculating filters?

http://www.ti.com/tool/coefficient-calc#descriptionArea

I don't understand all the parameters, but I clicked enable on no. 1, set type to high pass, butterworth 2, fc = 200, gain I set to 0 cause it doesn't seem to change anything, and BW (bandwidth?) and Q are greyed out so I assume those are standard values for a "butteworth 2" filter. You can drag the little red rectangle around to change the FC and gain and see the result, and from that it appears you can't actually get a gain greater than 0 with this filter, though you can make it negative.

Clicking the coeff button brings up a window with the coefficients, but they're in hex. You can also set the bits. I think maybe it should be set to 32 for your filter? I'm not sure. I also don't know if you need to alter the values or which correspond to which inputs for the filter.

But it seems like a pretty nice tool especially if you want to figure out how multiple filters will act on a single signal.

PaulStoffregen
01-31-2014, 11:06 AM
Hey Paul, how's this app for calculating filters?

Well, it seems to run ok on Linux using Wine, so that's a plus.... ;)



Clicking the coeff button brings up a window with the coefficients, but they're in hex. You can also set the bits. I think maybe it should be set to 32 for your filter? I'm not sure. I also don't know if you need to alter the values or which correspond to which inputs for the filter.


I don't know either. But it does seem like they have an extra factor-of-2 on the 2nd and maybe 4th coefficient. Maybe?

It's unlikely I'll get much time to play with this. But if anyone figures out how to use its output with AnalogFilterBiquad, please post details!!

scswift
01-31-2014, 11:48 AM
Perhaps this will shed some light on it?

http://www.ti.com/lit/an/slaa447/slaa447.pdf

scswift
01-31-2014, 12:01 PM
I should mention that the reason I'm looking into filtering is because I've got small speakers connected to the Teensy and if I don't filter out the low frequencies they sound horrible when you crank the volume up.

And while being able to design any filter is nice, it would be great if there were some simple functions to set up a low pass, mid pass, or high pass filter. Like something that takes simple easy to understand parameters like that webpage and calculates the coefficients for you.

[edit]

Going back through the thread I see Robsoles has begun working on something like that.

PaulStoffregen
01-31-2014, 12:21 PM
..... the pops I was getting with certain songs....

Could you please send me a (hopefully small) WAV file that reproduces the pop sound at the end? The 8 you sent have the extra silence to "fix" the problem.

I'd like to work today on fixing the pop-at-end bug. I know what's causing it, so I can probably fix it, but having a test file that reproduces the problem would really help me to confirm the fix.

scswift
01-31-2014, 02:40 PM
I emailed you a wav which doesn't have the extra silence at the end.

PaulStoffregen
01-31-2014, 03:57 PM
Thanks. My goal for today is fixing this bug.

scswift
01-31-2014, 07:31 PM
I want to implement this high pass filter, but this is bugging the hell out of me:

AudioMemory(12);

I get that it allocates 12 copies of this structure:


typedef struct audio_block_struct {
unsigned char ref_count;
unsigned char memory_pool_index;
int16_t data[AUDIO_BLOCK_SAMPLES];
} audio_block_t;


But I can't for the life of me see any rhyme or reason to why the wav player example allocates 5, the midi example allocates 15 and the filter example allocates 12.

I would assume in the wav player example since it's playing stereo you have two buffers for the input,, left and right, and two for the output. That's four. So why allocate 5?

And with the filter example, only the left input is used, but I guess maybe it has to allocate both? So that's two. Then you've got the filter. Again done on only one channel, but we'll say two for that. And then the output needs two. So worst case it seems like there should be six, not 12.

Are you allocating extra memory because you didn't feel like calculating exactly how much was needed for each example, or is there something else going on behind the scenes that requires the additional buffers?

PaulStoffregen
01-31-2014, 08:21 PM
But I can't for the life of me see any rhyme or reason to why the wav player example allocates 5, the midi example allocates 15 and the filter example allocates 12.


Ah yes, this is of course part of the joy of being an early adopter with beta test code before documentation is written.

Really, truly, I'm going to stop working on the code next week and write documentation....

In the meantime, maybe this will help. These 6 functions allow you to see the library's resource usage:

AudioProcessorUsage()
AudioProcessorUsageMax()
AudioProcessorUsageMaxReset()
AudioMemoryUsage()
AudioMemoryUsageMax()
AudioMemoryUsageMaxReset()

The CPU usage is an integer from 0 to 100, and the memory is from 0 to however many blocks you provided with AudioMemory().

The normal version tell you the amount used at that moment. The "Max" one tells you the maximum that has ever been used, which is really much more useful. The "MaxReset" ones reset the maximum value.




Are you allocating extra memory because you didn't feel like calculating exactly how much was needed for each example,


Yes, exactly.

And honestly, that's the general approach. Allocate a bunch of memory and see (hear) if things work. If you want to know how much is really needed, put something like this in loop()



Serial.println(AudioMemoryUsageMax());


But consider the memory usage can vary if you build a complex audio path. Some objects like the waveform generator only allocate memory when they're generating output. If you connect an object's out to multiple inputs, the library uses shared copy-on-write memory management, so the amount of memory actually allocated depends on whether the receiving objects request write access. Some objects, like the new fader, use different access at different times (eg, read-only when not changing the level, but writeable while fading in or out). Memory usage can also change depending on the order you create the objects, which determines the order they are updated and push data to each other.

The library has AudioMemoryUsageMax() so you can observe how much memory has actually be used at the worst case.

scswift
01-31-2014, 09:01 PM
I'm having a new issue.

I'm trying to get a high pass filter working, but something's not right.

Here's my setup code:



int HPF[8];

// Create the Audio components. These should be created in the order data flows, inputs/sources -> processing -> outputs
AudioPlaySDcardWAV wav;
AudioFilterBiquad filter(HPF);
AudioOutputI2S dac;

// Create Audio connections between the components

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

AudioConnection c1(wav, 0, filter, 0); // Left
AudioConnection c2(wav, 1, filter, 1); // Right
AudioConnection c3(filter, 0, dac, 0);
AudioConnection c4(filter, 1, dac, 1);

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




void calcFilter(int * f, float a0, float a1, float a2, float b1, float b2) {

const int n = 1073741824; // 2^30

f[0] = a0 * n;
f[1] = a1 * n;
f[2] = a2 * n;
f[3] = b1 * -n;
f[4] = b2 * -n;
f[5] = 0;
f[6] = 0;
f[7] = 0;

}




oid setup() {

//Serial.begin(9600);
//Serial.println("Debug");

// Initialize audio module:

AudioMemory(12); // (5) Audio connections require memory to work. For more detailed information, see the MemoryAndCpuUsage example

audioShield.enable();
audioShield.volume(100); // 0..100
audioShield.unmuteLineout();

calcFilter(HPF, 0.9899745214054891, -1.9799490428109783, 0.9899745214054891, -1.9798485601163545, 0.9800495255056021); // 100hz HPF

SPI.setMOSI(7);
SPI.setSCK(14);

SD.begin(10); // Init SD card for audio lib.



I tried it first without my filterCalc function, using the values you used in the example for the low pass filter, but the audio on my line out was super quiet, and the headphones were fairly quiet as well. Thinking this might simply be due to the audio at those frequencies being quiet, I then tried changing the parameters to create a high pass filter, and the audio on my headphones is now loud, but the left ear seems a lot louder than the right, and I'm getting no sound on the line out now.

Have I done something obviously wrong that you can see above, or should I put together another test program so you can find the bug?

My wav file is mono by the way, in case that's important. Same files as I sent you yesterday.

scswift
01-31-2014, 09:16 PM
The library has AudioMemoryUsageMax() so you can observe how much memory has actually be used at the worst case.

With a complex filter setup, like if you were making a synth or an S3M player, is it even possible to know for certain that you've seen the worst case though?

And why do we allocate the memory in advance instead of the library automatically allocating it as needed?

scswift
02-01-2014, 11:22 AM
Okay, so here's that simple test code again that used the numbered wav files, but this time with the filtering stuff I can't get to work in there. The comments explain the strange behavior I get with different setups:



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

// Audio:

int HPF[8] = {1062977024, -2125954048, 1062977024, 2125846144, -1052320192, 0, 0 , 0};

// Create the Audio components. These should be created in the order data flows, inputs/sources -> processing -> outputs
AudioPlaySDcardWAV wav;
AudioFilterBiquad filter(HPF);
AudioOutputI2S dac;

// Create Audio connections between the components

// Use this setup, sounds normal.

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

// Use this setup, sound from left ear at full volume, right at half.

// Comment these out get no audio.
AudioConnection c1(wav, 0, filter, 0); // Left
AudioConnection c2(filter, 0, dac, 0);

// Comment these out, still get full volume left ear, half volume right even though there should be no input to right channel.
AudioConnection c3(wav, 1, filter, 1); // Right
AudioConnection c4(filter, 1, dac, 1);

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


// ----------
// Timing:

unsigned long time; // Current global time in miliseconds.
unsigned long lastTime; // Previous global time.
unsigned long timeDelta; // Time since last loop.
float timeDeltaSec; // Time since last loop in seconds.


void setup() {

// Initialize audio module:

AudioMemory(12); // Audio connections require memory to work. For more detailed information, see the MemoryAndCpuUsage example

audioShield.enable();
audioShield.volume(100); // 0..100
audioShield.unmuteLineout();

SPI.setMOSI(7);
SPI.setSCK(14);

SD.begin(10); // Init SD card for audio lib.

// Record start time.
time = millis();

}

void loop()
{

// Timing:

lastTime = time; // Store start time of last update.
time = millis(); // Get current system time.
timeDelta = time - lastTime; // Calculate how long last update took, in milliseconds.
timeDeltaSec = float(timeDelta) / 1000.0; // Convert last update period into seconds.

updateState();


}






void updateState() {

// Statics: (These are initialized once and their value is maintained on each subsequent loop.)

static int state = 0;

static unsigned long songStart; // Song start time
static byte song = 0;

const unsigned long songTime[] = {63656, 63708, 67522, 63708, 63708, 63708, 63447, 180100};
//const unsigned long songTime[] = {10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000};

const char *songFile[] = {"001.wav", "002.wav", "003.wav", "004.wav", "005.wav", "006.wav", "007.wav", "008.wav"};

switch (state) {

case 0: // Reset

song = 0;
state = 1;

break;


case 1:

songStart = time;
wav.play(songFile[song]);
state = 2;

break;


case 2: // Wait for song to end.

if (time > (songStart + songTime[song])) {

song++; // Next song.
if (song > 7) { song = 0; }

state = 1;

}

break;

}

}


Only thing changed here is the setup for the audio in the first bit of code.

scswift
02-01-2014, 12:35 PM
Oh I forgot to mention, with the above filter code in place I no longer get any output on my line out. Only the headphones work.

opcode
02-02-2014, 04:21 AM
I have a transducer hooked through an amplifier that is hooked to a teensy 3.1's analog output, I do not have the teensy 3's audio board. I copied most of the "PlayFromSketch" example and for my use I only tested with 1 sound playing on a button push. When I trigger on falling edge on the button I play the "AudioSampleKick", I feel the transducer kick once, then it stops.

I was under the impression that after you do a "play" on an audio source it would play to the end, in my case it appears to only play once. The only difference in my case from the example is I don't do anything with setting up the AudioShield object at all (as I don't have one). Any ideas here on what I might be missing?

cartere
02-02-2014, 01:59 PM
Only plays once, very short sound stored in ram. sounds like one tap on a drum.

PaulStoffregen
02-03-2014, 07:41 PM
When I trigger on falling edge on the button I play the "AudioSampleKick", I feel the transducer kick once, then it stops.


Yes, exactly. The "Kick" sample is a single hit on a kick drum. You can follow that link to the freesounds website to hear what it's supposed to do. In fact, here's the link:

http://www.freesound.org/people/DWSD/sounds/171104/

For a longer sound, try the gong (triggered by pin 4) which plays for 10 seconds, or the cash register (triggered by pin 5).

Kondi
02-05-2014, 03:15 PM
Greetings!

I've just began familiarizing myself with this lib, and stuck a little bit. I can't get to understand how does the "Miditones.ino" example sketch work (and what is the function exactly). Anybody could give me a brief explanation? I'm kind of a beginner in coding, but one thing instantly meets my eye is that there is an "audioSyntWaveform" object named "sine2" declared but it has no connections, and i don't see it being used anywhere.

Thanks a lot in advance.

el_supremo
02-05-2014, 04:26 PM
Start with this minimal version of that sketch. I've removed all the extraneous code and comments.

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

AudioSynthWaveform mysine(AudioWaveformSine);

AudioOutputI2S dac;

AudioControlSGTL5000 codec;

// Connect the tone to the left and right channels
// The original code only output to the left channel
AudioConnection c1(mysine, 0, dac, 0);
AudioConnection c2(mysine, 0, dac, 1);

void setup() {
Serial.begin(115200);
while (!Serial) ;
delay(2000);
Serial.println("***************");

// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(15);

codec.enable();
codec.volume(50);

Serial.println("Begin AudioTest");

mysine.frequency(440);
mysine.amplitude(.8);
delay(1000);
mysine.amplitude(0);

}

void loop()
{
}

This generates a 440Hz tone for one second.

Pete

Kondi
02-05-2014, 04:37 PM
Start with this minimal version of that sketch. I've removed all the extraneous code and comments.

...

This generates a 440Hz tone for one second.

Pete

Yes, i understand that. Also, You've modified the code and get rid of the unused "sine2" object, and attached the output of "mysine" to the other input of the dac object. As far as i understand things going on, shouldn't these things be this way in the example sketch?

Becasue this is the behaviour i was expecting after looking at the code, but from the name (and the "william_tell_overture.c" attachment wich i believe is some midi note data converted to raw progmem code?!) I assumed that the "mysine" object would generate some sine waves according to the note information in the attached .c file.

el_supremo
02-05-2014, 05:37 PM
The example sketch was probably an early version of something that hasn't been finished yet. That particular example doesn't show up in the Arduino IDE list unless you change the name of the directory and/or the .ino so that they are the same.
I might try to write code to play that tune, but don't hold your breath.

You might find the DialTone_DTMF example useful. It generates two tones and sends them to the mixer.

Pete

PaulStoffregen
02-05-2014, 06:26 PM
The example sketch was probably an early version of something that hasn't been finished yet.

Yes, exactly. The library is very "beta" at the moment.

scswift
02-05-2014, 08:13 PM
Any luck with those pops? Or the problem I'm having with the filter?

robsoles
02-05-2014, 09:57 PM
Teensy 3.1 + Audio Adapter have arrived (thanks again Paul), I've attached them and tested round trip (line-in, apply-filter(ok, no noticeable artifacts on changes)-and-volume, headphone-out) which seemed to work pretty good.

I am still writing bits and pieces and testing stuff, possibly as much as a week before I make my first pull request but I might try to get one in by tomorrow night my time (is 8:50am Thursday where I am now) with some of the filter stuff.

el_supremo
02-05-2014, 10:36 PM
@Kondi
I've attached a modified PlayMidiTones sketch which plays the midi file. It uses the library's AudioSynthWaveform sine wave generators so it clicks a lot when tones are turned on or off. It would be better if much of the tone generation was done entirely within the library so that the onset and offset of a tone could be ramped to prevent the click.
Someday, maybe :)

1382
Pete

Kondi
02-05-2014, 10:37 PM
Pete

Thanks for the reply, I tought I was looking over something.

Another question: I believe the nature of the sampling should introduce 2x2.9=5.8ms RTL (round trip latency). I don't have the proper equipment, could anybody measure this?

I think what a really useful feature would be is to be able to create zero-latency pass through connections, to be able to implement DI box-like functionality. For example, to have an AudioInputAnalog/audioInputi2s sub-class that would forward each individual sample right after( in the next DAC cycle to be axact) the ADC cycle to one of the output candidates, and besides that, after all the 128 samples are gathered for the audio block, it could pass it through for further mangling in a regular update call.

I don't know if the implementation of audio streams would enable this, but i'm sure this would be a very useful feature.


@Kondi
I've attached a modified PlayMidiTones sketch which plays the midi file. It uses the library's AudioSynthWaveform sine wave generators so it clicks a lot when tones are turned on or off. It would be better if much of the tone generation was done entirely within the library so that the onset and offset of a tone could be ramped to prevent the click.
Someday, maybe :)

1382
Pete

That was fast, but unfortunately i didn't recieve my audio board yet so i can't try it. I was looking at that example because i'm starting to create some new audio effect objects, wich would involve MIDI. But unfortunately, interpreting MIDI is not so easy, so i think i will go the music-XML way.

PaulStoffregen
02-06-2014, 12:00 AM
I think what a really useful feature would be is to be able to create zero-latency pass through connections, to be able to implement DI box-like functionality. For example, to have an AudioInputAnalog/audioInputi2s sub-class that would forward each individual sample right after( in the next DAC cycle to be axact) the ADC cycle to one of the output candidates, and besides that, after all the 128 samples are gathered for the audio block, it could pass it through for further mangling in a regular update call.

That would be a whole new library. Maybe some of the existing code might help you get a start. I do not know if the DMA channels will help. You'll probably need to use the I2S interrupts. Processing individual samples will not be nearly as efficient, but the ARM chip is pretty fast, so you still might manage to get it to do something useful.

But do not fool yourself into thinking this might be possible in the context of this audio library, which is fundamentally based on block processing.

scswift
02-06-2014, 02:09 PM
I've discovered another bug. Volume control does not seem to work on the line out:



audioShield.enable();
audioShield.unmuteLineout();
audioShield.volume(5);


I tried unmuting the line out before and after changing the volume and in neither case did it have any effect.

robsoles
02-06-2014, 09:13 PM
I've discovered another bug. Volume control does not seem to work on the line out:



audioShield.enable();
audioShield.unmuteLineout();
audioShield.volume(5);


I tried unmuting the line out before and after changing the volume and in neither case did it have any effect.

As far as I can tell that is intentional and that is logical - proper line-out is more or less 'set appropriate level and forget' kind of stuff. I am going to add both line-out level control and DAC level control (soon!) but not to be used as volume controls.

scswift
02-06-2014, 10:06 PM
As far as I can tell that is intentional and that is logical - proper line-out is more or less 'set appropriate level and forget' kind of stuff. I am going to add both line-out level control and DAC level control (soon!) but not to be used as volume controls.

What constitutes a "proper" line out? These days, a line out and a headphone jack are virtually interchangeable. According to the codec's data sheet, the headphone jack on the audio shield could have functioned as one if Paul added a couple capacitors.

Also, I'm pretty sure on any PC out there the line out's volume level changes as you change the mixer settings on the PC.

What's so terrible about allowing the line out volume level to be adjustable anyway? "Standards" be damned. Standards change. The standard now is to have a jack that can function both as a headphone and line out.

Anyway, we need volume control on the line out because otherwise, if we want to connect an amplifier that is powered from the same power source as the Teensy, we would have to change every channel's gain individually to hack volume control in.

If you don't want to change the volume level of the line out, just don't change it. But I need that capability on both the line out and the DAC output. (I have not tested the DAC output yet, but now I'm wondering if that has volume control either.) It's silly not to have the feature. It's useful, and necessary when one is using an amp. Not all amplifier boards have volume controls and even when they do they're not always conveniently located so being able to connect a volume pot to the teensy to adjust the volume instead is nice.

robsoles
02-06-2014, 10:25 PM
If the functions for DAC volume control and Line-out level control I am going to submit to the library are accepted then they will give you this functionality basically.

The DAC volume control will influence both HP and LO outputs the same and the routines (left, right, both) that I have written to control them will give you functionality more or less just like the PC if you use those routines instead, they use '0-100'% - this will not override the HP volume level setting, that is applied after the DAC; similarly the LO level adjustment is applied after the DAC as well.

The LO level control routines I have written don't use the '0-100'% because the 'right' value for LO_VOL_CONTROL_LEFT/RIGHT calculates to 0x0D according to the datasheet (and my, albeit potentially limited, ability to understand it) and it is to attenuate the output to 'full-scale' for a Line-out. The datasheet goes on to say that once the 'correct' value has been determined the value can be varied to attenuate the signal(s) in +/- 0.5dB steps but my logic says that if 0x0D should make an equivalent of 0dB then you can only make attenuation between -(13*0.5) and +((31-13)*0.5) dB using this control.

My statement '... but not to be used as volume controls' was wrong, obviously the DAC volume control is just that, a volume control :o

robsoles
02-07-2014, 02:54 AM
I haven't tested everything I've done to my copy of Audio.cpp & Audio.h yet, and I haven't written a decent set of examples either, so I am not rushing to make a pull request but if anybody wants to take a peek at my initial additions/mods for the library they can look at my (albeit, I expect, temporary) fork at https://github.com/robsoles/Audio

Only testing by listening to headphones with an appropriate source on Line-in, the following sketch (compiled against my copy of the library) appears to meet my expectations - I am about to be dragged, kicking and screaming, roughly 1600 kilometres from anything I could use to more thoroughly test (and write sketches to test) my stuff for about three days so I figure I'll let anyone interested try this stuff sooner rather than later.
// filterCalc test sketch..

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


//For Filter Type: 0 = LPF, 1 = HPF, 2 = BPF, 3 = NOTCH, 4 = PeakingEQ, 5 = LowShelf, 6 = HighShelf
#define FILTER_LOPASS 0
#define FILTER_HIPASS 1
#define FILTER_BANDPASS 2
#define FILTER_NOTCH 3
#define FILTER_PARAEQ 4
#define FILTER_LOSHELF 5
#define FILTER_HISHELF 6


/*
typedef union
{
// uint32_t uint1;
unsigned long uint1;
// int32_t int1;
long int1;
} transInt;
*/

const int myInput = AUDIO_INPUT_LINEIN;
// const int myInput = AUDIO_INPUT_MIC;

// each filter requires a set up parameters
int BassFilterParameters[] = { // notch, Fc=2175 Hz
896456695, -1707514503, 896456695, 1707514503, -719171567, 0, 0, 0};
int TrebFilterParameters[]={0,0,0,0,0};

int updateFilter[5];

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

AudioInputI2S audioInput; // audio shield: mic or line-in
AudioFilterBiquad BassFilter(BassFilterParameters);
AudioOutputI2S audioOutput; // audio shield: headphones & line-out



// Create Audio connections between the components
//
AudioConnection c1(audioInput, 0, audioOutput, 0);
AudioConnection c2(audioInput, 0, BassFilter, 0);
AudioConnection c3(BassFilter, 0, audioOutput, 1);

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



void filterCalc(uint8_t filtertype, float fC, float dB_Gain, float Q, uint32_t quantization_unit, uint32_t fS, int *coef)
{

// I used resources like http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
// to make this routine, I tested most of the filter types and they worked. Such filters have limits and
// before calling this routine with varying values the end user should check that those values are limited
// to valid results.

float A;
if(filtertype<FILTER_PARAEQ) A=pow(10,dB_Gain/20); else A=pow(10,dB_Gain/40);
float W0 = 2*3.14159265358979323846*fC/fS;
float cosw=cos(W0);
float sinw=sin(W0);
//float alpha = sinw*sinh((log(2)/2)*BW*W0/sinw);
//float beta = sqrt(2*A);
float alpha = sinw / (2 * Q);
float beta = sqrt(A)/Q;
float b0,b1,b2,a0,a1,a2;

switch(filtertype) {
case FILTER_LOPASS:
b0 = (1.0F - cosw) * 0.5F; // =(1-COS($H$2))/2
b1 = 1.0F - cosw;
b2 = (1.0F - cosw) * 0.5F;
a0 = 1.0F + alpha;
a1 = 2.0F * cosw;
a2 = alpha - 1.0F;
break;
case FILTER_HIPASS:
b0 = (1.0F + cosw) * 0.5F;
b1 = -(cosw + 1.0F);
b2 = (1.0F + cosw) * 0.5F;
a0 = 1.0F + alpha;
a1 = 2.0F * cosw;
a2 = alpha - 1.0F;
break;
case FILTER_BANDPASS:
b0 = alpha;
b1 = 0.0F;
b2 = -alpha;
a0 = 1.0F + alpha;
a1 = 2.0F * cosw;
a2 = alpha - 1.0F;
break;
case FILTER_NOTCH:
b0=1;
b1=-2*cosw;
b2=1;
a0=1+alpha;
a1=2*cosw;
a2=-(1-alpha);
break;
case FILTER_PARAEQ:
b0 = 1 + (alpha*A);
b1 =-2 * cosw;
b2 = 1 - (alpha*A);
a0 = 1 + (alpha/A);
a1 = 2 * cosw;
a2 =-(1-(alpha/A));
break;
case FILTER_LOSHELF:
b0 = A * ((A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw));
b1 = 2.0F * A * ((A-1.0F) - ((A+1.0F)*cosw));
b2 = A * ((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
a0 = (A+1.0F) + ((A-1.0F)*cosw) + (beta*sinw);
a1 = 2.0F * ((A-1.0F) + ((A+1.0F)*cosw));
a2 = -((A+1.0F) + ((A-1.0F)*cosw) - (beta*sinw));
break;
case FILTER_HISHELF:
b0 = A * ((A+1.0F) + ((A-1.0F)*cosw) + (beta*sinw));
b1 = -2.0F * A * ((A-1.0F) + ((A+1.0F)*cosw));
b2 = A * ((A+1.0F) + ((A-1.0F)*cosw) - (beta*sinw));
a0 = (A+1.0F) - ((A-1.0F)*cosw) + (beta*sinw);
a1 = -2.0F * ((A-1.0F) - ((A+1.0F)*cosw));
a2 = -((A+1.0F) - ((A-1.0F)*cosw) - (beta*sinw));
}

a0=(a0*2)/(float)quantization_unit; // once here instead of five times there...
b0/=a0;
*coef++=(int)(b0+0.499);
b1/=a0;
*coef++=(int)(b1+0.499);
b2/=a0;
*coef++=(int)(b2+0.499);
a1/=a0;
*coef++=(int)(a1+0.499);
a2/=a0;
*coef++=(int)(a2+0.499);
}


void setup() {
// Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example
AudioMemory(12);
// Enable the audio shield and set the output volume.
audioShield.enable();
audioShield.inputSelect(myInput);
audioShield.route(1,1); // using I2S, using DAP
audioShield.dap_audio_eq(1); // using Biquad filters
audioShield.dap_peqs(1); // only using one of the filters.
filterCalc(FILTER_PARAEQ, 3300, 0, 0.2, 524288, 44100,TrebFilterParameters);
audioShield.load_peq(0,TrebFilterParameters);
audioShield.volume(60);
}

elapsedMillis chgMsec=0;
float lastbass=0;
float lastvol=0;

void loop() {
// every 10 ms, check for adjustment the tone & vol
if (chgMsec > 10) { // more regular updates for actual changes seems better.
float bass1 = analogRead(16);
bass1=((bass1-512)/512)*60;
bass1=(int)bass1;
bass1=bass1/10; // only changing for 0.1dB steps
if(lastbass!=bass1)
{
filterCalc(FILTER_PARAEQ, 220, bass1, 0.2, 2147483648, 44100, updateFilter);
filterCalc(FILTER_PARAEQ, 3300, -bass1, 0.2, 524288, 44100, TrebFilterParameters);
BassFilter.updateCoefs(updateFilter); // load for AudioBiquadFilter
audioShield.load_peq(0,TrebFilterParameters); // load for SGTL5000 PEQ.
lastbass=bass1;
}

float vol1=analogRead(15)/1.024;
vol1=(int)vol1;
vol1=vol1/10;
if(lastvol!=vol1)
{
audioShield.volume(vol1);
lastvol=vol1;
}
chgMsec = 0;
}
}Anything I've added to my copy of the library that isn't used in that sketch should be considered untested.

If you try the above sketch I want your feedback please. If you try any of the 'untested' stuff in my fork I want your feedback twice as much ;)

I will write tests/examples for the other bits and pieces as soon as practical for me. When opportunity allows I am going to use some audio sweeping equipment at my work to more thoroughly test filters and other details.

robsoles
02-07-2014, 03:56 AM
oh (facepalm) I have 3 pots connected to my teensy 3.1, am on a train atm, later I will borrow a desktop and post replacement loop() for single pot tone only test, sorry.

Musicfreak
02-07-2014, 10:22 AM
hi,
i have a stupid question.....
can someone tell me what the basic wirering is to use the onboar dac with a small headphone speaker?
thank you!
ps:
i'm so happy to find this thread !!

scswift
02-07-2014, 01:46 PM
The DAC isn't capable of driving a speaker by itself. If you try, you may damage the chip. You need to connect an amplifier to it.

To connect an amplifier, one wire on your input to the amplifier goes to ground, and the other goes to the DAC. Preferably through a small resistor (390 ohm will bring maximum mA you can draw from pin below 9mA reccomended maximum) to avoid any potential for damage. You will also want to call dac.analogReference(INTERNAL); to set the DAC to use the 1.2v internal reference (instead of the 3.3v reference it typically uses), which is close enough to the typical .894v spec for line level that you don't need to worry about sticking a voltage divider on there to get it down further.

A 1uf capacitor in series with the resistor might also be prudent but I don't think it would be necessary with the resistor on there protecting the pin, and the amplifier would have caps on it's own input.

I guess with the resistor on there you could drive a tiny speaker quietly directly from the DAC pin. You'd want to leave the analog reference at 3.3v. But it would be so quiet it's hardly worth it. I guess a piezo might be loud-ish but the audio quality would be terrible.

PaulStoffregen
02-07-2014, 03:53 PM
The DAC isn't capable of driving a speaker by itself. If you try, you may damage the chip. You need to connect an amplifier to it.


This is true, it's meant to only drive an amplifier. It's meant to output less than 1 mA current.

However, I did try connecting it to a speaker here, with a 10 uF capacitor in series. That Teensy 3.1 still works great. I wouldn't recommend anyone rely on this, but the DAC output does appear to be current limited. A tiny but audible amount of sound did come out the speaker.


I've been neglecting the audio library for the last few days while working on wrapping up a 1.18 release and a few other general PJRC business things. I'm going to get back onto the audio library development this weekend.

scswift
02-07-2014, 04:27 PM
Paul:
I thought any pin of the Teensy could source up to 9mA? (actually I think the absolute max is quite a bit higher, but 9mA max seems to be the recommended max from the datasheet) Is the DAC pin different?

I intend to make a shield soon for my own use which uses the DAC for audio output, and I want to be sure the design will be reliable. I chose a 390 ohm resistor in series because that would limit the current below 9mA but if the limit is actually 1mA then I would need to increase that. (I can't rely in it being current limited if the datasheet doesn't say it is.)

robsoles
02-08-2014, 01:12 AM
Just in case there is somebody with an Audio Adapter and only one pot who is actually interested, here is a replacement loop() which does the 'tone test' and doesn't influence the volume control. It may be worthwhile to change the volume to 80 in the setup() routine to make it easier to hear.
void loop() {
// every 10 ms, check for adjustment the tone & vol
if (chgMsec > 10) { // more regular updates for actual changes seems better.
float bass1 = analogRead(15);
bass1=((bass1-512)/512)*60;
bass1=(int)bass1;
bass1=bass1/10; // only changing for 0.1dB steps
if(lastbass!=bass1)
{
filterCalc(FILTER_PARAEQ, 220, bass1, 0.2, 2147483648, 44100, updateFilter);
filterCalc(FILTER_PARAEQ, 3300, -bass1, 0.2, 524288, 44100, TrebFilterParameters);
BassFilter.updateCoefs(updateFilter); // load for AudioBiquadFilter
audioShield.load_peq(0,TrebFilterParameters); // load for SGTL5000 PEQ.
lastbass=bass1;
}

/*float vol1=analogRead(15)/1.024;
vol1=(int)vol1;
vol1=vol1/10;
if(lastvol!=vol1)
{
audioShield.volume(vol1);
lastvol=vol1;
} */
chgMsec = 0;
}
}

mattomatto
02-08-2014, 02:06 PM
Apologies if I should have put this in a new thread but thought it might be relevant enough to put here.

I am currently writing my entire audio program within one class copied from the existing ones. Would it be more or less beneficial in terms of memory or cpu to have separate classes for, for example, oscillator, filter, adsr, etc?

Thanks!

Kondi
02-08-2014, 02:48 PM
That would be a whole new library. Maybe some of the existing code might help you get a start. I do not know if the DMA channels will help. You'll probably need to use the I2S interrupts. Processing individual samples will not be nearly as efficient, but the ARM chip is pretty fast, so you still might manage to get it to do something useful.

But do not fool yourself into thinking this might be possible in the context of this audio library, which is fundamentally based on block processing.

Maybe my jabbering was a bit unclear, but what i meant is more like an audio connection/special audio stream. Of course, to be able to do what I described(audio signal direct passthrough) I understand that within the bounds of this lib, it could be only achieveble (if achieveble at all) without those direct forwarded samples could be modified. As I said, i'm a beginner in MCU programming, i'm viewing this from the point of an audio-guy, and i know this functionallity is very very handy in a lot of situations.

As far as i understand the audio "engine" does this in every 2,9ms:
While the new 128 samples are gathered, the previously gathered samples are being processed, AND the previously processed samples are being sent to output. Correct me if this is wrong, but this should introduce 5,8ms IO RTL, and that could mean a drawback for a number of applications.

For example, a recording application: In some cases (Eg. a musician would like to record him/herself playing) this amount of latency can confuse the player if he/she is listening to him/herself via the teensys output while playing (this is called direct monitoring). By the way, around 6ms is tolerable for most of the people, but still doesn't feel natural, and is uncomfortable, also, anything else in the signal chain will introduce extra latency, and if this adds up higher than 10ms, then direct monitoring is unusable.
So, the only thing the musician can do, is plugging the instrument in a D.I box (wich basically just for splitting the signal in this scenario) , and listens to himself via the D.I output 1, and connects the DI-s other output to the teensy, wich records the performance,

Another advantage could be taken of this in time based effects like reverbs, delays, modulation(flanger etc)). And still without the necessarrity of modifying the "direct through" sample, only by processing the audio block in "the next round".

And yes, with the power of the ARM chip, per-sample processing would make possible a number type zero-latency effects (1 sample to be axact, so not zero, but only a fraction of a millisec) wich is very enticing. But from here we get out of the scope of the current audio library, i know. Also, a lot of DSP methods are unusable per-sample , so the usage of audio blocks would be still required. and processing audio blocks need time, so this gets complicated here.

If I had the kowledge to write an audio library, i would make a similar approach described above. The audio engine would be forwarding samples to the output in the sample rate, but also would populate an user adjustable size ring buffer with samples. From then, every audio object could decide how many the samples is necessarry, and could decide it's own audio block size, what would of course equal to latency, and time to process the block.

Don't get me wrong Paul, I admire all the work You've done. I just thought that what i'm talking about is achievable within the bounds of the current lib.
I've only done some basic arduino projects in the last years, and have basic C knowledge. AlthroughIi'm an IT guy, my field is networking, and i'm a professional musician, and I work a lot with digital audio. But I don't even understand about 80% of the code in the audio library, and You've wrote all of it (besides creating the Teensy) so if You say that this kind of behaviour is not achievable, i believe it :) I just felt that maybe You didn't get what i was talking about exactly.

EDIT: As i can see on the datasheet of the SGTL5000, it has capabilities to create this kind of direct through, visible on the Simplified Application Diagram on the first page. I believe maybe this is what they call "Stereo LINEIN - Codec bypass for low power" among the features. As i found out, this should be supported only for the line in, and it also by-passes the DAC. So not exactly what i'm after, but definately could do the trick, but sadly only between the line-in and the HP out.


Also here is a simple bit crusher, and downsampler audio effect I've created. Could anybody point out how this could be optimized for the ARM chip? Because the majority of objects like the audio mixer, I don't really get the logic, because the ARM optimization I think. Also, can anybody point out how does the mixer "mix" two samples? What formula is used?

ranjit
02-08-2014, 03:02 PM
I was looking at the processor's datasheet to try to understand I2S, and it looks like there are two TX and two RX lines. That makes me think that it would be possible to talk to two audio boards at once, to get four channel audio. If I'm all wrong about this, please dissuade me before I dive in and try to get this working!

el_supremo
02-08-2014, 04:43 PM
@Kondi:
A few comments on your "crusher".
There are a lot of syntax errors - undefined variables etc. (e.g. "data" is not defined)
The declarations of res and crushed_sample should be outside the while loop and crushed_sample doesn't need to be initialized to zero.
Declaring res as uint8_t won't work because if bit_depth is less than 10, res will be greater than a uint8_t can hold.
Your calculation of crushed_sample doesn't make sense. If data[i] is an integer multiple of res you use data[i] as the output. But if it isn't, you use data[i]/res. This is really going to mess up the output signal.
Your simulated downsampling leaves the signal at the 44100Hz sampling rate which is in effect a downsample followed by upsampling. The way that a signal is upsampled in DSP is to insert zero samples and then lowpass filter the resulting signal. The lowpass filter is essential to avoid aliasing and it will be the place where optimization is needed, probably by using the fir filter code in the ARM CMSIS DSP library.

For the mixer code, see AudioMixer4 in Audio.cpp

Pete

Kondi
02-08-2014, 05:11 PM
@Kondi:

Thanks for pointing out those all those errors, I've made some corrections. I don't want to flood the thread with my code so You can view it here (http://pastebin.com/UfYvy0ni).

Regarding the bulls*it logic that can be found in the crusher object:

What I am trying to create, is a very simple audio effect, that only simulates, the lower bitdepth, and sample rate. The results are the same resolution (44.1/16)on purpose. This effect has a very specific sound, and is often used as a creative effect.


Your calculation of crushed_sample doesn't make sense. If data[i] is an integer multiple of res you use data[i] as the output. But if it isn't, you use data[i]/res. This is really going to mess up the output signal.

EDIT2: I've looked over on that error for first, and corrected. (The paste is refreshed too (http://pastebin.com/UfYvy0ni))Now it should behave like intended, but i thought that there should be a much simpler solution to this "round to integer with desired resolution".


Your simulated downsampling leaves the signal at the 44100Hz sampling rate which is in effect a downsample followed by upsampling. The way that a signal is upsampled in DSP is to insert zero samples and then lowpass filter the resulting signal. The lowpass filter is essential to avoid aliasing and it will be the place where optimization is needed, probably by using the fir filter code in the ARM CMSIS DSP library.

Like I've mentioned, that's on purpose. The aliasing, and artifacts are a part of the effect. It approaches the sample rate conversion like "sample and hold" . It takes a sample and holds it for however many steps it needs to.

The implementation of this effect should be OK imho (I'm not sure the negative numbers are handled well currently), althrough it can be implemented in a much easier way i think.

I can see the mixer object in audio.ccp, but unable to interpret the logic behind, that is why I've asked for it.

cartere
02-08-2014, 10:52 PM
Just in case there is somebody with an Audio Adapter and only one pot who is actually interested, here is a replacement loop() which does the 'tone test' and doesn't influence the volume control. It may be worthwhile to change the volume to 80 in the setup() routine to make it easier to hear.
void loop() {
// every 10 ms, check for adjustment the tone & vol
if (chgMsec > 10) { // more regular updates for actual changes seems better.
float bass1 = analogRead(15);
bass1=((bass1-512)/512)*60;
bass1=(int)bass1;
bass1=bass1/10; // only changing for 0.1dB steps
if(lastbass!=bass1)
{
filterCalc(FILTER_PARAEQ, 220, bass1, 0.2, 2147483648, 44100, updateFilter);
filterCalc(FILTER_PARAEQ, 3300, -bass1, 0.2, 524288, 44100, TrebFilterParameters);
BassFilter.updateCoefs(updateFilter); // load for AudioBiquadFilter
audioShield.load_peq(0,TrebFilterParameters); // load for SGTL5000 PEQ.
lastbass=bass1;
}

/*float vol1=analogRead(15)/1.024;
vol1=(int)vol1;
vol1=vol1/10;
if(lastvol!=vol1)
{
audioShield.volume(vol1);
lastvol=vol1;
} */
chgMsec = 0;
}
}


Bass seems to work with minimum clicks. Can't seem to get the load_peg filter to do anything.

robsoles
02-09-2014, 12:15 AM
Thanks for trying it cartere, I tested it by playing a song with lots of bass kick and high hat (Breathe - The Prodigy). Rolling the 'tone' pot back and forth seemed to influence both to my ear - more 'kick' and less 'hiss' at one end, more 'hiss' and less 'kick' at the other end, fairly 'normal' in the middle.

I brought the teensy with me coz I thought I could show it off to the people we are visiting here, they have a PC (I'm using to post this now) but I don't want to set up stuff on it that they will never use so I'm not reprogramming it here. I found an OK online tone generator and (just now) set it to output 3300Hz - I couldn't hear a difference to that in any position on the 'tone' pot.

I can imagine it is the load_peq(..) routine itself except that if that is applying wrong values it should still have an influence (expect ugly...) so it must be one or more the route selection or dap_enable(1) or dap_peqs(1) that isn't doing as it should.

I am here for another few hours, I will get home so late tonight (~1am my time) that I should just go to bed hoping not to be a zombie at work tomorrow but I should find the failure and fix it by Tuesday afternoon for sure. (lol, so embarrassed I might sit up tonight till I've got it (facepalm)).

The influence I thought I heard it applying to the high hat may be the width of influence the bass filter is setup at.

cartere
02-09-2014, 12:25 AM
I have done all the bit diddling on paper and can't see why it should not work, but I suspect the "modify" routine is not working right in the library. Must confess I am lost in the C++ world of private vs protected, public makes sense. If modify does not work nothing works................which unfortunately seems to be the case.

robsoles
02-09-2014, 12:33 AM
OH! I just reviewed my copy on github and now I think it is definitely the load_peq(..) routine just missing the simplest thing - it loads the coefficients to the appropriate registers but it doesn't do the following
write(DAP_FILTER_COEF_ACCESS,(uint16_t)0x100|filte rNum);
delay(10); // may be unnecessarily long, 1ms or even none is probably OK.
write(DAP_FILTER_COEF_ACCESS,(uint16_t)filterNum);


So, if you can't wait for me to make the addition you could modify your local copy of Audio.cpp to make load_peq(...) look more like
// SGTL5000 PEQ Coefficient loader
void AudioControlSGTL5000::load_peq(uint8_t filterNum, int *filterParameters)
{
// 1111 11111111 11111111

write(DAP_COEF_WR_B0_MSB,(*filterParameters>>4)&65535);
write(DAP_COEF_WR_B0_LSB,(*filterParameters++)&15);
write(DAP_COEF_WR_B1_MSB,(*filterParameters>>4)&65535);
write(DAP_COEF_WR_B1_LSB,(*filterParameters++)&15);
write(DAP_COEF_WR_B2_MSB,(*filterParameters>>4)&65535);
write(DAP_COEF_WR_B2_LSB,(*filterParameters++)&15);
write(DAP_COEF_WR_A1_MSB,(*filterParameters>>4)&65535);
write(DAP_COEF_WR_A1_LSB,(*filterParameters++)&15);
write(DAP_COEF_WR_A2_MSB,(*filterParameters>>4)&65535);
write(DAP_COEF_WR_A2_LSB,(*filterParameters++)&15);

write(DAP_FILTER_COEF_ACCESS,(uint16_t)0x100|filte rNum);
delay(10); // may be unnecessarily long, 1ms or even none is probably OK.
write(DAP_FILTER_COEF_ACCESS,(uint16_t)filterNum);
}So, with that addition we would get to hear whether or not the values are being applied correctly.

I don't want to play with the github editor so in roughly 13 hours I will push this update to my copy on github from my home PC, and then I will go to bed :)

edit:
I have done all the bit diddling on paper and can't see why it should not work, but I suspect the "modify" routine is not working right in the library. Must confess I am lost in the C++ world of private vs protected, public makes sense. If modify does not work nothing works................which unfortunately seems to be the case.If the modify(..) routine returns expected results of bit diddling then it should (even so much as easily) be applying the value it returns to the selected rergister because it uses a 'known-to-work' (write(..)) method to apply it.

I have to get off this PC but I am checking the thread on my phone fairly regularly.

cartere
02-09-2014, 01:05 AM
OK I'm bad :) Works much better now, had been trying to get lo_lvl_right to work and not seeing any change, that works as well as the filter now.

cartere
02-09-2014, 01:07 AM
BTW: write(DAP_FILTER_COEF_ACCESS,(uint16_t)filterNum); should be write(DAP_FILTER_COEF_ACCES,(uint16_t)filterNum);

PaulStoffregen
02-09-2014, 02:16 AM
Hi everyone. Just checking in here. I haven't touched the audio library in the last few days. As you can see from other threads across the forum, I'm on a bug fixing rampage... leading up to an official 1.18 release.

Planning to work only on the audio library all next week.

robsoles
02-09-2014, 03:43 AM
BTW: write(DAP_FILTER_COEF_ACCESS,(uint16_t)filterNum); should be write(DAP_FILTER_COEF_ACCES,(uint16_t)filterNum);

I didn't check Paul's defines, sorry about that. I think I will add the second S to them rather than take it off elsewhere.

Glad to hear things seem working now.

@Paul: Was silly of me to decide I would make the pull request without further review, please reject that one and I will make a much better considered one later.

Musicfreak
02-09-2014, 04:49 PM
Back to the technical stuff ... ;)
Whow can i Input line Level Audio into the Teensy 3.1 direct ? Without any Audio shields ...
I read in the datasheet that it has two adc's with 16bit And could handle 44.1khz...
Soooo what is the wirering side ?
The Outputs will be finished this afternoon ;)
Thank you for this !!

Rossitron
02-09-2014, 08:17 PM
I'm having issues getting sane data into the main input buffer for a FFT using AudioInputAnalog reading a microphone.
Using the latest Audio lib pushed to github, I've tried Teensyduino 1.18 RC2, and also what changes Paul made in cores as of this posting. Looking at the buffer going into the FFT, something is up with the reads. I've spent a few hours trying to track down what is going wrong in Audio.cpp but haven't been able to get a full buffer of sane data. Using analogRead by itself works perfectly. Attached to this post is a picture of how the hardware is setup.


#include <Audio.h> // at line ~979, changed to use analogReference(DEFAULT);
#include <Wire.h>
#include <SD.h>

AudioInputAnalog analogPinInput(A9); // analog A9 (pin 23)
AudioAnalyzeFFT256 myFFT(1);
AudioConnection c0(analogPinInput, 0, myFFT, 0);

void setup() {
AudioMemory(12); // MemoryAndCpuUsage example, where is it?
}

void loop() {
if (myFFT.available()) {
Serial.println("444444"); // resets the xPos in processing
for (int i=0; i<128; i++) {
Serial.println(myFFT.output[i]);
}
}
}

el_supremo
02-09-2014, 08:41 PM
@Rossitron
AFAIK, the FFT code is part of the Audio library which won't work unless the Audio shield is present.

Pete

Rossitron
02-09-2014, 09:39 PM
AFAIK, the FFT code is part of the Audio library which won't work unless the Audio shield is present.




The Teensy3 Audio Board, available and in stock as of Jan 3, 2014, is the primary hardware intended to be used with this library. It's also possible to use PWM output and ADC input.


The FFT code is from the CMSIS-DSP (http://www.keil.com/pack/doc/CMSIS/DSP/html/group___complex_f_f_t.html) lib and seems to work fine. AudioInputAnalog almost works for me. About half of the buffer looks good.

el_supremo
02-09-2014, 09:41 PM
Looks like the audio library can read from an analog pin but you will have to set up the connections properly.
The PlayMidiTones example has some commented code which sets up pin 16 as an analog input. If you read this and the FFT example you should be able to see how to set up the connections.

Pete

robsoles
02-10-2014, 12:45 AM
OK I'm bad :) Works much better now, had been trying to get lo_lvl_right to work and not seeing any change, that works as well as the filter now.

I pushed the fix to github and have tested it myself now, much better - definitely hearing the difference, still haven't taken it to the audio sweep equipment at work yet tho.

I tried updating the AudioFilterBiquad coefficients without resetting the other elements to zero and I thought I perceived less artefacts introduced, you can try it by adding ',true' to the call as in
BassFilter.updateCoefs(updateFilter,true);if you would, please.

cartere
02-10-2014, 04:08 PM
I pushed the fix to github and have tested it myself now, much better - definitely hearing the difference, still haven't taken it to the audio sweep equipment at work yet tho.

I tried updating the AudioFilterBiquad coefficients without resetting the other elements to zero and I thought I perceived less artefacts introduced, you can try it by adding ',true' to the call as in
BassFilter.updateCoefs(updateFilter,true);if you would, please.


I don't hear any now.

Rossitron
02-10-2014, 07:32 PM
I've attributed one issue to the DC offsetting done in Audio.cpp, inside of AudioInputAnalog::update (line 1120). One of my tests was to blow on the microphone, knowing well what that should look like on the output data. This code would create a large (2000+ 16bit counts) error in the DC offset likely related to clipping from blowing on the mic.



// find and subtract DC offset....
// TODO: this may not be correct, needs testing with more types of signals
dc = dc_average;
p = out_left->data;
end = p + AUDIO_BLOCK_SAMPLES;
do {
s = (uint16_t)(*p) - dc; // TODO: should be saturating subtract
*p++ = s;
dc += s >> 13; // approx 5.38 Hz high pass filter
} while (p < end);
dc_average = dc;


After setting a static DC offset by hand, looking at the buffer made it clear what the other issue was. The data is nearly correct now, but some kind of settling/ramp up or lower bit error is happening between blocks of data coming back from the ADC over DMA. See attached image. I've tried other AUDIO_BLOCK_SAMPLES sizes (in AudioStream.h) and get the same settling/ramp up at the start of each block. The settling does not happen when doing charting a series of analogRead(). My next step is to hook up an oscope and check for anything that could explain this.

Printing this data is done in Audio.cpp after copy_to_fft_buffer(buffer+256, block->data); in void AudioAnalyzeFFT256::update(void):


for (int i = 0; i < 512; i = i + 2){ // input data has imaginary numbers in the odd bins
Serial.println(buffer[i]);
}


Pete: I looked over PlayMidiTones example and tried a bunch of things, but I don't think the commented out sections apply anymore. "'class AudioInputAnalog' has no member named 'connect'"

robsoles
02-10-2014, 10:28 PM
I don't hear any now.

Excellent, pending no discovery of why not to I will change the routine so that the 'natural' behaviour is to leave the other three elements un-touched when updating the coefficients. I'll make the ',true' flag reset the other three elements of the filter parameter array to zero instead.

I intend to test everything else I've added by writing (or, where possible, modifying existing) examples for the majority, I expect I will have the most done by the end of the coming weekend - work switched codecs to a Texas Instruments one so catering to that on their time instead slows my progress on the SGTL5000 atm.



@Paul: Will you require examples covering every addition I propose before you accept my pull request? if I rename flterCalc(..) to calcBiquad(..) and add it publicly to the library can that be acceptable? Is there anything else I need to do before you can accept the pull request?

geekguy
02-11-2014, 03:53 PM
Is it possible to use the Audio shield and SD card board together with a Teensy 3.1?

8-Dale

cartere
02-11-2014, 03:56 PM
I believe the audio shield has a SD card.

geekguy
02-11-2014, 03:58 PM
I believe the audio shield has a SD card.
Oh, I see it does now! I missed that! Thanks much. :)

8-Dale

geekguy
02-11-2014, 09:09 PM
I'm ordering one of these audio shields next month, and a Teensy3.1 to dedicate to using it. I've been looking for something nice to handle audio processing on W.A.L.T.E.R. 2.0. :)

8-Dale

stephanschulz
02-11-2014, 09:17 PM
just received the audio shield.
i tried these examples successfully:
PassThrough
PlayWavFromSdCard
FFT

i am now wondering how i could record something to the sd card and then play it back, in the same sketch?
do you have such an example handy?

thanks.

el_supremo
02-11-2014, 10:04 PM
While playing with the audio shield using various effects I've noticed that the first time I output audio to line output after the shield has been powered up, it starts at very low volume and takes about 4 seconds to reach full power. Any further audio is at its correct output level.
Anyone else noticed that? No doubt I'm forgetting to set something properly.

This mp3 demonstrates: http://members.shaw.ca/el.supremo/sw...load_16000.mp3
I played an audio frequency sweep with the shield and recorded the line output using GoldWave on Win 7. The sweep actually goes from 10 to 22kHz but the mp3 compression cuts it off just above 18kHz. The first half of the file was recorded soon after the Teensy and audio shield had been powered up. The second half was recorded a minute or so later. Even if you can't view this with a spectrum analyzer or with Goldwave, you can still hear that the first few seconds of the first part are noticeably quieter than the second part.


Pete
[* edit] This also occurs on the headphone output

robsoles
02-11-2014, 11:50 PM
OK, swept at last. The filters look good, the SGTL5000 applies all 7 biquad filters to both channels and the AudioFilterBiquad is only being applied to the right channel (or was it left channel and I accidentally swapped sides :o) - both look as they ought on the sweeper when it tests the right channel.

There is a little 'wobble' in the gain-intensity between ~4Khz and ~20KHz but the TWR-AUDIO-SGTL I was using had the same 'feature' when I swept it, it is there with no filters in play as well. It is not easy to perceive using human hearing anyway. The sweeper is using 96KS/s and sweeps to 48KHz, the SGTL5000 has a low pass @~20KHz but it doesn't attenuate well above ~23KHz but only a pathological audiophile with the hearing of a child might care.

The attached sweep is line-in thru I2S to DAP to line-out, right channel only. I changed line out level (CHIP_LINE_OUT_VOL) to 0x0808 for this sweep to make the parametric EQ max gain drive output to max out only just above 0dB but the value Paul sets (0x0505) in his .init() makes the output level closer to the input level so it is a better value to use for most purposes.

The green line is with the 'tone' pot in the middle, purple and red lines are 'tone' pot at extremities.

saxen
02-12-2014, 02:44 PM
Hi!
I've started playing with teensy 3.1 and this great audio library. First of all, thank you for providing such a great hardware and software.
I have got a couple of questions...
- As somebody else mentioned earlier in this thread, AudioSynthWaveform does not work very well at high frequencies. Has anybody found a solution for this matter? Btw, I changed the constructor of the AudioSynthWaveform to accept an initial non-zero phase (e.g., for having a cosine). If someone else is interested, I can post the code.

- About the CPU usage. Reading through the code I got how all the update() thing works. Basically, all the chain processing on the 128 samples has to be done in 2.9ms (i.e., the playing time of 128 samples in 44.1KHz, that is about 278kcycles at 96MHz.). Am I right? I tried to use AudioProcessorUsageMax() in the loop(), but printed values are higher than 100. What does that mean? Am I out of the 2.9ms?

Thanks,
Enrico

mattomatto
02-13-2014, 12:07 PM
Hi!
- As somebody else mentioned earlier in this thread, AudioSynthWaveform does not work very well at high frequencies. Has anybody found a solution for this matter?


It's an aliasing problem. The waveforms in the wavetables will be calculated to contain a certain number of partials. If your highest partial is greater than the nyquist frequency (Half the sample rate) you will hear aliasing, so as you increase the frequency, the partials move upwards and you will hear them come back down as aliased partials. You can't simple calculate the wave with fewer partials because it will sound dull and less resemble the waveform you are trying to achieve. This is a fundamental part of digital and additive synthesis theory and it's great fun and a challenge to work around this problem and come up with your own unique code.

The best practical tutorial out there for my money is this: http://www.earlevel.com/main/2012/05/04/a-wavetable-oscillator%E2%80%94part-1/

I personally think it would be a shame if the audio library did this all for you, as anyone who really needs to get rid of aliasing artifacts in their project could benefit from learning about it!

el_supremo
02-13-2014, 03:43 PM
I have written my own tone generator using the ARM sin function which is also table based. It uses cubic interpolation which takes longer to calculate but gives a much better result.
The output of the generator, according to the spectrum display in GoldWave, is clean down to at least -80dB.
I have also implemented the square wave and sawtooth and am working on the triangle. I'll try to package it as a pull request for Paul to evaluate.

Pete

el_supremo
02-13-2014, 05:00 PM
@Paul,
Don't know if this fits here or in Tech Support.

While writing the code to synthesize the sine wave, etc. I found that the square wave that is shown by GoldWave is anything but square. I do not have a scope so I can't check this out for at least a week when I can probably borrow one.
The problem also occurs with the Audio library's AudioSynthWaveform so I have played a 60Hz square wave into the PC and recorded it using Goldwave using this sketch:



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

AudioSynthWaveform mysine(AudioWaveformSquare);

AudioOutputI2S dac;

AudioControlSGTL5000 codec;

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

int volume = 0;

void setup() {
Serial.begin(115200);
while (!Serial) ;
delay(2000);

AudioMemory(4);

codec.enable();
codec.volume(40);
// I want output on the line out too
codec.unmuteLineout();

delay(200);
Serial.println("Begin AudioTest");
delay(50);

mysine.frequency(60);
mysine.amplitude(.5);
delay(1000);

Serial.println("setup done");
AudioProcessorUsageMaxReset();
AudioMemoryUsageMaxReset();
}

unsigned long last_time = millis();
void loop() {
if(1) {
if(millis() - last_time >= 2000) {
Serial.print("Proc = ");
Serial.print(AudioProcessorUsage());
Serial.print(" (");
Serial.print(AudioProcessorUsageMax());
Serial.print("), Mem = ");
Serial.print(AudioMemoryUsage());
Serial.print(" (");
Serial.print(AudioMemoryUsageMax());
Serial.println(")");
last_time = millis();
}
}

int n;
n = analogRead(15);
if (n != volume) {
volume = n;
codec.volume((float)n / 10.23);
}

}


This image shows the waveform as recorded by Goldwave:
http://members.shaw.ca/el.supremo/Teensy/PlaySynthSquare_waveform.gif

It obviously isn't square and a 100Hz signal has the same general shape although perhaps not quite as bad. It could also be the audio card in my PC but is this to be expected with low frequency waveforms?

Pete

el_supremo
02-13-2014, 05:18 PM
@saxen

AudioSynthWaveform does not work very well at high frequencies.
This is the spectrum of a 5kHz signal from AudioSynthWaveform:
http://members.shaw.ca/el.supremo/Teensy/PlaySynthSine_5000_spectrum.gif


and this is the spectrum of a 5kHz signal from my code which uses the arm sine function.
http://members.shaw.ca/el.supremo/Teensy/my_sine_5000_1.gif

The cubic interpolation used by the arm sine function will produce better results than linear interpolation but it takes a bit more cpu time. AudioSynthWaveform uses up to 2% whereas my arm sine version uses 6%.


I tried to use AudioProcessorUsageMax() in the loop(), but printed values are higher than 100. What does that mean? Am I out of the 2.9ms?

Once the processor usage exceeds 100 you won't be able to produce output buffers fast enough and the missing buffers will be replaced with zeros which will distort the output audio.

Pete

PaulStoffregen
02-13-2014, 06:49 PM
Just another quick check-in here, and yet another apology for neglecting the audio library over the last several days.

Really, I *really* want to be working on the audio library right now. At the moment I'm still wrapping up the Teensyduino 1.18 release. PJRC is also going to start making a OctoWS2811 board to help make big LED projects easier. Yesterday I spent the whole afternoon rewiring my 1920 LED test board to do a final test of that hardware before we order a batch of PCBs. The good news is the test was successful and those PCBs were ordered, so at least that project isn't demanding attention right now.

Yeah, yeah, excuses, excuses.....

I really am going to get back to the audio library soon. I'm sorry for the delay.

MichaelMeissner
02-13-2014, 07:01 PM
I suspect the Octows2811 board will be helpful to the big led setups.

It would be nice if there was a one stop shopping kit for those of us that do neopixels on a smaller scale, but has all of the recommended practices in place (setups for doing 5/3.3v power from batteries, appropriate capacitors, resistor on the data line, voltage regulator for the pixels, 74hc245 or similar for coming from 3.3v boards, heat shield if needed for the voltage regulator).

Rossitron
02-15-2014, 12:24 AM
Looks like the "approx 5.38 Hz high pass filter" in AudioInputAnalog was the second issue. Just installed 1.18 and here is what is required to get a nice FFT working from AudioInputAnalog (built-in ADC).

Audio.cpp changes:


line 7:
static arm_cfft_radix4_instance_f32 fft_inst;

line 12:
arm_cfft_radix4_init_f32(&fft_inst, 256, 0, 1);

under line 89 add:
float32_t FloatBuffer[512];
for (uint32_t i = 0; i < 512; i++) FloatBuffer = float32_t(buffer[i]);

line 91:
arm_cfft_radix4_f32(&fft_inst, FloatBuffer);

line 95:
arm_cmplx_mag_f32(FloatBuffer, FloatBuffer, 128);

line 98:
output[i] = int32_t(FloatBuffer[i]);

line 102:
output[i] += int32_t(FloatBuffer[i]);

line 960:
#define PDB_PERIOD 72e3 // 1087 does nothing

line 981:
//analogReference(INTERNAL); // range 0 to 1.2 volts
analogReference(DEFAULT); // range 0 to 3.3 volts

line 983:
analogReadAveraging(1);

line 985:
//for (i=0; i < 1024; i++) {
// sum += analogRead(pin);
//}
analogRead(pin); // sets up mux
dc_average = 32810; // this should be public

line 1131:
//dc += s >> 13; // approx 5.38 Hz high pass filter

line 1133:
//dc_average = dc;


The floating point FFT (f32) produces a [I]much nicer output vs fixed point (q15). Didn't think it would be that much different given the input data is fixed point. Maybe something else is wrong I didn't catch when trying q15.

Teensyduino code:

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

AudioInputAnalog analogPinInput(A9); // analog A9 (pin 23)
AudioAnalyzeFFT256 myFFT(1);
AudioConnection c1(analogPinInput, 0, myFFT, 0);

void setup() {
AudioMemory(12);
}

void loop() {
if (myFFT.available()) {
for (int i=0; i<128; i++) {
Serial.print(myFFT.output[i]);
Serial.print(" ");
}
Serial.println("");
}
}

Processing Code:


import processing.serial.*;
import processing.opengl.*;

Serial myPort;

int MaxValue = 20000;
int fftsize = 128;
boolean dataready = false;
String[] fftarray = new String[128];
byte xPos = 0;

public void setup() {
size(128, 980, OPENGL); //P2D);
background(0);
myPort = new Serial(this, Serial.list()[0], 9600);
myPort.bufferUntil('\n');
}

public void draw() {
if (dataready) {
dataready = false;
background(0);
stroke(255);
for (int i = 0; i < fftsize; i++) {
float y0 = 0;
if (i != 0) y0 = height - map(float(fftarray[i-1]), 0, MaxValue, 0, height);
float y1 = height - map(float(fftarray[i]), 0, MaxValue, 0, height);
if (i != 0) line(i-1, y0, 0, i, y1, 0);
}
}
}

void serialEvent (Serial myPort) {
String inString = myPort.readStringUntil('\n');
if (inString != null) {
fftarray = inString.split("\\s+");
dataready = true;
}
}


Still an issue is the sampling rate. I found 72e3 is the fastest (FFT update rate) somewhat sane value for PDB_PERIOD, but the FFT tops out at around 3700Hz (iirc), meaning the actual sampling rate is double that. Something is up because this should be quite a bit faster than 22500Hz (48e6).

mattomatto
02-15-2014, 05:32 PM
Before trying to nail it down myself, I was wondering if anyone else has a problem with periodical (every few minutes) bursts of distortion through their audio shield? It sounds as if the audio processing is being interrupted to deal with something else and the sound crackles, just for about 1 second.

I've tried to reproduce the issue, but it doesn't seem to be in response to any specific code or user interaction. It usually happens within 10 seconds of the sketch being uploaded.

Any ideas?

Thanks

Kondi
02-17-2014, 09:27 PM
Finally my audio board has arrived! (Thanks Pieter! (http://floris.cc))
I just finished soldering all together, and tested the HP output with a modified version of PlaySineMikroe. Actually I didn't found anything among the example sketches with wich I can test the HP output without a microsd, even it's simple like a stick, i share it, maybe it will be handy for somebody


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

AudioControlSGTL5000 codec;
AudioSynthWaveform mysine(AudioWaveformSine);
AudioOutputI2S dac;

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

int volume = 0;

void setup() {
codec.enable();
AudioMemory(15);
mysine.frequency(440);
mysine.amplitude(0.9);

codec.volume(70);

Serial.println("setup done");
}

void loop() {
int n;
n = analogRead(15);
Serial.println(n);
if (n != volume) {
volume = n;
codec.volume((float)n / 10.23);
}
}

Some initial toughts:
I have the 25k "volume" pot on the audio board, but even in the lowest setting, the output of mysine is audible. Pic1
If I begin increasing the volume, the next step introduces quite a big jump in volume (>20dB). The led on the T3.1 flickers when going frpom 0->1. Is this normal, that with the volume set to 0 i still have the output of mysine?

Also, as you can see on the pictures, I experience a constant, high pitched background noise, I guess this has to do something with ground problems, or the power source(laptop usb output)? On pic 2, i added a sh*tload of gain to the signal with the volume turned all the way down. Here is an audio sample about this. (https://drive.google.com/file/d/0BwJ8y-cbxTgMbjQ2ZzR1VzAyQW8/edit?usp=sharing)

Any advices?

@mattomatto: I don't experience the periodical bursts of distortion when running the sketch above (althrough as You see my situation is a bit worse :) )

robsoles
02-18-2014, 12:23 AM
I have managed a bit of time toward my fork of the Audio Library, fixed some things were amiss, and added examples for headphone & DAC balance. I've a filtering example I just need to test a bit more which I expect to add later. I will start on the AVC stuff shortly too. https://github.com/robsoles/Audio


@mattomatto: I have mostly been experimenting with 'LINE_IN->I2S_OUT->MCU->I2S_IN->DAP->DAC->LINE_OUT+HP' and listening carefully to adjustments and filters I am testing with no sign of a 'distortion burst' at any point so maybe you are playing from SD or something which I haven't even looked at yet - can you post an example sketch which seems to suffer the problem the most? (Not promising to look myself but others would and I might even :D)

robsoles
02-18-2014, 01:00 AM
...
Some initial toughts:
I have the 25k "volume" pot on the audio board, but even in the lowest setting, the output of mysine is audible. Pic1
If I begin increasing the volume, the next step introduces quite a big jump in volume (>20dB). The led on the T3.1 flickers when going frpom 0->1. Is this normal, that with the volume set to 0 i still have the output of mysine?

Also, as you can see on the pictures, I experience a constant, high pitched background noise, I guess this has to do something with ground problems, or the power source(laptop usb output)? On pic 2, i added a sh*tload of gain to the signal with the volume turned all the way down. Here is an audio sample about this. (https://drive.google.com/file/d/0BwJ8y-cbxTgMbjQ2ZzR1VzAyQW8/edit?usp=sharing)

Any advices?

...

The 'AudioControlSGTL5000::volume(..)' function in (Paul's original and not modified in my fork of) the Audio Library mutes the headphone output in the SGTL5000 itself when codec.volume(0) is applied so your result suggests that it 'leaks' a little when muted anyway. I will not suggest it is 'normal' but it may be a weakness of the SGTL5000 that cannot (easily) be overcome.

If you take a copy of my fork and try it with 'AudioControlSGTL5000:dac_vol(..)' you can see if muting it at the DAC instead is any more effective at absolute mute or not, I am pretty sure of the register fiddling I do there but I have not tested for 'leak/bleed' when muting either or both channels.
#include <Audio.h>
#include <Wire.h>
#include <SD.h>

AudioControlSGTL5000 codec;
AudioSynthWaveform mysine(AudioWaveformSine);
AudioOutputI2S dac;

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

int volume = 0;

void setup() {
codec.enable();
AudioMemory(15);
mysine.frequency(440);
mysine.amplitude(0.9);

codec.volume(90);

Serial.println("setup done");
}

void loop() {
float n = analogRead(15)/10.23;
n=floor(n); // volume update call will only occur for 0-100 steps of movement on the pot rather than 1024 steps where ~923 are moot.
if ((int)n!=volume) {
Serial.println(n);
volume =(int)n;
codec.dac_vol(n); // this influences both HP & LINE_OUT
}
}

PaulStoffregen
02-18-2014, 01:05 AM
@Paul: Will you require examples covering every addition I propose before you accept my pull request?

No, that's not required, but an example or two that demonstrate the main features would be nice. Often with Arduino example, "less is more". Best to keep things simple and demonstrate one concept clearly.

PaulStoffregen
02-18-2014, 01:35 AM
And yes, with the power of the ARM chip, per-sample processing would make possible a number type zero-latency effects (1 sample to be axact, so not zero, but only a fraction of a millisec) wich is very enticing. But from here we get out of the scope of the current audio library, i know. Also, a lot of DSP methods are unusable per-sample , so the usage of audio blocks would be still required. and processing audio blocks need time, so this gets complicated here.
.....
I just thought that what i'm talking about is achievable within the bounds of the current lib.


I do understand what you're getting at.

Per-sample processing, or even variable or mixed size block processing is outside the scope of this library.

Developing this library is already quite challenging. I'm not going to add complexity to an already difficult project by deviating from the fixed block size approach. I believe the roughly 40 year history of modern software has shown restricting the scope of a design to highly uniform structure is good strategy. But it is a path that requires being able to say "no" to certain features.

The current structure was not picked arbitrarily. I've been working on this library for many months, and in the early days a LOT of time and effort went into investigating the feasibility of doing many types of projects people want with the many design trade-offs. A decision was made, and I'm sticking with it.



Also, can anybody point out how does the mixer "mix" two samples? What formula is used?

It's 16 bit saturating addition. Here's the relevant code from the applyGainThenAdd() function:



if (mult == 65536) {
do {
uint32_t tmp32 = *dst;
*dst++ = signed_add_16_and_16(tmp32, *src++);
tmp32 = *dst;
*dst++ = signed_add_16_and_16(tmp32, *src++);
} while (dst < end);


Inside the loop "tmp32 = *dst" reads two audio samples at once (from the "dst" buffer) into a 32 bit temporary variable. Then the next line reads two more audio samples from the "src" buffer. The signed_add_16_and_16() function is merely a QADD16 instruction, which you can find documented here:

http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0473j/dom1361289886623.html

It simply adds both 16 bit halves. Each addition is done with "saturation", which is basically the same as clipping in analog circuitry, if the two numbers sum to greater than the audio range. The 32 bits are stored back into the "dst" buffer, and the point is incremented to the next 32 bits. This is done twice in the loop, so the overhead of checking for the end of the buffer is cut in half.

The mixer was one of the first objects I wrote. I've since learned its even faster than read 8 audio samples into variables. Even though the code is the same, the Cortex-M4 processor uses a special faster burst mode to access the RAM if you read consecutive 32 bit words back-to-back. So eventually I'll rewrite this to use 4 temporary variables, but the code will be pretty much identical, just 4 copies inside the loop instead of 2, and the operations rearranged.

You might notice the check if "mult" is 65536, which represents unity gain for the mixer channel. If it's some other number, a slight more complex version is used, where the 2 samples are multiplied by the gain, then right shifted so a multiply by 65536 becomes a multiply by 1. The 2 multiplied values are packed back into a single 32 bit variable, and then the same QADD16 instruction is used to add them. This code might also benefit from loop unrolling a bit.... eventually I'll go through most of these objects and do more optimization. For now, the goal is to get things simply working well with enough speed to be usable.