diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown index d22a845..fdac231 100644 --- a/CHANGELOG.markdown +++ b/CHANGELOG.markdown @@ -5,5 +5,18 @@ Changelog corresponds with a tagged and signed Git commit. This marks the chang A tagged commit may or may not have a corresponding binary version available. Format: Tag: `` +* Version 1.06 (Tag v1.016) + * Added support in Synthesiser for strings longer than 99 characters (Credits to @Skylion007) + +* Version 1.05 (Tag: v1.015) + * Improved language support for recognizer (Credits to @duncanj) + * Add support for multiple responses for recognizer (Credits to @duncanj) + * Add profanity filter toggle support for recognizer (Credits to @duncanj) + +* Version 1.01 (Tag: v1.01) + * Fixed state functions for Microphones + * Fixed encoding single byte frames + * Support Multiple Languages + * Version 1.00 (Tag: v1.00) - * Initial Release \ No newline at end of file + * Initial Release diff --git a/CREDITS.markdown b/CREDITS.markdown index e712672..96cd957 100644 --- a/CREDITS.markdown +++ b/CREDITS.markdown @@ -16,5 +16,8 @@ The following people/organizations have helped provide functionality for the API * Allows for text to speech translation * Homepage: http://google.com +* JSON-lib: http://json-lib.sourceforge.net/ +* JUnit: http://junit.org/ + I would like to thank the above so much for your work, this wrapper/API could not have been created without it. \ No newline at end of file diff --git a/java-speech-api.iml b/java-speech-api.iml deleted file mode 100644 index ac3e458..0000000 --- a/java-speech-api.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/src/com/darkprograms/speech/recognizer/FlacEncoder.java b/src/com/darkprograms/speech/recognizer/FlacEncoder.java index 83027bd..017544d 100644 --- a/src/com/darkprograms/speech/recognizer/FlacEncoder.java +++ b/src/com/darkprograms/speech/recognizer/FlacEncoder.java @@ -10,6 +10,8 @@ import java.io.File; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Class that contains methods to encode Wave files to FLAC files @@ -31,7 +33,7 @@ public FlacEncoder() { * @param inputFile Input wave file * @param outputFile Output FLAC file */ - public void convertWaveToFlac(File inputFile, File outputFile) { + public void convertWaveToFlac(File inputFile, File outputFile) throws Exception { StreamConfiguration streamConfiguration = new StreamConfiguration(); @@ -80,7 +82,9 @@ public void convertWaveToFlac(File inputFile, File outputFile) { flacOutputStream.close(); } catch (Exception ex) { - ex.printStackTrace(); + Logger.getLogger(FlacEncoder.class.getName()).log(Level.SEVERE, ex.getClass().getName(), ex); + + throw ex; } } @@ -93,7 +97,11 @@ public void convertWaveToFlac(File inputFile, File outputFile) { * @param outputFile Output FLAC file */ public void convertWaveToFlac(String inputFile, String outputFile) { - convertWaveToFlac(new File(inputFile), new File(outputFile)); + try { + convertWaveToFlac(new File(inputFile), new File(outputFile)); + } catch (Exception ex) { + Logger.getLogger(FlacEncoder.class.getName()).log(Level.SEVERE, null, ex); + } } diff --git a/src/com/darkprograms/speech/recognizer/GoogleResponse.java b/src/com/darkprograms/speech/recognizer/GoogleResponse.java index a673a54..f3e9e5e 100644 --- a/src/com/darkprograms/speech/recognizer/GoogleResponse.java +++ b/src/com/darkprograms/speech/recognizer/GoogleResponse.java @@ -1,4 +1,4 @@ -package com.darkprograms.speech.recognizer; +package com.darkprograms.speech.recognizer; import java.util.ArrayList; import java.util.List; @@ -6,7 +6,7 @@ /** * Class that holds the response and confidence of a Google recognizer request * - * @author Luke Kuza, Duncan Jauncey + * @author Luke Kuza, Duncan Jauncey, Víctor Martín Molina */ public class GoogleResponse { @@ -22,7 +22,7 @@ public class GoogleResponse { /** * List that holds other possible responses for this request. */ - private List otherPossibleResponses = new ArrayList(20); + private List otherPossibleResponses = new ArrayList(20); /** * Constructor @@ -76,4 +76,35 @@ public List getOtherPossibleResponses() { return otherPossibleResponses; } + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof GoogleResponse)) { + return false; + } + GoogleResponse googleResponse = (GoogleResponse) obj; + if (!response.equals(googleResponse.response)) { + return false; + } + if (!confidence.equals(googleResponse.confidence)) { + return false; + } + if (!otherPossibleResponses.equals(googleResponse.otherPossibleResponses)) { + return false; + } + + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 59 * hash + (this.response != null ? this.response.hashCode() : 0); + hash = 59 * hash + (this.confidence != null ? this.confidence.hashCode() : 0); + hash = 59 * hash + (this.otherPossibleResponses != null ? this.otherPossibleResponses.hashCode() : 0); + return hash; + } + } diff --git a/src/com/darkprograms/speech/recognizer/Recognizer.java b/src/com/darkprograms/speech/recognizer/Recognizer.java index 3b6095a..ac20605 100644 --- a/src/com/darkprograms/speech/recognizer/Recognizer.java +++ b/src/com/darkprograms/speech/recognizer/Recognizer.java @@ -1,13 +1,18 @@ -package com.darkprograms.speech.recognizer; +package com.darkprograms.speech.recognizer; import java.io.*; import java.net.URL; import java.net.URLConnection; +import java.util.logging.Level; +import java.util.logging.Logger; +import net.sf.json.JSONArray; +import net.sf.json.JSONException; +import net.sf.json.JSONObject; /** * Class that submits FLAC audio and retrieves recognized text * - * @author Luke Kuza, Duncan Jauncey + * @author Luke Kuza, Duncan Jauncey, Víctor Martín Molina */ public class Recognizer { @@ -15,6 +20,8 @@ public class Recognizer { * URL to POST audio data and retrieve results */ private static final String GOOGLE_RECOGNIZER_URL = "https://www.google.com/speech-api/v1/recognize?xjerr=1&client=chromium"; + + private static final String CONFIDENCE = "confidence"; private boolean profanityFilter = true; private String language = null; @@ -28,6 +35,14 @@ public class Recognizer { public Recognizer() { } + /** + * + * @return Google's profanity filter + */ + public boolean isProfanityFilter() { + return profanityFilter; + } + /** * Enable/disable Google's profanity filter (on by default). * @param profanityFilter @@ -36,6 +51,13 @@ public void setProfanityFilter(boolean profanityFilter) { this.profanityFilter = profanityFilter; } + /** + * @return Language code. This language code must match the language of the speech to be recognized. ex. en-US ru-RU + */ + public String getLanguage() { + return language; + } + /** * Language code. This language code must match the language of the speech to be recognized. ex. en-US ru-RU * Setting this to null will make Google use it's own language detection. @@ -161,49 +183,34 @@ public GoogleResponse getRecognizedDataForFlac(String flacFile) throws Exception * Parses the raw response from Google * * @param rawResponse The raw, unparsed response from Google - * @return Returns the parsed response. Index 0 is response, Index 1 is confidence score + * @return Returns the parsed response. */ - private void parseResponse(String rawResponse, GoogleResponse googleResponse) { - if (!rawResponse.contains("utterance")) - return; - - String array = substringBetween(rawResponse, "[", "]"); - String[] parts = array.split("}"); - System.out.println(parts.length); - - boolean first = true; - for( String s : parts ) { - if( first ) { - first = false; - String utterancePart = s.split(",")[0]; - String confidencePart = s.split(",")[1]; - - String utterance = utterancePart.split(":")[1]; - String confidence = confidencePart.split(":")[1]; - - utterance = stripQuotes(utterance); - confidence = stripQuotes(confidence); - - if( utterance.equals("null") ) { - utterance = null; - } - if( confidence.equals("null") ) { - confidence = null; + void parseResponse(String rawResponse, GoogleResponse googleResponse) { + try { + JSONObject json = JSONObject.fromObject(rawResponse); + int status = json.getInt("status"); + + if (status == 0) { + JSONArray hypotheses = json.getJSONArray("hypotheses"); + + for (int index = 0; index < hypotheses.size(); index++) { + JSONObject hypothese = (JSONObject) hypotheses.get(index); + + if (hypothese.containsKey(CONFIDENCE)) { + googleResponse.setResponse(hypothese.getString("utterance")); + googleResponse.setConfidence(hypothese.getDouble(CONFIDENCE) + ""); + } else { + googleResponse.getOtherPossibleResponses().add(hypothese.getString("utterance")); + } } - - googleResponse.setResponse(utterance); - googleResponse.setConfidence(confidence); } else { - String utterance = s.split(":")[1]; - utterance = stripQuotes(utterance); - if( utterance.equals("null") ) { - utterance = null; - } - googleResponse.getOtherPossibleResponses().add(utterance); + Logger.getLogger(Recognizer.class.getName()).log(Level.WARNING, "status: {0}", status); } + } catch (JSONException e) { + Logger.getLogger(Recognizer.class.getName()).log(Level.WARNING, e.getLocalizedMessage(), e); } } - + /** * Performs the request to Google with a file
* Request is buffered @@ -271,31 +278,4 @@ private String rawRequest(File inputFile, int maxResults) throws Exception { } - private String substringBetween(String s, String part1, String part2) { - String sub = null; - - int i = s.indexOf(part1); - int j = s.indexOf(part2, i + part1.length()); - - if (i != -1 && j != -1) { - int nStart = i + part1.length(); - sub = s.substring(nStart, j); - } - - return sub; - } - - private String stripQuotes(String s) { - int start = 0; - if( s.startsWith("\"") ) { - start = 1; - } - int end = s.length(); - if( s.endsWith("\"") ) { - end = s.length() - 1; - } - return s.substring(start, end); - } - - } diff --git a/src/com/darkprograms/speech/synthesiser/Synthesiser.java b/src/com/darkprograms/speech/synthesiser/Synthesiser.java index bbedb0d..1f89ba1 100644 --- a/src/com/darkprograms/speech/synthesiser/Synthesiser.java +++ b/src/com/darkprograms/speech/synthesiser/Synthesiser.java @@ -4,11 +4,13 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; /** * Synthesiser class that connects to Google's unoffical API to retreive data * - * @author Luke Kuza + * @author Luke Kuza, Aaron Gokaslan (Skylion) */ public class Synthesiser { @@ -32,7 +34,13 @@ public Synthesiser() { * @throws Exception Throws exception if it can not complete the request */ public InputStream getMP3Data(String synthText) throws Exception { - String encoded = URLEncoder.encode(synthText, "UTF-8"); //Encode + + if(synthText.length()>99){ + List fragments = stringParser(synthText); + return getMP3Data(fragments); + } + + String encoded = URLEncoder.encode(synthText, "UTF-8"); //Encode URL url = new URL(GOOGLE_SYNTHESISER_URL + encoded); //create url @@ -44,5 +52,69 @@ public InputStream getMP3Data(String synthText) throws Exception { return urlConn.getInputStream(); } + + /** + * Gets an InputStream to MP3Data for the returned information from a request + * @param synthText List of Strings you want to be synthesized into MP3 data + * @return Returns an input stream of all the MP3 data that is returned from Google + * @throws Exception Throws exception if it cannot complete the request + */ + public InputStream getMP3Data(List synthText) throws Exception{ + InputStream complete = getMP3Data(synthText.remove(0)); + for(String part: synthText){ + complete = new java.io.SequenceInputStream(complete, getMP3Data(part));//Concatenate with new MP3 Data + } + return complete; + } + /** + * Separates a string into smaller parts so that Google will not reject the request. + * @param input The string you want to separate + * @return A List of the String fragments from your input.. + */ + private List stringParser(String input){ + return stringParser(input, new ArrayList()); + } + + /** + * Separates a string into smaller parts so that Google will not reject the request. + * @param input The string you want to break up into smaller parts + * @param fragments List that you want to add stuff too. + * If you don't have a List already constructed "new ArrayList()" works well. + * @return A list of the fragments of the original String + */ + private List stringParser(String input, List fragments){ + if(input.length()<100){//Base Case + fragments.add(input); + return fragments; + } + else{ + int space = findLastWord(input);//Checks if a space exists + if(space<0){ + fragments.add(input.substring(0,99));//In case you sent gibberish to Google. + return stringParser(input.substring(99), fragments); + }else{ + fragments.add(input.substring(0,space));//Otherwise, adds the last word to the list for recursion. + return stringParser(input.substring(space), fragments); + } + } + } + + /** + * Finds the last word in your String (before the index of 99) by searching for spaces and ending punctuation. + * @param input The String you want to search through. + * @return The index of where the last word of the String ends before the index of 99. + */ + private int findLastWord(String input){ + if(input.length()<100) + return input.length(); + for(int i = 99; i>=0; i--){ + char tmp = input.charAt(i); + if( tmp == ' ' || tmp == '.' || tmp == '!' || tmp == '?' || tmp == ';' || tmp == ':' || tmp == '؟' + || tmp == '|'){ //Ending punctuation for all languages according to Wikipedia + return i; + } + } + return -1; + } } diff --git a/test/com/darkprograms/speech/recognizer/RecognizerTest.java b/test/com/darkprograms/speech/recognizer/RecognizerTest.java new file mode 100644 index 0000000..71ee713 --- /dev/null +++ b/test/com/darkprograms/speech/recognizer/RecognizerTest.java @@ -0,0 +1,194 @@ +/* + * + * This file is free software; you can redistribute it and/or modify it under the terms of GNU + * General Public License as published by the Free Sortware Foundation Inc. (59 TEmple Place, Suite 330, + * Boston, MA 02111-1307 USA); either version 2 of the License, or + * (at your option) any later version. + * + * This file 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 General Public + * License for mor details. + * + */ + +package com.darkprograms.speech.recognizer; + +import java.io.File; +import java.io.FileNotFoundException; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import static org.junit.Assert.*; + +/** + * + * @author Víctor Martín Molina + */ +public class RecognizerTest { + + public RecognizerTest() { + // prueba + } + + @BeforeClass + public static void setUpClass() { + } + + @AfterClass + public static void tearDownClass() { + } + + @Before + public void setUp() { + } + + @After + public void tearDown() { + } + + /** + * Test of setProfanityFilter method, of class Recognizer. + */ + @Test + public void testSetProfanityFilter() { + System.out.println("setProfanityFilter"); + boolean profanityFilter = false; + Recognizer instance = new Recognizer(); + instance.setProfanityFilter(profanityFilter); + assertFalse(instance.isProfanityFilter()); + } + + /** + * Test of setLanguage method, of class Recognizer. + */ + @Test + public void testSetLanguage() { + System.out.println("setLanguage"); + String language = ""; + Recognizer instance = new Recognizer(); + instance.setLanguage(language); + assertEquals("", instance.getLanguage()); + } + + /** + * Test of getRecognizedDataForWave method, of class Recognizer. + */ + @Test(expected = NullPointerException.class) + public void testGetRecognizedDataForWave_File_int() throws Exception { + System.out.println("getRecognizedDataForWave"); + File waveFile = null; + int maxResults = 0; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForWave(waveFile, maxResults); + } + + /** + * Test of getRecognizedDataForWave method, of class Recognizer. + */ + @Test(expected = FileNotFoundException.class) + public void testGetRecognizedDataForWave_String_int() throws Exception { + System.out.println("getRecognizedDataForWave"); + String waveFile = ""; + int maxResults = 0; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForWave(waveFile, maxResults); + } + + /** + * Test of getRecognizedDataForFlac method, of class Recognizer. + */ + @Test(expected = NullPointerException.class) + public void testGetRecognizedDataForFlac_File_int() throws Exception { + System.out.println("getRecognizedDataForFlac"); + File flacFile = null; + int maxResults = 0; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForFlac(flacFile, maxResults); + } + + /** + * Test of getRecognizedDataForFlac method, of class Recognizer. + */ + @Test(expected = FileNotFoundException.class) + public void testGetRecognizedDataForFlac_String_int() throws Exception { + System.out.println("getRecognizedDataForFlac"); + String flacFile = ""; + int maxResults = 0; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForFlac(flacFile, maxResults); + } + + /** + * Test of getRecognizedDataForWave method, of class Recognizer. + */ + @Test(expected = NullPointerException.class) + public void testGetRecognizedDataForWave_File() throws Exception { + System.out.println("getRecognizedDataForWave"); + File waveFile = null; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForWave(waveFile); + } + + /** + * Test of getRecognizedDataForWave method, of class Recognizer. + */ + @Test(expected = FileNotFoundException.class) + public void testGetRecognizedDataForWave_String() throws Exception { + System.out.println("getRecognizedDataForWave"); + String waveFile = ""; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForWave(waveFile); + } + + /** + * Test of getRecognizedDataForFlac method, of class Recognizer. + */ + @Test(expected = NullPointerException.class) + public void testGetRecognizedDataForFlac_File() throws Exception { + System.out.println("getRecognizedDataForFlac"); + File flacFile = null; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForFlac(flacFile); + } + + /** + * Test of getRecognizedDataForFlac method, of class Recognizer. + */ + @Test(expected = FileNotFoundException.class) + public void testGetRecognizedDataForFlac_String() throws Exception { + System.out.println("getRecognizedDataForFlac"); + String flacFile = ""; + Recognizer instance = new Recognizer(); + instance.getRecognizedDataForFlac(flacFile); + } + + @Test + public void parseResponseNullRawResponse() { + Recognizer instance = new Recognizer(); + GoogleResponse googleResponse = new GoogleResponse(); + instance.parseResponse(null, googleResponse); + assertNull(googleResponse.getResponse()); + assertNull(googleResponse.getConfidence()); + assertTrue(googleResponse.getOtherPossibleResponses().isEmpty()); + } + + @Test(expected = NullPointerException.class) + public void parseResponseNullGoogleResponse() { + Recognizer instance = new Recognizer(); + instance.parseResponse("{status=0,hypotheses=[{utterance:\"hello world\",confidence:0.9}]}", null); + } + + @Test + public void parseResponse() { + Recognizer instance = new Recognizer(); + GoogleResponse actual = new GoogleResponse(); + instance.parseResponse("{status=0,hypotheses=[{utterance:\"hello world\",confidence:0.9}, {utterance:\"hello gold\"}]}", actual); + GoogleResponse expected = new GoogleResponse(); + expected.setConfidence("0.9"); + expected.setResponse("hello world"); + expected.getOtherPossibleResponses().add("hello gold"); + assertEquals(expected, actual); + } +} \ No newline at end of file