Kirjautuminen

Haku

Tehtävät

Koodit: Java: Reaaliajassa generoidun äänen toistaminen

Kirjoittaja: Hennkka

Kirjoitettu: 07.11.2012 – 07.11.2012

Tagit: kirjaston käyttö, ohjelmointitavat, ääni, koodi näytille, vinkki

Useissa kielissä ohjelman itse, reaaliajassa luoman äänen toistaminen on hankalaa. Javassa se on kuitenkin yllättävän helppoa – kunhan tietää kuinka se tehdään. Tämän koodivinkin on tarkoitus näyttää juuri se.

Lisäksi vinkki esittelee (harmoniallisen) siniaallon luonnin.

Käytännössä äänen toistaminen alkaa kaiutinyhteyden avaamisella, minkä jälkeen puskuria vuoronperään täytetään ja tungetaan kaiuttimelle. Simppeliä siis :)

import javax.sound.sampled.*;

public class Aani {

    private static double siniaalto(double aika, int nuotti) {
        // Lasketaan nuotista taajuus Wikipediasta löytyvällä kaavalla
        double taajuus = Math.pow(2, (nuotti - 49) / 12.0) * 440;

        // Lasketaan sin taajuuden ja kohdan perusteella
        return Math.sin(taajuus * aika * 2 * Math.PI);
    }

    private static double siniaaltoHarmonialla(double aika, int nuotti) {
        // Lasketaan taajuus samalla kaavalla kuin yllä
        double taajuus = Math.pow(2, (nuotti - 49) / 12.0) * 440;
        double tulos = 0;

        int kierroksia = 5;
        // Jakaja, jotta saadaan tulos skaalattua välille [-1, 1]
        int jakaja = 0;

        // Lisätään uusi siniaalto edellisen päälle harmoniassa
        for (int i = 0; i < kierroksia; i++) {
            // Lasketaan sinifunktion arvo ja kerrotaan se painoarvolla (ylä-äänet saavat pienemmät painoarvot)
            tulos += Math.sin(taajuus * aika * 2 * Math.PI) * (kierroksia - i);
            // Lisätään jakajaan panoarvo
            jakaja += kierroksia - i;
            // Taajuus kasvaa aina kahdella eli siirrytään oktaavia ylemmäs kierrosten välillä
            taajuus *= 2;
        }

        // Lasketaan lopullinen tulos
        tulos /= jakaja;

        return tulos;
    }

    public static void main(String[] args) {

        // Avataan kaiutinyhteys:
        // new AudioFormat(näytteenottotaajuus, nöytteen bittien määrä, kanavien määrä,
        //                 etumerkillinen, bigEndian
        AudioFormat formaatti = new AudioFormat(48000, 8, 1, true, true);
        SourceDataLine linja = null;
        DataLine.Info info = new DataLine.Info(SourceDataLine.class, formaatti);

        // Tarkistetaan, tuetaanko äänentoistoa
        if (!AudioSystem.isLineSupported(info)) {
            System.err.println("VIRHE: Äänentoistoa ei tuettu");
            System.exit(1);
        }

        // Avataan äänentoistolinja
        try {
            linja = (SourceDataLine) AudioSystem.getLine(info);
            linja.open(formaatti);
        } catch (LineUnavailableException ex) {
            ex.printStackTrace();
            System.exit(1);
        }

        // Luodaan puskuri, jonka koko on sama kuin linjan
        byte[] puskuri = new byte[linja.getBufferSize()];

        // Aloitetaan toistaminen
        linja.start();

        // Muuttuja toistettujen sekuntien määrälle
        double aika = 0;

        // Päälooppi, jossa puskuria täytetään
        while (true) {
            for (int i = 0; i < puskuri.length; i++) {
                // Haetaan siniaallon arvo tietyssä kohdassa yllä määritetyillä funktioilla ja skaalataan se tavuun mahtuvaksi
                puskuri[i] = (byte) (128 * siniaalto(aika, 49));

                // Lisätään yhden samplen toistoaika sekunteina
                aika += 1.0 / formaatti.getSampleRate();
            }

            // Tyhjennetään koko puskuri linjaan
            linja.write(puskuri, 0, puskuri.length);
        }
    }
}

ps. Mikrofonin lukeminen on aivan yhtä helppoa, sen kuin muuttaa linjan tyypin SourceDataLinestä TargetDataLineksi :)

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta