I am using a seekbar to change the volume of my MediaPlayer. The progress level is what I am using which gives a "1 to 100" int. I need to convert that into the float range of 0.0f to 1.0f. What is the correct way of doing that?
Thanks guys
float fVal = (float)val / 100; should do the trick.
divide by 100
int intValue = 1;
float floatValue = intValue/100.0;
Very late in the day, but as I was just reading up on this very subject I thought it worth posting an answer that doesn't just perform a straight linear scaling of the slider value 0 - 100 into a float value 0.0 - 1.0 and explain why you should be doing it differently.
So the API documentation for MediaPlayer.setVolume(float, float) states, in passing, that "UI controls should be scaled logarithmically" but doesn't explain why.
Sound as we hear it is measured in decibels (db), on a logarithmic scale. In simplified terms (over-simplified if you are an audio buff), twice the decibels = twice the volume. But because the decibel scale is logarithmic, the distance on the scale from (for example) 0 - 3db is bigger than the distance on the scale from 3db to 6db.
The most obvious effect of using linear scaling instead of logarithmic is that the volume with the slider at maximum is much more than twice as loud as the volume at half way, so most of the noticeable change in volume level happens in the lower three quarters (approximately) of the slider range rather than in an (apparently) linear fashion across the full slider range. And this is why straight linear scaling isn't quite the right way to translate a slider position into a value for the setVolume method.
Here's a simple function that will take your slider value (assumed to lie in the range 0 - 100), convert it into a logarithmic value and scale it:
private float scaleVolume(int sliderValue) {
double dSliderValue = sliderValue;
double logSliderValue = Math.log10(dSliderValue / 10);
double logMaxSliderValue = Math.log10(10);
float scaledVolume = (float) (logSliderValue / logMaxSliderValue);
return scaledVolume;
}
Now, the slider at 50 (the center position) will produce a sound that is about half as loud as when the slider is at 100 (the top position), and the slider at 25 will produce a sound that is half as loud as when the slider is at 50.
Be aware that your perception of what is "twice as loud" will be affected by the kind of audio you are playing as well as the quality of the speaker and how hard it is being pushed...
To map linearly to the range 0.0 to 1.0 use
int n = <some value>;
float val = (float)(n - 1)/99;
Related
I am trying to make a simple signalgenerator for isochronic pulsating sounds.
Basically a "beatFrequency" is controlling the amplitude variation of the main (pitch) frequency.
It works pretty well except that for some pitch frequencies above 4-5 K Hz, there is a second tone generated with lower frequency.
It's not for all frequencies but for quite many I can definetly hear a second tone.
What can this be? Some kind of resonance? I tried increasing the sampling rate, but its not changing anything, and using 44100 should be enough up to around 20 KHz, if I understand correctly?
I really can't figure it out on my own, so thankful for all help!
Here is an example code with beatFreequency 1 Hz, Pitch frequency 5000 Hz and samplerate 44100.
public void playSound() {
double beatFreq = 1;
double pitch = 5000;
int mSampleRate = 44100;
AudioTrack mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, mSampleRate,
AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
256, AudioTrack.MODE_STREAM);
int loopLength = 2 * this.mSampleRate;
while (isPlaying) {
double[] mSound = new double[loopLength];
for (int i = 0; i < loopLength; i = i + 1) {
mSound[i] = beatFreq*Math.sin((1.0*Math.PI * i/(this.mSampleRate/pitch)));
mBuffer[i] = (short) (mSound[i] * Short.MAX_VALUE);
}
mAudioTrack.play();
mAudioTrack.write(mBuffer, 0, loopLength);
}
}
Here is (added) an image of the frequencies when I play the tone 4734Hz.. And for example there is a rather large peak at around 1100 Hz as well as many higher.
The code is now just using the pitch, I have removed the beat Freq:
In your code, you are using beatFreq*Math.sin((1.0*Math.PI * i/(this.mSampleRate/pitch)));to determine the frequency (Missing some sort of assignment here)
However, mSampleRate and pitch are int values, leading to an integer division instead of a double division. For special cases for pitch and samplerate , this will result into lower frequencies than intended. For greater pitch values the effect should getting worse.
Try to use double instead of int, that should get rid of the problem.
Rewriting answer, after better understanding question, that the OP wants to do amplitude modulation.
Yes, Java can do Amplitude Modulation. I've made a passable cricket sound, for example, by taking a 4.9KHz tone and modulating the volume at 66Hz, and giving the resulting tone an AR envelope.
In your code, the variable beatFreq remains constant over an entire for-loop. Isn't your intention to vary this as well over the course of time?
I think you should simultaneously compute the beatFreq wave value in its own function (but also using the varying i), and multiply that result (scaled to range from 0 to 1) against the value computed for the faster tone.
** EDIT
To move redundant calculations out of the inner loop the following is a possibility:
Have the following as instance variables:
private double incr;
private double incrSum;
private final double TWO_PI = Math.PI * 2;
Have the following calculation done only one time, in your constructor:
incr = pitch / audioFmt.getSampleRate();
incr *= TWO_PI;
This assumes pitch is a value stated in Hertz, and `audioFormat' is the Java AudioFormat being used. With Android, I don't know how the audio format sample rate is stored or accessed.
With this in place you can have a method that returns the next double PCM value with some very simple code:
private double getNextSinePCM()
{
incrSum += incr;
if (incrSum > TWO_PI)
{
incrSum -= TWO_PI;
}
return Math.sin(incrSum);
}
Note: do not reset incrSum to zero as you stated in your comment. This can introduce a discontinuity.
If you have a second tone, it would get its own increment and running sum. This the two results, you can then multiply them to get amplitude modulation.
Now, as to the question as how to properly convert the PCM double value returned to something Android can use, I can't give you a definitive answer as I am not running Android.
Just a comment: it seems to me you are enthusiastic about working with sound, but maybe self-taught or lagging a bit in basic programming techniques. Moving redundant calculations outside of a loop is kind of fundamental. So is the ability to make simple test cases for testing assumptions and trouble-shooting. As you go forward, I want to encourage you to dedicate some time to developing these fundamentals as well as pursuing the interest in sound! You might check out the StackOverflow code reading group as a resource for more tips. I am also self-taught and have learned a lot from there as well as the code-reading course at JavaRanch called "CattleDrive".
I'm working on a variant of the Conway's Game of Life called Wireworld, and I want to have a slider control so the user can change the speed of the simulation at will. I think I just have a simple math hiccup, but what I'm doing is getting the difference between the current nano time and the last update, then dividing by 1,000,000,000 (to convert to seconds), and checking to see if it's greater than or equal to the update time (0.016667) divided by the speed slider value (a value between 0.05 and 1.0).
Here is the code as I have it...
new AnimationTimer() {
private long lastUpdate = 0;
public void handle(long currentNanoTime) {
if((double)((currentNanoTime - lastUpdate) / 1000000000) >= (updateTime / speedSlider.getValue())) {
And then at the end of the update cycle, I change lastUpdate to the value of currentNanoTime.
Is there something wrong with this approach? The animation moves very slow at the default speed of 0.5, and there's no noticeable difference when moving the slider to either extreme.
It seems the problem was where I was casting the two longs in the conditional. Instead of this...
if((double)((currentNanoTime - lastUpdate) / 1000000000) >= (updateTime / speedSlider.getValue())) {
I needed to do this...
if((((double)currentNanoTime - (double)lastUpdate) / 1000000000.0) >= (updateTime / speedSlider.getValue())) {
I suspected this was the problem. The problem being that, like integer division, even if you convert it to a double, the division happens first, so it would be a round number, rather than an accurate floating point number. I guess I just needed to take a break and think about it. Works fine now!
I'm practicing some simple 2D game programming, and came up with a theory that during animation (the actual change in a image position is best calculated with floating point numbers). I have a feeling that if you move an image around with ints the animation won't be as smooth.
In Java it seems you can't draw an image with floating point numbers to give an image a position. But apparently when you initially declare your x and y 's, you can declare them as Double, or Float, and when it comes to actually drawing the image you have to cast them to ints. Like I find HERE :
/**
* Draw this entity to the graphics context provided
*
* #param g The graphics context on which to draw
*/
public void draw(Graphics g) {
sprite.draw(g,(int) x,(int) y);
}
My question is about how Java handles the conversion?
If the code casts these doubles at the last minute, why have them as doubles in the first place?
Does Java hide the numbers after the decimal?
I know in C and C++ the numbers after the decimal get cut off and you only see whats before it. How does Java handle this casting?
Pixels on a display are discrete and limited in number; therefore display coordinates need to be integer numbers - floating point numbers make no sense, as you do not physically have a pixel at e.g. (341.4, 234,7).
That said, integers should only be used at the final drawing stage. When you calculate object movement, speeds etc, you need to use floating point numbers. Integers will cause an amazing number of precision problems. Consider the following snippet:
a = 1;
x = (a / 2) * 2;
If a and x are floating point numbers, x will finally have the expected number of 1. If they are integers, you will get 0.
Baseline: use floating point types for physics computations and convert to int at drawing time. That will allow you to perform the physics calculations with as much precision as required.
EDIT:
As far as the conversion from FP numbers to integers is concerned, while FP numbers have a greater range, the values produced by your physics calculation after normalization to your drawing area size should not normally overflow an int type.
That said, Java truncates the floating point numbers when converting to an integer type, which can create artifacts (e.g. an animation with no pixels at the rightmost pixel column, due to e.g. 639.9 being converted to 639 rather than 640). You might want to have a look at Math.round() or some of the other rounding methods provided by Java for more reasonable results.
Java truncates the decimals. Eg:
(int) 2.34 == 2
(int) 2.90 == 2
The reason for not being able to draw at a floating position is simply that there's no half pixels etc :)
Java casts floats to int by dropping the decimal. But I don't think having x and y coordinates in floats make any sense. You have pixel on the screen which cannot be presented in anything less than one pixel. For example you can't draw a pixel .5px x .5px because on the screen it will just be 1px x 1px pixel. I am not a computer game programmer but I have written one animation engine in Java and it was very smooth. I can share this if you'd like.
Note that you should draw using ints but do all your calculation using doubles. For things like rotating or anything that relies on a mathematical formula should be done in decimal.
The reason x and y need to be doubles is for when they need to be computed mathematically, for example:
x += (delta * dx) / 1000;
You want to avoid overflows and loss of precision up until you paint the pixel.
I'm able to display waveform but I don't know how to implement zoom in on the waveform.
Any idea?
Thanks piccolo
By Zoom, I presume you mean horizontal zoom rather than vertical. The way audio editors do this is to scan the wavform breaking it up into time windows where each pixel in X represents some number of samples. It can be a fractional number, but you can get away with dis-allowing fractional zoom ratios without annoying the user too much. Once you zoom out a bit the max value is always a positive integer and the min value is always a negative integer.
for each pixel on the screen, you need to have to know the minimum sample value for that pixel and the maximum sample value. So you need a function that scans the waveform data in chunks and keeps track of the accumulated max and min for that chunk.
This is slow process, so professional audio editors keep a pre-calculated table of min and max values at some fixed zoom ratio. It might be at 512/1 or 1024/1. When you are drawing with a zoom ration of > 1024 samples/pixel, then you use the pre-calculated table. if you are below that ratio you get the data directly from the file. If you don't do this you will find that you drawing code gets to be too slow when you zoom out.
Its worthwhile to write code that handles all of the channels of the file in an single pass when doing this scanning, slowness here will make your whole program feel sluggish, it's the disk IO that matters here, the CPU has no trouble keeping up, so straightforward C++ code is fine for building the min/max tables, but you don't want to go through the file more than once and you want to do it sequentially.
Once you have the min/max tables, keep them around. You want to go back to the disk as little as possible and many of the reasons for wanting to repaint your window will not require you to rescan your min/max tables. The memory cost of holding on to them is not that high compared to the disk io cost of building them in the first place.
Then you draw the waveform by drawing a series of 1 pixel wide vertical lines between the max value and the min value for the time represented by that pixel. This should be quite fast if you are drawing from pre built min/max tables.
Answered by https://stackoverflow.com/users/234815/John%20Knoeller
Working on this right now, c# with a little linq but should be easy enough to read and understand. The idea here is to have a array of float values from -1 to 1 representing the amplitude for every sample in the wav file. Then knowing how many samples per second, we then need a scaling factor - segments per second. At this point you simply are reducing the datapoints and smoothing them out. to zoom in really tight give a samples per second of 1000, to zoom way out maybe 5-10. Note right now im just doing normal averaing, where this needs to be updated to be much more efficent and probably use RMS (root-mean-squared) averaging to make it perfect.
private List<float> BuildAverageSegments(float[] aryRawValues, int iSamplesPerSecond, int iSegmentsPerSecond)
{
double nDurationInSeconds = aryRawValues.Length/(double) iSamplesPerSecond;
int iNumSegments = (int)Math.Round(iSegmentsPerSecond*nDurationInSeconds);
int iSamplesPerSegment = (int) Math.Round(aryRawValues.Length/(double) iNumSegments); // total number of samples divided by the total number of segments
List<float> colAvgSegVals = new List<float>();
for(int i=0; i<iNumSegments-1; i++)
{
int iStartIndex = i * iSamplesPerSegment;
int iEndIndex = (i + 1) * iSamplesPerSegment;
float fAverageSegVal = aryRawValues.Skip(iStartIndex).Take(iEndIndex - iStartIndex).Average();
colAvgSegVals.Add(fAverageSegVal);
}
return colAvgSegVals;
}
Outside of this you need to get your audio into a wav format, you should be able to find source everywhere to read that data, then use something like this to convert the raw byte data to floats - again this is horribly rough and inefficent but clear
public float[] GetFloatData()
{
//Scale Factor - SignificantBitsPerSample
if (Data != null && Data.Length > 0)
{
float nMaxValue = (float) Math.Pow((double) 2, SignificantBitsPerSample);
float[] aryFloats = new float[Data[0].Length];
for (int i = 0; i < Data[0].Length; i++ )
{
aryFloats[i] = Data[0][i]/nMaxValue;
}
return aryFloats;
}
else
{
return null;
}
}
Alright, this is probably gonna be a pretty simple question to answer. I haven't had a math class dealing with logarithms in a few years, so I apologize. So I have a USB Controller that I'm using to control the mouse on the screen with the left joystick. Now how this works right now is the controller returns a double between 0.00 and 1.00 depending on how far the push the joystick in the direction (0.00 center, 1.00 pushed all the way over). I'm using this to adjust the speed of the mouse movement by multiplying the returned value by a given speed (returned double * speed). This gives me a linear speed. But for the purpose of accuracy of the mouse and clicking things on screen, I'd like it to be more logarithmic, so as it's really slow when barely pushing, and then the speed increases logarithmically as you move the joystick farther. That way you can get good speed for moving across the screen, while also having good sensitivity when moving it slowly. So I just need help with the formula, as I'm sure it's pretty simple. Also, I'm working in Java.
Right now my formula is:
double value (value given by controller)
int speed = 25;
value += value * speed;
I then use this to move the mouse.
Thanks,
Brayden
I presume you meant exponential. An exponential function looks like http://hotmath.com/images/gt/lessons/genericalg1/exponential_graph.gif: the more value is, the larger the derivative (the more speed will increase for the same change in value).
double value = ...;
int base = 25;
value = java.lang.Math.pow(base, value);
Not sure if java.lang.Math is necessary in its full form or whether you need to import java.lang.Math first. I'm not a Java programmer.
I agree with MvanGeest, I think you want an exponential formula. That way its small with little distance, and very big with larger distances.
I'm not sure what mouse speed values are fast or slow, but you could do something like
double value (value given by controller);
int speed (maximum speed value);
value = Math.pow(speed, value);
You could also make the value something like 2*(whatever the controller gives you) to make a wider range of speeds.
Something like:
f(value) = value * MAXSPEED * Math.Pow (base, 1 - value)
f(0) = 0 // no movement
f(1) = MAXSPEED // maximum movement when joystick is full throttle
All values should be covered in the range. Base in this case can be any value greater than 1.