/** * Copyright 2000-2009 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.IOException; import java.io.InputStream; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; import marytts.util.data.audio.AudioDoubleDataSource; import marytts.util.signal.SignalProcUtils; //Mixes the given audio source with a pre-recorded audio file //by handling continuity and energy levels in real-time public class AudioMixer implements InlineDataProcessor { private int silenceStart; // Silence before starting the mix in samples private int silenceInBetween; // Total silent samples between two consecutive mixes private int samplingRate; AudioDoubleDataSource mixSignalSource; double[] mixSignal; // int mixStart; // Index to copy & paste rom mixSignal double mixAmount; double oneMinusMixAmount; int bufferSize; int quarterBufferSize; double dataEn; double mixEn; double scale; double avgEn; double[] frm; boolean bFixed; // Plays the sound in fixed amount in the background double dataEnLongTerm; // long term average sample energy of the input data int enLongTermSize; double[] enLongTermBuff; int enLongTermInd; boolean bFirstEnBuff; // stSil: silence before starting the mix in seconds // stBwn: silence between consecutive mixes in seconds // fs: sampling rate in Hz public AudioMixer(InputStream inputStream, double stSil, double stBwn, int fs, int buffSize, double amount, boolean isFixed) { AudioInputStream inputAudio = null; try { inputAudio = AudioSystem.getAudioInputStream(inputStream); } catch (UnsupportedAudioFileException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } if (inputAudio != null) { int mixFs = (int) inputAudio.getFormat().getSampleRate(); mixSignalSource = new AudioDoubleDataSource(inputAudio); this.samplingRate = fs; if (mixFs != fs) { System.out.println("Error! Sampling rates do not match, will not do any modificaiton on input"); mixSignalSource = null; } else { int i; silenceStart = (int) Math.floor(stSil * samplingRate + 0.5); silenceInBetween = (int) Math.floor(stBwn * samplingRate + 0.5); silenceStart = Math.max(0, silenceStart); silenceInBetween = Math.max(0, silenceInBetween); mixSignal = new double[(int) mixSignalSource.getDataLength() + silenceInBetween]; mixSignalSource.getData(mixSignal); avgEn = SignalProcUtils.getAverageSampleEnergy(mixSignal, (int) mixSignalSource.getDataLength()); for (i = (int) mixSignalSource.getDataLength(); i < (int) mixSignalSource.getDataLength() + silenceInBetween; i++) mixSignal[i] = 0.0; mixStart = 0; mixAmount = amount; mixAmount = Math.max(mixAmount, 0.0); mixAmount = Math.min(mixAmount, 1.0); oneMinusMixAmount = 1.0 - mixAmount; bufferSize = buffSize; quarterBufferSize = (int) Math.floor(bufferSize * 0.25 + 0.5); frm = new double[bufferSize]; bFixed = isFixed; scale = 1.0; enLongTermSize = 40; enLongTermBuff = new double[enLongTermSize]; for (i = 0; i < enLongTermSize; i++) enLongTermBuff[i] = 0.0; bFirstEnBuff = true; enLongTermInd = -1; } } else { mixSignalSource = null; } } public void applyInline(double[] data, int pos, int len) { if (data.length == bufferSize) { int i; for (i = 0; i < bufferSize; i++) { frm[i] = mixSignal[(mixStart + i) % mixSignal.length]; } if (!bFixed) { dataEn = Math.sqrt(SignalProcUtils.getAverageSampleEnergy(data)); mixEn = Math.sqrt(SignalProcUtils.getAverageSampleEnergy(frm)); scale = dataEn / mixEn; } else { dataEn = Math.sqrt(SignalProcUtils.getAverageSampleEnergy(data)); enLongTermInd++; if (bFirstEnBuff) { if (enLongTermInd < enLongTermSize) enLongTermBuff[enLongTermInd] = dataEn; if (enLongTermInd == enLongTermSize - 1) { bFirstEnBuff = false; enLongTermInd = -1; } } dataEnLongTerm = 0.0; for (i = 0; i < enLongTermSize; i++) dataEnLongTerm += enLongTermBuff[i]; dataEnLongTerm /= enLongTermSize; scale = 0.25 * dataEnLongTerm / avgEn; } for (i = 0; i < bufferSize; i++) { data[i] = oneMinusMixAmount * data[i] + mixAmount * scale * frm[i]; } mixStart += quarterBufferSize; } } }