Audio For Teensy3 - What Features Would You Want?

Status
Not open for further replies.
Great. I'll look forward to seeing it when you're ready.

With the DMA-based library, there's very little interrupt overhead for transmitting the buffer. An interrupt is triggered every 64 samples, but it only runs for several microseconds, so the total CPU time to stream the data out is just a couple percent.
 
I got the XM/IT volume envelope support implemented and ported the player to Teensy (no asm optimized yet though), but the audio quality on Teensy is pretty poor compared to Arduino. Is it possible that the output voltage of pins isn't constant depending on which pins are on/off, thus making resistor DAC behave poorly? Or there's some bug in the code.

Edit: After some testing, it seems that this is indeed a problem with Teensy that zero-pins behave as sinks with some resistance and mess up the resistor DAC. I guess this should be fixable with diodes?
 
Last edited:
Are you sure it's really a hardware problem?

I did some testing with PWM driving lower value resistors high and low, to measure the on-resistance of the mosfets inside the chip. I got about 20 ohms between the pin and 3.3V for logic high, and about 15 ohms between the pin and ground for logic low. That's not perfectly symmetrical.

Could you post schematic or photo of the wiring, a small test program that just drives from 0 to 255 (or whatever the max is) in a loop?
 
Yes, I'm sure. I have the DAC connected to Teensy pins 0-7 (PORTD). If I only connect pin 0 and keep flipping bit 0 (other bits are 0) in PORTD at 20KHz, I hear high frequency sound (10KHz). Now if I connect pin 1 in Teensy to the DAC (the code is same and pin 1 is still low) I can clearly hear that the sound gets attenuated (this doesn't happen on Arduino).

I don't have schematic, but pin 0-7 are connected to 10467Ohm, 5194Ohm, 2597Ohm, 1304Ohm, 650Ohm, 326Ohm, 163Ohm, 81Ohm resistors respectively (i.e. about power-of-two resistances). Output of the resistors is connected to potentiometer, whose output is connected to speaker. And speaker ground is connected to Teensy GND (next to pin 0).

So when pin 1 is low, my guess is that some electricity goes from pin 0 through the 10467Ohm resistor BUT then back through 5194Ohm resistor to the ground when pin 1 is low /: If I put diode between pin 1 and the 5194Ohm resistor, the attenuation doesn't happen. I don't have enough diodes to properly test if this fixes the problem though.

Ps. I don't want to hijack this thread, so if you want, feel free to move these posts to some other thread.
 
Yes, I'm sure. I have the DAC connected to Teensy pins 0-7 (PORTD). If I only connect pin 0 and keep flipping bit 0 (other bits are 0) in PORTD at 20KHz, I hear high frequency sound (10KHz). Now if I connect pin 1 in Teensy to the DAC (the code is same and pin 1 is still low) I can clearly hear that the sound gets attenuated (this doesn't happen on Arduino).

Without seeing the code, it's hard to know what's going on here.

But one thing that's different between Teensy3 and Arduino is the default state of the pins. After a reset, every pin on Teensy3 is disabled. They only become enabled after calling pinMode.

Another thing to consider is PORTD is emulated in software. When you write to the PORTD register, on Teensy3 there is no AVR register. Instead, some software in avr_emulation.h emulates that register by writing to the 8 pins. They're not all updated at precisely the same moment.
 
Before starting the playback I set the pins for PORTD like this:
DDRD=0xff;
and the timer interrupt is just:
Code:
static bool s_val=false;
s_val=!s_val;
PORTD=s_val?0x01:0x00;
Another thing to consider is PORTD is emulated in software. When you write to the PORTD register, on Teensy3 there is no AVR register. Instead, some software in avr_emulation.h emulates that register by writing to the 8 pins. They're not all updated at precisely the same moment.
Ah, that's good to know. So I should use GPIOX_PSOR registers instead?
 
If you'd like me to look into this, please start a new thread, with a (hopefully small) complete program and any other info needed to reproduce the problem.

When I can reproduce a problem here, I can do something about it. Otherwise, all I can do it guess and post general info.
 
I've been looking at the SoundFont 2.04 spec. It's quite complex. There is still much I need to digest, but it seems the core function is playing sampled clips at variable rates, where the latter part of the clip can loop. A delay-attack-hold-decay-sustain-release envelope, resonant lowpass filter, tremolo, vibrato, chorus and reverb effects can be applied and the parameters for all those effects can vary. There also seems to be some sort of layering capability, involving multiple clips playing and mixing together, but I do not yet understand that part of the SoundFont format.

