Wave Table Osc / Using Bitwise operators.

153 views
Skip to first unread message

studiorat

unread,
Oct 19, 2010, 3:03:47 PM10/19/10
to Android Music Developers

Hi All,
Been reading through this group for a while while researching a
college project.

I was very interested to read about Adam Smith's Etheral Dialpad app.
And I must
say well done to him for making a great program and being kind enough
to share it.
Needless to say I have a few questions about it. The thread about dial
pad seems to be
closed so I'm hoping to start some discussion here.

I'm new to programming only doing it for about a year and mostly in C.
So apologies in advance if I come across as a bit of a spanner!! I'm
completely new to Java.

So down to business.

In Adams WtOsc.java class it opens by declaring 3 public static final
variables.

public static final int BITS = 8;
public static final int ENTRIES = 1<<(BITS-1);
public static final int MASK = ENTRIES-1;

I'm trying to figure out why declare a BITs size and what BITS refers
to.
I'm guessing ENTRIES is what I'd call SAMPLES each value in the wave
table, so why left shift (<<) of 7 ?
And of course what's MASK used for?

Next issue is a "public syncronized boolean render":

public synchronized boolean render (final float[]buffer)
{
for (i = 0; i < CHUNK_SIZE; i++)
{
float scaled = phase*ENTRIES;
final float fraction = scaled-(int)scaled;
final int index = (int)scaled;
buffer[i] = (1.0f-fraction) * table[index&MASK] + fraction *
table[(index+1)&MASK];
phase = (phase+cyclesPerSample) - (int) (phase);
}

return true;



So I'm figuring that this doesn't actually create the Wave Table but
reads through all CHUNK_SIZE values in the table. So what does it do?
And why all the fuss when all that's being returned is True!!!???

I would appreciate all the help I can get as I'm doing this one all
alone.

Sincere Thanks,




rndmcnlly

unread,
Oct 19, 2010, 4:59:51 PM10/19/10
to Android Music Developers
Hey David (and the rest of AMD who might be interested in synth
library implementation),

If you are coming from a C background, so of the tricks I used will
probably be more natural to you than a career Java guy.

For a bit of context, I want to point out that the "table" array in
WtOsc is the actual wavetable, a mostly-static snippet of audio data
that will be repeated many many times to make the output audio. In any
wave table synthesizer there is a tradeoff between audio quality and
memory/cache efficiency. A tiny wavetable will be able to render
faster, but, even with fancy interpolation, it might not sound exactly
like the waveform you thought you put into it. Using a larger
wavetable might waste memory (usually not a problem at all these
days), but it will allow you to get better audio quality even with a
crappier interpolation method. This interpolation shows up in
"render(.)" where samples are read out of "table" and stuffed into
"buffer", an array of samples that is being passed in from UGens
upstream of the WtOsc.

Ok, let's look the bit shifting stuff. The size of the wavetable is a
designer choice (as I described above), so, in that one night I
happened to be making WtOsc, I had to pick some arbitrary number to
use as the number of the samples ("ENTRIES"). Suppose I had picked
500. When it came time to implement "render(.)", there is a piece of
code where want to make sure that I never attempt to read any samples
past the end of the wavetable. Initially, you'd think that "index%500"
would do the trick (and it actually would!), but this function
contains the inner-innermost loop of my entire program. The ability to
playback audio without skipping relies on WtOsc#render not wasting too
much time. So, given the opportunity to replace that one division-
equivalent operation (remember, this is interpreted code on a battery-
powered mobile device), with a (probably) much faster bitwise-and, I
took it.

The snippets "index % ENTRIES" is equivalent to "index & (ENTRIES-1)"
iff "ENTRIES" was an integer power of two. Think about mentally
computing some number % 10, this just means hacking off all of the
upper decimal digits in a number and leaving the rest. You can do the
same trick in binary where % 256 means simply hack off (zero out) all
bits above the bottom eight. So, the fiddling with bitshifts in
"BITS", "ENTRIES", and "MASK" is to that I can specify how many bits
of precision I want in my wavetable index counter, the number (a power
of two) of entries that implies that the table contains, and a bitmask
I can use to zero out all of the overflowing bits I don't want.

The basic idea? "(index+1)&MASK" simply advances "index" around to the
next sample in the wavetable, wrapping if necessary, without having to
do a division/modulus operation.

Why did I pick 8? Dunno. It sounded fine so I left it wherever it was
last set in testing.

Ok, now for why "CHUNK_SIZE" seems to be disconnected from the
wavetable size. Simple answer: they are disconnected. More interesting
answer: "CHUNK_SIZE" is the size of the upstream audio buffer that may
(or may not) make it all the way to the user's ears, with some effects
added along the way. By analogy to 3D graphics, think of "CHUNK_SIZE"
as telling you something about the size of the window in your fancy
game and "ENTRIES" as the size of the texture you are mapping onto
various polygons. If a triangle is far away, the contents of your
texture (wavetable) might be repeated many time to fill a small area
of the output window (this is the case for a high pitched note). Other
polygons might be closer, even sitting at screen depth, where pixels
in the texture are mapped almost one-to-one with pixels in the window.
Just as using a larger window size might slow down the framerate of
your game on a slower machine, using a larger "CHUNK_SIZE" in the
synth libraries implies additional latency in the interaction between
the user with the touchpad and what they hear through the speakers (my
value, which happened to be 256 also, happens to allow an audio
interaction framerate of about 86 fps (Hz), comfortably faster than
touch events are pushed in by the UI framework).

Basic idea? The wavetable (holding "ENTRIES" samples) is repeated many
times at different speeds to fill a fixed size audio frame window
(holding "CHUNK_SIZE" samples). Coincidence: they both happen to be
256.

Finally, why all the work to return true at the end!!!??? Suppose you
are implementing an effect UGen like a delay or a flanger. When
someone asks you to render your audio into a buffer they provide, you
first have to ask the UGens downstream of you for them to render into
temporary buffers before you can apply your effect to the audio. But,
suppose you were told whether the downstream UGens were only going to
producing silence; then you could be lazy and not attempt to compute
the effect of silence reverberating, saving some tough computation.
To support this kind of dynamic laziness in computation, I made the so
that UGens must explicitly ask for their kids (the ones who provide
their input) to render, and that calling render on them should produce
a boolean describing whether or not the kid actually produced any
audio.

Take a look the ExpEnv (envelope) UGen. At the very top of its render
method, it checks if its own attenuation value is below a certain near-
silent threshold. If so, it simply returns false right away -- it is
saying that it won't be producing any non-silent output this frame,
and that it didn't even need to bother consulting its kids to decide
if they had any output (potentially saving a whole chain of UGens from
rendering this frame). On the next step, it asks its kids to render
into an audio buffer. Checking the return value, it knows that, even
if the envelope isn't closed off, that applying any gain change to a
silent signal is a waste of time, so it again returns false right
away. Finally, only if the envelope wasn't closed and the kids had
some actual audio, does the envelope calculation actually run this
frame (returning true at the end to say that it thinks that it
produced some real output that the envelope's parents might want).

Back in WtOsc, oscillators always produce audio every frame. So, of
course, WtOsc is hardcoded to return true every time. If you place an
envelope right after the oscillator in the dsp chain, the envelopes
logic for laziness will keep the oscillator from doing unnecessary
computation. The result, in Ethereal Dialpad, is that, if you stop
poking at the screen with your finger, the processor usage quickly
ramps down as the various UGens give up and go silent. It doesn't
quite get to zero processor usage (even for the DSP chain) because I
didn't implement lazy logic for all of the UGens. Delay, in
particular, doesn't attempt to estimate how much audio is left in its
internal buffer (which might take a long time to go near-silent,
depending on the feedback setting) so it just always returns true
also, meaning anything upstream of it will probably run every frame.

If you are going to take any ideas away from my little UGen library,
take high level ideas such as an example of how wavetables work, a DSP
graph structure, and short-cutting lazy traversal of the graph. Don't
worry too much about low level details like the bit shifting stuff.
Either you are making a processor-bound app where you need every tiny
bit of performance you can get (in which case you should be back in C
using the NDK instead of my fluffy Java stuff), or you have plenty of
processor to spare and you should be optimizing for what sounds the
nicest, is the easiest to program, and the most fun to play with, and
let the fancy new Android just-in-time compiler save you from
yourself.

On a final design note, I've looked at the implementation of a few
other synth libraries and seen one other big design paradigm which
isn't represented in my library. My library is based on computing
whole "frames" at a time (256 sample chunks). This is nice and
efficient, but it makes for some ugliness in programming because every
single UGen has this same stupid loop all over the place. I started
writing a LPF, but I scrapped it because it just didn't feel pretty
enough to me. The alternative is to have a graph where the render()
operation only produces a single sample at a time (STK works this way,
for example). This makes many UGens very easy and clean to write, but
it means that you need to be running on hardware that can handle doing
a bunch of function calls per sample of output. At the time I made
Ethereal Dialpad (testing on a G1), this was not the case. However, I
expect that on current generation hardware and either native or jitted
code, this might be feasible. Dunno, someone should try it.

Hope this helps! Make sure to share any big discoveries you make.

Adam

studiorat

unread,
Nov 9, 2010, 3:38:26 PM11/9/10
to Android Music Developers

Adam,
Apologies for not getting back sooner. Thanks so much for taking the
time to answer my questions.
Your help has been invaluable.
I've just managed to get the thing working, after a couple of evenings
reading up on threading and
the like, every day's a school day here.

For what it's worth it was indeed "skippy" on the emulator, changing
the CHUNK_SIZE sorted that out,
I guess it'll introduce some latency in the UI later on.

Now I'm hoping to add something on to it, keep you posted.

D.

studiorat

unread,
Nov 16, 2010, 4:39:33 PM11/16/10
to Android Music Developers

Hi,
Back again with more problems...

So, I've got the code to make a noise as I've said already.
But I'm having problems with a new class to make the Frequency
change.
I thought it would simply be a matter of taking a value from my UI in
this case the SeekBar and
sending it to setfreq. However this doesn't seem to be the case, or
something I dunno!

public class Synth_Nov_9 extends Activity {

SeekBar sb1;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

sb1 = (SeekBar) findViewById(R.id.sweep);
sb1.setOnSeekBarChangeListener(new OnSeekBarChangeListener(){

public void onStopTrackingTouch (SeekBar seekBar){
return;
//start oscillator???
}

public void onStartTrackingTouch (SeekBar seekBar){
return;
//start oscillator??
}
public void onProgressChanged (SeekBar seekBar, int seekFreq,
boolean fromUser){

}
});


WtOsc ugOscA1 = new WtOsc();
ugOscA1.setFreq(seekFreq + 200); ///why can't I do this???
ugOscA1.fillWithHardSin(7.0f);


final Dac ugDac = new Dac();
ugOscA1.chuck(ugDac);

Thread thisThread = new Thread(new Runnable()
{
public void run()
{
ugDac.open();
while(true)
{
ugDac.tick();
}
}
});
thisThread.setPriority(Thread.MAX_PRIORITY);
thisThread.start();
}
}


Has anybody got a clue please...

D.

rndmcnlly

unread,
Nov 16, 2010, 6:14:34 PM11/16/10
to Android Music Developers
Is this the complete class or have you edited it down for the email?
Often sending the complete class helps other people spot problems, but
it's best to put the code as a link (http://pastie.org/) to keep the
email clean.

It looks like you are trying to use one of the arguments to the nested
OnSeekBarChangeListener in the outer scope of onCreate for the
activity. I'd suggest starting the oscillator with some default
frequency (200 Hz, whatever), and then calling setFreq again when you
get progress change events. Also, don't forget to set a maximum value
for the seek bar so you know what range of values to expect for
seekFreq.

Eventually, I assume you'll want to be working with midi pitch numbers
instead of raw frequency values. Here's a Java expression which turns
an integer midi pitch number into a float frequency for use in the
osc:

freq = (float)Math.pow(2, (pitch-69.0f)/12.0f) * 440.0f;

studiorat

unread,
Nov 17, 2010, 5:03:58 PM11/17/10
to Android Music Developers

It's part of a class and it's rubbish! Ignore it!

I've done a bit more since, dsp works on it's own, UI works on it's
own, but I can't fit them together, here's the relevant code...

(http://pastie.org/1306776)
In this part I set up the seek-bar-listener and then run a thread
which I thought would begin a version of the Usage class which I've
called synthOn.Java.

However in the synthOn class I keep getting errors on the
fillWithHardSin(7.0f) line and the setFreq line. see next code...
The IDE is also prompting me to either make the synthOn class abstract
or to add in a render method...

(http://pastie.org/1306807)

The rest of the unit generators are as is...

I was hoping to use the Orientation listener to control frequency and
perhaps modulate one oscillator with another, so the seek bar is just
an experiment to try and get frequency changing on the fly. I've also
got a similar wtOsc type thing going on in C which might get thrown in
if I can figure out how to turn hello JNI into a wtOscillator. The C
code works well, I'll need to stick in an interpolation function for
the smaller size needed for Andriod I'd expect.

I'm also wondering is it better to write a new wavetable of one period
with each frequency or whether to send my freq variable into the
native layer or whether to calculate the wavetable in C and just read
it in Java at different speeds.

I'm doing a project on music development for Android and I'm using the
E.dialpad library as an example along with one or two others.
It's due on Monday so I might have another question or two...

I'm a recording engineer usually, but I'm getting into this coding
business bit by bit.

Thanks again,
D.








Reply all
Reply to author
Forward
0 new messages