Προγραμματισμός

Μιλώντας Java!

Γιατί θα θέλατε να κάνετε τις εφαρμογές σας να μιλήσουν; Για αρχή, είναι διασκεδαστικό και κατάλληλο για διασκεδαστικές εφαρμογές όπως παιχνίδια. Και υπάρχει μια πιο σοβαρή πλευρά προσβασιμότητας. Σκέφτομαι εδώ όχι μόνο εκείνες που είναι φυσικά μειονεκτούντες όταν χρησιμοποιώ μια οπτική διεπαφή, αλλά και εκείνες τις καταστάσεις όπου είναι αδύνατο - ή ακόμη και παράνομο - να αφαιρέσετε τα μάτια σας από αυτό που κάνετε.

Πρόσφατα συνεργάζομαι με ορισμένες τεχνολογίες για τη λήψη πληροφοριών HTML και XML από τον Ιστό [βλ. "Πρόσβαση στη μεγαλύτερη βάση δεδομένων στον κόσμο με συνδεσιμότητα βάσης δεδομένων Ιστού" (JavaWorld, Μάρτιος 2001)]. Μου συνέβη ότι θα μπορούσα να συνδέσω αυτήν την εργασία και αυτή την ιδέα μαζί για να δημιουργήσω ένα πρόγραμμα περιήγησης στο Web. Ένα τέτοιο πρόγραμμα περιήγησης θα αποδειχθεί χρήσιμο για την ακρόαση αποσπασμάτων πληροφοριών από τους αγαπημένους σας ιστότοπους - για παράδειγμα, τίτλοι ειδήσεων - όπως το να ακούτε ραδιόφωνο ενώ περπατάτε έξω το σκυλί σας ή οδηγείτε στη δουλειά. Φυσικά, με την τρέχουσα τεχνολογία θα πρέπει να μεταφέρετε τον φορητό υπολογιστή σας με το κινητό σας τηλέφωνο, αλλά αυτό το πρακτικό σενάριο θα μπορούσε να αλλάξει στο εγγύς μέλλον με την άφιξη έξυπνων τηλεφώνων με δυνατότητα Java όπως το Nokia 9210 ΜΑΣ).

Ίσως πιο χρήσιμο βραχυπρόθεσμα θα ήταν ένα πρόγραμμα ανάγνωσης email, επίσης δυνατό χάρη στο JavaMail API. Αυτή η εφαρμογή θα ελέγχει περιοδικά τα εισερχόμενά σας και η προσοχή σας θα προσελκύεται από μια φωνή πουθενά αναφέροντας "Έχετε νέο μήνυμα, θα θέλατε να το διαβάσω σε εσάς;" Με παρόμοιο τρόπο, σκεφτείτε μια υπενθύμιση που μιλάει - που συνδέεται με την εφαρμογή ημερολογίου σας - που φωνάζει "Μην ξεχάσετε τη συνάντησή σας με το αφεντικό σε 10 λεπτά!"

Αν υποθέσουμε ότι πουλάτε με αυτές τις ιδέες ή έχετε κάποιες καλές ιδέες, θα προχωρήσουμε. Θα ξεκινήσω δείχνοντας πώς να βάλω το παρεχόμενο αρχείο zip για να λειτουργήσει, ώστε να μπορείτε να ξεκινήσετε αμέσως και να παραλείψετε τις λεπτομέρειες εφαρμογής, αν νομίζετε ότι είναι πάρα πολύ σκληρή δουλειά.

Δοκιμάστε τη μηχανή ομιλίας

Για να χρησιμοποιήσετε τη μηχανή ομιλίας, θα πρέπει να συμπεριλάβετε το αρχείο jw-0817-javatalk.zip στο CLASSPATH και να εκτελέσετε το com.lotontech.speech.Talker κλάση από τη γραμμή εντολών ή από ένα πρόγραμμα Java.

Για να το εκτελέσετε από τη γραμμή εντολών, πληκτρολογήστε:

java com.lotontech.speech.Talker "h | e | l | oo" 

Για να το εκτελέσετε από ένα πρόγραμμα Java, απλώς συμπεριλάβετε δύο γραμμές κώδικα:

com.lotontech.speech.Talker talker = νέο com.lotontech.speech.Talker (); talker.sayPhoneWord ("h | e | l | oo"); 

