0

The following was taken from an Android app:

    public void genTone(int freq){
        for(int i = 0; i<numSamples; i++){
            samples[i] = Math.pow(-1, (float)(i / (sampleRate/freq)));
        }
        int idx = 0;
        int volume = 32767 * cx/wide;
        for (final double dVal : samples) {
            final short val = (short) ((dVal+1) * volume);

            generatedSnd[idx++] = (byte) (val & 0x00ff);
            generatedSnd[idx++] = (byte) ((val & 0xff00) >>> 8);
            if(isRecording){
                toRec.add((byte)(val & 0x00ff));
                toRec.add((byte)((val & 0xff00) >>> 8));
            }
        }
    }

The above code is a Java function in an Android app the generate a square wave with a specified frequency. The frequency is determined by an integer 'note' which is the last recorded position of a MotionEvent divided by the height of the screen. The frequency is 440 * 2^(note/12). I've made the program output in text the note and frequency, and it outputs what I want it to, but at certain notes, even though it outputs a different frequency in text, it sounds exactly the same. Is 8000 too low a sampleRate(per second)? Is this a well-known bug? Anything you can help me with?

AstroCB
  • 12,337
  • 20
  • 57
  • 73
user2649681
  • 750
  • 1
  • 6
  • 23
  • is sampleRate an int or float? – jaket Dec 04 '14 at 22:59
  • @jaket Int: 8000, does it make a difference? Admitadly, idk how rounding works exactly. – user2649681 Dec 05 '14 at 00:30
  • What are cx, wide, numSamples and freq in each of the cases that you expect to be different but which sound the same? Smells like integer overflow to me. 8 KHz is not a problem unless you're generating some high frequency tones. – Scott Dudley Dec 05 '14 at 03:22
  • @Scott Dudley numSamples is duration(1) * sampleRate(8000), so 8000. Wide is the width of the screen, cx is the last x location of a motionevent. Wide and cx are only to control the volume, which I think is working right. For examples, freq=739 and freq=783 sound exactly the same, even though they should be a semitone apart. – user2649681 Dec 05 '14 at 11:55
  • But what exactly are cx and wide? It's potentially important. – Scott Dudley Dec 05 '14 at 12:38
  • Wide is the width of the screen(1200), and I change cx between about 0 and 600, it adjusts the volume like I'd expect, but doesn't have any effect on the pitch. – user2649681 Dec 05 '14 at 13:51

1 Answers1

1

The primary issue is that you need to leave the period as a float, because 8000/789 and 8000/739 otherwise round to the same integer:

float period = (float)sampleRate / (float)freq;

for(int i = 0; i<numSamples; i++){
    samples[i] = ((int)(i/period) % 2)==0 ? 0 : 1;
}

As a secondary stylistic issue, I would also suggest removing the "+1" from the following line and instead requiring that cx/wide (which you should really call "gain") be exclusively in the range [0,1]:

final short val = (short) ((dVal) * volume);

Your original code was effectively generating samples as (0, +2, 0, +2,...) because of the +1, and then multiplying by cx/wide, but this would cause an overflow in your short val if cx/wide were ever greater than 1/2 (because 2*32767*1/2 is the maximum value of a short).

A more common practice would be to specify cx/wide as a gain in the range of (0,1) (where 0=min volume and 1=max volume) and also to enforce that constraint in the function.

A tertiary issue is that you are probably only using half of the dynamic range of the output. If the output is supposed to be 16-bit signed samples (look it up), then you can generate the original samples as ? -1 : 1 instead of 0/1. If the output stream accepts 16-bit unsigned samples, then you'd want to leave the samples as 0/1, but change volume to 65535, and use an int instead of a short for val.

Scott Dudley
  • 3,256
  • 1
  • 18
  • 30
  • Your [other question](http://stackoverflow.com/questions/27296929/record-audio-played-on-audiotrack) suggests that you want ENCODING_PCM_16BIT, which would be unsigned. – Scott Dudley Dec 05 '14 at 14:50
  • Isn't it already producing between 1, -1? Wait, does ENCODING_PCM_16BIT accept unsigned? – user2649681 Dec 05 '14 at 14:56
  • Your original `samples[]` would have been -1, 1, -1, -1 but your `dVal+1` turns that into 0,2,0,2. – Scott Dudley Dec 05 '14 at 14:58
  • It should be unsigned. If in doubt, you can try generating samples of 0x7fff,0x8000,0x7fff,0x8000. If it sounds like a loud 4KHz tone, it's signed. If you can't hear much of anything, it's unsigned. – Scott Dudley Dec 05 '14 at 15:00
  • I've modified my code to produce the square wave between -1 and 1, and I'm multiplying by 32767 * cx/wide(btw, what's the difference between volume and gain? Aren't they both just amplitude?), and it's very audible. Actually, switching the type cast solved the problem. Thank you. – user2649681 Dec 05 '14 at 15:03