Comment générer des effets sonores en Java?


Je recherche du code Java qui peut être utilisé pour générer du son au moment de l'exécution - PAS la lecture de fichiers audio existants.

Par exemple, quel est le meilleur code pour générer une forme d'onde en dents de scie à 440 Hz pendant une durée de 2 millisecondes? Code source apprécié!

Je me souviens que mon Commodore 128 avait une commande Sonore simple qui prenait comme paramètres la voix, la fréquence, la forme d'onde et la durée pour définir un son. Qui a très bien fonctionné dans beaucoup de cas simples (rapide et des jeux sales, des expériences avec le son, etc.).

Je cherche spécifiquement des effets sonores comme des sons, pas de la musique ou du MIDI (que la bibliothèque JFugue couvre assez bien).

Author: David Koelle, 2008-11-17

4 answers

Vous pouvez facilement générer des données sonores échantillonnées en Java et les lire sans utiliser de code natif. Si vous parlez des choses MIDI peuvent devenir difficiles, mais je ne l'ai pas tâté dans ce domaine.

Pour générer des données sonores échantillonnées, vous devez effectuer le processus à l'envers. Nous allons agir comme le A-to-D et échantillonner une fonction sonore continue au fil du temps. Votre carte son fait la même chose pour l'audio via un micro ou une entrée ligne.

Tout d'Abord, choisissez une fréquence d'échantillonnage (PAS la fréquence de la tonalité, nous sommes générer). Allons-y avec 44100 hz car c'est probablement le taux de lecture de la carte son (donc pas de conversion de taux d'échantillonnage, ce n'est pas facile à moins que le matériel ne le fasse).

// in hz, number of samples in one second
sampleRate = 44100

// this is the time BETWEEN Samples
samplePeriod = 1.0 / sampleRate

// 2ms
duration = 0.002;
durationInSamples = Math.ceil(duration * sampleRate);

time = 0;
for(int i = 0; i < durationInSamples; i++)
{
  // sample a sine wave at 440 hertz at each time tick
  // substitute a function that generates a sawtooth as a function of time / freq
  // rawOutput[i] = function_of_time(other_relevant_info, time);
  rawOutput[i] = Math.sin(2 * Math.PI * 440 * time);
  time += samplePeriod;
}

// now you can playback the rawOutput
// streaming this may be trickier
 4
Author: basszero, 2008-12-17 22:07:01

Voici un exemple qui pourrait aider. Cela génère des ondes de péché:

package notegenerator;

import java.io.IOException;

/**
 * Tone generator and player.
 * 
 * @author Cesar Vezga [email protected]
 */
public class Main {

public static void main(String[] args) throws IOException {

    Player player = new Player();

    player.play(BeachRock.getTack1(),BeachRock.getTack2());

}

 }

 package notegenerator;

 public class BeachRock {

// GUITAR
static String gs1 = "T332 A4-E4 F#5-C6 E5-A5 T166 G5 A5 F#5 A5 F5 A5 E5-A5 E3 G3 G#3 ";
static String gs2 = "A3 A3 A3 G3 E3 E3 G3 G#3 ";
static String gs3 = "A3 A3 A3 G3 E3 A3 C4 C#4 ";
static String gs4 = gs2 + gs2 + gs2 + gs3;
static String gs5 = "D4 D4 D4 C4 A3 A3 C4 D#4 ";
static String gs6 = "D4 D4 D4 C4 A3 E3 G3 G#3 ";
static String gs7 = gs4 + gs5 + gs6 + gs2 + "A3 A3 A3 G3 E3 B3 D3 D#3 ";
static String gs8 = "E4 E4 E4 D4 B3 B3 E4 B3 " + gs6 + gs2;
static String gs9 = "x E3-B3 E3-B3 E3-B3 E3-B3 E3 G3 G#3 ";
static String gs10 = gs7 + gs8 + gs9;
static String gs11 = "A3-D4 X*7 X*16 X*5 E3 G3 G#3 ";
static String guitar = gs1 + gs10 + gs11 + gs10 + gs11 + "A3 A3 A3";

// DRUMS
static String ds1 = "D2 X D3 D3 X*2 D3 X ";
static String ds2 = "D2 X D3 D3 X D3 D3 D3 ";
static String ds3 = "D2 D3 D3 D3 D3 T83 D3 D3 T166 D3 ";
static String ds4 = ds1 + ds1 + ds1 + ds2;
static String ds5 = ds1 + ds1 + ds1 + ds3;
static String ds6 = "D2*2 D3 D3 X*2 D3*2 ";
static String ds7 = "D2*2 D3 D3 X D3 D3 D3 ";
static String ds8 = ds6 + ds6 + ds6 + ds7;

static String drums = "V25 T166 X*16 " + ds4 + ds4 + ds5 + ds8 + ds4 + ds4
        + ds5 + ds8;

public static String getTack1(){
    return guitar;
}

public static String getTack2(){
    return drums;
}


}

package notegenerator;

