Audio Library

Status
Not open for further replies.
Awesome! Thank you very much. It is fun to see how the resolution improved with this technique.

But, I also need to slow down the sampling rate (and consequently get an improved resolution). I am stuck in the following situation: Although CHIP_CLK_CTRL does reduce the sample rate, the resolution is not improving. I am not sure why it is happening. Is it possible that although the sample rate was changed from 44.1K to 22.05K, the library is still receiving data at 44.1KHz? Possibly with two consecutive readings being identical?

Thanks. Gustavo.

Code:
#ifndef pdb_h_
#define pdb_h_

#include "kinetis.h"

// Multiple input & output objects use the Programmable Delay Block
// to set their sample rate.  They must all configure the same
// period to avoid chaos.

#define PDB_CONFIG (PDB_SC_TRGSEL(15) | PDB_SC_PDBEN | PDB_SC_CONT | PDB_SC_PDBIE | PDB_SC_DMAEN)

#if F_BUS == 60000000
  #define PDB_PERIOD (1360-1)
#elif F_BUS == 56000000
  #define PDB_PERIOD (1269-1)  // 0.026% error
#elif F_BUS == 48000000
  #define PDB_PERIOD (1088-1)
#elif F_BUS == 36000000
  #define PDB_PERIOD (816-1)
#elif F_BUS == 24000000
  #define PDB_PERIOD (544-1)
#elif F_BUS == 16000000
  #define PDB_PERIOD (363-1)  // 0.092% error
#else
  #error "Unsupported F_BUS speed"
#endif

#endif

This is Audio/Utility/pdb.h. If under the define for F_BUS==48000000 you double (1088) you will get a 22khz sample rate " on the analog input pin on the teensy ". I tried this on a much older version of the library and it did work. I ran a hardware jumper from lineout to the analog input pin which let me use all the other library objects. Have not tested with the current version of the library - your mileage may vary.
 
Why ?
a) i could check if the samplerate of the file i'm playing is the same.
i don't like "if (mp3file.samplerate == 44100) ...
b) it is documentated
c) 17.64706 Hz are irrelevant for this :)
 
Last edited:
Even quicker solution: what is the pitch change from playing a 44,100 sample at 44117.64706?

1200 x log(44117.64706/44100)/log(2) = 0.692632 cents.

100 cents is a semitone. 10 cents is just about detectable change in pitch. 0.7 cents - even for a trained musician blessed with perfect pitch - is not detectable.
 
You have misunderstood me, there were two different issues. My mistake, maybe I should have made it clearer. It does not matter whether 44118 or 44100 Hz. I needed this just to see if the file has the same sample rate.

If I want to play MP3 with 48000 or 32000 Hz, do I need a way such as a sample rate converter - or something else.
 
Yes.. but then, i could convert the files to *.wav too :)
When i have time, in some weeks/month i try to convert the samplerates..does not sound too hard..

