Currenty, I am working on a text-to-speech program, and I recently added threads to play the sound. I've found that when enough threads are made, the GUI console no longer prints out and a "NegativeArraySizeException" is printed out in the Eclipse console. I couldn't find anything for this error involving just a string and not a normal array, so I'm not sure where to start fixing this...
The error in question is:
Exception in thread "Thread-99" java.lang.NegativeArraySizeException: -248
at java.base/java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:89)
at java.base/java.lang.StringBuilder.<init>(StringBuilder.java:119)
at javafx.scene.control.TextArea$TextAreaContent.get(TextArea.java:102)
at javafx.scene.control.TextArea$TextAreaContent.get(TextArea.java:311)
at javafx.scene.control.TextArea$TextAreaContent.get(TextArea.java:88)
at javafx.scene.control.TextInputControl$TextProperty.get(TextInputControl.java:1386)
at javafx.scene.control.TextInputControl.updateSelectedText(TextInputControl.java:178)
at javafx.scene.control.TextInputControl.replaceText(TextInputControl.java:1273)
at javafx.scene.control.TextInputControl.filterAndSet(TextInputControl.java:1229)
at javafx.scene.control.TextInputControl$TextProperty.doSet(TextInputControl.java:1480)
at javafx.scene.control.TextInputControl$TextProperty.set(TextInputControl.java:1393)
at javafx.scene.control.TextInputControl.setText(TextInputControl.java:361)
at net.oijon.algonquin.gui.GUI$2$1.run(GUI.java:144)
at java.base/java.lang.Thread.run(Thread.java:833)
The thread being run is:
String message = IPA.createAudio(IPA.getFileNames(insert.getText()), fileNameField.getText(), packField.getText());
console.setText(message);
System.out.println(message);
There's also the two methods of getFileNames and createAudio. Both return strings so I can put info messages on the GUI console, as I'm not sure how I'd go about just routing the actual console to it. There's probably a better way to do that, but it should be fine for now. getFileNames relies on three arrays of every IPA character. There's a list of all IPA sounds, a list of prediacritics, and a list of postdiacritics. These are:
static char[] ipaList = {'p', 'b', 't', 'd', 'ʈ', 'ɖ', 'c', 'ɟ', 'k', 'g', 'ɡ', 'q', 'ɢ', 'ʔ', 'm', 'ɱ', 'n', 'ɳ', 'ɲ', 'ŋ', 'ɴ', 'ʙ', 'r', 'ʀ', 'ⱱ', 'ɾ', 'ɽ', 'ɸ', 'β', 'f', 'v', 'θ', 'ð', 's', 'z', 'ʃ', 'ʒ', 'ʂ', 'ʐ', 'ç', 'ʝ', 'x', 'ɣ', 'χ', 'ʁ', 'ħ', 'ʕ', 'h', 'ɦ', 'ɬ', 'ɮ', 'ʋ', 'ɹ', 'ɻ', 'j', 'ɰ', 'l', 'ɭ', 'ʎ', 'ʟ', 'ʍ', 'w', 'ɥ', 'ʜ', 'ʢ', 'ʡ', 'ɕ', 'ʑ', 'ɺ', 'ɧ', 'i', 'y', 'ɨ', 'ʉ', 'ɯ', 'u', 'ɪ', 'ʏ', 'ʊ', 'e', 'ø', 'ɘ', 'ɵ', 'ɤ', 'o', 'ə', 'ɛ', 'œ', 'ɜ', 'ɞ', 'ʌ', 'ɔ', 'æ', 'ɐ', 'a', 'ɶ', 'ɑ', 'ɒ'};
static char[] preDiacriticList = {'ᵐ', 'ⁿ', 'ᶯ', 'ᶮ', 'ᵑ'};
static char[] postDiacriticList = {'̥', 'ː', '̊', '̬', 'ʰ', '̹', '̜', '̟', '̠', '̈', '̽', '̩', '̯', '˞', '̤', '̰', '̼', 'ʷ', 'ʲ', 'ˠ', 'ˤ', '̴', '̝', '̞', '̘', '̙', '̪', '̺', '̻','̃', 'ˡ', '̚', '-'};
//g and ɡ are the same sound, however two different points in unicode. as such, they need to both be in there to prevent disappearing chars
getFileNames:
public static String[] getFileNames(String input) {
String[] fileNames = new String[input.length()];
int inputLength = input.length();
int currentFileName = 0;
for (int i = 0; i < inputLength; i++) {
char c = input.charAt(i);
boolean isPreDiacritic = false;
boolean isPostDiacritic = false;
//handles spaces.
if (c == ' ') {
//if space, set to space.wav
fileNames[currentFileName] = "space";
currentFileName++;
}
for (int j = 0; j < postDiacriticList.length; j++) {
if (c == postDiacriticList[j]) {
isPostDiacritic = true;
//shouldnt actually be a problem, but just in case...]
if (currentFileName != 0) {
//if diacritic, add to file name of previous char.
currentFileName--;
fileNames[currentFileName] += Character.toString(c);
currentFileName++;
} else {
System.err.println("Postdiacritic \'" + c + "\' attempted to be added to non-existant character! Skipping...");
}
}
}
for (int l = 0; l < preDiacriticList.length; l++) {
if (c == preDiacriticList[l]) {
System.out.println(preDiacriticList[l]);
isPreDiacritic = true;
if (currentFileName != fileNames.length) {
//if prediacritic, add to file name of next char.
fileNames[currentFileName] = Character.toString(c);
} else {
System.err.println("Prediacritic \'" + c + "\' attempted to be added to non-existant character! Skipping...");
}
}
}
//skips if the character was a diacritic, should speed things up...
if (isPostDiacritic == false && isPreDiacritic == false) {
for (int k = 0; k < ipaList.length; k++) {
if (c == ipaList[k]) {
//sets file name to character and goes to the next file name
//checks if null because if not, prediacritics would be overwritten.
if (fileNames[currentFileName] == null) {
fileNames[currentFileName] = Character.toString(c);
} else {
fileNames[currentFileName] += Character.toString(c);
}
currentFileName++;
}
}
}
//TODO: handle supersegmentals
}
return fileNames;
}
and createAudio:
public static String createAudio(String[] fileNames, String name, String packName){
String exception = "";
long fileLength = 0;
try {
URL packURL = new File(System.getProperty("user.home") + "/AlgonquinTTS/packs/" + packName).toURI().toURL();
} catch (MalformedURLException e1) {
URL packURL = IPA.class.getResource("/" + packName);
exception += "packURL is malformed! Getting resources from jar instead...";
exception += e1.toString();
e1.printStackTrace();
}
AudioInputStream allStreams[] = new AudioInputStream[fileNames.length];
try {
for (int i = 0; i < fileNames.length; i++) {
URL url;
File clipFile = new File(System.getProperty("user.home") + "/AlgonquinTTS/packs/" + packName + "/" + fileNames[i] + ".wav");
try {
url = clipFile.toURI().toURL();
} catch (MalformedURLException e1) {
url = IPA.class.getResource("/" + packName + "/" + fileNames[i] + ".wav");
exception += "url is malformed! Getting resources from jar instead...";
exception += e1.toString();
e1.printStackTrace();
}
if (clipFile.exists() == false) {
if (fileNames[i] != null) {
boolean foundValid = false;
for (int j = 0; j < fileNames[i].length(); j++) {
for (int k = 0; k < ipaList.length; k++) {
if (fileNames[i].charAt(j) == ipaList[k]) {
foundValid = true;
exception += "Invalid sound " + fileNames[i] + " detected! This usually means the sound hasn't been added yet. Reverting to " + fileNames[i].charAt(j) + "\n";
fileNames[i] = Character.toString(fileNames[i].charAt(j));
}
}
}
if (foundValid == false) {
exception += "Invalid sound " + fileNames[i] + " detected! No valid replacement found, skipping...\n";
fileNames[i] = "space";
}
}
else {
fileNames[i] = "space";
}
}
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(
System.getProperty("user.home") + "/AlgonquinTTS/packs/" + packName + "/" + fileNames[i] + ".wav").getAbsoluteFile());
allStreams[i] = ais;
}
for (int i = 1; i < allStreams.length; i++) {
AudioInputStream temp = new AudioInputStream(
new SequenceInputStream(allStreams[0], allStreams[i]),
allStreams[0].getFormat(),
allStreams[0].getFrameLength() + allStreams[1].getFrameLength());
allStreams[0] = temp;
}
AudioSystem.write(allStreams[0], AudioFileFormat.Type.WAVE, new File(System.getProperty("user.home") +
"/AlgonquinTTS/" + name + ".wav"));
exception += "Created file " + System.getProperty("user.home") +
"/AlgonquinTTS/" + name + ".wav\n";
Clip clip = AudioSystem.getClip();
AudioInputStream ais = AudioSystem.getAudioInputStream(
new File(System.getProperty("user.home") +
"/AlgonquinTTS/" + name + ".wav").getAbsoluteFile()
);
clip.open(ais);
clip.start();
fileLength += clip.getMicrosecondLength();
while(clip.getMicrosecondLength() != clip.getMicrosecondPosition())
{
}
ais.close();
exception += "Successfully played " + Arrays.toString(fileNames) + "\n";
} catch (Exception e) {
exception = e.toString();
e.printStackTrace();
}
return exception;
}
A NegativeArraySizeException is thrown if an application tries to create an array with negative size.
It need not be your code directly, but use the stack trace as a hint where such an array might get created. I doubt you will find something like
new int[-4];
but it might be more something likeor even more obscure. If you find the offending situation, by reading the code you may understand either what needs to be changed, or in case it is not your own code understand how not to call it.