I found numerous dead links while searching. Here's a copy of the Soundfont spec I found after much searching.....
View attachment 903

Hi, I just stumbled across this, and I think what you're making here looks terrific.

I didn't see that anyone had responded to the bolded part yet, and I have some familiarity with SoundFonts, so I thought I'd offer some help in case you still need it. The basic idea is that some instruments cannot be reproduced effectively as a single sample being pitch scaled. One obvious example would be the human voice and formants -- ideally you don't want the formants to scale the same way as the pitch of the note. So by layering multiple samples with different playback characteristics, you get more control over this. Or, for example, you might be making a snare drum patch that has a combination of a noise and a pitched oscillator, and want to do different things with each -- give them different envelopes, different velocity scaling, etc. And at the extreme end, you could simply be designing fancy multitimbral synthesizer patches where each note is intentionally made up of several different layered sounds.

Overall, the Teensy and a board like this seems like it might be a great tool for making a DIY synth, or possibly even a groovebox. I don't actually have a Teensy yet, but I definitely plan to get one, and this board should be perfect for the kind of stuff I'm interested in.
 
My opinions/suggestions:


A few ideas for use cases:

  • Small battery-powered music player
  • Signal generator
  • Synthesizer for a portable musical instrument
  • Speech synthesizer for robotics
  • Signal processor for sensor applications, e.g. water flow monitoring via contact mic on water pipe
  • Sonar experimentation platform
  • Low-end radio transceiver (?), e.g. something that works in the unrestricted bands like garage door openers
  • 802.15.4 transceiver (?)

I would like to add a few items to the list:
- Haptic technology - "rumble" and other effects for home theater and games
- guitar effects
 
Sonar experimentation platform.
Very important for my updated version of cora.

CoraB might use about 5 teensy boards.
 
paul - would you be willing to leak some details of your wavetable/DDS implementation? - i've been poking around for software capable of editing/generating arbitrary waveforms recently, which is a very convenient thing to have in this connection, and was wondering what kind of format you were aiming at, mostly as i'd like to keep things compatible as far as possible with whatever you come up with (knowing it's going to be far more decent) ? single cycle / integer arrays? or something more intricate? existing editors seem to be geared towards this or that wavetable synthesizer, with formats that tend not to be overly memory efficient; there's only a few exporting single cycles and i found none generating band-limited single cycle sequences, which i guess would be the ideal scenario here. myself i've been playing around with a scheme that generates the band-limited single cycles during set up (16x256 integer arrays), writing them into the SD card and loads them when needed, but there's probably smarter ways of achieving that, say, preparing the wavetables in advance and just writing them into the flash.
 
would you be willing to leak some details of your wavetable/DDS implementation?

Normally I wouldn't release any code early, and honestly this probably isn't useful without the rest of the library, but here's my DDS sine wave implementation.

It's really pretty simple, just a 32 bit phase accumulator (phase and phase_increment are uint32_t, not float, even though it's assigned from a converted float) where the upper 8 bits index into a sine lookup table, and the next lower 16 bit are used to linear interpolate between 2 table entries. The lowest 8 bits aren't used, other than accumulating phase. Those low 8 bit might never have anything useful, since the increment is converted from a float with only 23+1 bit mantissa. Maybe later I'll consider an improved conversion from float to uint32 that properly maps the original mantissa to all 32 bits, but that's a relatively minor issue.

Code:
void AudioSineWave::frequency(float f)
{
        if (f > AUDIO_SAMPLE_RATE_EXACT / 2 || f < 0.0) return;
        phase_increment = (f / AUDIO_SAMPLE_RATE_EXACT) * 4294967296.0f;
}