Another Question: I think, we need something like "input"-Streams or a general interface for incoming data too. This way, the source of played audio would be more flexible and it would be no need for different source-dependend "soundobjects" (for example "rawplayer, memoryplayer, wav-sd-player "mp3-sd player" : i mean input(memory, sd, audiostream, whatever) -> player -> other objects -> output (whereever).

What do you think ?
 
One of the design choices that made the audio library possible was only supporting a single sample rate. The library is a large project, and CPU intensive. Using only a single sample rate greatly simplified the project, which made it possible to do in about 1 year.

I'm not sure what should be done about 48 and 32 kHz media.
 
I think we need a "re sampler " stream interface to adapt different frequencies to play at 44khz

I have to write something similiar to allow me to play samples at different frequencies.
 
I just took a quick look at that paper. On the last page, table 16 shows the computational requirement on a Blackfin DSP (16 bit fixed point). 43 MIPS (for stereo 48 -> 44.1 kHz) on such a DSP is probably as much or more computational power than Teensy 3.1 at 100% CPU usage. :(
 
And we have different goals; I want to play 44khz samples at higher or lower frequencies to adjust their pitch.

Frank wants to play 48 or 32 kHz audio without pitch change, at 44khz.

One is basically a pitchshifter and one is a resampler

Worth mentioning I am in mid-port of a pitchshifter which _might_ do a reasonable job of 48 to 44khz, but I'd imagine the artefacts at 32khz > 44khz would be ugly on your ears.
 
Last edited:
Is this for wavetable synthesis, as in playing a short sound sample as different musical notes?

Or are you looking to pitch shift live streaming audio?
 
Is this for wavetable synthesis, as in playing a short sound sample as different musical notes?

Or are you looking to pitch shift live streaming audio?

The first one, wavetable synthesis, is my ultimate goal for my main project, I guess? Forgive my ignorance, I always thought wavetable synthesis was a bit more complicated than that?

for the second usecase, that is project no2, the pitchshifter port which is just for the crack and the learning process.
 
I can get some sounds, just haven't got to that bit yet, just wanted to get a piano sample playing different notes first really.

Is it possible to load the samples into the wavetable from a WAV on the SDCard at the moment?

If not, I'll put that on my list as I want to be able to change drumkits on my project but I'll be loading a bunch of them into sketch memory, so I need to add a mechanism to the library which pokes the contents of a small WAV file into sketch ram, after checking sizing etc. No doubt more complicated than it sounds.
 
And we have different goals; I want to play 44khz samples at higher or lower frequencies to adjust their pitch.

Frank wants to play 48 or 32 kHz audio without pitch change, at 44khz.

One is basically a pitchshifter and one is a resampler

hmm, from the music point of view at least, sample rate conversion doesn't seem very interesting per se (i'd say). pitch-shifting / time-stretching does, but seems difficult as has been pointed out in various threads (though probably doable, for someone who knows what he/she is doing -- apparently it can be done on stm32f4). 'varispeed' seems the least demanding. i know i've posted this before, but something à la puredata's [tabread4~] strikes me as a good model, it can be fed pretty much anything to move through the sample indices. it's not that different from DDS + interpolation (i think), just not with single cycles.
 
Last edited:
Before I write anything about my long-term plans, I want to be absolutely clear that I'm currently working on 2 hardware projects and slowly working though a long list of issues to investigate, as well as the usual day-to-day answering of questions and solving immediate issues. I do indeed plan to do a LOT more with the audio library, but it will be sometime next year.

I know a lot of people want wavetable synthesis. I want to do it. But I want to do it well, with a good solution that will really give a lot of capability to everyone.

Already a lot of the raw components are in place inside the library. For example, AudioSynthWaveform has an arbitrary waveform feature. In theory, you could bandwidth limit your repeating waveform and use that to play it at different speeds. You can use the sample player to play an attack waveform, if needed, and you can use the multiplier or filter and waveform objects to apply effects like vibrato. But even if you put all these pieces together, there's still the issue of composing samples.

My long term plan involves SoundFont. That plan may change, but so far, I've looked at a LOT of audio stuff and the SoundFont format seems to have by far the most good quality wavetable sound libraries published. At least a few very good, completely free SoundFont libraries exist covering all the general MIDI sounds. If there are other formats used to publish wavetable synthesis sounds, I'm certainly willing to consider them. However, I'm not willing to consider legacy formats, so if any reading this has a lot of nostalgia for Amiga, please know I'm not considering MOD format (but I wouldn't be opposed to accepting a contribution to play MOD...)

Of course, you're not going to load a SoundFile file directly into Teensy's flash memory, or even the SPI Flash chip. Some sort of conversion utility will load the SoundFile file and let you choose which voice you wish to use. It'll need to have some GUI controls to let you trade-off size vs quality in a variety of ways. Then it'll output raw data, optimized for the library, which you can copy into your sketch, or (someday) put onto the SPI Flash.

SoundFont is complex, but at least it's an open and (fairly) well documented file format. I'll attach the spec.

Chapter 9 is probably the most interesting part to read first. The diagram on page 48 shows most of the components needed. The "Oscillator" part actually plays an attack sample, followed by looping a waveform sample. Both are resampled to the desired note.

Resampling is done over only limited ranges, so separate samples can be used for different ranges of notes, different ranges of velocity, and so on. There's a lot of info (and jargon) in the document about how to associate different samples and parameters to note, velocity and control changes.

I've read this spec several times, and still quite a number of things aren't entirely clear to me. It's probably going to take some digging through actual SoundFont data and experimenting to really nail down details. I'm also imagining a bottom-up approach implementing the algorithms in the library meeting a top-down requirement from the spec, with the GUI tool bridging the gap to convert actual SoundFont data into something optimized to efficiently play by the library.

So, unless someone like Adrian Freed come in with some great advice about DSP and music stuff or knows of a better format than SoundFont (which actually has a lot of high quality material already published), this is roughly my plan.

If you do work on this, I hope you'll at least use the SoundFont synthesis model as a guideline. It's an open, widely used, and well proven way of doing wavetable synthesis. Hopefully, if you and others who take any of this on use SoundFont as a guideline, all our efforts can eventually converge to a wonderful future where you can import SoundFont to a web-based GUI, adjust stuff, and export to data that the library can play as high-quality wavetable synthesis.
 

Attachments

  • sounfont_specifications_v2.04.pdf
    518 KB · Views: 374
I know a lot of people want wavetable synthesis. I want to do it. But I want to do it well, with a good solution that will really give a lot of capability to everyone.

Already a lot of the raw components are in place inside the library. For example, AudioSynthWaveform has an arbitrary waveform feature. In theory, you could bandwidth limit your repeating waveform and use that to play it at different speeds. You can use the sample player to play an attack waveform, if needed, and you can use the multiplier or filter and waveform objects to apply effects like vibrato. But even if you put all these pieces together, there's still the issue of composing samples.

I agree that the existing AudioSynthWaveform does a lot of what is needed for single-cycle wavetable synthesis. Here are my own notes on what is done now and what could usefully be added.

It has a table (or direct calculation) for each wavetype, and a phase pointer. Although the tables are small, the quality is greatly improved by using the fractional part of the phase pointer to linearly interpolate between adjacent table entries. The phase pointer can be reset for hard sync (the type of sync you would get in the analog worldwith a saw core oscillator) and further can be set to an arbitrary value so you can easily make quadrature oscillators (three sines at 120 degrees apart, 4 at 90 and so on). A static pulse width can be set for the pulse waveform. There are a lot of suitable single-cycle waveforms around (such as the AdventureKid waveforms).

The current limitations are:
- lack of frequency modulation (only sine_fm has this
- lack of audio-rate pulse-width modulation
- the same table is used for all notes so they are not bandlimited. This leads to aliasing at higher frequencies (and, for the table-based waveforms, a lack of higher harmonics at lower frequencies)
- lack of table based short-sample (rather than single-cycle) table-based support
- no through-zero frequency modulation
- no soft sync (also known as reversing sync, as you would get from an analog triangle-core oscillator)

The first limitation is readily addressed, using sine_fm as a model to create waveform_fm. Audio-rate PWM I did start on, but then put aside as (dayjob) work spiked up. Implementation is the same as you would do in the analog domain - start with a triangle wave, bring in an audio signal like sine_fm does, use a comparator on these to select high or low values for the pulse train.

The bandlimited case could be dealt with for the square/pulse/PWM types by usingbandlimited impulse trains (BLIT) (another method) to construct square and pulse waves rather than mathematical, zero-rise-time pulses. The bandlimited case for table-based waveforms is typically addressed by having multiple, bandlimited tables (such as one per octave, across the ten-octave MIDI note span); abrupt loss of harmonics on changing octave can be dealt with by interpolating between two tables using the note number within the octave (0 to 11) as the interpolant. That could also nbe used for aliasing-free triangle and saw/ramp waves

Through-zero FM is much more easily handled than in the analog case: just allow frequency to be a signed value and use the absolute value. Soft sync can be handled simply by having a static variable holding the direction (currently, the direction is forwards only, ie the phase pointer is incremented). Soft sync is a parameterless function which reverses the direction.

That leaves the tracker-like resampling/repitching as discussed in recent messages in this thread. This has been done in the past on much more limited hardware, from the mid-80s onwards, at a time when a 12-bit DAC was an expensive component and 1MHz was a fast CPU speed. So there must be ways, as long as a certain amount of aliasing and other digital crunchiness can be passed off as "retro character".

It is also done a lot nowadays (sample-based instruments on platforms such as Kontakt) but those run on desktp/laptop CPUs with lots of CPU speed, RAM, and disk. They typically have multiple samples per note (to round-robin so it sounds less mechanical), sampled at multiple volumes (because a quiet note and a loud one have different spectral responses) and then extrapolate for note-ranges outside the ones sampled, or to do pitch bends. That translates to a very large amount of disk space and a fair CPU load (although the desktop ones are also doing things like 128-note polyphony) and would seem well outside Teensy capabilities, mainly because of the storage requirements.
 
Last edited:
Seeing mention of SPI Flash, three of those arrived yesterday from an ebay vendor in Shenzen. I'm aware that the Eurorack module (Eurotrash) by mxxx uses them (but I don't yet have the PCB for that, nor the components). I'm aware the Audio Adapter has space for one, but no code to drive it. mxxx, would your code work for the Audio Adapter? (I'm aware the Flash SPI CS is on a different pin in your code). If so, would a pull requrest on the Audio Library to add SPI Flash support be something you would consider?
 
.... and would seem well outside Teensy capabilities, mainly because of the storage requirements.

I'm optimistic that an impressive and useful subset can be possible, using the SPI Flash, highly optimized code, and some crafty tricks to distill the data somewhat during the data conversion process.
 
Status
Not open for further replies.
Back
Top