Dépôt officiel du code source de l'ERP OpenConcerto
Blame | Last modification | View Log | RSS feed
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
*
* The contents of this file are subject to the terms of the GNU General Public License Version 3
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each file.
*/
package org.openconcerto.erp.utils;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.SourceDataLine;
public class SoundGenerator {
private int sampleRate = 44100; // 16 * 1024 or ~16KHz
public static final double A4 = 440.;
public static final byte FADE_NONE = 0;
public static final byte FADE_LINEAR = 1;
public static final byte FADE_QUADRATIC = 2;
public static final byte WAVE_SIN = 0;
public static final byte WAVE_SQUARE = 1;
public static final byte WAVE_TRIANGLE = 2;
public static final byte WAVE_SAWTOOTH = 3;
private static final double BYTE_OSCI = 127.0; // max value for byte
private static double linearFade(double i, int max) {
return (max - (float) i) / max;
}
private static double quadraticFade(double i, int max) {
// (-1/ max^2) * i^2 + 1
return (-1 / Math.pow(max, 2)) * Math.pow(i, 2) + 1;
}
private static double sinWave(double i, double period) {
return Math.sin(2.0 * Math.PI * i / period);
}
private static double squareWave(double i, double period) {
return (i % period) / period < .5 ? 1 : -1;
}
private static double triangleWave(double i, double period) {
return Math.asin(Math.sin((2 * Math.PI) * (i / period)));
}
private static double sawtoothWave(double i, double period) {
return -1 + 2 * ((i % period) / period);
}
public void playGradient(double fstart, double fend, double duration, double volume, byte fadeend, byte wave) {
byte[] freqdata = new byte[(int) (duration * sampleRate)];
// Generate the sound
for (int i = 0; i < freqdata.length; i++) {
freqdata[i] = (byte) generateValue(i, duration, fstart + (fend - fstart) * (i / (double) freqdata.length), volume, fadeend, wave);
}
// Play it
try {
final AudioFormat af = new AudioFormat(sampleRate, 8, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, sampleRate);
line.start();
line.write(freqdata, 0, freqdata.length);
line.drain();
line.close();
} catch (LineUnavailableException e) {
throw new IllegalStateException(e);
}
}
/**
* Same as playSound but plays several frequences at the same time<br/>
* <code>
* double[] freqs = {440.0,440*1.5}; <br/>
* SoundGenerator.playSound(freqs,1.0,0.5,SoundGenerator.FADE_LINEAR,SoundGenerator.WAVE_SIN);
* </code>
*
*/
public void playSounds(double[] freqs, double duration, double volume, byte fade, byte wave) {
if (freqs.length == 0) {
throw new IllegalArgumentException("no frequences");
}
volume = volume / freqs.length; // ease volume to avoid overflow
double[][] soundData = new double[freqs.length][];
for (int i = 0; i < soundData.length; i++) {
soundData[i] = generateSoundData(freqs[i], duration, volume, fade, wave);
}
byte[] freqdata = new byte[soundData[0].length];
for (int i = 0; i < soundData[0].length; i++) {
for (int j = 0; j < soundData.length; j++) {
freqdata[i] += (byte) (soundData[j][i]);
}
}
// Play it
try {
final AudioFormat af = new AudioFormat(sampleRate, 8, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, sampleRate);
line.start();
line.write(freqdata, 0, freqdata.length);
line.drain();
line.close();
} catch (LineUnavailableException e) {
throw new IllegalStateException(e);
}
}
/**
* Play a sound at a given frequency freq during duration (in seconds) with volume as strenght
* <br/>
* <br/>
* <code>SoundGenerator.playSound(440.0,1.0,0.5,SoundGenerator.FADE_LINEAR,SoundGenerator.WAVE_SIN);</code><br/>
* Available fades : FADE_NONE, FADE_LINEAR, FADE_QUADRATIC<br/>
* Available waves : WAVE_SIN, WAVE_SQUARE, WAVE_TRIANGLE, WAVE_SAWTOOTH<br/>
*/
public void playSound(double freq, double duration, double volume, byte fade, byte wave) {
double[] soundData = generateSoundData(freq, duration, volume, fade, wave);
byte[] freqdata = new byte[soundData.length];
for (int i = 0; i < soundData.length; i++) {
freqdata[i] = (byte) soundData[i];
}
// Play it
try {
final AudioFormat af = new AudioFormat(sampleRate, 8, 1, true, true);
SourceDataLine line = AudioSystem.getSourceDataLine(af);
line.open(af, sampleRate);
line.start();
line.write(freqdata, 0, freqdata.length);
line.drain();
line.close();
} catch (LineUnavailableException e) {
throw new IllegalStateException(e);
}
}
public double generateValue(double i, double duration, double freq, double volume, byte fade, byte wave) {
double period = (double) sampleRate / freq;
double fadeData = 0;
double waveData = 0;
int length = (int) (duration * sampleRate);
switch (fade) {
case FADE_NONE:
fadeData = 1.0D;
break;
case FADE_LINEAR:
fadeData = linearFade(i, length);
break;
case FADE_QUADRATIC:
fadeData = quadraticFade(i, length);
break;
default:
fadeData = 1.0D;
}
switch (wave) {
case WAVE_SIN:
waveData = sinWave(i, period);
break;
case WAVE_SQUARE:
waveData = squareWave(i, period);
break;
case WAVE_TRIANGLE:
waveData = triangleWave(i, period);
break;
case WAVE_SAWTOOTH:
waveData = sawtoothWave(i, period);
break;
default:
waveData = sinWave(i, period);
}
return (waveData * BYTE_OSCI * fadeData * volume);
}
public double[] generateSoundData(double freq, double duration, double volume, byte fade, byte wave) {
double[] freqdata = new double[(int) (duration * sampleRate)];
// Generate the sound
for (int i = 0; i < freqdata.length; i++) {
freqdata[i] = generateValue(i, duration, freq, volume, fade, wave);
}
return freqdata;
}
public void sampleRate(int sr) {
sampleRate = sr;
}
public int sampleRate() {
return sampleRate;
}
}