Here's a little polyphonic soft synth I've been hacking on, it might be useful to give a few pointers for people wanting to work with audio in Java. There's no filter (yet?), but it does still have some nice bass/string sounds.

If you can get it working you can either use MIDI or the keyboard to play notes. If using the keyboard make sure one of the sliders has focus first, use F1-8 to select the octave, and hit space or enter to release any stuck notes.

I know it's a bit crappy, but it didn't take too long to write, and I'm really happy with it. It has two saw/square oscillators per voice, and 8 voices. The oscillators themselves are not antialiased, but it does have 4x oversampling on the output, and the sound is pretty good.

Well that's a good job keep it up. I only didn't consider Java for sound because of the latency. Without ASIO and other low level drivers you don't get the benefit being able to sustain 12ms through a full arranged song.

Probably best to keep the overdrive cranked up when using large amounts of resonance. I thought about interpolating the lowpass sweep, but it would make the filter inner-loop a bit less efficient. I'll see what I can do...

It actually has hard clipping already, so the spikes are never more than full-scale. Turning up the overdrive lessens the difference between the spikes and the audio, so they aren't as annoying.

I've got a couple things I want to try to limit this. I think the cutoff should be on a logarithmic scale, which should improve things quite a bit, and make things more controllable. Most of the other controls have log scales already.

Another one would be to experiment with limiting the maximum change in cutoff frequency every 20ms, so any large changes are handled in smaller steps, but without the complexity of having to interpolate the lowpass (and recalculate a bunch of parameters) every sample.

All I can say is that the waveforms sound really nice; they are very rich. Although it was a tone I suggested, but thanks for taking it on board. It makes it much easier to do interesting pitch bends with it when it's not so high.

So where are you going with this? All you need is sequencing and a little sampler and you've got yourself a mini workstation

I'll change the pitch bend to a tone in the next release. I've not been using the pitch wheel, as you can probably guess. My keyboard skills are currently up to the level of playing the riff from "Cars" over and over again, something that this synth is actually quite good at

EDIT:

I've been meaning to write a little tracker-type program for a while now, it might be nice to use this in the audio engine.

I've also started something like that some time ago, except that there was no MIDI support or keyboard support yet, it's just a library for creating a modular synth in java. Everything can be connected however you like, and it does have some nice modules which might be of some use to you (RingModulator, VCF, ADSREnvelope LFO, FMOperator etc), and it works with 32bit float accuracy.If you're interested, I can send you the sources or upload it somewhere.

As always, the sources are in the jar file It's pretty well-organised, but it's not particularly re-usable. -- I designed it as more of a self-contained "sound box". There's not much more I want to add to this program, a filter envelope would be nice to have.

I wrote it in mostly fixed point arithmetic, because I had J2ME at the back of my mind, but also because fixed point makes more sense when interpolating. It's noticably quicker, probably because indexing an array with a float requires both a modulo-division and a cast-to-int, which is quite heavy compared to a left shift.

