I have seen similar questions similar to this one on this website but my question is a little different. The code I am using to capture the audio is this. I would like to simply take the captured audio and apply an FFT to it with 256 points.
I realized that this line count = line.read(buffer, 0, buffer.length); breaks up the audio into "chunks".
Also the FFT I am using can be found here.
My questions are:
I would like to know if there is a way to apply the FFT to the whole audio recording not just a buffered amount.
I see that the code for the FFT requires a real and imaginary part, how would I get the real and imaginary parts from the code with the audio file.
All the javax.sound.sampled package does is read the raw bytes from the file and write them to the output. So there's an 'in between' step that you have to do which is converting the samples yourself.
The following shows how to do this (with comments) for PCM, taken from my code example WaveformDemo:
public static float[] unpack(
byte[] bytes,
long[] transfer,
float[] samples,
int bvalid,
AudioFormat fmt
) {
if(fmt.getEncoding() != AudioFormat.Encoding.PCM_SIGNED
&& fmt.getEncoding() != AudioFormat.Encoding.PCM_UNSIGNED) {
return samples;
}
final int bitsPerSample = fmt.getSampleSizeInBits();
final int bytesPerSample = bitsPerSample / 8;
final int normalBytes = normalBytesFromBits(bitsPerSample);
/*
* not the most DRY way to do this but it's a bit more efficient.
* otherwise there would either have to be 4 separate methods for
* each combination of endianness/signedness or do it all in one
* loop and check the format for each sample.
*
* a helper array (transfer) allows the logic to be split up
* but without being too repetetive.
*
* here there are two loops converting bytes to raw long samples.
* integral primitives in Java get sign extended when they are
* promoted to a larger type so the & 0xffL mask keeps them intact.
*
*/
if(fmt.isBigEndian()) {
for(int i = 0, k = 0, b; i < bvalid; i += normalBytes, k++) {
transfer[k] = 0L;
int least = i + normalBytes - 1;
for(b = 0; b < normalBytes; b++) {
transfer[k] |= (bytes[least - b] & 0xffL) << (8 * b);
}
}
} else {
for(int i = 0, k = 0, b; i < bvalid; i += normalBytes, k++) {
transfer[k] = 0L;
for(b = 0; b < normalBytes; b++) {
transfer[k] |= (bytes[i + b] & 0xffL) << (8 * b);
}
}
}
final long fullScale = (long)Math.pow(2.0, bitsPerSample - 1);
/*
* the OR is not quite enough to convert,
* the signage needs to be corrected.
*
*/
if(fmt.getEncoding() == AudioFormat.Encoding.PCM_SIGNED) {
/*
* if the samples were signed, they must be
* extended to the 64-bit long.
*
* so first check if the sign bit was set
* and if so, extend it.
*
* as an example, imagining these were 4-bit samples originally
* and the destination is 8-bit, a mask can be constructed
* with -1 (all bits 1) and a left shift:
*
* 11111111
* << (4 - 1)
* ===========
* 11111000
*
* (except the destination is 64-bit and the original
* bit depth from the file could be anything.)
*
* then supposing we have a hypothetical sample -5
* that ought to be negative, an AND can be used to check it:
*
* 00001011
* & 11111000
* ==========
* 00001000
*
* and an OR can be used to extend it:
*
* 00001011
* | 11111000
* ==========
* 11111011
*
*/
final long signMask = -1L << bitsPerSample - 1L;
for(int i = 0; i < transfer.length; i++) {
if((transfer[i] & signMask) != 0L) {
transfer[i] |= signMask;
}
}
} else {
/*
* unsigned samples are easier since they
* will be read correctly in to the long.
*
* so just sign them:
* subtract 2^(bits - 1) so the center is 0.
*
*/
for(int i = 0; i < transfer.length; i++) {
transfer[i] -= fullScale;
}
}
/* finally normalize to range of -1.0f to 1.0f */
for(int i = 0; i < transfer.length; i++) {
samples[i] = (float)transfer[i] / (float)fullScale;
}
return samples;
}
public static int normalBytesFromBits(int bitsPerSample) {
/*
* some formats allow for bit depths in non-multiples of 8.
* they will, however, typically pad so the samples are stored
* that way. AIFF is one of these formats.
*
* so the expression:
*
* bitsPerSample + 7 >> 3
*
* computes a division of 8 rounding up (for positive numbers).
*
* this is basically equivalent to:
*
* (int)Math.ceil(bitsPerSample / 8.0)
*
*/
return bitsPerSample + 7 >> 3;
}
That piece of code assumes float[] and your FFT wants a double[] but that's a fairly simple change. transfer and samples are arrays of length equal to bytes.length * normalBytes and bvalid is the return value from read. My code example assumes AudioInputStream but the same conversion should be applicable to a TargetDataLine. I am not sure you can literally copy and paste it but it's an example.
Regarding your two questions:
You can take a very long FFT on the entire recording or average the FFTs from each buffer.
The FFT you linked to computes in place. So the real part is the audio samples and the imaginary part is an empty array (filled with zeros) of length equal to the real part.
But when the FFT is done there's still a couple things you have to do that I don't see the linked class doing:
Convert to polar coordinates.
Typically discard the negative frequencies (the entire upper half of the spectrum which is a mirror image of the lower half).
Potentially scale the resulting magnitudes (the real part) by dividing them by the length of the transform.
Edit, related:
How do I use audio sample data from Java Sound?
Related
hello i'm starting with java and i have to make a simulation of a rabbit population on 20 years and i barely passed the sixth year, can anyone help me please to optimize my program ?
it's in French but the first loop is for the months then I'll grow my rabbits and remove the dead ones
the next for the new born babies
if you need more details about the rabbit class tell me.
Ps i've already deleted a for loop.
thanks
package com.company;
import java.util.ArrayList;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
MTRandom MT = new MTRandom();
int nbMales;
int nbFemales;
int nbAnnee;
int[] init = {0x123, 0x234, 0x345, 0x456};
MT.setSeed(init);
//Bloc d'entrée
ArrayList<Rabbit> Rabbits = new ArrayList<>();//changer pour linked ?
System.out.println("Nombre de lapins males ?"); //creation males
nbMales = input.nextInt();
System.out.println("Nombre de lapins femelles ?");// creation femelles
nbFemales = input.nextInt();
System.out.println("Nombre d'année que va durer la simu ?");
nbAnnee = input.nextInt();
//for (int j = 0; j < 10; j++) {
//Rabbits.clear();
for (int i = 0; i < nbMales; i++) {
Rabbits.add(new Rabbit('m', 12, true));
}
for (int i = 0; i < nbFemales; i++) {
Rabbits.add(new Rabbit('f', 12, true));
}
//Bloc Principale
for (int i = 1; i <= nbAnnee * 12; i++) { // Defilement des Mois
for (int k = 0; k < Rabbits.size(); k++) {
Rabbits.get(k).setAge(); // incrementation de l'age des lapins en mois
Rabbits.removeIf(rabbit -> !rabbit.isAlive);
Rabbits.trimToSize();
}
for (int k = 0; k < Rabbits.size(); k++) { // boucle pour les projenitures
if (Rabbits.get(k).sexe == 'f' && Rabbits.get(k).isActiveSex && Rabbits.get(k).nbPortee > 0) {
double y = MT.nextDouble();
int n = 0;
if (y <= 0.3) {
n = 4;
}
if (y > 0.3 && y <= 0.5) {
n = 3;
}
if (y > 0.5 && y <= 0.7) {
n = 5;
}
if (y > 0.7 && y <= 0.85) {
n = 2;
}
if (y > 0.85) {
n = 6;
}
for (int l = 0; l < n; l++) {
double x = MT.nextDouble();
if (x <= 0.5) {
Rabbits.add(new Rabbit('m', 0, false));
Rabbits.get(k).setPortee();
} else if (x > 0.5) {
Rabbits.add(new Rabbit('f', 0, false));
Rabbits.get(k).setPortee();
}
}
}
if (i % 12 == 0) {
Rabbits.get(k).reinitPortee();
Rabbits.get(k).setChanceSurvie();
Rabbits.get(k).kill();
}
}
System.out.println("month " + i%12 + " year " + i/12 + " we have " + Rabbits.size()); // affichage chaque mois
}
System.out.println("Apres une simulation de " + nbAnnee + " nous avons :"); //affichage resultat final
System.out.println(" une population de " + Rabbits.size());
}
//}
}
Rabbit Class
package com.company;
public class Rabbit {
//Age
int ageMois;
int ageAnnee;
boolean isMature;
boolean isActiveSex = false;
double chanceSurvie = 0.5;
boolean isAlive = true;
//Les femelles
boolean isSterile = false;
char sexe;
int nbPortee = 0;
int nbPorteeInit;
MTRandom MT = new MTRandom();
public Rabbit(char sexe, int ageMois,boolean isMature){
int[] init ={0x123, 0x234, 0x345, 0x456};
MT.setSeed(init);
this.sexe = sexe;
this.ageMois = ageMois;
this.isMature = isMature;
double x = MT.nextDouble();
double y = MT.nextDouble();
if(this.sexe == 'f'){
if(x<0.1){
this.isSterile = true;
}
if(!this.isSterile){
if(y<=0.4){
this.nbPorteeInit = 6;
}
if(y>0.4 && y<=0.6){
this.nbPorteeInit = 5;
}
if(y>0.6 && y<=0.8){
this.nbPorteeInit = 7;
}
if(y>0.8 && y<=0.875){
this.nbPorteeInit = 4;
}
if(y>0.95&& y<=0.975){
this.nbPorteeInit = 8;
}
if(y>975){
this.nbPorteeInit = 3;
}
if(y>975){
this.nbPorteeInit = 9;
}
this.nbPortee = this.nbPorteeInit;
}
}
}
public void setAge(){ //en mois
double x = MT.nextDouble();
this.ageMois += 1;
this.ageAnnee = this.ageMois/12;
if(this.ageMois == 5) {
if (x <= 0.2) {
isMature = true;
if(!isSterile){
isActiveSex = true;
}
}
}
else if(this.ageMois == 6){
if (x >0.2 && x <= 0.5){
isMature = true;
if(!isSterile){
isActiveSex = true;}
}
}
else if(this.ageMois == 7){
if(x > 0.5 && x <= 0.8){
isMature = true;
if(!isSterile){
isActiveSex = true;}
}
}
else if(this.ageMois > 8){
if(x > 0.8){
isMature = true;
isActiveSex = true;
}
}
}
public void setChanceSurvie(){
if(this.isMature){
this.chanceSurvie = 0.75;
}
switch(this.ageAnnee){
case 8: this.chanceSurvie = 0.6;
case 9: this.chanceSurvie = 0.45;
case 10: this.chanceSurvie = 0.3;
case 11: this.chanceSurvie = 0.15;
case 12: this.chanceSurvie = 0;
}
}
public void setPortee(){
this.nbPortee -=1;
}
public void reinitPortee(){
this.nbPortee = this.nbPorteeInit;
}
public void kill(){
if(MT.nextDouble() >this.chanceSurvie){
isAlive = false;
}
}
}
5 years experience
month 1 year 0 we have 6
month 2 year 0 we have 11
month 3 year 0 we have 11
month 4 year 0 we have 11
month 5 year 0 we have 11
month 6 year 0 we have 20
month 7 year 0 we have 39
month 8 year 0 we have 45
month 9 year 0 we have 45
month 10 year 0 we have 45
month 11 year 0 we have 58
month 0 year 1 we have 116
month 1 year 1 we have 20
month 2 year 1 we have 33
month 3 year 1 we have 39
month 4 year 1 we have 39
month 5 year 1 we have 39
month 6 year 1 we have 69
month 7 year 1 we have 113
month 8 year 1 we have 151
month 9 year 1 we have 160
month 10 year 1 we have 164
month 11 year 1 we have 206
month 0 year 2 we have 310
month 1 year 2 we have 116
month 2 year 2 we have 160
month 3 year 2 we have 167
month 4 year 2 we have 167
month 5 year 2 we have 167
month 6 year 2 we have 366
month 7 year 2 we have 621
month 8 year 2 we have 713
month 9 year 2 we have 756
month 10 year 2 we have 758
month 11 year 2 we have 1150
month 0 year 3 we have 2032
month 1 year 3 we have 458
month 2 year 3 we have 664
month 3 year 3 we have 692
month 4 year 3 we have 692
month 5 year 3 we have 692
month 6 year 3 we have 1368
month 7 year 3 we have 2312
month 8 year 3 we have 2808
month 9 year 3 we have 2891
month 10 year 3 we have 2896
month 11 year 3 we have 4204
month 0 year 4 we have 7320
month 1 year 4 we have 1858
month 2 year 4 we have 2604
month 3 year 4 we have 2653
month 4 year 4 we have 2653
month 5 year 4 we have 2653
month 6 year 4 we have 5456
month 7 year 4 we have 9401
month 8 year 4 we have 11077
month 9 year 4 we have 11303
month 10 year 4 we have 11321
month 11 year 4 we have 17109
month 0 year 5 we have 29899
MT => Pseudo Random Numbers generator:
package com.company;
import java.util.Random;
/**
* #version 1.0
* #author David Beaumont, Copyright 2005
* <p>
* A Java implementation of the MT19937 (Mersenne Twister) pseudo
* random number generator algorithm based upon the original C code
* by Makoto Matsumoto and Takuji Nishimura (see
* <a href="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html">
* http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html</a> for
* more information.
* <p>
* As a subclass of java.util.Random this class provides a single
* canonical method next() for generating bits in the pseudo random
* number sequence. Anyone using this class should invoke the public
* inherited methods (nextInt(), nextFloat etc.) to obtain values as
* normal. This class should provide a drop-in replacement for the
* standard implementation of java.util.Random with the additional
* advantage of having a far longer period and the ability to use a
* far larger seed value.
* <p>
* This is <b>not</b> a cryptographically strong source of randomness
* and should <b>not</b> be used for cryptographic systems or in any
* other situation where true random numbers are required.
* <p>
* <!-- Creative Commons License -->
* <img alt="CC-GNU LGPL" border="0" src="http://creativecommons.org/images/public/cc-LGPL-a.png" /><br />
* This software is licensed under the CC-GNU LGPL.
* <!-- /Creative Commons License -->
*
* <!--
* <rdf:RDF xmlns="http://web.resource.org/cc/"
* xmlns:dc="http://purl.org/dc/elements/1.1/"
* xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
*
* <Work rdf:about="">
* <license rdf:resource="http://creativecommons.org/licenses/LGPL/2.1/" />
* <dc:type rdf:resource="http://purl.org/dc/dcmitype/Software" />
* </Work>
*
* <License rdf:about="http://creativecommons.org/licenses/LGPL/2.1/">
* <permits rdf:resource="http://web.resource.org/cc/Reproduction" />
* <permits rdf:resource="http://web.resource.org/cc/Distribution" />
* <requires rdf:resource="http://web.resource.org/cc/Notice" />
* <permits rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
* <requires rdf:resource="http://web.resource.org/cc/ShareAlike" />
* <requires rdf:resource="http://web.resource.org/cc/SourceCode" />
* </License>
*
* </rdf:RDF>
* -->
*
*/
public class MTRandom extends Random {
/**
* Auto-generated serial version UID. Note that MTRandom does NOT
* support serialisation of its internal state and it may even be
* necessary to implement read/write methods to re-seed it properly.
* This is only here to make Eclipse shut up about it being missing.
*/
private static final long serialVersionUID = -515082678588212038L;
// Constants used in the original C implementation
private final static int UPPER_MASK = 0x80000000;
private final static int LOWER_MASK = 0x7fffffff;
private final static int N = 624;
private final static int M = 397;
private final static int MAGIC[] = { 0x0, 0x9908b0df };
private final static int MAGIC_FACTOR1 = 1812433253;
private final static int MAGIC_FACTOR2 = 1664525;
private final static int MAGIC_FACTOR3 = 1566083941;
private final static int MAGIC_MASK1 = 0x9d2c5680;
private final static int MAGIC_MASK2 = 0xefc60000;
private final static int MAGIC_SEED = 19650218;
private final static long DEFAULT_SEED = 5489L;
// Internal state
private transient int[] mt;
private transient int mti;
private transient boolean compat = false;
// Temporary buffer used during setSeed(long)
private transient int[] ibuf;
/**
* The default constructor for an instance of MTRandom. This invokes
* the no-argument constructor for java.util.Random which will result
* in the class being initialised with a seed value obtained by calling
* System.currentTimeMillis().
*/
public MTRandom() { }
/**
* This version of the constructor can be used to implement identical
* behaviour to the original C code version of this algorithm including
* exactly replicating the case where the seed value had not been set
* prior to calling genrand_int32.
* <p>
* If the compatibility flag is set to true, then the algorithm will be
* seeded with the same default value as was used in the original C
* code. Furthermore the setSeed() method, which must take a 64 bit
* long value, will be limited to using only the lower 32 bits of the
* seed to facilitate seamless migration of existing C code into Java
* where identical behaviour is required.
* <p>
* Whilst useful for ensuring backwards compatibility, it is advised
* that this feature not be used unless specifically required, due to
* the reduction in strength of the seed value.
*
* #param compatible Compatibility flag for replicating original
* behaviour.
*/
public MTRandom(boolean compatible) {
super(0L);
compat = compatible;
setSeed(compat?DEFAULT_SEED:System.currentTimeMillis());
}
/**
* This version of the constructor simply initialises the class with
* the given 64 bit seed value. For a better random number sequence
* this seed value should contain as much entropy as possible.
*
* #param seed The seed value with which to initialise this class.
*/
public MTRandom(long seed) {
super(seed);
}
/**
* This version of the constructor initialises the class with the
* given byte array. All the data will be used to initialise this
* instance.
*
* #param buf The non-empty byte array of seed information.
* #throws NullPointerException if the buffer is null.
* #throws IllegalArgumentException if the buffer has zero length.
*/
public MTRandom(byte[] buf) {
super(0L);
setSeed(buf);
}
/**
* This version of the constructor initialises the class with the
* given integer array. All the data will be used to initialise
* this instance.
*
* #param buf The non-empty integer array of seed information.
* #throws NullPointerException if the buffer is null.
* #throws IllegalArgumentException if the buffer has zero length.
*/
public MTRandom(int[] buf) {
super(0L);
setSeed(buf);
}
// Initializes mt[N] with a simple integer seed. This method is
// required as part of the Mersenne Twister algorithm but need
// not be made public.
private final void setSeed(int seed) {
// Annoying runtime check for initialisation of internal data
// caused by java.util.Random invoking setSeed() during init.
// This is unavoidable because no fields in our instance will
// have been initialised at this point, not even if the code
// were placed at the declaration of the member variable.
if (mt == null) mt = new int[N];
// ---- Begin Mersenne Twister Algorithm ----
mt[0] = seed;
for (mti = 1; mti < N; mti++) {
mt[mti] = (MAGIC_FACTOR1 * (mt[mti-1] ^ (mt[mti-1] >>> 30)) + mti);
}
// ---- End Mersenne Twister Algorithm ----
}
/**
* This method resets the state of this instance using the 64
* bits of seed data provided. Note that if the same seed data
* is passed to two different instances of MTRandom (both of
* which share the same compatibility state) then the sequence
* of numbers generated by both instances will be identical.
* <p>
* If this instance was initialised in 'compatibility' mode then
* this method will only use the lower 32 bits of any seed value
* passed in and will match the behaviour of the original C code
* exactly with respect to state initialisation.
*
* #param seed The 64 bit value used to initialise the random
* number generator state.
*/
public final synchronized void setSeed(long seed) {
if (compat) {
setSeed((int)seed);
} else {
// Annoying runtime check for initialisation of internal data
// caused by java.util.Random invoking setSeed() during init.
// This is unavoidable because no fields in our instance will
// have been initialised at this point, not even if the code
// were placed at the declaration of the member variable.
if (ibuf == null) ibuf = new int[2];
ibuf[0] = (int)seed;
ibuf[1] = (int)(seed >>> 32);
setSeed(ibuf);
}
}
/**
* This method resets the state of this instance using the byte
* array of seed data provided. Note that calling this method
* is equivalent to calling "setSeed(pack(buf))" and in particular
* will result in a new integer array being generated during the
* call. If you wish to retain this seed data to allow the pseudo
* random sequence to be restarted then it would be more efficient
* to use the "pack()" method to convert it into an integer array
* first and then use that to re-seed the instance. The behaviour
* of the class will be the same in both cases but it will be more
* efficient.
*
* #param buf The non-empty byte array of seed information.
* #throws NullPointerException if the buffer is null.
* #throws IllegalArgumentException if the buffer has zero length.
*/
public final void setSeed(byte[] buf) {
setSeed(pack(buf));
}
/**
* This method resets the state of this instance using the integer
* array of seed data provided. This is the canonical way of
* resetting the pseudo random number sequence.
*
* #param buf The non-empty integer array of seed information.
* #throws NullPointerException if the buffer is null.
* #throws IllegalArgumentException if the buffer has zero length.
*/
public final synchronized void setSeed(int[] buf) {
int length = buf.length;
if (length == 0) throw new IllegalArgumentException("Seed buffer may not be empty");
// ---- Begin Mersenne Twister Algorithm ----
int i = 1, j = 0, k = (N > length ? N : length);
setSeed(MAGIC_SEED);
for (; k > 0; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >>> 30)) * MAGIC_FACTOR2)) + buf[j] + j;
i++; j++;
if (i >= N) { mt[0] = mt[N-1]; i = 1; }
if (j >= length) j = 0;
}
for (k = N-1; k > 0; k--) {
mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >>> 30)) * MAGIC_FACTOR3)) - i;
i++;
if (i >= N) { mt[0] = mt[N-1]; i = 1; }
}
mt[0] = UPPER_MASK; // MSB is 1; assuring non-zero initial array
// ---- End Mersenne Twister Algorithm ----
}
/**
* This method forms the basis for generating a pseudo random number
* sequence from this class. If given a value of 32, this method
* behaves identically to the genrand_int32 function in the original
* C code and ensures that using the standard nextInt() function
* (inherited from Random) we are able to replicate behaviour exactly.
* <p>
* Note that where the number of bits requested is not equal to 32
* then bits will simply be masked out from the top of the returned
* integer value. That is to say that:
* <pre>
* mt.setSeed(12345);
* int foo = mt.nextInt(16) + (mt.nextInt(16) << 16);</pre>
* will not give the same result as
* <pre>
* mt.setSeed(12345);
* int foo = mt.nextInt(32);</pre>
*
* #param bits The number of significant bits desired in the output.
* #return The next value in the pseudo random sequence with the
* specified number of bits in the lower part of the integer.
*/
protected final synchronized int next(int bits) {
// ---- Begin Mersenne Twister Algorithm ----
int y, kk;
if (mti >= N) { // generate N words at one time
// In the original C implementation, mti is checked here
// to determine if initialisation has occurred; if not
// it initialises this instance with DEFAULT_SEED (5489).
// This is no longer necessary as initialisation of the
// Java instance must result in initialisation occurring
// Use the constructor MTRandom(true) to enable backwards
// compatible behaviour.
for (kk = 0; kk < N-M; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
mt[kk] = mt[kk+M] ^ (y >>> 1) ^ MAGIC[y & 0x1];
}
for (;kk < N-1; kk++) {
y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
mt[kk] = mt[kk+(M-N)] ^ (y >>> 1) ^ MAGIC[y & 0x1];
}
y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N-1] = mt[M-1] ^ (y >>> 1) ^ MAGIC[y & 0x1];
mti = 0;
}
y = mt[mti++];
// Tempering
y ^= (y >>> 11);
y ^= (y << 7) & MAGIC_MASK1;
y ^= (y << 15) & MAGIC_MASK2;
y ^= (y >>> 18);
// ---- End Mersenne Twister Algorithm ----
return (y >>> (32-bits));
}
// This is a fairly obscure little code section to pack a
// byte[] into an int[] in little endian ordering.
/**
* This simply utility method can be used in cases where a byte
* array of seed data is to be used to repeatedly re-seed the
* random number sequence. By packing the byte array into an
* integer array first, using this method, and then invoking
* setSeed() with that; it removes the need to re-pack the byte
* array each time setSeed() is called.
* <p>
* If the length of the byte array is not a multiple of 4 then
* it is implicitly padded with zeros as necessary. For example:
* <pre> byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06 }</pre>
* becomes
* <pre> int[] { 0x04030201, 0x00000605 }</pre>
* <p>
* Note that this method will not complain if the given byte array
* is empty and will produce an empty integer array, but the
* setSeed() method will throw an exception if the empty integer
* array is passed to it.
*
* #param buf The non-null byte array to be packed.
* #return A non-null integer array of the packed bytes.
* #throws NullPointerException if the given byte array is null.
*/
public static int[] pack(byte[] buf) {
int k, blen = buf.length, ilen = ((buf.length+3) >>> 2);
int[] ibuf = new int[ilen];
for (int n = 0; n < ilen; n++) {
int m = (n+1) << 2;
if (m > blen) m = blen;
for (k = buf[--m]&0xff; (m & 0x3) != 0; k = (k << 8) | buf[--m]&0xff);
ibuf[n] = k;
}
return ibuf;
}
}
I'm writing a Sudoku solver and I have to calculate the what I learned was called the hamming distance of an int to 0, e.g. the hamming distance of 7 (111 in binary) to 0 is 3. So I simply do:
for(int dist = 0 ; num != 0 ; num>>=1) dist += (num&1);
Although that works fine, I find it a bit clumsy. I tried to come up with a binary operation trick to calculate the distance (mostly for fun), but I could only find a way that works for a distance of 1:
(num-1) ^ ((num<<1)-1) == num → true only if hamming dist to 0 == 1
I looked on StackOverflow and on the Net, but I couldn't find anything.
Assuming that num is never negative and always smaller than 512, is there a nicer/more elegant way to evaluate it, perhaps some binary operations tricks? If not, given the assumptions above, is there an approximation of the hamming distance that would always be within an error < 1?
To create a lookup table for 9 bits (since this is for Sudoku):
int dist[512];
dist[0] = 0;
for (i=1; i<512; i++)
dist[i] = (i&1) + dist[i/2];
To avoid the initial calculation this could also be written as a memoizing recursive function.
int dist(int bits) {
static _dist[512] = {};
if (bits == 0)
return 0;
else if (_dist[bits] == 0)
_dist[bits] = (bits & 1) + dist(bits/2);
return _dist[bits];
In java you can use the static method Integer.bitCount(int i)
If you need it in another language, this is the java source which should be pretty strait forward to translate.
/**
* Returns the number of one-bits in the two's complement binary
* representation of the specified {#code int} value. This function is
* sometimes referred to as the <i>population count</i>.
*
* #param i the value whose bits are to be counted
* #return the number of one-bits in the two's complement binary
* representation of the specified {#code int} value.
* #since 1.5
*/
public static int bitCount(int i) {
// HD, Figure 5-2
i = i - ((i >>> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
i = (i + (i >>> 4)) & 0x0f0f0f0f;
i = i + (i >>> 8);
i = i + (i >>> 16);
return i & 0x3f;
}
Not sure, if this helps, but out of curiosity I implemented it via templates:
template <int N>
struct Hamming {
enum { value = Hamming< (N/2) >::value + (N&1)};
};
template <>
struct Hamming<0>
{
enum { value = 0 };
};
int main() {
std::cout << Hamming<7>::value << std::endl;
return 0;
}
It can only be used if N is known at compile time, thus I think you will have to use something else in your case. However, it nicely demonstrates how any computation at runtime can (in principle) be completely avoided.
I am creating tones in my Java-program.
So there is a number of oscillator modules which produce the samples I need so that I can play more than one tone at a time. These samples are added and played.
If I select a low amplitude, e.g. 100, everything works fine (normalized it in Audacity):
But if I select a higher amplitude, in this case Short.MAX_VALUE, the created tone looks and sounds strange:
The samples are created here (source: http://www.wolinlabs.com/blog/java.sine.wave.html):
short value;
if (type == TYPE_SINE)
{
cycleIncrease = frequency / parent.getSamplingRate();
value = (short) (amplitude * Math.sin(2 * Math.PI * cyclePosition));
cyclePosition += cycleIncrease;
if (cyclePosition > 1)
cyclePosition -= 1;
return value;
}
With sawtooth waves I have the same problem:
else if (type == TYPE_SAW)
{
cycleIncrease = frequency / parent.getSamplingRate();
double doubleValue = cyclePosition - Math.floor(cyclePosition);
cyclePosition += cycleIncrease;
value = (short) (amplitude * doubleValue);
if (cyclePosition > 1)
cyclePosition -= 1;
return value;
}
amplitude has the value I mentioned above, so in these two cases 100 and Short.MAX_VALUE.
The samples are summed up in this way, where sum is the output sample and value is the calculated sample (0 if no tone is played):
int n = 0;
int sum = 0;
for (Wire inputWire:inputWires)
{
short value = inputWire.getNextSample();
if (value != 0)
{
n += 1;
}
sum += value;
x++;
}
if (n != 0)
sum = sum / n;
I also made sure that there is just the one sample from one oscillator if just one tone is played, so sum = value.
But I don't understand why different tones are created for different amplitudes with the same code, so why is this happening?
I also have a second question: If I end playing a sine tone with low amplitude, I can hear a short noise, which looks like this:
Where does this come from and how can I fix it?
Thank you for your help!
Question 1:
It looks as though you may be wrapping the waveform:
if (cyclePosition > 1)
cyclePosition -= 1; // You are reducing the amplitude by half? (goes from 1 to -1)
This is where some clipping should occur:
// For example...
if (cyclePosition > 1)
cyclePosition = 1
if (cyclePosition < -1)
cyclePosition = -1
The Audio may sound bad, but this is what normally happens when the Amplitude is too large.
Maybe try scaling the amplitude to always fit it within bounds:
scale = 1 / highestAmplitude;
foreach (point in wavePoints)
{
point *= scale;
}
// or something similar
Question 2:
Make sure the sine wave ends at 0 Amplitude
Verify that the sampleSizeInBits of your AudioFormat comports with your chosen amplitude. If not, you may be seeing a "wrap-around" effect when multiplying by amplitude. For reference, this example scales the sample size to eight bits representing a signed byte value in the range -128..127.
AudioFormat af = new AudioFormat(Note.SAMPLE_RATE, 8, 1, true, true);
…
sin[i] = (byte)(Math.sin(angle) * 127f);
I'm working on a Java program that prints a sine wave to the console. This is what I've written so far:
int num = 7;
for (double y = 2; y >= 0; y-=0.2) {
for (double x = 0; x <= num; x+=0.2) {
if ( ((0.1+x) >= Math.asin((double)y-1)) && (((double)x-0.1) <= Math.asin((double)y-1)) )
System.out.print('*');
else
System.out.print(' ');
}
System.out.println();
}
Essentially, this program treats each character on each line as a 0.2 x 0.2 area on a coordinate plane. If the sine function crosses this area, an asterisk is printed to the screen. Otherwise, a space is printed.
When run, this is printed to the console:
*
*
*
*
*
*
Can anybody tell me why my program stops after printing the first quarter of the wave?
The reason it only prints the first quarter of the sine wave is because of the indicated range of Math.asin:
Returns the arc sine of a value; the returned angle is in the range -pi/2 through pi/2.
Once you advance x past Math.PI / 2, then the if statement's condition will always be false.
You can take advantage of the fact that
sin(π - x) = sin(x)
and
sin(2π - x) = -sin(x)
by including more conditions. (I've also removed the unnecessary casts to double for clarity.)
if ( (0.1+x >= Math.asin(y-1)) && (x-0.1 <= Math.asin(y-1)) ||
(0.1+x >= Math.PI - Math.asin(y-1)) && (x-0.1 <= Math.PI - Math.asin(y-1))
(2*Math.PI -(0.1 + x) <= -Math.asin(y-1)) && (2*Math.PI -(x-0.1) >= -Math.asin(y-1)) )
Output:
*
* *
* *
* * *
* * *
* * *
* *
* *
* *
* *
*
The problem seems to arise from the condition of your if-statement, which almost always evaluates to false. You should check the calculus behind your conditional expression. I find increasing num does not really help here.
Maybe you should print another character instead of a whitespace by replacing System.out.print(' '); with System.out.print('_'); for instance. This way you may be able to figure out why your program behaves that way. Reworking that algorithm of yours may also be helpful.
Edit: Whoops, skimmed the code a little too fast. This is wrong.
Well, you set num to 7, then in your loop you take x from 0 to num-1, so you're only looping over 7 columns. Increase num and your graph should add additional columns. You'll have to increase the range of y values you loop over too, if you want the graph to extend to negative numbers.
However, your algorithm is extremely inefficient. You're calculating asin(y-1) twice for every square on your graph, when all you really need to do is calculate sin(x) once for every column. Why not just fill an array with precalculated sine values and then consult the array while drawing the graph? (I realize, of course, that on a modern computer it's fast enough to be practically irrelevant either way.)
Thank you all for the help! Based on #dasblinkenlight's suggestion to use sin instead of asin, I found a very simple solution:
int num = 7;
for (double y = 1; y >= -1; y-=0.2) {
for (double x = 0; x <= num; x+=0.2) {
double sin = Math.sin(x);
if ( (0.1+y) >= sin && (y-0.1) <= sin )
System.out.print('*');
else
System.out.print(' ');
}
System.out.println();
}
This prints this to the console:
*****
** *
* **
* * **
* * *
* * *
* *
* *
* *
** **
****
I'm getting a slight distortion (sounds like buzzing) in the background when I run the following code. Because of its subtle nature it makes believe there is some sort of aliasing going on with the byte casting.
AudioFormat = PCM_SIGNED 44100.0 Hz, 16 bit, stereo, 4 bytes/frame, big-endian
Note: code assumes (for now) that the data is in big endian.
public static void playFreq(AudioFormat audioFormat, double frequency, SourceDataLine sourceDataLine)
{
System.out.println(audioFormat);
double sampleRate = audioFormat.getSampleRate();
int sampleSizeInBytes = audioFormat.getSampleSizeInBits() / 8;
int channels = audioFormat.getChannels();
byte audioBuffer[] = new byte[(int)Math.pow(2.0, 19.0) * channels * sampleSizeInBytes];
for ( int i = 0; i < audioBuffer.length; i+=sampleSizeInBytes*channels )
{
int wave = (int) (127.0 * Math.sin( 2.0 * Math.PI * frequency * i / (sampleRate * sampleSizeInBytes * channels) ) );
//wave = (wave > 0 ? 127 : -127);
if ( channels == 1 )
{
if ( sampleSizeInBytes == 1 )
{
audioBuffer[i] = (byte) (wave);
}
else if ( sampleSizeInBytes == 2 )
{
audioBuffer[i] = (byte) (wave);
audioBuffer[i+1] = (byte)(wave >>> 8);
}
}
else if ( channels == 2 )
{
if ( sampleSizeInBytes == 1 )
{
audioBuffer[i] = (byte) (wave);
audioBuffer[i+1] = (byte) (wave);
}
else if ( sampleSizeInBytes == 2 )
{
audioBuffer[i] = (byte) (wave);
audioBuffer[i+1] = (byte)(wave >>> 8);
audioBuffer[i+2] = (byte) (wave);
audioBuffer[i+3] = (byte)(wave >>> 8);
}
}
}
sourceDataLine.write(audioBuffer, 0, audioBuffer.length);
}
Your comments say that the code assumes big-endian.
Technically you're actually outputting in little-endian, however it doesn't seem to matter because through a lucky quirk your most significant byte is always 0.
EDIT: to explain that further - when your value is at its maximum value of 127, you should be writing (0x00, 0x7f), but the actual output from your code is (0x7f, 0x00) which is 32512. This happens to be near the proper 16 bit maximum value of 32767, but with the bottom 8 bits all zero. It would be better to always use 32767 as the maximum value, and then discard the bottom 8 bits if required.
This means that even though you're outputting 16-bit data, the effective resolution is only 8 bit. This seems to account for the lack of sound quality.
I've made a version of your code that just dumps the raw data to a file, and can't see anything otherwise wrong with the bit shifting itself. There's no unexpected changes of sign or missing bits, but there is a buzz consistent with 8 bit sample quality.
Also, for what it's worth your math will be easier if you calculate the wave equation based on sample counts, and then worry about byte offsets separately:
int samples = 2 << 19;
byte audioBuffer[] = new byte[samples * channels * sampleSizeInBytes];
for ( int i = 0, j = 0; i < samples; ++i )
{
int wave = (int)(32767.0 * Math.sin(2.0 * Math.PI * frequency * i / sampleRate));
byte msb = (byte)(wave >>> 8);
byte lsb = (byte) wave;
for (int c = 0; c < channels; ++c) {
audioBuffer[j++] = msb;
if (sampleSizeInBytes > 1) {
audioBuffer[j++] = lsb;
}
}
}
I assume you are calling this code repeatedly to play a long sound.
Is there a chance that the wave you are generating is not getting to complete a full period before it is written?
If the wave gets "cut-off" before it completes a full period and then the next wave is written to the output, you will certainly hear something strange and I assume that may be what is causing the buzzing.
For example:
/-------\ /-------\ /-------\
-----/ \ -----/ \ -----/ \
\ \ \
\----- \----- \-----
Notice the disconnect between parts of this wave. That might be causing the buzzing.