In Java, is there a way to synthesize a tone of a specific frequency?
Asked Answered
B

2

7

I'm doing some work with binaural beats and am trying to build a Java application that can play two sounds at slightly different frequencies...around 25-30Hz difference.

In a perfect world, I'd like to give Java two integer inputs, say 440 and 410, and have Java play back a 440Hz tone and a 410Hz tone.

Given what I've seen in the javax.sound.* classes, it appears that Java only supports the equivalent of piano notes...A4 @ 440Hz, then A-sharp-4 @ 466.164Hz, and in the other direction, G-sharp-4 @ 415.305Hz. While "adjacent" are generally within the range to produce a binaural beat, I'm trying to achieve more control in the frequency of my tones.

Since simple audio synthesis is nothing more than frequency, intensity and length-of-time, it would appear to me that somewhere in the bowels of the javax.sound.* classes, there's some kind of lookup that says when I tell Java to play "A4", that's 440Hz . The question becomes whether or not there's a way to hack that table to say that "A4.1" is the equivalent of 449Hz.

I've been messing with javax.sound.midi, have not explored javax.sound.sampled yet; it appears that I'd need to sample my tones to use the sampled classes; I prefer to synthesize. Steer me right if I'm mistaken.

Most of the third party interfaces I've seen are geared specifically toward music production and manipulation, and, as such, are limited in their ability to work with microtones. Does anyone have any experience with or recommendations for a solution?

Blocking answered 23/5, 2012 at 23:52 Comment(4)
Are you using javax.sound.midi or javax.sound.sampled?Collotype
@LaurenceGonsalves I've been messing with midi, have not explored sampled yet; it appears that I'd need to sample my tones to use the sampled classes; I prefer to synthesize. Steer me right if I'm mistaken.Blocking
I wouldn't expect midi to provide nearly that fine-grained control. It's more meant as a control interface between different devices, and has a limited number of notes available.Leftward
@Blocking The reason I asked is that originally you only mentioned "javax.sound", but that's effectively two separate libraries, midi and sampled.Collotype
T
5

You can generate samples and send them to the soundcard using the classes in javax.sound.sampled.*; basically create a software oscillator.

It requires some knowledge, but can be really fun when you get it to work ;)

I was playing with these classed when I created this: http://bobusumisu.net/testing/bobusynth-alpha1/

Here is the tutorial that got me started: http://www.drdobbs.com/jvm/230500178

Terrazzo answered 24/5, 2012 at 0:8 Comment(1)
Neat synth app! javax.sound.sampled is definitely the way to go. Thanks for tutorial link, I am looking forward to reading it in detail. By the way, when I played the synth, I got "stuck" at the triangle wave. The other knobs worked okay for me though.Tombola
T
4

This is just to supplement the answer already provided and accepted (which I gave a +1).

You can use wavetables as an alternative to running trig functions on the fly--sort of half sampled/half synthesized. I also use a sine wave table with six independent cursors pointing into it for FM synthesis, and have duplicated several Yamaha DX7 patches this way. But this is all done via javax.sound.sampled. Once a soft-synth has been built, you might want to control it with the midi library classes.

Let's say you populate a 1K array with floats for a single sine wave.

If you "play" the wave table by incrementing and looping through it and extracting each array member in turn (to write to the sound card via a SourceDataLine), you will get a pitch directly related to your sample rate. For 44100 samples per second, the 1024-member array will cycle 44100/1024 = 43.066... times in to fill that "second" with data (a very low pitch--roughly 43 Hz). If you skip every second table member, the pitch is twice that, etc. To get the pitch 440, one needs to find the correct "increment" to use to step through the wave table array, which can be found: incr = (size of waveTable * desired pitch) / sample rate

For example (1024 * 440 ) / 44100 gives an increment of: 10.21678... Thus, if the first value from the waveTable is at array location 0, the second value to be used would be in between locations 10 and 11. To get a value that lies in between two array locations, use linear interpolation.

I use this method, with javax.sound.sampled libraries, for a "Theremin" at this link. There is a keyboard displayed, but you can easily hear/see microtonal control as you move the mouse across the keys.

http://www.hexara.com/VSL/JTheremin.htm

In the above, the mouse position (called via MouseMotionListener) is used to calculate desired pitch via this function:

return Math.pow(2, ((mouseX + tuningLoc) / (octaveWidth)));

where octaveWidth is the number of pixels that covers an octave.

Tombola answered 24/5, 2012 at 20:44 Comment(2)
@philfreihofer - swoon. i play a theremin, and am in the middle of trying to hack an xbox kinect motion sensor to create inputs for a software theremin. This link just f'n rocks. You gave me something to mess around with this weekend. Wave table is an interesting idea as well. :)Blocking
Glad to hear you are enjoying the link! :D I should mention, there are some complications in handling an event stream when controlling volume/pitch that I haven't fully "solved"--but the implementation is still pretty good in terms of responsiveness. (Java does not provide "real time" guarantees.) I'm happy to provide more info when you get there. Pretty cool that you have and play a Theremin! You are the first Theremin player to give me feedback, and I am happy to hear suggestions.Tombola

© 2022 - 2024 — McMap. All rights reserved.