Ich habe mit einem Java-Programm (von anderen Leuten entwickelt) für die Text-zu-Sprache-Synthese gearbeitet. Die Synthese erfolgt durch Verkettung von "Di-Phones". In der ursprünglichen Version gab es keine Signalverarbeitung. Die Diphone wurden nur gesammelt und verkettet, um die Ausgabe zu erzeugen. Um die Ausgabe zu verbessern, habe ich versucht, eine "Phasenanpassung" der verkettenden Sprachsignale durchzuführen. Die von mir vorgenommene Änderung ist hier zusammengefasst:Wie reduziert man Rauschen in Signalüberschneidungen in Java?
- Audiodaten werden vom AudioInputStream in einem Byte-Array gesammelt. Da die Audiodaten 16 Bit haben, habe ich das Byte-Array in ein kurzes Array konvertiert.
- Die "Signalverarbeitung" erfolgt auf dem kurzen Array.
- Um die Audiodaten auszugeben, wird das kurze Array erneut in ein Byte-Array konvertiert.
Hier ist der Teil des Codes, die ich in das bestehende Programm geändert haben:
Audioeingang
Dieses Segment für jedes Diphon genannt wird.
Original Version
audioInputStream = AudioSystem.getAudioInputStream(sound);
while ((cnt = audioInputStream.read(byteBuffer, 0, byteBuffer.length)) != -1) {
if (cnt > 0) {
byteArrayPlayStream.write(byteBuffer, 0, cnt);
}
}
Meine Version
// public varialbe declarations
byte byteSoundFile[]; // byteSoundFile will contain a whole word or the diphones of a whole word
short shortSoundFile[] = new short[5000000]; // sound contents are taken in a short[] array for signal processing
short shortBuffer[];
int pos = 0;
int previousPM = 0;
boolean isWord = false;
public static HashMap<String, Integer> peakMap1 = new HashMap<String, Integer>();
public static HashMap<String, Integer> peakMap2 = new HashMap<String, Integer>();
// code for receiving and processing audio data
if(pos == 0) {
// a new word is going to be processed.
// so reset the shortSoundFile array
Arrays.fill(shortSoundFile, (short)0);
}
audioInputStream = AudioSystem.getAudioInputStream(sound);
while ((cnt = audioInputStream.read(byteBuffer, 0, byteBuffer.length)) != -1) {
if (cnt > 0) {
byteArrayPlayStream.write(byteBuffer, 0, cnt);
}
}
byteSoundFile = byteArrayPlayStream.toByteArray();
int nSamples = byteSoundFile.length;
byteArrayPlayStream.reset();
if(nSamples > 80000) { // it is a word
pos = nSamples;
isWord = true;
}
else { // it is a diphone
// audio data is converted from byte to short, so nSamples is halved
nSamples /= 2;
// transfer byteSoundFile contents to shortBuffer using byte-to-short conversion
shortBuffer = new short[nSamples];
for(int i=0; i<nSamples; i++) {
shortBuffer[i] = (short)((short)(byteSoundFile[i<<1]) << 8 | (short)byteSoundFile[(i<<1)+1]);
}
/************************************/
/**** phase-matching starts here ****/
/************************************/
int pm1 = 0;
int pm2 = 0;
String soundStr = sound.toString();
if(soundStr.contains("\\") && soundStr.contains(".")) {
soundStr = soundStr.substring(soundStr.indexOf("\\")+1, soundStr.indexOf("."));
}
if(peakMap1.containsKey(soundStr)) {
// perform overlap and add
System.out.println("we are here");
pm1 = peakMap1.get(soundStr);
pm2 = peakMap2.get(soundStr);
/*
Idea:
If pm1 is located after more than one third of the samples,
then threre will be too much overlapping.
If pm2 is located before the two third of the samples,
then where will also be extra overlapping for the next diphone.
In both of the cases, we will not perform the peak-matching operation.
*/
int idx1 = (previousPM == 0) ? pos : previousPM - pm1;
if((idx1 < 0) || (pm1 > (nSamples/3))) {
idx1 = pos;
}
int idx2 = idx1 + nSamples - 1;
for(int i=idx1, j=0; i<=idx2; i++, j++) {
if(i < pos) {
shortSoundFile[i] = (short) ((shortSoundFile[i] >> 1) + (shortBuffer[j] >> 1));
}
else {
shortSoundFile[i] = shortBuffer[j];
}
}
previousPM = (pm2 < (nSamples/3)*2) ? 0 : idx1 + pm2;
pos = idx2 + 1;
}
else {
// no peak found. simply concatenate the audio data
for(int i=0; i<nSamples; i++) {
shortSoundFile[pos++] = shortBuffer[i];
}
previousPM = 0;
}
Audioausgang
Nachdem alle Diphone eines Wortes zu sammeln, dieses Segment genannt wird, um die Audioausgabe spielen .
Original Version
byte audioData[] = byteArrayPlayStream.toByteArray();
... code for writing audioData to output steam
Meine Version
byte audioData[];
if(isWord) {
audioData = Arrays.copyOf(byteSoundFile, pos);
isWord = false;
}
else {
audioData = new byte[pos*2];
for(int i=0; i<pos; i++) {
audioData[(i<<1)] = (byte) (shortSoundFile[i] >>> 8);
audioData[(i<<1)+1] = (byte) (shortSoundFile[i]);
}
}
pos = 0;
... code for writing audioData to output steam
Aber nach der Änderung gemacht hat, hat der Ausgang schlechter geworden. Es gibt eine Menge Rauschen in der Ausgabe.
Hier ist ein Beispiel-Audio mit Änderung: modified output
Hier ist ein Beispiel von Audio von der ursprünglichen Version: original output
Jetzt schätzt ich würde es, wenn jemand den Grund kann darauf hinweisen, dass das Rauschen erzeugt und wie man es entfernt. Mache ich etwas falsch im Code? Ich habe meinen Algorithmus in Mablab getestet und es hat gut funktioniert.
Ich denke, Sie sollten etwas expliziter über die Änderungen, die Sie versucht haben, und das Ergebnis, das Sie erwarten. Sie geben uns Ihr Problem und erwarten eine Lösung, indem Sie eine ganze Menge Code kopieren. Es würde auch helfen, wenn Sie einige Refactoring auch durchführen würden (vielleicht sogar einige OO?) –
überprüfen Sie, dass die kurzen Dinge nicht durcheinander bringen - ich empfehle dringend, mit Bytes zu bleiben und die gesamte Verarbeitung mit Bytes - Sie wissen, was Sie beschäftigen mit - kein Unsinn Kurz – gpasch