( EDIT: Sorry, just occured to me, indexing the array doesn't need modulo-division, but getting at the fractional part in order to eg. linear-interpolate between two samples does ... )

And then there's the prospect of denormalisation. When the FPU encounters a really small number, it switches into a different mode, which can really kill performance, apparently. Not a problem when interpolating, but it needs to be considered, you don't want a denormalised number in one of your filter coefficients, for example.

I got the filter code from musicdsp.org, and that has so far resisted my attempts to integerize it. I managed to make it work using long integers with 24-bit fractional accuracy, but the sound quality was not as good -- it's quite flat and muddy. The code is also really ugly compared to the floating point version, although it seems to run at about the same speed, possibly slightly faster. I think I'll bite the bullet and go to floating point where it makes sense in this program, ie. keeping the oscillators in fixed-point but replacing the use of sine/log tables with floating point -- it will make the code more readable.

erikd: I can't promise I'll do anything with it, but by all means send me the library. Maybe I'll have a go at wiring the MIDI code up to it

Fortunately it's pretty easy to avoid division in audio, the only use of division in the synth that I can think of is in the pitch-bend wheel code (1 semitone is 1/12 of an octave). There's also some in the envelope code, to calculate the number of samples in 1ms (even that could be replaced with a shift by approximating 1ms to 1/1024 seconds).

(EDIT: Heh, actually division is used both in the LFO and the envelopes, and somewhat differently to how I thought. I don't know my own code, and I've just written it

In fairness to the GBA it wasn't really intended for 3D And the provision of a single-cycle multiply-add unit was very generous of them. They could easily have used a 68000 instead. To be honest, that would probably have resulted in better games

Couldn't get any new features working tonight, but I've set the pitch bend to a full tone, and the filter slider has had further tweaks -- the previous version didn't allow it to be fully opened, so the sound is now back to its usual brightness!

**Massive** improvements to the filter, the lowpass is interpolated on a per-sample basis, now it sounds as smooth as silk This should also completely solve the feedback problems.

There's also a simple filter envelope. I didn't want to make this synth too complicated with loads of sliders so it only has a decay phase. Even so, it's enough for me. There's a big range of sounds possible now!

erikd: I can't promise I'll do anything with it, but by all means send me the library. Maybe I'll have a go at wiring the MIDI code up to it

That's okay, I've implemented it myself now, and it can now play MIDI files too.I'll upload it later, but I've got some issues to sort out first (it's *far* too slow at the moment, only being able to play about 16 stereo voices with 2 oscillators, 2 LPF's, and 2 LFO's each in real time on a 2GHz machine. It should be easily possible to have at least 64 voice polyphony with 4 oscillators each imho).

I'll upload it later, but I've got some issues to sort out first (it's *far* too slow at the moment, only being able to play about 16 stereo voices with 2 oscillators, 2 LPF's, and 2 LFO's each in real time on a 2GHz machine. It should be easily possible to have at least 64 voice polyphony with 4 oscillators each imho).

Yeah, easily. This synth running 16 oscillators @ 192khz (internally) takes 2% of my 1.5ghz Sempron. The filter takes about 6% on top of that. Modern computers are insanely quick!

I try to calculate everything in as big chunks as possible, and minimise memory usage. There are 2 buffers in the synth, one for the mixing and oversampling, and a byte buffer for writing to the audio device.

Here's another version. I've added a portamento control, and extended the per-sample smoothing technique to the pitch and amplitude of the oscillators. The changes are pretty invasive, so I hope I haven't broken anything too badly

Quote from: erikd on June 21, 2007, 07:35:11 amI'll upload it later, but I've got some issues to sort out first (it's *far* too slow at the moment, only being able to play about 16 stereo voices with 2 oscillators, 2 LPF's, and 2 LFO's each in real time on a 2GHz machine. It should be easily possible to have at least 64 voice polyphony with 4 oscillators each imho).

Yeah, easily. This synth running 16 oscillators @ 192khz (internally) takes 2% of my 1.5ghz Sempron. The filter takes about 6% on top of that. Modern computers are insanely quick!

I'm guessing that my performance issue is caused by the massive amount of calls through java interfaces that's slowing things down (my lib is basically a collection of little building blocks like oscillators, filters etc which you can connect in every way you like). Maybe I'll try to create a more hardcoded synth and see if that makes things fast.

I'm guessing that my performance issue is caused by the massive amount of calls through java interfaces that's slowing things down (my lib is basically a collection of little building blocks like oscillators, filters etc which you can connect in every way you like). Maybe I'll try to create a more hardcoded synth and see if that makes things fast.

Thanks!

Obviously I haven't seen your design, but I don't think you really need to hardcode anything to avoid the call-overhead (if indeed that is the problem). For example if you are feeding an LFO into an oscillator, you can generate the LFO samples into a temporary buffer the size of 1 tick (ie 10-20ms) and use that buffer in the interpolation loop for the oscillator. With a few hints to the LFO you could probably get the it to generate the logarithmic step-values directly and simply read them out once per sample in the oscillator inner loop. A scheme to reuse the temporary buffers and keep everything cached would be a good idea but it should be both flexible and quick.

Yes after doing some more tests, I'm pretty convinced the large amounts of virtual calls per sample are responsible for the crap performance.But let's not discuss that here (I don't mean to steal the thread), I might start a new thread about that.

Yes after doing some more tests, I'm pretty convinced the large amounts of virtual calls per sample are responsible for the crap performance.

You're making method calls per sample?! The mind boggles

I've got another version of the synth:

Soft clipping in the filter, much nicer sound when overdriven.Improved waveform generation. There is more than one way to morph a saw into a square, hopefully this one will be a bit more useful.Minor fixes. You could confuse it by holding a chord down when switching to porta mode!

Too much info: I've just had my ears "syringed" at the doctors (they stick a pipe in your ears and wash the wax out). it's like I have a new pair of ears! These oscillators sure have a lot of "character" now (not that I think that's necessarily a bad thing). I've been scouring the code looking for off-by-ones, but I think it might just be all the alias harmonics beating against each other

Yes, the soft-clipping is what I was asking for and protects your speakers - having said that so does a low pass filter, not to mention that my amp will temporarily click off when the signal spikes.

Ah I thought you were referring more to the massive amplitude swings you got when sweeping the lowpass too quickly in the early implementations of the filter. Well, it's all solved now

The clipping is actually performed after the filter, as it prevents it from becoming unstable, so the filter is not much help in softening the distortion. It does sound a lot more pleasant, now though.

I also balanced the waveforms a bit, as the square wave was distorting much more than the sawtooth. The area under a square wave is double that of a sawtooth, so I just halved the amplitude of the square.

You're synth is getting better and better it really sounds very pleasant.

Quote

You're making method calls per sample?! The mind boggles

My lib has different goals than your synth; the main goal was to be able to easily programmatically create any synth you want using the available modules, sort of like DigiDesign's TurboSynth.So yeah, every sample goes through more (interface) method calls per sample as you create more complex synths.

Very interesting, thanks! Yes, I think conversion to VST is the next job!I don't know how VST works, but if I'm guessing correctly it would also provide much better latency.

Cheers,Martin

Well yes! Java sound does not go through DirectKS/WDM/ASIO, and going through the KMixer (that is used with DirectSound, etc) has a manditory latency of 20ms no matter what you do. By being a VST instrument you can utilize the output stream of the VST host.

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org