import java.util.HashMap;

/**
 * 
 * Physics of Music - Notes
 * 
 * Frequencies for equal-tempered scale
 * This table created using A4 = 440 Hz
 * Speed of sound = 345 m/s = 1130 ft/s = 770 miles/hr
 *  
 *  ("Middle C" is C4 )
 * 
 * http://www.phy.mtu.edu/~suits/notefreqs.html
 * 
 * @author Cesar Vezga <[email protected]>
 *
 */
 public class Notes {


private static final Object[] notes = {
"C0",16.35,
"C#0/Db0",17.32,
"D0",18.35,
"D#0/Eb0",19.45,
"E0",20.6,
"F0",21.83,
"F#0/Gb0",23.12,
"G0",24.5,
"G#0/Ab0",25.96,
"A0",27.5,
"A#0/Bb0",29.14,
"B0",30.87,
"C1",32.7,
"C#1/Db1",34.65,
"D1",36.71,
"D#1/Eb1",38.89,
"E1",41.2,
"F1",43.65,
"F#1/Gb1",46.25,
"G1",49.00,
"G#1/Ab1",51.91,
"A1",55.00,
"A#1/Bb1",58.27,
"B1",61.74,
"C2",65.41,
"C#2/Db2",69.3,
"D2",73.42,
"D#2/Eb2",77.78,
"E2",82.41,
"F2",87.31,
"F#2/Gb2",92.5,
"G2",98.00,
"G#2/Ab2",103.83,
"A2",110.00,
"A#2/Bb2",116.54,
"B2",123.47,
"C3",130.81,
"C#3/Db3",138.59,
"D3",146.83,
"D#3/Eb3",155.56,
"E3",164.81,
"F3",174.61,
"F#3/Gb3",185.00,
"G3",196.00,
"G#3/Ab3",207.65,
"A3",220.00,
"A#3/Bb3",233.08,
"B3",246.94,
"C4",261.63, // Middle C
"C#4/Db4",277.18,
"D4",293.66,
"D#4/Eb4",311.13,
"E4",329.63,
"F4",349.23,
"F#4/Gb4",369.99,
"G4",392.00,
"G#4/Ab4",415.3,
"A4",440.00,
"A#4/Bb4",466.16,
"B4",493.88,
"C5",523.25,
"C#5/Db5",554.37,
"D5",587.33,
"D#5/Eb5",622.25,
"E5",659.26,
"F5",698.46,
"F#5/Gb5",739.99,
"G5",783.99,
"G#5/Ab5",830.61,
"A5",880.00,
"A#5/Bb5",932.33,
"B5",987.77,
"C6",1046.5,
"C#6/Db6",1108.73,
"D6",1174.66,
"D#6/Eb6",1244.51,
"E6",1318.51,
"F6",1396.91,
"F#6/Gb6",1479.98,
"G6",1567.98,
"G#6/Ab6",1661.22,
"A6",1760.00,
"A#6/Bb6",1864.66,
"B6",1975.53,
"C7",2093.00,
"C#7/Db7",2217.46,
"D7",2349.32,
"D#7/Eb7",2489.02,
"E7",2637.02,
"F7",2793.83,
"F#7/Gb7",2959.96,
"G7",3135.96,
"G#7/Ab7",3322.44,
"A7",3520.00,
"A#7/Bb7",3729.31,
"B7",3951.07,
"C8",4186.01,
"C#8/Db8",4434.92,
"D8",4698.64,
"D#8/Eb8",4978.03

};

private HashMap<String,Double> noteMap;

public Notes(){
    noteMap = new HashMap<String,Double>();
    for(int i=0; i<notes.length; i=i+2){
        String name = (String)notes[i];
        double freq = (Double)notes[i+1];
        String[] keys = name.split("/");
        for(String key : keys){
            noteMap.put(key,  freq);
            System.out.println(key);
        }
    }
}


public byte[] getCordData(String keys, double duration){
    int N = (int) (8000 * duration/1000);
    byte[] a = new byte[N+1];
    String[] key = keys.split(" ");
    int count=0;
    for(String k : key){
        double freq = getFrequency(k);
        byte[] tone = tone(freq,duration);
            if(count==0){
               a = tone;
            }else{
               a = addWaves(a,tone);
            }
        count++;
    }

    return a;
}


public byte[] addWaves(byte[] a, byte[] b){
    int len = Math.max(a.length, b.length);
    byte[] c = new byte[len];
    for(int i=0; i<c.length; i++){
        byte aa = ( i < a.length ? a[i] : 0);
        byte bb = ( i < b.length ? b[i] : 0);

           c[i] = (byte) (( aa + bb ) / 2);
    }
    return c;
}


public double getFrequency(String key){
    Double f = noteMap.get(key);
    if(f==null){
        System.out.println("Key not found. "+key);
        f = 0D;
    }
    return f;
}

public byte[] tone(String key, double duration) {
    double freq = getFrequency(key);

    return tone(freq,duration); 
 } 

 public byte[] tone(double hz, double duration) {
        int N = (int) (8000 * duration/1000);
        byte[] a = new byte[N+1];
        for (int i = 0; i <= N; i++) {
            a[i] = (byte) ( Math.sin(2 * Math.PI * i * hz / 8000) * 127 );
        }
        return a; 
 } 


}

