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 :)