/** * Copyright 2007 DFKI GmbH. * All Rights Reserved. Use is subject to license terms. * * This file is part of MARY TTS. * * MARY TTS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package marytts.signalproc.process; import java.io.File; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import marytts.signalproc.window.Window; import marytts.util.data.BufferedDoubleDataSource; import marytts.util.data.audio.AudioDoubleDataSource; import marytts.util.data.audio.DDSAudioInputStream; /** * @author Oytun Türk * */ public class Chorus implements InlineDataProcessor { private double[] buffIn; private double[] buffOut; private boolean[] bFirstInBuff; private int[] delays; private double[] amps; private int buffInStart; private int numChannels; private double sumAmps; public Chorus(int samplingRate) { this(null, null, samplingRate); } public Chorus(int[] delaysInMiliseconds, double[] ampsIn, int samplingRate) { // If null parameters, use a default chorus boolean bDelete = false; if (delaysInMiliseconds == null) { bDelete = true; delaysInMiliseconds = new int[2]; delaysInMiliseconds[0] = 466; delaysInMiliseconds[1] = 600; ampsIn = new double[2]; ampsIn[0] = 0.54; ampsIn[1] = -0.10; } numChannels = Math.min(delaysInMiliseconds.length, ampsIn.length); if (numChannels > 0) { delays = new int[numChannels]; amps = new double[numChannels]; bFirstInBuff = new boolean[numChannels]; int i; for (i = 0; i < numChannels; i++) delays[i] = (int) (Math.floor(delaysInMiliseconds[i] / 1000.0 * samplingRate + 0.5)); int buffInLen = delays[0]; for (i = 0; i < numChannels; i++) { if (buffInLen < delays[i]) buffInLen = delays[i]; } if (buffInLen < 1) buffInLen = 1; buffIn = new double[buffInLen]; for (i = 0; i < buffInLen; i++) buffIn[i] = 0.0; buffOut = null; buffInStart = 1; for (i = 0; i < numChannels; i++) amps[i] = ampsIn[i]; sumAmps = 1.0; for (i = 0; i < numChannels; i++) sumAmps += amps[i]; for (i = 0; i < numChannels; i++) bFirstInBuff[i] = true; } else { buffIn = null; buffOut = null; bFirstInBuff = null; delays = null; amps = null; buffInStart = 1; numChannels = 0; sumAmps = 1.0; } if (bDelete) { delaysInMiliseconds = null; ampsIn = null; } } public void applyInline(double[] data, int pos, int buffOutLen) { if (buffOutLen != data.length) buffOutLen = data.length; if (buffOutLen > 0) { // Perform processing on each channel if (buffOut == null || buffOut.length != buffOutLen) buffOut = new double[buffOutLen]; int i, j, ind; for (i = 0; i < buffOutLen; i++) buffOut[i] = 0.0; for (j = 1; j <= buffOutLen; j++) { buffIn[buffInStart - 1] = data[j - 1]; for (i = 1; i <= numChannels; i++) { if (i == 1) buffOut[j - 1] = 1.0 / sumAmps * buffIn[buffInStart - 1]; // Delay-less channel ind = buffInStart - delays[i - 1]; if (!(bFirstInBuff[i - 1]) && ind < 1) ind += buffIn.length; if (buffInStart + 1 > buffIn.length) bFirstInBuff[i - 1] = false; if (ind >= 1) buffOut[j - 1] += amps[i - 1] / sumAmps * buffIn[ind - 1]; } buffInStart++; if (buffInStart > buffIn.length) buffInStart = 1; } for (i = 0; i < buffOutLen; i++) data[i] = buffOut[i]; } } public static void main(String[] args) throws Exception { // Simple stadium effect int[] delaysInMiliseconds = { 366, 500 }; double[] amps = { 0.54, -0.10 }; // for (int i = 0; i < args.length; i++) { AudioInputStream inputAudio = AudioSystem.getAudioInputStream(new File(args[i])); int samplingRate = (int) inputAudio.getFormat().getSampleRate(); AudioDoubleDataSource signal = new AudioDoubleDataSource(inputAudio); FrameOverlapAddSource foas = new FrameOverlapAddSource(signal, Window.HANNING, true, 1024, samplingRate, new Chorus( delaysInMiliseconds, amps, samplingRate)); DDSAudioInputStream outputAudio = new DDSAudioInputStream(new BufferedDoubleDataSource(foas), inputAudio.getFormat()); String outFileName = args[i].substring(0, args[i].length() - 4) + "_chorusAdded.wav"; AudioSystem.write(outputAudio, AudioFileFormat.Type.WAVE, new File(outFileName)); } } }