reconnaissance vocal
bonjour,
J'ai commencé par regarder les outils de reconnaissance vocal et je me suis trouver fortement intéressé par NSSpeechRecognizer je me suis donc mit au swift et j'ai pondu ce code :
import Cocoa
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet weak var stopButton: NSButton!
@IBOutlet weak var startButton: NSButton!
@IBOutlet weak var descriptionAction: NSTextField!
@IBOutlet weak var historiqueAction: NSTextField!
@IBOutlet weak var window: NSWindow!
var recognition = NSSpeechRecognizer()
var commandeTab : [String] = [String]()
func applicationDidFinishLaunching(aNotification: NSNotification) {
window.center()
let location = "/Users/XXX/Desktop/commandes.txt"
let fileContent = NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
var tmp :String = toString(fileContent)
commandeTab = tmp.componentsSeparatedByString("\n")
recognition.commands = commandeTab
window.showsResizeIndicator = false
}
@IBAction func startClicked(sender: NSButton) {
recognition.startListening()
descriptionAction.stringValue = "en écoute"
}
@IBAction func stopClicked(sender: NSButton) {
recognition.stopListening()
descriptionAction.stringValue = "en pause"
}
func speechRecognizer(recognition : NSSpeechRecognizer, command: String){
}
}
mon problème est petit, mon app se lance mais lorsque je la lance la fenêtre qui affiche les commandes disponibles a un "Optional()" que j'aimerai faire disparaitre voici une image qui est plus compréhensible que mon charabia. Si vous avez des remarques sur mon code je suis ouvert à toutes idées d'améliorations
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Tu as un Optional<String> (Autrement appellé "String?", donc une String qui est Optional) quelque part et tu l'affiches donc c'est normal qu'il t'affiche ça.
Si tu veux extraire la valeur faut utiliser un "if let" ou ce genre de construction classique en Swift pour gérer les Optionals
Je vois pas trop dans ton code où tu affectes ta String? à ton élément d'interface (une NSTableView?) et donc où ce trouve cette chaà®ne qui en fait est une Optional<String> (pas très clair juste en lisant ton code comment tu gères tout ça) mais le problème me paraà®t logique, tu as une Optional il t'affiche une Optional dans le texte.
Tout d'abord merci de ta réponse elle m'a permis de trouvé la solution.
J'ai affiché le contenu de commandeTab et il contient :
"Optional(jouer une musique
saluer)"
dans commandeTab[0] il y a "Optional(jouer une musique"
j'en ai conclu que le problème venait d'ailleurs.
Et le problème viens de NSString(contentOfFile: location, encoding: SNUTF8StringEncoding, error: nil) Il retourne un string? j'ai donc rajouter un ! après cet instruction et ça fonctionne.
j'ai aussi fait une variante avec le if let, laquelle des deux solutions est la plus élégante (celle avec le ! ou celle avec le if let )?
Donc à éviter à tout prix, sauf si tu sais vraiment que ça peut vraiment pas être nil, c'est à dire par exemple que ton code est déjà dans un "if" qui a testé avant si justement la valeur était non-nil par exemple, ce genre de choses.
Il faut toujours préférer un "if let", qui va te forcer à définir ce que tu dois faire / te forcer à réfléchir à la quesiton "tiens, d'ailleurs, si jamais ma valeur est nil, je veux faire quoi dans ce cas particulier ?". Dans ton cas ça peut être peut-être ne rien faire, peut-être masquer ta NSTableView et afficher à la place un message en rouge "aucun élément", etc... mais au moins tu traites le cas, et sans risque de plantage au runtime, contrairement à "!" qui force la main et plante si l'Optional était nil.
Merci de ta longue réponse, à l'avenir je ferai un if let.
Une autre chose me dérange dans ce code, la fonction NSString(contentsOfFile: location, encoding: NSUTF8StringEncoding, error: nil)
Habituellement pour spécifié le type de la variable je mettais var nom : typeVar = valeur, pareil dans les déclarations de fonctions(pour spécifier le type du paramètre).
Je suis un peut perdu je ne comprends pas à quoi correspondes toutes ces choses. contentOfFile est le type d'objet qu'attend la fonction NSString ? si oui pourquoi n'est-il pas après la nom comme ceci
func sayGoodbye(personName: String) {
Si non qu'est ce que c'est ?
Dans l'doute (ne parlant pas Swift), j'aurais tendance à dire Class Method (Type Method) vs Instance Method.
- "fileContent" : nom de la variable/constante
- "let" devant : indique que fileContent sera une constante, qu'on ne compte pas la modifier par la suite
- ici, le type de la variable est omis et va être déterminé automatiquement par le compilateur (typage implicite). Il est fréquent en Swift que, quand on déclare une variable et qu'on lui affecte dans la foulée une valeur (ou le résultat d'une fonction), on ne s'embête pas à indiquer le type de la variable, car le compilateur peut la déduire tout seul en fonction du type de la donnée qu'on lui affecte.
- "NSString(contentsOfFile: ...)" est un appel au constructeur de NSString. C'est une fonction qui va te construire une instance d'objet NSString.
- Une classe a parfois plusieurs constructeurs (initializer), c'est le cas de NSString, où tu peux construire une NSString avec "NSString(format:, ...)" ou "NSString(string: ...)", etc. qui construisent des NSString et sont sûrs de ne jamais échouer, du coup ils ne retournent pas d'Optional mais vraiment une NSString (jamais nil).
- D'autres constructeurs (appelés "failable initializers") peuvent échouer, et vont alors retourner soit une NSString s'ils ont réussi, soit nil s'ils ont échoué... donc le type de retour sera en fait "Optional<NSString>" (ou "NSString?", c'est pareil). C'est le cas de "NSString(contentsOfFile:...encoding:...error:...)" qui peut effectivement échouer dans certains cas (par exemple si le fichier n'existe pas, ou s'il n'a pas réussi à lire le fichier avec l'encoding indiqué, etc)
Si tu voulais donc spécifier explicitement le type de ta constante "fileContent" (plutôt que de laisser le compilateur le déterminer pour toi implicitement), tu devrais alors écrire :Ou encore, si tu préfères cette notation (totalement équivalente, le "?" n'était que du "sucre syntaxique" pour déclarer un Optional) :
Si tu avais essayé de déclarer "let fileContent : NSString" en indiquant un type "NSString" (et pas "NSString?" ou "Optional<NSString>"), le compilateur t'aurais indiqué une erreur, disant que puisque tu lui dit, de force, que fileContent est de type NSString, ce n'est pas compatible avec le type retourné par "NSString(contentsOfFile: ... encoding:... error:...)", qui lui retourne une "NSString?".
d'accord je crois que je compris.
Dernière question et j'arrête de vous embêter promis
Comment associer une action à une commande ? Dans la doc d'apple je dois utilisé un truc appelé delegate mais je sais pas du tout ce que c'est, habituellement je m'aidais de mes bases du c++ pour faire des rapports et comprendre mais la, il n'existe rien de telle en c++(à ma connaissance) et je suis donc (à nouveau) perdu. Ma fonction func speechRecognizer(recognition, command) n'est pas appelée lorsque une commande est reconnue (je sais qu'elle est reconnue car il y a la commande qui s'affiche au dessus du micro lorsque je prononce la commande).
La délégation est un design pattern, c'est une façon de structurer des classes d'objet et de les faire causer entre elles.
Un design pattern est peu dépendant du langage de programmation utilisé. Le principe de fonctionnement est identique en C++ et en Objective-C ou en Swift.
Une introduction aux Design Patterns se trouve dans la doc d'Apple, avec un exemple explicatif.
Plus concrètement pour déclencher une action sur la commande, les explications se trouvent dans la doc de NSSpeechRecognizer et de NSSpeechRecognizerDelegate, ainsi que dans le document d'introduction, on y trouve des petits bouts de code en exemple.
tout d'abord un grand merci à vous. Voici ce que fait : j'ai créé une classe qui hérite (ça se dit pour un protocole "hériter" ?) de NSObject et de NSSpeechRecognizerDelegate puis j'ai créer une instance de cette classe et j'ai fait instanceDeSpeechReco.delegate = maClass()
voici le code si vous avez n'importe quelles remarques je suis preneur
D'ailleurs, il existe la classe NSObject, mais aussi le protocole NSObject. Les nommer différemment aurait été une bonne idée, mais c'est ainsi.
Du coup en Swift "NSObject" désigne la classe, quant au protocole, il est nommé "NSObjectProtocol".
- hérite de la classe NSObject
- se conforme au protocole NSSpeechRecognizerDelegate
Note également que par convention, on nomme toujours une classe avec une majuscule, pour les différencier des instances / variables / propriétés, qu'on commence toujours par une minuscule. Du coup je t'invite à renommer ta classe en "DelegateOfSpeechRecognition" (ou pour faire + anglophone, "SpeechRecognitionDelegate" par exemple), avec donc la première lettre en majuscule.