void AudioSineWave::update(void)
{
        audio_block_t *block;
        uint32_t i, ph, inc, index, scale;
        int32_t val1, val2;

        //Serial.println("AudioSineWave::update");
        block = allocate();
        if (block) {
                ph = phase;
                inc = phase_increment;
                for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
                        index = ph >> 24;
                        val1 = sine_table[index];
                        val2 = sine_table[index+1];
                        scale = (ph >> 8) & 0xFFFF;
                        val2 *= scale;
                        val1 *= 0xFFFF - scale;
                        block->data[i] = (val1 + val2) >> 16;
                         //Serial.print(block->data[i]);
                         //Serial.print(", ");
                         //if ((i % 12) == 11) Serial.println();
                        ph += inc;
                }
                 //Serial.println();
                phase = ph;
                transmit(block);
                release(block);
                return;
        }
        phase += phase_increment * AUDIO_BLOCK_SAMPLES;
}

Edit: also, at some point, I'll probably try to optimize this code using the SIMD instructions. That might require doubling the lookup table size, so each pair of values is 32 bit aligned? I have measured this non-optimized code using about 3% of the CPU time, running at the default 44.1 kHz sample rate.

This could pretty easily be turned into any arbitrary waveform by replacing the sine lookup table with whatever waveform you want. I'm planning to do something like that for the library....

but there's probably smarter ways of achieving that, say, preparing the wavetables in advance and just writing them into the flash.

Yes, I've actually thought quite a bit about converting soundfont to an intermediate format. So far, only ideas, not any real code.

I've been focusing on efficient I/O, the library base class that provides all the infrastructure, and a ton of mundane things needed to actually get the hardware released. Currently I'm working on the test strategy and bed-of-nails test system...
 
Last edited:
you might save one multiplication by doing something like:

block->data = val1 + (scale*(val2-val1) >> 16);

The 0xFFFF-scale subtraction is traded for val2-val1.
 
Thanks. Later (probably much later) I'm going to work on optimizations. I'll certainly look into this.

But this sort of optimization involves a lot of studying the actual generated assembly. All multiplies in this chip are single cycle. There's even a SIMD instruction that does two 16x16 multiplies and adds then to the same accumulator, all in a single cycle. There are also double fetch instructions, that fetch 32 bits using a single bus cycle, but populate two registers which each 16 bit half. I'm pretty excited about the optimization possibilities. I've done a LOT of assembly language for microcontrollers over the last 20 years, but never before with such an amazing chip with SIMD instructions.

It's tempting to fiddle with optimizations now, but that would only delay releasing with perfectly good if not optimally fast code.
 
Normally I wouldn't release any code early, and honestly this probably isn't useful without the rest of the library, but here's my DDS sine wave implementation.
[...]
Yes, I've actually thought quite a bit about converting soundfont to an intermediate format. So far, only ideas, not any real code.

thanks -- that's much more than i wanted to know actually. but good to know, 8 bit then.

as to soundfont, never had to do with it i must say. a quick look at the specifications tells me this might be very much about "preset" sounds? i couldn't find anything relating to wavetables except soundfont provides a wavetable oscillator, but maybe i was looking at the wrong document. anyways, thanks again!
 
Last edited:
Here is the soundfont specification I'm reading.

http://forum.pjrc.com/attachment.php?attachmentid=903&d=1378670117

From chapter 6, starting on page 20:

The smpl sub-chunk, if present, contains one or more "samples" of digital audio information in the form of linearly coded
sixteen bit, signed, little endian (least significant byte first) words.

Also, about the looping of the sample data:

Within each sample, one or more loop point pairs may exist. The locations of these points are defined within the pdta-list chunk, but the sample data points themselves must comply with certain practices in order for the loop to be compatible across multiple platforms.

The loops are defined by "equivalent points" in the sample. This means that there are two sample data points which are logically equivalent, and a loop occurs when these points are spliced atop one another. In concept, the loop end point is never actually played during looping; instead the loop start point follows the point just prior to the loop end point. Because of the bandlimited nature of digital audio sampling, an artifact free loop will exhibit virtually identical data surrounding the equivalent points.

In actuality, because of the various interpolation algorithms used by wavetable synthesizers, the data surrounding both the loop start and end points may affect the sound of the loop. Hence both the loop start and end points must be surrounded by continuous audio data. For example, even if the sound is programmed to continue to loop throughout the decay, sample data points must be provided beyond the loop end point. This data will typically be identical to the data at the start of the loop. A minimum of eight valid data points are required to be present before the loop start and after the loop end.

The eight data points (four on each side) surrounding the two equivalent loop points should also be forced to be identical. By forcing the data to be identical, all interpolation algorithms are guaranteed to properly reproduce an artifact-free loop.