package notegenerator;

import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;

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

public class Player {

private SourceDataLine line = null;

private Notes notes = new Notes();

private long time = 250;

private double volumen = 1;

public void play(String keys) {

    byte[] data = parse(keys);

    start();

    line.write(data, 0, data.length);

    stop();

}

public void play(String... track) {

    byte[] data2 = parseAll(track);

    if (data2 != null) {
        start();

        line.write(data2, 0, data2.length);

        stop();
    }

}

private byte[] parseAll(String... track) {

    byte[] data2 = null;

    for (String t : track) {
        byte[] data1 = parse(t);
        if (data2 == null) {
            data2 = data1;
        } else {
            data2 = notes.addWaves(data1, data2);
        }
    }

    return data2;

}

private byte[] parse(String song) {
    time = 250;

    volumen = 1;

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    String[] key = song.split(" ");

    byte[] data = null;

    for (String k : key) {
        int mult = 1;

        if (k.indexOf("*") > -1) {
            String keyAux = k.split("\\*")[0];
            mult = Integer.parseInt(k.split("\\*")[1]);
            k = keyAux;
        } else if (k.startsWith("T")) {
            time = Long.parseLong(k.substring(1));
            continue;
        } else if (k.startsWith("V")) {
            volumen =  Double.parseDouble(k.substring(1)) / 100;

            if(volumen>1) volumen = 1;
            if(volumen<0) volumen = 0;

            continue;
        }

        if (k.indexOf("-") > -1) {
            k = k.replaceAll("-", " ").trim();
            data = notes.getCordData(k, time * mult);
        } else {
            data = notes.tone(k, time * mult);
        }

        volumen(data);

        try {
            baos.write(data);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    return baos.toByteArray();

}



private void volumen(byte[] data) {
    for(int i=0; i<data.length; i++){
        data[i] = (byte) (data[i] * volumen);
    }

}

private void stop() {
    line.drain();
    line.stop();

}

private void start() {

    AudioFormat format = new AudioFormat(8000.0F, 8, 1, true, false);

    SourceDataLine.Info info = new DataLine.Info(SourceDataLine.class,
            format); // format
    // is
    // an
    // AudioFormat
    // object
    if (!AudioSystem.isLineSupported(info)) {
        System.out.println("Format not supported");
        System.exit(1);
    }

    // Obtain and open the line.
    try {
        line = (SourceDataLine) AudioSystem.getLine(info);
        line.open(format);
    } catch (LineUnavailableException ex) {
        ex.printStackTrace();
    }

    // Assume that the TargetDataLine, line, has already
    // been obtained and opened.
    int numBytesRead;

    line.start();

}

public void save(String track, String fname) throws IOException {
    byte[] data = parse(track);

    FileOutputStream fos = new FileOutputStream(fname);

    fos.write(data);
    fos.flush();
    fos.close();

}

}
 8
Author: cvezga, 2012-08-15 17:58:00

Le Java media framework fait les deux. Vous pouvez lire des sons enregistrés ou utiliser l'interface MIDI pour synthétiser vos propres sons et musiques. Il fournit également une API de mixage.

Bien sûr, si vous connaissez les détails de la forme d'onde que vous souhaitez lire, vous pouvez "échantillonner" la fonction à intervalles réguliers et transmettre les échantillons résultants à l'API de lecture, comme s'il s'agissait d'un fichier audio préenregistré.

Le JMF n'est pas activement maintenu par Sun, mais il fonctionne distributions pour différentes plates-formes.

Mon premier ordinateur était le Commodore 64, et je me souviens de "Everybody Wants to Rule the World" de Tears for Fears lors du pompage de sa puce SID. Je ne peux pas dire sicet émulateur Java SID pur est open source ou non, mais il pourrait vous donner quelques conseils sur la mise en œuvre de fonctionnalités Attaque-Décroissance-Maintien-Libération et forme d'onde de niveau supérieur.

 4
Author: erickson, 2008-11-18 16:17:52

Ce que vous voulez n'est probablement pas une API sonore mais une sorte de code de synthétiseur, je suis sûr que vous avez besoin de plus de contrôle de pilote sonore de bas niveau que Java ne le permettrait (il s'agit d'un langage interprété fonctionnant normalement dans un "sandbox").

Mais la bonne nouvelle est qu'une recherche rapide pour " java sound synthesizing" sur Google a révélé un plugin appelé JSyn qui utilise des méthodes C natives (ce que j'ai deviné était une façon de le faire) pour générer du son. Il semble être gratuit pour utilisation non commerciale et disponible dans les licences commerciales ainsi. :)

 0
Author: Stein G. Strindhaug, 2008-11-17 22:15:53