Sine Wave Sound Generator en Java

Quelle est la façon la plus simple de générer un son sinusoïdal à N'importe quelle fréquence en Java? Une taille d'échantillon de plus de 2 octets aiderait, mais cela n'a pas vraiment d'importance.

Merci

10
demandé sur Andrew Thompson 2011-12-26 03:20:56

6 réponses

Voir Beeper pour un exemple.


peut-être quelque chose de plus simple?

que 51 lignes de snippet (répété ci-dessous-espacées pour les commentaires de ligne simple et en ligne) comme montré en haut de la réponse liée, est aussi simple que de générer un gets de ton (OK, vous pouvez prendre 5+ lignes pour l'harmonique).

les Gens semblent supposer qu'il devrait être une méthode intégrée dans la boîte à outils pour produire un son pur. Il n'est pas, et prend un peu de calcul pour en faire un.

/** Generates a tone, and assigns it to the Clip. */
public void generateTone()
    throws LineUnavailableException {
    if ( clip!=null ) {
        clip.stop();
        clip.close();
    } else {
        clip = AudioSystem.getClip();
    }
    boolean addHarmonic = harmonic.isSelected();

    int intSR = ((Integer)sampleRate.getSelectedItem()).intValue();
    int intFPW = framesPerWavelength.getValue();

    float sampleRate = (float)intSR;

    // oddly, the sound does not loop well for less than
    // around 5 or so, wavelengths
    int wavelengths = 20;
    byte[] buf = new byte[2*intFPW*wavelengths];
    AudioFormat af = new AudioFormat(
        sampleRate,
        8,  // sample size in bits
        2,  // channels
        true,  // signed
        false  // bigendian
        );

    int maxVol = 127;
    for(int i=0; i<intFPW*wavelengths; i++){
        double angle = ((float)(i*2)/((float)intFPW))*(Math.PI);
        buf[i*2]=getByteValue(angle);
        if(addHarmonic) {
            buf[(i*2)+1]=getByteValue(2*angle);
        } else {
            buf[(i*2)+1] = buf[i*2];
        }
    }

    try {
        byte[] b = buf;
        AudioInputStream ais = new AudioInputStream(
            new ByteArrayInputStream(b),
            af,
            buf.length/2 );

        clip.open( ais );
    } catch(Exception e) {
        e.printStackTrace();
    }
}
10
répondu Andrew Thompson 2017-05-23 12:34:14

Java Sound API et Math.sin pour créer les niveaux d'onde réels.

http://www.developer.com/java/other/article.php/2226701 a un excellent tutoriel à ce sujet que j'avais mentionné il y a quelque temps. http://jsresources.org/examples/ était une autre référence utile.

8
répondu ziesemer 2011-12-25 23:23:27

Si vous voulez un peu de code facile à démarrer, ce qui devrait aider

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;

public class SinSynth {
    //
   protected static final int SAMPLE_RATE = 16 * 1024;


   public static byte[] createSinWaveBuffer(double freq, int ms) {
       int samples = (int)((ms * SAMPLE_RATE) / 1000);
       byte[] output = new byte[samples];
           //
       double period = (double)SAMPLE_RATE / freq;
       for (int i = 0; i < output.length; i++) {
           double angle = 2.0 * Math.PI * i / period;
           output[i] = (byte)(Math.sin(angle) * 127f);  }

       return output;
   }



   public static void main(String[] args) throws LineUnavailableException {
       final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
       SourceDataLine line = AudioSystem.getSourceDataLine(af);
       line.open(af, SAMPLE_RATE);
       line.start();

       boolean forwardNotBack = true;

       for(double freq = 400; freq <= 800;)  {
           byte [] toneBuffer = createSinWaveBuffer(freq, 50);
           int count = line.write(toneBuffer, 0, toneBuffer.length);

           if(forwardNotBack)  {
               freq += 20;  
               forwardNotBack = false;  }
           else  {
               freq -= 10;
               forwardNotBack = true;  
       }   }

       line.drain();
       line.close();
    }

}
8
répondu Thumbz 2014-05-29 08:16:58

Dans un premier je le conseil à créer de la classe Remarque:, qui renvoie les fréquences de note, et les convertit en byte array.

Puis de le transmettre très facilement

    protected static final int SAMPLE_RATE = 8 * 1024;


    public static void main(String[] args) throws LineUnavailableException {
        final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
        SourceDataLine line = AudioSystem.getSourceDataLine(af);
        line.open(af, SAMPLE_RATE);
        line.start();

        // fist argument is duration of playing note 
        byte[] noteDo = Note.DO.getTone(1, SAMPLE_RATE);
        byte[] noteRe = Note.RE.getTone(0.5, SAMPLE_RATE);
        byte[] noteMi = Note.MI.getTone(1.5, SAMPLE_RATE);

        line.write(noteDo, 0, noteDo.length);
        line.write(noteRe, 0, noteRe.length);
        line.write(noteMi, 0, noteMi.length);

        line.drain();
        line.close();
    }



public enum Note {

    DO(0.0f), DO_DIEZ(1.0f),
    RE(2.0f), RE_DIEZ(3.0f),
    MI(4.0f),
    FA(5.0f), FA_DIEZ(6.0f),
    SOL(7.0f),SOL_DIEZ(8.0f),
    LYA(9.0f),LYA_DIEZ(10.0f),
    SI(11.0f);


    private final double mPhase;

    Note(double phase) {
        mPhase = phase;
    }

    public double getNoteFrequencies() {

        double index = getmPhase()/ 12.0d;

        return 440 * Math.pow(2, index);
    }

    public static Note getNote(double phase) throws Exception {

        Note findNote = null;

        for (Note note : Note.values()){
            if (note.getmPhase() == phase){
                findNote = note;
            }
        }

        if (findNote == null)
            throw new Exception("Note not found: Ilegal phase " + phase);
        else
            return findNote;
    }

    public byte[] getTone(double duration, int rate){

        double frequencies = getNoteFrequencies();

        int maxLength = (int)(duration * rate);
        byte generatedTone[] = new byte[2 * maxLength];

        double[] sample = new double[maxLength];
        int idx = 0;

        for (int x = 0; x < maxLength; x++){
            sample[x] = sine(x, frequencies / rate);
        }


        for (final double dVal : sample) {

            final short val = (short) ((dVal * 100f));

            // in 16 bit wav PCM, first byte is the low order byte
            generatedTone[idx++] = (byte) (val & 0x00ff);
            generatedTone[idx++] = (byte) ((val & 0xff00) >>> 8);

        }

        return generatedTone;
    }

    private double sine(int x, double frequencies){
        return Math.sin(  2*Math.PI * x * frequencies);
    }

    public double getmPhase() {
        return mPhase;
    }
}
1
répondu Vahe Gharibyan 2016-10-29 17:12:28

je voudrais juste signaler qu'il existe un algorithme efficace pour la génération d'ondes sinusoïdales.

astuce DSP: générateur de tonus sinusoïdal http://www.dspguru.com/dsp/tricks/sine_tone_generator

0
répondu Albin Stigo 2015-02-11 16:51:48

si vous cherchez juste une classe pour appeler un bip, alors essayez ceci: (un code emprunté à Thumbz)

  package ditdah;

 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioSystem;
 import javax.sound.sampled.LineUnavailableException;
 import javax.sound.sampled.SourceDataLine;

 public class Beep {

protected static final int SAMPLE_RATE = 16 * 1024;

public void play(double freq, int length) {
    final AudioFormat af = new AudioFormat(SAMPLE_RATE, 8, 1, true, true);
    try {
        SourceDataLine line = AudioSystem.getSourceDataLine(af);
        line.open(af, SAMPLE_RATE);
        line.start();

        byte[] toneBuffer = this.createSinWaveBuffer(freq, length);
        say.it(toneBuffer.toString() + " " + toneBuffer.length);
        int count = line.write(toneBuffer, 0, toneBuffer.length);
        line.drain();
        line.close();
    } catch (LineUnavailableException e) {
        say.it(e.getLocalizedMessage());
    }
}

public byte[] createSinWaveBuffer(double freq, int ms) {
    int samples = (int) ((ms * SAMPLE_RATE) / 1000);
    byte[] output = new byte[samples];
    //
    double period = (double) SAMPLE_RATE / freq;
    for (int i = 0; i < output.length; i++) {
        double angle = 2.0 * Math.PI * i / period;
        output[i] = (byte) (Math.sin(angle) * 127f);
    }

    return output;
}

}

0
répondu Baruch Atta 2018-07-17 19:29:59