It is true that most of the verbiage throughout the spec is about a lot of features for "presets", which seems to be a variety of ways to associate samples and parameters for the resonant filter, reverb and other effects to ranges of notes and velocity values. There's also a LOT of stuff about modulators and other forward-looking features that apparently aren't used in any actual soundfonts.

I must confess, I still haven't actually implemented any of this yet, so everything i know so far is only based on reading through this spec a couple times.
 
Last edited:
ah ok, googling around a bit i can see better what this is doing now (i was looking at the wrong places first - there's only 5 hits for soundfont on puredata.hurleur etc vs a LOT of them on sites where people discuss VST plugins and the like). it would no doubt be a powerful feature, for sampling applications and such, if that would be supported out of the box, albeit a quite high level one, more application than api.

the wavetable "format" i had in mind was the one nantonos had mentioned a few posts back, which more directly pertains to the core DDS. for example, if you look at the mutable instruments algorithms, the latest of which are done for a cortex M4/fixed point as well, the wavetables tend to be 16 (or 17) single cycle 8bit waveforms, each covering a certain frequency range; here's another example, same idea, http://www.earlevel.com/main/2012/05/25/a-wavetable-oscillator—the-code/ where the wavetables are implemented as a struct. it's not such a big deal, of course, and something i/we/the users can come up with themselves, should they see the need.
 
For some time I've been looking at building a good quality digital audio system. My conclusions have led to a design using a board running Linux, and an audio board for USB to S/PDIF conversion. A lot of the USB boards also offer I2S. And there are a lot of them, for example at http://hifiduino.wordpress.com/2013/04/03/c-media-cm6631a-based-usb-i2s-interface/#comment-12246

In my scenario, the Linux board acts as front end, running XBMC for it its utter ubiquity. Basically a pretty interface to the filesystem, and of passing them to the sound stage. I'd agree, connectors take up a lot of space, and unfortunatley there seem to be a lot of physical standards. Maybe a block of pins to allow the use to choose what headers are used? An external coax connector can carry many channels, breakout into channels can be made in an amp. Also, the board will quite possibly be used in a chassis, with other boards, where physical headers are a liability.

The are two issues that remain in constant discussion. Correct clocking and hence reduction of jitter, and quality of the power supply. An (optional) $100 power supply is not uncommon. Talk of clocks would span volumes. I don't assume to understand the fine points of either of these issues, but the fact is that people spend a lot of time and effort in making them work correctly. Allowing people to choose their own DAC is a benefit, same with PSUs. Make it modular; perhaps have an effects board, or a MIDI board. People can then use them or not.

My use case may be different; I only want this for stereo music. The surround audio from video goes directly to the decoder in my receiver. So, my need is for a more direct signal path,

What a great opportunity! Customers in the design loop :)
 
My main application would be digital output (I2S) of uncompressed/lossless audio files stored in an SD card and support for the different families of audio frequencies.
Also external audio frequency clock input (two clocks for the 44.1K and the 48K families) in order to avoid complex derivation of audio frequencies as to minimize jitter
 
I had just looked back at some previous ideas. I think a SATA port would be great, if not that then a USB port for storage. With over 200GB of music files a SATA drive would be the preferred method of storage. Then like I said, the board could be quite simple in function: read files from SATA drive, output them to S/PDIF or I2S. No mixing, DSP, volume - all that should be done by later stages.
 
I had just looked back at some previous ideas. I think a SATA port would be great, if not that then a USB port for storage. With over 200GB of music files a SATA drive would be the preferred method of storage. Then like I said, the board could be quite simple in function: read files from SATA drive, output them to S/PDIF or I2S. No mixing, DSP, volume - all that should be done by later stages.

I really don't think a SATA port is something worthy and useful in an audio application such as this. In fact, it could easily become useless for like 99.9% of the buyers that will use this audio board. I mean, if you buy an audio board, you would expect it to have audio-specific functions, not storage-specific functions. An SD card slot already takes so much space on a board like this, and could easily become useless to most of the buyers; I can't imagine for a SATA port.
 
I believe we're several years away from microcontrollers having SATA capability. For now, SDHC is pretty much the available media.
 
Is the discussion here also about the audio libraries? For playing files out of SD card to I2S (and taking the master clock externally) there is no need for a separate board.
 
Status
Not open for further replies.
Back
Top