[résolu] comment sauvegarder en Cocoa une structure C et la rappeler en C++?
Herve
Membre
Bonjour,
J'avance bien dans mon apprentissage Core Audio. Je fais en ce moment une appli qui envoie des structures C vers le Audio Unit : cela permet d'envoyer des valeurs plus nombreuses permettant un travail de synthèse sonore plus poussé qu'avec les seuls AU paramètres. Quand il y a un seul son, mon programme envoie de Cocoa à AU une structure C (contenant des tableaux d'autres structures d'ailleurs.) Tout marche bien, super!
Mon idée serait maintenant, lorsque j'utilise mon AU, non pas avec mon appli mais depuis un Host (Garage band par exemple : il est super ce petit host, très sympa!!) appeler les sons du synthé par MIDI Program Change.
Mon idée serait alors :
un son = une structure C
tous les sons rappelables = tableau de ces structures. (ou structure contenant un tableau de ...)
Lorsque j'appelle un MIDI Program Change, le AU copie la structure correspondante comme paramètres.
Il faudrait :
- en Cocoa, écrire ce fichier. Je l'ai fait avec NSData (un NSMutable Array de NSData de type :
Je voudrais rappeler ce tableau de structures à l'initialisation du AU. Mais C++ ne connaà®t pas les NSMutable Array.
Auriez-vous une idée? Communiquer via le HD (et non plus directement, ça je sais le faire) des structures C de Cocoa à C++?
Par ailleurs, est-ce que quelqu'un saurait m'expliquer la valeur d'un truc comme :
Que veut dire le "x" en particuliers? J'ai vu plusieurs encodages qui "ressemblent", mais après? Ici, cela "veut dire" que on va envoyer un message MIDI CC.
J'avance bien dans mon apprentissage Core Audio. Je fais en ce moment une appli qui envoie des structures C vers le Audio Unit : cela permet d'envoyer des valeurs plus nombreuses permettant un travail de synthèse sonore plus poussé qu'avec les seuls AU paramètres. Quand il y a un seul son, mon programme envoie de Cocoa à AU une structure C (contenant des tableaux d'autres structures d'ailleurs.) Tout marche bien, super!
Mon idée serait maintenant, lorsque j'utilise mon AU, non pas avec mon appli mais depuis un Host (Garage band par exemple : il est super ce petit host, très sympa!!) appeler les sons du synthé par MIDI Program Change.
Mon idée serait alors :
un son = une structure C
tous les sons rappelables = tableau de ces structures. (ou structure contenant un tableau de ...)
Lorsque j'appelle un MIDI Program Change, le AU copie la structure correspondante comme paramètres.
Il faudrait :
- en Cocoa, écrire ce fichier. Je l'ai fait avec NSData (un NSMutable Array de NSData de type :
[data addObject:[NSNumber numberWithInt:mesProgrammes.lesPatchs[t].typePatch]];
Je voudrais rappeler ce tableau de structures à l'initialisation du AU. Mais C++ ne connaà®t pas les NSMutable Array.
Auriez-vous une idée? Communiquer via le HD (et non plus directement, ça je sais le faire) des structures C de Cocoa à C++?
Par ailleurs, est-ce que quelqu'un saurait m'expliquer la valeur d'un truc comme :
cc16[0] = 0xB0
Que veut dire le "x" en particuliers? J'ai vu plusieurs encodages qui "ressemblent", mais après? Ici, cela "veut dire" que on va envoyer un message MIDI CC.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
0x signifie héxadécimal
0xB0=176
Pour le tableau de structure, j'ai du mal à évaluer ton problème, tu peux avoir ce tableau en mémoire quelque part et passer l'adresse de ce tableau aux AudioUnits.
En effet l'AU (en C++) et l'IHM (en ObjC) tournent dans des processus séparés, alors on ne peut pas passer facilement des données par un simple échange de buffer, puisque leurs espaces mémoire sont différents.
Ceci dit, ils échangent déjà des données: en effet, quand on déplace un curseur dans l'IHM, ça modifie bien les paramètres de l'AU. Je ne rappelle plus comment cela fonctionne (on passe un CFDictionary ?), mais tu pourrais utiliser le même principe.
Pour le Program Change, il s'agit d'un message MIDI comme les autres (comme Key On, Key Off), alors il suffit de l'interpréter dans l'AU.
Pour l'hexadécimal, c'est le "x" que je ne comprends pas. Mais en te lisant, mpergand, et en considérant que B0 fait 11*16, soit 176, je devine que ce "0x" ne sert à rien sinon à dire que c'est de l'hexadécimal, non???
Merci Céroce aussi pour tes éclaircissements. Une solution pourrait être (le but étant d'appeler ce tableau créé préalablement par l'appli sur le HD depuis le AU lorsqu'il est hébergé dans le host) d'utiliser l'interface Cocoa du Audio Unit (qui peut être différente de celle de l'appli, pas de problème là dessus) et coder dedans l'appel du ficher qui serait ensuite renvoyé vers le AU (l'envoi de structures C de Cocoa à AU (C++) marche très très bien, c'est très puissant et très commode).
A défaut...
Tant que j'y suis, il y a une structure que je ne comprends pas : ce n'est pas une structure, ni une fonction, cela sert à appeler la valeur tempo du host dans le AU :
Apparemment, utiliser cela suppose que le AU reçoit déjà un "HostCallback_GetBeatAndTempo". Auriez-vous une idée de comment faire marcher cela? En fait, je ne sais même pas ce que c'est en C++ ce truc là ... Il me faudrait récupérer dans mes calculs le "*outCurrentTempo".
Merci encore pour vos conseils.
C'est les conventions d'écriture du C, on peut entrer une valeur en décimal, hexa ou octal:
C'est un pointeur de fonction.
Ceci est le typage d'une fonction.
En gros, ça dit que les fonctions de type "HostCallback_GetBeatAndTempo" prennent tels paramètres en entrée et renvoient un OSStatus.
On est ici dans le cas typique, puisque ça sert souvent à passer un pointeur sur une fonction de callback (c'est à dire qui sera appelée sur un événement précis).
De fait, il doit exister une fonction du genre:
Pour fixer la callback, on écrit:
Je comprends un peu, et j'ai cherché une fonction du type proposé.
J'ai trouvé pour l'instant ceci :
ce qui correspond à ce que tu dis, mais pour l'instant je n'arrive pas à la faire marcher.
J'ai aussi trouvé cela :
Mais les changements de tempo dans garageBand ne donnent rien (pour l'instant, je fais juste un printf("tempo appelé %e", outCurrentTempo))
Bon, je cherche encore!
En fait, j'ai dû dériver les méthodes
pour pouvoir utiliser les contrôles MIDI. Cela doit être la même chose avec le tempo.
Merci mpergand pour les explications. Cela me servira à écrire dans le host les données MIDI.
J'ai presque résolu le problème des appels de sons par MIDI. Ceci marche très bien :
Lorsque le host charge le plug, il charge le fichier à l'adresse indiquée avec.
Mon problème est que je ne peux pas laisser l'adresse du fichier ainsi car elle est à mon nom. Celle-ci ne pourra pas être modifiée par l'utilisateur une fois le programme diffusé. J'ai essayé les adresses /Library et /Library/Preferences mais rien n'est écrit dedans. L'OS bloque apparemment, et interdit à mon plug l'accès aux bibliothèques.
Sinon, si j'écris ce fichier dans Document, je crois me souvenir qu'au lieu de mettre mon nom entre /Users et /Documents, je peux le remplacer par un nom générique, mais je ne me souviens plus lequel. Quelqu'un ici s'en souviendrait? je n'arrive pas à retrouver un exemple que je crois avoir vu une fois...
Merci!
(pas encore trouvé la bonne fonction pour le tempo, mais je vais reprendre cela maintenant)
Tu peux le créer dans le dossier temporaire de l'utilisateur. Sous Cocoa, utilise la fonction [tt]NSString * NSTemporaryDirectory (void);[/tt]
(Par contre, en C++, je ne sais pas trop, quoiqu'on qu'on peut obtenir le chemin grâce aux variables d'environnement).
En fait, l'idée est que le Audio Unit charge une banque de sons "préférée" dans le host sans passer par un menu d'ouverture. Avec Cocoa en fait, le fichier n'est appelé que si ouvre l'interface utilisateur. En appelant un fichier C/C++ depuis la méthode d'initialisation du AU cela marche mieux : les sons sont dispo de suite. Je pense que c'est indispensable.
Normalement, le host se souvient des positions des paramètres, mais un son avec mon synthé, ce sont des centaines de paramètres, d'où l'utilisation de structures C. D'où le chargement de ces structures C sans devoir passer par l'interface utilisateur ou l'appli : on n'a plus pour rejouer son morceau qu'à écrire le bon MIDI programme change en début de morceau et c'est calé (lorsqu'on travaille souvent sur son morceau, c'est indispensable)
Là , je viens d'essayer ceci :
Le fichier est écrit, mais pas lu!!
pareil avec , et là je n'ose pas, franchement, c'est mettre le bazar!
Il me faudrait un nom de fichier accessible par tout utilisateur que l'OS ne bloque pas en fait.
Par contre, on ne peut pas écrire n'importe où, et Apple va ajouter de plus en plus de restrictions à ce niveau (renseigne-toi à propos du Mac App Store et du "Sandbox").
Je pense que dans ton cas, la place naturelle est dans Application Support:
~/Library/Application Support/DazibaoEdition/
Autrement
/Users/Shared/preferedMIDIBankSound.dat
fonctionne aussi. Mais en vrac comme cela je ne sais pas si c'est "légal" dans le MAS
Le dossier "~/Library/Application Support" est fait pour tous ces fichiers de ce genre, et c'est la raison d'être de ce dossier (à toi de créer un dossier dédié portant le nom de ton application dans ce dossier Application Support pour y mettre ton fichier .dat).
C'est déjà (1) la bonne pratique à faire et à suivre pour pas mettre des fichiers partout et n'importe où ailleurs à des endroits pas faits pour. Y'a un dossier pour, autant l'utiliser (2) mais aussi un des seuls emplacements auxquels tu auras accès en écriture si tu distribues ton appli via le MacAppStore et doit donc suivre les restrictions sur les répertoires auxquels tu as accès et la Sandbox (justement fait pour éviter que tout le monde foute le dawa partout là où c'est pas fait pour)
Merci à tous, et en particulier à mpergand pour les lignes de code. Je vais essayer tout cela. Je vous tiens au courant.
Merci encore!
Avec NSFileManager, je peux créer un dossier dans /Library, je peux créer le bon fichier dedans (il est écrit) mais ensuite son accès est bloqué. Le AU n'y a pas accès, lorsque je lis les infos du fichier, l'accès est interdit. (!!! >:()
Avec les différentes méthodes de NSFileManager, je sais récupérer les NSDictionary concernant les attributs du fichier, mais je ne vois pas quel paramètre je dois modifier. Ce sont :
attributesOfItemForPath:
2012-01-14 16:21:19.273 DazibaoEdition[571:60b] key 0 = NSFileOwnerAccountID, valeur = 0
2012-01-14 16:21:19.273 DazibaoEdition[571:60b] key 1 = NSFileSystemFileNumber, valeur = 7512711
2012-01-14 16:21:19.274 DazibaoEdition[571:60b] key 2 = NSFileExtensionHidden, valeur = 0
2012-01-14 16:21:19.274 DazibaoEdition[571:60b] key 3 = NSFileSystemNumber, valeur = 234881026
2012-01-14 16:21:19.275 DazibaoEdition[571:60b] key 4 = NSFileSize, valeur = 2244
2012-01-14 16:21:19.276 DazibaoEdition[571:60b] key 5 = NSFileGroupOwnerAccountID, valeur = 0
2012-01-14 16:21:19.276 DazibaoEdition[571:60b] key 6 = NSFileOwnerAccountName, valeur = root
2012-01-14 16:21:19.277 DazibaoEdition[571:60b] key 7 = NSFileCreationDate, valeur = 2011-06-16 22:57:28 +0000
2012-01-14 16:21:19.278 DazibaoEdition[571:60b] key 8 = NSFilePosixPermissions, valeur = 509
2012-01-14 16:21:19.278 DazibaoEdition[571:60b] key 9 = NSFileType, valeur = NSFileTypeDirectory
2012-01-14 16:21:19.279 DazibaoEdition[571:60b] key 10 = NSFileGroupOwnerAccountName, valeur = wheel
2012-01-14 16:21:19.279 DazibaoEdition[571:60b] key 11 = NSFileReferenceCount, valeur = 66
2012-01-14 16:21:19.280 DazibaoEdition[571:60b] key 12 = NSFileModificationDate, valeur = 2012-01-14 13:58:16 +0000
Je pense qu'il faut modifier NSFilePosixPermissions, mais je ne trouve pas dans la doc la valeur à lui donner.
Quelqu'un ici saurait-il cela? Merci d'avance.
En fait, plus exactement, si je crée manuellement depuis le finder le dossier, le fichier est écrit mais pas lu (accès totalement bloqué), mais en codant avec NSFileManager, rien ne s'écrit dans Library. Plus étonnant, si je crée un dossier avec NSFileManager dans Documents, les fichiers écrits dedans sont tout aussi inaccessibles. Le problème est donc bien l'autorisation d'accès aux fichiers que donne l'OS.
mais pour ma classe c++? CFString permet-il cela??
J'ai vraiment l'art de me compliquer la vie parfois, mais bon...
Si vous pouvez m'indiquer le bon document pour coder les autorisations d'accès, je suis preneur.
Il y a un tuto pour créer/sauvegarder un Property list en Core Foundation:
http://developer.apple.com/library/mac/#documentation/CoreFoundation/Conceptual/CFPropertyLists/CFPropertyLists.html#//apple_ref/doc/uid/10000130-SW1
Le code Cocoa
crée un dossier dans le répertoire utilisateur (de type Users/leNomUtilisateur/Library (ouDocuments)/leNomDuDossier.
Je comprends pas quelle fonction fait ceci dans le code que tu me proposes.
Ah là là !! C'est dommage, c'est la seule chose qui manque à mon appli, tout le reste marche maintenant. Le tempo obéit au Host sequencer en particulier (merci Ceroce de m'avoir bien aiguillé, il fallait employer les fonctions trouvées, mais dans la méthode render et non ailleurs)
Par ailleurs ceci :
me crée un fichier dont l'accès est ensuite interdit. Je n'arrive pas à trouver dans la doc ce qu'il faut modifier au NSDictionnary pour que le contenu du dossier soit ouvert.
A last little help please?
Ca peut te paraà®tre du pinaillage ces histoires de nom et d'emplacement, mais ce sont les conventions de OSX. Et je pense que rien qu'en les respectant et mettant les fichiers au bon endroit tu t'enlèveras bien des soucis (de droits d'accès notamment), c'est aussi pour ça que je dis ça
Crée "fichier.txt" dans le dossier "Mon Appli" dans "library/Application Support" de l'utilisateur.
le fichier créé a les droits de l'utilisateur courant (tartampion (moi) lecture/écriture)
c'est Boolean createSupportFolder(CFStringRef folderName,FSRef* fRef)
Ceci est un exemple de création du dossier support en CoreFoundation (puisque la biblio Cocoa n'est pas dispo).
J'ai cherché cet après-midi à comprendre tout cela. Les préférences sont, un peu à la manière des dictionnaires, ou de NSCoding, des associations Clef/Valeur. Est-ce qu'une structure C peut être considérée comme une valeur? A ce que j'ai compris, non. Je pensais sinon associer un URL à la clef, puisque c'est permis, mais cela ne résout pas le problème me semble t-il.
Voilà où j'en suis ce soir, la suite demain...
Non, mais tu pourrais imaginer prendre chaque champ de la structure.
Les préférences ne sont pas faites pour ça, mais vraiment pour conserver les réglages de l'utilisateur.
Un fichier dans "Application Support" est vraiment tout indiqué.
c'est très confus tout ça ???
Revenons au problème initial: sauvegarder un tableau de structure dans un fichier:
Exemple de structure:
Il ne faut pas que cette structure contienne de pointeurs !
Sauvegarde en Cococa:
Sauvegarde les données de la structure dans ~/Library/Application Support/Mon Appli/Parametres.data
Pour les récupérer dans ton audio unit en C:
qui affiche:
param1 1024
param2 2024
param3 3024
Les données du fichier sont bien récupérées !
NB:
faut créer un fichier .h avec la def de la struct et les #define:
Merci beaucoup, je vous tiens au courant.
ton code est venu à bout du problème!!
(Comme d'habitude, j'ai commencé à bricoler n'importe quoi à partir de , et puis quand je suis revenu à ton code, cela a marché impeccable. Ce que je peux m'énerver moi-même parfois...)
Merci beaucoup, cette petite fonction ajoutée rendra de grands services aux musiciens qui utiliseront le synthé.
Merci aussi à toutes les autres personnes qui ont participé à cette discussion. Je commence à enregistrer des démos du truc, je vous ferai écouter.