Σε αυτό το σημείο πιθανώς αναρωτιέστε για τη μορφή του "h | e | l | oo" συμβολοσειρά που παρέχετε στη γραμμή εντολών ή παρέχετε στο sayPhoneWord (...) μέθοδος. ΑΣΕ με να εξηγήσω.

Η μηχανή ομιλίας λειτουργεί συνδυάζοντας δείγματα μικρού ήχου που αντιπροσωπεύουν τις μικρότερες μονάδες ανθρώπου - σε αυτήν την περίπτωση Αγγλικά - ομιλία. Αυτά τα δείγματα ήχου, που ονομάζονται αλλόφωνα, επισημαίνονται με ένα αναγνωριστικό ενός, δύο ή τριών γραμμάτων. Ορισμένα αναγνωριστικά είναι προφανή και άλλα όχι τόσο προφανή, όπως μπορείτε να δείτε από τη φωνητική αναπαράσταση της λέξης "γεια".

  • η - ακούγεται όπως θα περίμενε κανείς
  • μι - ακούγεται όπως θα περίμενε κανείς
  • μεγάλο - ακούγεται όπως θα περίμενε κανείς, αλλά προσέξτε ότι έχω μειώσει το διπλό "l" σε ένα
  • Οο - είναι ο ήχος για "γεια", όχι για "bot" και όχι για "επίσης"

Ακολουθεί μια λίστα με τα διαθέσιμα αλλόφωνα:

  • ένα - όπως στη γάτα
  • σι - όπως στην καμπίνα
  • ντο - όπως στη γάτα
  • ρε - όπως στην τελεία
  • μι - όπως στο στοίχημα
  • φά - όπως στον βάτραχο
  • σολ - όπως στον βάτραχο
  • η - όπως στο γουρούνι
  • Εγώ - όπως στο χοίρο
  • ι - όπως στο jig
  • κ - όπως στο βαρέλι
  • μεγάλο - όπως στο πόδι
  • Μ - όπως συναντήθηκε
  • ν - όπως στην αρχή
  • ο - όπως όχι
  • Π - όπως στο ποτ
  • ρ - όπως στο σάπιο
  • μικρό - όπως στο sat
  • τ - όπως στο sat
  • εσύ - όπως στο put
  • β - όπως στο έχουν
  • β - όπως στο βρεγμένο
  • γ - όπως ακόμα
  • ζ - όπως στον ζωολογικό κήπο
  • αα - όπως στο ψεύτικο
  • ναι - όπως στο σανό
  • εε - όπως στη μέλισσα
  • ii - όπως στο υψηλό
  • Οο - όπως εν κινήσει
  • ΒΒ - παραλλαγή του b με διαφορετική έμφαση
  • δδ - παραλλαγή του d με διαφορετική έμφαση
  • τσεκ - παραλλαγή του g με διαφορετική έμφαση
  • ω - παραλλαγή του h με διαφορετική έμφαση
  • θα είμαι - παραλλαγή του l με διαφορετική έμφαση
  • νν - παραλλαγή του n με διαφορετική έμφαση
  • γρ - παραλλαγή του r με διαφορετική έμφαση
  • tt - παραλλαγή του t με διαφορετική έμφαση
  • εε - παραλλαγή του y με διαφορετική έμφαση
  • αρ - όπως στο αυτοκίνητο
  • αερ - όπως στη φροντίδα
  • χρ - όπως στο οποίο
  • ck - όπως στον έλεγχο
  • αυτί - όπως στην μπύρα
  • Ερ - όπως αργότερα
  • πλανώμαι - όπως αργότερα (μακρύτερος ήχος)
  • ng - όπως στη διατροφή
  • ή - όπως στο νόμο
  • ε - όπως στον ζωολογικό κήπο
  • ου - όπως στον ζωολογικό κήπο (μακρύτερος ήχος)
  • οου - όπως στην αγελάδα
  • εντάξει - όπως στο αγόρι
  • SH - όπως στο κλείσιμο
  • ου - όπως στο πράγμα
  • dth - όπως σε αυτό
  • εω - παραλλαγή του u
  • πο - όπως πού
  • ω - όπως στην Ασία

