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 SourceDataLine
stä TargetDataLine
ksi :)