Στην ανθρώπινη ομιλία η κλίση των λέξεων ανεβαίνει και πέφτει σε κάθε προφορική πρόταση. Αυτός ο τονισμός κάνει τον ήχο ομιλίας πιο φυσικό, πιο συναισθηματικό και επιτρέπει στις ερωτήσεις να διακρίνονται από τις δηλώσεις. Εάν έχετε ακούσει ποτέ τη συνθετική φωνή του Stephen Hawking, καταλαβαίνετε τι μιλάω. Εξετάστε αυτές τις δύο προτάσεις:

  • Είναι ψεύτικο - f | aa | k
  • Είναι ψεύτικο; - f | AA | k

Όπως ίσως μαντέψατε, ο τρόπος για να αυξήσετε τον τονισμό είναι να χρησιμοποιήσετε κεφαλαία γράμματα. Πρέπει να πειραματιστείτε με αυτό λίγο, και η υπόδειξή μου είναι ότι πρέπει να επικεντρωθείτε στους μεγάλους ήχους φωνήεντος.

Αυτό είναι το μόνο που πρέπει να γνωρίζετε για να χρησιμοποιήσετε το λογισμικό, αλλά αν σας ενδιαφέρει αυτό που συμβαίνει κάτω από την κουκούλα, διαβάστε παρακάτω.

Εφαρμόστε τη μηχανή ομιλίας

Η μηχανή ομιλίας απαιτεί μόνο μία τάξη για εφαρμογή, με τέσσερις μεθόδους. Χρησιμοποιεί το Java Sound API που περιλαμβάνεται στο J2SE 1.3. Δεν θα παράσχω έναν ολοκληρωμένο οδηγό για το Java Sound API, αλλά θα μάθετε για παράδειγμα. Θα διαπιστώσετε ότι δεν υπάρχουν πολλά, και τα σχόλια σας λένε τι πρέπει να γνωρίζετε.

Εδώ είναι ο βασικός ορισμός του Ομιλητής τάξη:

πακέτο com.lotontech.speech; εισαγωγή javax.sound.sample. *; εισαγωγή java.io. *; εισαγωγή java.util. *; εισαγωγή java.net. *; ομιλητής δημόσιας τάξης {private SourceDataLine line = null; } 

Εάν τρέχετε Ομιλητής από τη γραμμή εντολών, το κύριος(...) Η παρακάτω μέθοδος θα χρησιμεύσει ως σημείο εισόδου. Παίρνει το πρώτο όρισμα γραμμής εντολών, εάν υπάρχει, και το μεταφέρει στο sayPhoneWord (...) μέθοδος:

/ * * Αυτή η μέθοδος εκφωνεί μια φωνητική λέξη που καθορίζεται στη γραμμή εντολών. * / public static void main (String args []) {Talker player = νέο Talker (); if (args.length> 0) player.sayPhoneWord (args [0]); System.exit (0); } 

ο sayPhoneWord (...) η μέθοδος καλείται από κύριος(...) παραπάνω, ή μπορεί να κληθεί απευθείας από την εφαρμογή Java ή από μικροεφαρμογή που υποστηρίζεται από plug-in. Φαίνεται πιο περίπλοκο από ό, τι είναι. Ουσιαστικά, απλά ακολουθεί τη λέξη αλλόφωνα - διαχωρισμένη με "|"σύμβολα στο κείμενο εισαγωγής - και τα αναπαράγει ένα προς ένα μέσω ενός καναλιού εξόδου ήχου. Για να το ακούσω πιο φυσικό, συγχωνεύω το τέλος κάθε δείγματος ήχου με την αρχή του επόμενου:

/ * * Αυτή η μέθοδος μιλά τη δεδομένη φωνητική λέξη. * / public void sayPhoneWord (Λέξη συμβολοσειράς) {// - Ρυθμίστε μια πλαστή συστοιχία byte για τον προηγούμενο ήχο - byte [] previousSound = null; // - Διαχωρίστε τη συμβολοσειρά εισόδου σε ξεχωριστά αλλόφωνα - StringTokenizer st = new StringTokenizer (λέξη, "|", false); while (st.hasMoreTokens ()) {// - Κατασκευάστε ένα όνομα αρχείου για το αλλόφωνο - String thisPhoneFile = st.nextToken (); thisPhoneFile = "/ allophones /" + thisPhoneFile + ". au"; // - Λήψη των δεδομένων από το αρχείο - byte [] thisSound = getSound (thisPhoneFile); if (previousSound! = null) {// - Συγχώνευση του προηγούμενου αλλοφώνου με αυτό, αν μπορούμε - int mergeCount = 0; if (previousSound.length> = 500 && thisSound.length> = 500) mergeCount = 500; για (int i = 0; i

Στο τέλος του sayPhoneWord (), θα δείτε κλήσεις playSound (...) για έξοδο ενός μεμονωμένου δείγματος ήχου (ένα αλλόφωνο) και καλεί διοχετεύω(...) για να ξεπλύνετε το κανάλι ήχου. Εδώ είναι ο κωδικός για playSound (...):

/ * * Αυτή η μέθοδος αναπαράγει ένα δείγμα ήχου. * / private void playSound (byte [] data) {if (data.length> 0) line.write (δεδομένα, 0, data.length); } 

Και για διοχετεύω(...):

/ * * Αυτή η μέθοδος ξεπλένει το κανάλι ήχου. * / private void drain () {if (line! = null) line.drain (); δοκιμάστε το {Thread.sleep (100);} catch (Exception e) {}} 

Τώρα, αν κοιτάξετε πίσω το sayPhoneWord (...) μέθοδος, θα δείτε ότι υπάρχει μια μέθοδος που δεν έχω καλύψει ακόμη: getSound (...).

getSound (...) διαβάζει σε ένα ηχογραφημένο δείγμα ήχου, ως δεδομένα byte, από ένα αρχείο au. Όταν λέω ένα αρχείο, εννοώ έναν πόρο που διατηρείται μέσα στο παρεχόμενο αρχείο zip. Κάνω τη διάκριση γιατί με τον τρόπο που κρατάτε έναν πόρο JAR - χρησιμοποιώντας το getResource (...) μέθοδος - προχωρά διαφορετικά από τον τρόπο που κρατάτε ένα αρχείο, ένα όχι προφανές γεγονός.

Για έναν λογαριασμό χτύπημα με ανάγνωση της ανάγνωσης των δεδομένων, τη μετατροπή της μορφής ήχου, την παρουσίαση μιας γραμμής εξόδου ήχου (γιατί το ονομάζουν SourceDataLine, Δεν ξέρω), και συγκεντρώνοντας τα δεδομένα byte, σας παραπέμπω στα σχόλια στον κώδικα που ακολουθεί:

/ * * Αυτή η μέθοδος διαβάζει το αρχείο για ένα μόνο αλλόφωνο και * κατασκευάζει ένα διάνυσμα byte. * / ιδιωτικό byte [] getSound (String fileName) {try {URL url = Talker.class.getResource (fileName); StreamInputStream = AudioSystem.getAudioInputStream (url); Μορφή AudioFormat = stream.getFormat (); // - Μετατροπή ήχου ALAW / ULAW σε PCM για αναπαραγωγή - εάν ((format.getEncoding () == AudioFormat.Encoding.ULAW) || (format.getEncoding () == AudioFormat.Encoding.ALAW)) { AudioFormat tmpFormat = νέο AudioFormat (AudioFormat.Encoding.PCM_SIGNED, format.getSampleRate (), format.getSampleSizeInBits () * 2, format.getChannels (), format.getFrameSize () * 2, format.getFrameRate (), true); stream = AudioSystem.getAudioInputStream (tmpFormat, ροή); μορφή = tmpFormat; } DataLine.Info info = new DataLine.Info (Clip.class, format, ((int) stream.getFrameLength () * format.getFrameSize ())); if (line == null) {// - Η γραμμή εξόδου δεν έχει δημιουργηθεί ακόμα - // - Μπορούμε να βρούμε ένα κατάλληλο είδος γραμμής; - DataLine.Info outInfo = νέο DataLine.Info (SourceDataLine.class, μορφή); εάν (! AudioSystem.isLineSupported (outInfo)) {System.out.println ("Γραμμή αντιστοίχισης" + outInfo + "δεν υποστηρίζεται."); ρίξτε νέα εξαίρεση ("Αντιστοίχιση γραμμής" + outInfo + "δεν υποστηρίζεται."); } // - Ανοίξτε τη γραμμή δεδομένων προέλευσης (γραμμή εξόδου) - γραμμή = (SourceDataLine) AudioSystem.getLine (outInfo); line.open (μορφή, 50000); line.start (); } // - Ορισμένοι υπολογισμοί μεγέθους - int frameSizeInBytes = format.getFrameSize (); int bufferLengthInFrames = line.getBufferSize () / 8; int bufferLengthInBytes = bufferLengthInFrames * frameSizeInBytes; byte [] data = νέο byte [bufferLengthInBytes]; // - Διαβάστε τα byte δεδομένων και μετρήστε τα - int numBytesRead = 0; if ((numBytesRead = stream.read (data))! = -1) {int numBytesRemaining = numBytesRead; } // - Περικοπή του πίνακα byte στο σωστό μέγεθος - byte [] newData = new byte [numBytesRead]; για (int i = 0; i

Αρα αυτο ειναι. Ένας συνθέτης ομιλίας σε περίπου 150 γραμμές κώδικα, συμπεριλαμβανομένων σχολίων. Αλλά δεν έχει τελειώσει.

Μετατροπή κειμένου σε ομιλία

Ο ορισμός λέξεων φωνητικά μπορεί να φαίνεται λίγο κουραστικός, οπότε αν σκοπεύετε να δημιουργήσετε μία από τις παραδείγματα εφαρμογών που πρότεινα στην εισαγωγή, θέλετε να παρέχετε συνηθισμένο κείμενο ως εισαγωγή για εκφώνηση.

Αφού εξέτασα το ζήτημα, παρείχα μια πειραματική τάξη μετατροπής κειμένου σε ομιλία στο αρχείο zip. Όταν το εκτελείτε, η έξοδος θα σας δώσει πληροφορίες για το τι κάνει.

Μπορείτε να εκτελέσετε έναν μετατροπέα κειμένου σε ομιλία με μια εντολή όπως αυτή:

java com.lotontech.speech.Converter "Γεια σου" 

Αυτό που θα δείτε ως έξοδος μοιάζει με:

γεια -> h | e | l | oo εκεί -> dth | aer 

Ή, πώς να το τρέξετε όπως:

java com.lotontech.speech.Converter "Μου αρέσει να διαβάζω JavaWorld" 

για να δείτε (και να ακούσετε) αυτό:

i -> ii like -> l | ii | k to -> t | ouu read -> r | ee | a | d java -> j | a | v | ένας κόσμος -> w | err | l | d 

Εάν αναρωτιέστε πώς λειτουργεί, μπορώ να σας πω ότι η προσέγγισή μου είναι αρκετά απλή, αποτελούμενη από ένα σύνολο κανόνων αντικατάστασης κειμένου που εφαρμόζονται σε μια συγκεκριμένη σειρά. Ακολουθούν ορισμένοι παραδείγματα κανόνων που μπορεί να θέλετε να εφαρμόσετε διανοητικά, για τις λέξεις "μυρμήγκι", "θέλω", "επιθυμητό", "ανεπιθύμητο" και "μοναδικό":

  1. Αντικαταστήστε το "* μοναδικό *" με το "| y | ou | n | ee | k |"
  2. Αντικαταστήστε το "* want *" με το "| w | o | n | t |"
  3. Αντικαταστήστε το "* a *" με το "| a |"
  4. Αντικαταστήστε το "* e *" με το "| e |"
  5. Αντικαταστήστε το "* d *" με το "| d |"
  6. Αντικαταστήστε το "* n *" με το "| n |"
  7. Αντικαταστήστε το "* u *" με το "| u |"
  8. Αντικαταστήστε το "* t *" με το "| t |"

Για το «ανεπιθύμητο» η ακολουθία θα ήταν έτσι:

ανεπιθύμητοςun [| w | o | n | t |] ed (κανόνας 2) [| u |] [| n |] [| w | o | n | t |] [| e |] [| d |] (κανόνες 4, 5, 6, 7) u | n | w | o | n | t | e | d (με τους αφαιρετικούς χαρακτήρες) 

Θα πρέπει να δείτε πώς λέξεις που περιέχουν τα γράμματα συνηθισμένος θα εκφωνηθεί με διαφορετικό τρόπο σε λέξεις που περιέχουν τα γράμματα μυρμήγκι. Θα πρέπει επίσης να δείτε πώς ο κανόνας της ειδικής περίπτωσης για την πλήρη λέξη μοναδικός υπερισχύει των άλλων κανόνων έτσι ώστε αυτή η λέξη να αναφέρεται ως ε | ε ... προκειμένου εσύ | ν ....