SYNTHLAB Dossier de conception Marie CHESNEAU Yves DEMIRDJIAN Yorick PERRET Adrien ROUSSEAU Charles SALIFOU 1
Sommaire I Préambule II Couche métier 1 Architecture globale 2 Diagramme de classe 3 Ports 4 Câbles 5 Les extensions 6 Module 7 Descriptif de chaque module 8 Workspace III Couche IHM 1 Architecture globale 2 Diagramme de classe 3 Le workspace 4 Sauvegarde / Chargement 5 Les modules 6 Les ports 7 Les câbles IV Conclusion 2
I - Préambule Ce projet avait pour but de créer un synthétiseur audio analogique sous forme d un logiciel de simulation. La conception de ce projet a demandé dans un premier temps de créer une architecture globale et modulaire afin de poser les bases du synthétiseur, et dans un second temps de créer les différents modules sans se préoccuper de l'architecture. Nous avons essayé de séparer au maximum les différents métiers. Notre projet repose essentiellement sur la librairie JSyn dans sa version 16.5.14. Celle-ci contient tout l'attirail nécessaire pour manipuler le signal à son plus bas niveau (au niveau de l'échantillonnage) et propose des composants facilement inter-connectables et extensibles. Cependant il s agit encore d une version bêta où plusieurs manques et problèmes subsistent, nous y reviendrons par la suite. Malgré tout, celle-ci s est révélée très efficace au fur et à mesure de nos découvertes sur ses fonctionnalités. Notre projet se décompose en deux sous-projets, synthlab et synthlabgui. Le premier est la couche métier utilisant directement JSyn. Le second, synthlabgui, repose sur synthlab : il s agit du projet IHM. Celui-ci n a aucune interaction directe avec JSyn. Ci-dessous est présentée l organisation générale du projet : Pour lancer le projet, il suffit de se placer au niveau du projet synthlabgui et exécuter la classe Main. Il est aussi possible d exécuter le jar mis à disposition. 3
II - Couche métier 1. Architecure globale On peut résumer le coeur de la couche métier par cet ensemble de classes : La couche métier est composée de quatre entités : le câble, le port, le module et le workspace. A eux quatre, ils représentent déjà la base d une conception d un synthétiseur. Ajouter un module est simple : il suffit de créer une interface étendant IModule et une classe qui étend Module en implémentant cette même interface. Le workspace représente le montage d un synthétiseur. On peut ajouter ou retirer des modules. A cette architecture minimale, nous avons ajouté au fur et à mesure de la conception différents concepts, à 4
commencer par des extensions à JSyn. Il est possible de les trouver dans le package unitextensions. On peut constater aussi la présence du package signal qui contient des méthodes utiles et des constantes concernant le signal (exemple une amplitude maximum en Volt). De plus, nous avons mis en place une factory pour créer ces divers composants (câbles, ports, modules, etc). Nous allons maintenant décrire les différentes facettes de ces entités. Ci dessous, les diagrammes qui vous serviront de repère : 2. Diagramme de classe 5
6
3. Le port JSyn possède, a priori, la notion de port. Il distingue les ports d entrées des ports de sorties. Nous avons maintenu cette séparation. Nous avons commencé par généraliser le port avec les classes IPort et Port. Par la suite, les interfaces IInPort, IOutPort étendent l interface générale IPort et les classes InPort et OutPort étendent Port. Port n est pas directement instanciable car il n existe pas, dans notre implémentation, de port qui soit à la fois d entrée et de sortie. Nous retrouvons donc dans la généralisation du port : Une référence sur un câble Un port JSyn Un label (qui peut être affiché sur une interface) Une référence sur le module qui contient le port Un filtre superviseur qui surveille le signal (nous y reviendrons) Une liste d observateurs Nous avons utilisé le pattern observer pour observer un port. Dans l implémentation, seul le module qui a créé et qui contient le port l observe. On peut ainsi savoir si par exemple un câble vient de se connecter ou de se déconnecter. Le filtre superviseur est une des extensions que nous avons ajouté dans le package unitextension. Il est de type FilterAmplitude. Sans décrire le filtre dans les détails, celui-ci joue le rôle de proxy, (au sens général du terme, pas comme pattern). On pourrait penser que le signal reçu dans le port provient du module distant, or il n en est rien, celui-ci passe, avant, par le superviseur. Le filtre superviseur est bien inclus dans la classe Port mais seule OutPort l utilise (avoir un filtre à chaque extrémité du câble n est pas utile). Dans l utilisation, le filtre sert au port à : détecter si le signal qui le traverse est en surtension (par rapport à la valeur d amplitude maximum définie dans la classe Signal) détecter si un signal circule ou non produire un signal nul (d amplitude 0) Nous pourrions dire, par analogie, que tous les ports de notre synthétiseur sont dotés d une puce électronique. (Par exemple les ports jack d un PC sont capables de déterminer si un câble est branché ou non). Les différentes détections décrites ci-dessus vont permettre d informer l utilisateur via une interface graphique (dans la notre, par exemple, le câble est entouré de rouge en cas de surtension). Notre filtre superviseur est aussi capable de produire un signal nul, même si en entrée d autres valeurs lui sont données. Cela est dû à une malfonction ou un oubli de JSyn. En effet, lorsqu un composant JSyn A est éteint et qu un composant B relié à A est démarré, l entrée du composant B garde la dernière valeur d amplitude fournie par A, ce qui fausse la réalité. Pour pallier à cela, notre port produit une tension nulle. 7
4. Le câble La notion de câble dans la partie métier n était pas obligatoire pour simuler le synthétiseur. En effet, nous aurions pu référencer le port distant au niveau du port adjacent. Mais cela ne semble pas coller avec la réalité. Un port n a, a priori, aucune indication sur le port distant auquel il est connecté. Nous avons donc décidé d introduire la notion de câble. Dans notre implémentation, un câble connaît un port d entrée et un port de sortie, c est un composant passif. Lorsque que l on définit les références des ports au câble, celui-ci met automatiquement à jour sa référence au niveau des ports et forme donc un pont entre les deux. 5. Les extensions En plus des composants JSyn nous avons ajouté nos propres extensions qui se trouvent dans le package unitextension. La plupart étend UnitFilter, UnitGenerator ou UnitBinaryOperator, des classes de JSyn. Ces extensions ont été créées pour subvenir aux besoins de l implémentation des différents modules et par la même occasion pour combler certains manques de JSyn. Elle prennent toutes au moins un flux d entrée (un tableau de valeurs d amplitudes) et un flux de sortie. Les valeurs du flux d entrée sont souvent modifiées. Nous avons regroupé ces extensions en deux catégories : Les filtres : ils prennent au moins un flux d entrée et transforment le flux en sortie Les générateurs : ils génèrent un flux en fonction des paramètres donnés 8
Les filtres sont regroupés eux-mêmes en sous catégories : Les filtres atténuant le son Les enveloppes Les filtres d interception : ils récupèrent les valeurs en temps réel avant de les renvoyer en sortie Les filtres modulant le signal Les filtres de supervision : ils observent le signal afin de détecter un motif particulier ou des valeurs spécifiques et corrigent le signal si besoin Ci-dessous, un récapitulatif des différents filtres que nous avons implémentés : 9
Nom Type Usage Fonction FilterAttenuator Atténuateur MicroModule MultiplexerModule OutModule Atténue le son d entrée en sortie. FilterEnvelopeAHDSR Enveloppe EGModule Si un front montant est détecté en entrée, produit une enveloppe de la forme AHDSR. Ce filtre existait déjà dans JSyn mais ne réagissait pas comme prévu, les descentes decay et release n étaient pas linéaires et ne respectaient pas les durées données en paramètre, ce qui nous a conduit à récrire le filtre nous-même. FilterInterception Intercepteur OSCModule Implémente le pattern observer. Le signal de sortie est identique à celui d entrée mais les valeurs sont envoyées aux observateurs du filtre entre temps. FilterAmplitudeModulation Modulateur VCAModule Récupère un flux d entrée A et l'atténue ou l amplifie en sortie grâce à un paramètre. Si un flux d entrée B est présent, le signal en entrée A est en plus atténué ou amplifié (+/-1V en entrée B = +/-12dB sur la sortie). FilterBinaryModulation Modulateur -- Si le signal est inférieur à un seuil déterminé en paramètre la sortie vaut 0, sinon elle vaut 1. FilterFrequencyModulation Modulateur VCFHPModule VCFLPModule VCOModule Prend une tension en entrée et calcule une fréquence selon la règle : +1V => fréquence doublée. FilterAmplitude Superviseur Producteur Port VCFHPModule VCFLPModule Implémente le pattern observer. Surveille les valeurs d amplitudes du signal. Peut tronquer le signal à une amplitude max. Prévient les observateurs d un signal nul ou d une surtension (borne supérieure d amplitude définie en paramètre). Ce filtre a la spécificité de générer un signal d amplitude nulle en ne tenant pas compte du signal d entrée. FilterRecordMinMaxAmplitude Superviseur VCFHPModule VCFLPModule Récupère les minimums et les maximums d un signal en entrée dans un temps indéfini. FilterRisingEdge Superviseur SequencerModule Implémente le pattern observer. Détecte les fronts montants d un signal et avertit les observateurs. BrownianNoise Producteur NoiseModule Génère un bruit de type brownien. SimpleProducer Producteur PianoModule SequencerModule Génère une tension continue déterminée grâce à un paramètre. 10
6. Les modules Le module est l essence même du synthétiseur. Nous avons décidé que la notion de module devait être constituée au minimum de : Un port (sortie ou entrée) Un composant JSyn ou une extension Ceci est la caractérisation minimum de ce que peut contenir nos modules, autrement dit une entrée/sortie et un traitement sur les données. De plus, nos modules contiennent le plus souvent : Des paramètres D autres ports D autres composants ou extensions Des traitements Des méthodes pour récupérer des valeurs spécifiques aux modules JSyn possède a priori la notion de module à travers de ce qu ils appellent un Circuit. Un circuit possède un ou plusieurs ports et englobe plusieurs composants connectés en interne. De l extérieur, il ne s'agit que d un super composant. Par analogie, on peut identifier un circuit à une puce électronique. Toute notre implémentation repose sur les circuits. Nos modules sont, en fait, des adaptateurs du circuit. Même si le module ne contient qu un seul composant, celui-ci est ajouté à un circuit avant d être ajouté au synthétiseur. Nous avons d ailleurs remarqué qu il y avait beaucoup de problèmes en faisant une utilisation partielle des circuits. Le signal était déformé. De ce fait, deux solutions sont possibles : l utilisation totale ou non des circuits. Nos modules reposant sur une base identique, chaque implémentation de module étend une super classe Module qui implémente IModule. Celle-ci contient : Un circuit JSyn (nous avons encapsulé le circuit JSyn dans notre module) Un nom unique (vérifié par le Workspace) Une factory Un état indiquant si le module est en marche ou à l arrêt Une liste d observateurs pour connaître l état du module.ci-dessous, le processus de création dans la conception d un module : 1 Création d une interface étendant IModule 2 Création d une classe étendant Module et implémentant l interface précédemment créée 3 Instantiation des composants 4 Création des ports via la factory, définition des extrémités (quelle entrée de composant pour tel port ou quelle sortie pour tel autre port) 5 Connexions des divers composants via les ports JSyn 6 Ajouts de tous les composants au circuit 11
Par la suite, libre à chacun d ajouter des méthodes spécifiques. Un exemple de module servant de récapitulatif avant de voir en détail chacun des modules créés. Comme décrit précédemment nous retrouvons les différentes facettes qui composent notre module. Dans notre implémentation, seuls les ports observent si le module est en marche ou non. A la création d un port dans le constructeur du Module, le module s enregistre automatiquement auprès du port et le port s enregistre automatiquement auprès du module. 7. Descriptif de chaque module Le composant PassThrough ne sera pas mentionné dans la ligne composants utilisés s il n a pas un rôle prépondérant au module. Celui-ci sert à mixer plusieurs signaux d entrées pour obtenir une ou plusieurs sortie (signal mixé et répliqué). Certains des composants listés sont des extensions que nous avons nousmême implémentées (se référer au tableau des extensions), les autres étant déjà inclus dans la librairie JSyn. 12
EG Fonction Créé une enveloppe AHDSR Composants utilisés Mise en oeuvre Paramètres FilterEnvelopeAHDSR Le filtre que nous avons créé est relié au port d entrée gate et de sortie out. Le filtre détecte un front montant et envoie sur sa sortie une tension correspondant dans le temps à une enveloppe AHDSR. Le temps en secondes attack, hold, decay, et release. L atténuation en db pour sustain. EQ Fonction Atténue ou accentue une ou plusieurs bandes de fréquences composant un signal audio. Composants utilisés Mise en oeuvre Paramètres FilterPeakingEQ (permet d obtenir une atténuation ou un gain autour d une fréquence). Le module contient un tableau de constantes représentant une suite de fréquences intéressantes. Les filtres FilterPeakingEQ sont connectés en série aux fréquences respectives. Le premier filtre est relié au port in tandis que le dernier est connecté au port out. Le gain en db, pour chacune des fréquences du module, qui est donné en tension aux filtres. EQView Fonction Permet de visualiser les amplitudes maximales autour d une fréquence. Composants utilisés FilterBandPassEQ (permet de ne garder que les fréquences autour d une fréquence de référence et d atténuer les plus lointaines). PeakFollower (envoie l amplitude maximale d un signal d entrée) Mise en oeuvre Le module utilise le même tableau de fréquence que EQModule. Le port d entrée du module est connecté en parallèle aux FilterBandPass pour obtenir une fenêtre autour d une fréquence. Chacun de ces filtres est connecté en série avec un PeakFollower pour obtenir l amplitude maximale autour de la fréquence. Le port de sortie du module est connecté 13
directement au port d entrée (pas de modification du signal). Paramètres Aucun Micro Fonction Récupère le signal sonore provenant d un microphone. Composants utilisés LineIn (composant de périphérique d entrée) FilterAttenuator (pour atténuer le signal) Mise en oeuvre Paramètres Le module possède un port de sortie relié à l atténuateur qui est lui-même connecté au LineIn. Le gain en db qui est donné en Volts au filtre. Multiplexer Fonction Composants utilisés Mise en oeuvre Paramètres A la fois réplicateur et mixeur. Il fusionne les signaux aux ports d entrée et réplique le mixage sur les ports de sortie. FilterAttenuator PassThrough Chacun des signaux aux ports d entrée passent d abord par un FilterAttenuator. Ceux-ci sont ensuite mixés à l aide d un composant JSyn : le PassThrough. Tous les ports de sortie du module sont connectés à la sortie du PassThrough. Atténuation en db pour chacun des filtres FilterAttenuator (ou autrement pour chacun des ports d entrée) Noise Fonction Produit un bruit en sortie de type : Blanc, Rose, Brownien Composants utilisés Mise en oeuvre Paramètres PinkNoise WhiteNoise BrownianNoise Chacune des sorties de ces filtres est connectée à un port out correspondant au type de bruit voulu. Aucun 14
OSC Fonction Oscilloscope. Visualise le signal (la tension au cours du temps) Composants utilisés Mise en oeuvre Paramètres FilterInterception L entrée du filtre est connectée au port d entrée du module. Le port de sortie est directement connecté au port d entrée (pas de modification du signal). Le module observe le filtre et récupère les valeurs qui y circulent. Ces valeurs sont stockées dans une zone tampon. Si celle-ci n est pas vidée, le module génère une erreur personnalisée. Ce module est donc conçu pour fonctionner avec un contrôleur et une présentation qui se chargeront de vider le buffer pour en afficher les valeurs. Aucun OUT Fonction Module de sortie. Le signal est envoyé aux hauts parleurs pour être transformé en variations de pression atmosphérique. Composants utilisés LineOut FilterAttenuator PassThrough Mise en oeuvre Paramètres Les ports d entrées sont connectés au LineOut. Selon le port connecté (left ou right), un des deux hauts parleurs diffuse le son. Si le mode distribué est activé, le son sort sur les deux hauts parleurs quel que soit le port connecté. Ce mode est possible grâce au composant PassThrough qui fusionne le son pour le répartir entre les deux hauts-parleurs. Les ports d entrée sont connectés à l entrée du filtre FilterAttenuator qui est lui même connecté au LineOut. La valeur d atténuation en db donné en tension au filtre FilterAttenuator Le mode de distribution du son choisi Piano Fonction Composants utilisés Simule un piano de synthétiseur. A l appui d une touche, il produit une tension spécifique. Il se connecte en général à un VCO sur l entrée fm. SimpleProducer 15
Mise en oeuvre Paramètres Le module possède une méthode play qui prend en paramètre une note déterminée via un type Enum de Java et un entier correspondant à l octave de la note. Cette méthode détermine ensuite la tension de sortie grâce à la tension de référence (la note LA) en octave 3. La valeur de la tension résultante est ensuite passée au SimpleProducer qui va se charger de diffuser une tension continue à la valeur spécifiée. Ce simpleproducer est connecté au port de sortie out. Lors de l appel de la méthode play, un autre SimpleProducer va produire une tension continue de 5V sur le port gate. Cette tension indique qu une touche du piano est enfoncée. Ce signal passe à 0V lors d un relâchement d une touche ou autrement dit à l appel de la méthode stopplay. Les touches du piano Séquencer Fonction Produit une tension spécifique dans un ensemble déterminé de valeurs à chaque front montant du signal. Au front montant suivant, la tension attribuée est égale à la tension d indice +1 dans l ensemble des valeurs. Composants utilisés SimpleProducer FilterRisingEdge Mise en oeuvre Paramètres Le port d entrée du module est connecté au filtre FilterRisingEdge pour détecter les fronts montants. Le module observe le filtre pour en être informé. Lorsque qu un front montant est effectivement détecté, l indice courant est incrémenté et la tension obtenue est attribuée au SimpleProducer (connecté au port de sortie du module). Cette sortie se connecte généralement à l entrée fm d un VCO. Une amplitude entre -5V et 5V pour chacun des indices du tableau. VCA Fonction Amplifie ou atténue un signal à l aide d un potentiomètre et/ou d un signal modulant. Composants utilisés Mise en oeuvre Paramètres FilterAmplitudeModulation (module l amplitude du signal sur une entrée 2 via l amplitude d un autre signal sur l entrée 1) Le port d entrée in du module est connecté à un filtre FilterAmplitudeModulation nommé filtre A. Le port d entrée am du module est connecté à un autre filtre FilterAmplitudeModulation nommé filtre B sur l entrée 1. La sortie du filtre A est connectée à l autre entrée du filtre B, entrée 2. La sortie du filtre B est connectée au port de sortie. Une atténuation en db pour atténuer ou amplifier le signal sortant. 16
VCF HP Fonction Atténue les fréquences inférieures à une fréquence de coupure donnée en paramètre. Composants utilisés FilterHighPass FilterFrequencyModulation Mise en oeuvre Le module possède deux entrées. La première entrée in est celle du signal qui va être atténué aux fréquences inférieures à la fréquence de coupure. La deuxième entrée fm est celle du signal qui va moduler la fréquence de coupure autour de sa valeur initiale. Paramètres La fréquence de coupure en Hz VCF LP Fonction Atténue les fréquences supérieures à une fréquence de coupure donnée en paramètre. Composants utilisés FilterLowPass FilterFrequencyModulation Mise en oeuvre Le module fonctionne identiquement au VCF HP mais possède deux FilterLowPass en série pour obtenir une atténuation de 24dB/octave (aucun paramètre n était disponible sur le composant JSyn pour régler l atténuation). Paramètres La fréquence de coupure en Hz Le coefficient Q de résonnance VCO Fonction Génère un signal correspondant à un motif particulier à une fréquence donnée. Ce signal peut être modulé par la fréquence d un autre signal. Composants utilisés SineOscillator SquareOscillator TriangleOscillator SawtoothOscillator FilterFrequencyModulation FilterAmplitude Mise en oeuvre Les 4 ports de sorties de ce module correspondent aux 4 sorties des oscillateurs. L entrée du filtre FilterAmplitude est connectée au port d entrée fm et se charge de surveiller que la tension ne dépasse pas un certain seuil (définie dans la classe Signal par AMAXMODULATION). Si le seuil est dépassé, le module en est averti (pour afficher sur la 17
présentation un message) et le signal est tronqué. La sortie de ce filtre est connecté à l entrée du filtre FilterFrequencyModulation, lui même connecté aux entrées des oscillateurs JSyn. Paramètres La fréquence en Hz 8. Le Workspace IWorkspace et Workspace représentent l implémentation de notre Workspace. Un synthétiseur est composé de plusieurs modules que l on peut retirer, ajouter ou relier entre eux. Notre logiciel étant une simulation, nous voulions introduire en plus l aspect plan de travail, d où le nom de Workspace. Le Workspace est donc le synthétiseur. Il encapsule le synthétiseur de JSyn et possède des méthodes d ajout et de retrait et bien sûr une liste de modules. Étant donné que nous sommes encore au niveau de la couche métier, il n y a pas encore de notions de placement de modules, de sauvegarde, etc. Le Workspace est le point d entrée de l application. Par ailleurs, il vérifie lors de sa création si un microphone est branché. Si celui-ci est branché il devient alors possible d ajouter le module MicroModule. Malheureusement, nous n avons pas été en mesure de détecter le branchement physique d un micro en cours d'exécution. Il faut alors redémarrer l application pour prendre en compte le micro. 18
III - Couche IHM 1. Architecture globale Notre couche IHM repose sur la partie métier du projet. Afin de rendre à l utilisateur une extension graphique, nous avons choisi d utiliser le pattern PAC + Proxy + Héritage. Ce pattern prend la forme suivante : A représente l abstraction, c est à dire les classes et interfaces de la couche métier. C signifie contrôleur et P présentation. Le contrôleur se charge de jouer l intermédiaire entre la présentation graphique pure (qui ne possède que des méthodes de dessins et des composants graphiques) et l abstraction. Chacun des composants de notre application devant être affiché utilise ce pattern. Nous avons étendu notre factory pour qu elle puisse instancier des contrôleurs. 2. Diagramme de classe 19
20
3. Le Workspace Notre Workspace est divisé en deux grandes parties. La partie haute de l application qui permet de rajouter des composants ou sauvegarder son travail. La partie basse de l application est une sorte de grand plateau où l on interagit avec les différents modules ajoutés. Chacun de ses modules peut bouger librement en mettant à jour les coordonnées du câble. Un clic ou un déplacement de module entraîne la mise en avant du module si celui-ci se trouvait en-dessous d un autre module. 21
Diagramme de séquence d initialisation du Workspace: 4. La sauvegarde / le chargement CWorkspace possède des méthodes pour charger et sauvegarder les modules présents sur le Workspace. L ensemble de la sauvegarde est effectué dans une unique fonction. Cela est devenu possible en utilisant le mécanisme de reflection de Java. De ce fait, si on ajoute un nouveau module, on a aucunement besoin de se préoccuper de sa sauvegarde. La fonction que nous avons implémentée est, grâce à la reflection, la plus générique possible. Elle lit les attributs intéressants de chaque module dans le workspace, même ceux avec une visibilité private et sauvegarde le tout dans un fichier XML. Il en est de même pour les ports. Les attributs de type IPort sont lus afin de savoir s ils sont liés à un câble et donc de savoir à quel autre module ils sont connectés. Malheureusement, le manque de temps ne nous a pas permis d être le plus générique possible et nous prenons en charge uniquement : les attributs de type primitif non statiques les tableaux de type primitif (pas de List par exemple) les enum (pas de tableaux d enum) les ports (pas de tableaux de ports) 22
Nous avons exploré d autres pistes intéressantes que nous avons abandonnées par la suite : la sérialisation : après avoir défini tous les attributs non sérialisables (l ensemble de JSyn en fait partie...), nous avons constaté qu il y avait trop de problèmes à la dé-serialisation car des attributs n étaient plus instanciés correctement et la charge de travail pour y remédier n était pas envisageable. la redéfinition d une méthode save dans chaque module : bien que la plus facile à mettre en oeuvre, cette méthode aurait demandé de modifier l ensemble des modules (envisageable pour ce projet mais pas dans le cas où l on se retrouvait avec 200 modules...). Nous voulions quelque chose de moins brute. De plus, l ajout d un attribut dans le futur serait source d erreur si on oublie aussi de modifier la méthode save. La sérialisation des attributs grâce à la reflection : envisagée trop tard lors de la conception, cette méthode aurait sûrement été la plus efficace. Il s agit d une solution hybride qui consisterai à sérialiser la valeur d un attribut sans se préoccuper du type et donc de comment en extraire la valeur. Le chargement aurait été tout aussi efficace. Le chargement du module se fait juste à la lecture du fichier XML où le nom de chaque attribut y est sauvegardé. Il n y a plus qu à donner la valeur à l attribut après un parcours via la reflection. Comme dit précédemment la meilleure solution aurait été de dé-serialiser la valeur sans prendre connaissance du type de l attribut. 5. Le module De la même manière que pour la couche métier, il existe un CModule et un PModule. Toutes les présentations des différents modules étendent PModule. Celui-ci contient le minimum graphique : une taille minimale le nom du module un bouton start un bouton supprimer Cependant les contrôleurs des différents modules n étendent pas CModule mais étendent l abstraction de leur module respectif. En revanche, les interfaces s étendent hiérarchiquement. Une interface de contrôleur de module étend en général ICModule et l interface de l abstraction (qui elle même étend IModule). 23
Pour la présentation, nous retrouvons une interface avec sa classe : 24
Diagramme de séquence d ajout d un module VCO : : 6. Les ports De la même façon que pour la couche métier, on distingue un port d entrée et un port de sortie. Selon le port où l utilisateur interagit, les actions au niveau du contrôleur seront différentes (pour créer un câble nous devons cliquer d abord sur un port de sortie puis sur un port d entrée). Un retour graphique informe alors l utilisateur de ce qu il peut faire ou non. 7. Les câbles En plus de la couche métier, le contrôleur de câble met à jour la présentation lors de la redéfinition des méthodes. De plus, celui-ci observe les 2 modules auxquels il est rattaché. Lorsqu un module se déplace sur le plateau, il avertit les contrôleurs des câbles pour qu ils mettent à jour leurs coordonnées en fonction des ports auxquels ils sont rattachés. La présentation quant à elle possède deux méthodes de placement, l une où l on définit deux points, l autre où l on définit deux IPPort. Elle peut changer de couleur à la création d un câble grâce à la roulette de la souris. Par ailleurs, une animation est démarrée si un signal traverse le câble. 25
Diagramme de séquence d ajout d un cable étape 1 : 26
Diagramme de séquence d ajout d un cable étape 2 : 27
IV - Conclusion Dès le départ, nous nous sommes dit qu il était important de poser une bonne base au niveau de la couche métier. C est pour cela que nous nous sommes réservés deux journées en début de projet pour discuter des bonnes pratiques et de l architecture de notre application. Grâce aux enseignements dispensés au premier semestre nous avons pu mettre en pratique nos différents acquis. Par ailleurs, nous savions déjà que nous utiliserions PAC pour la couche IHM. Ce pattern permet de discerner de façon propre la présentation des opérations métiers. Le bon découpage nous a permis de rapidement ajouter de nouveaux modules les uns après les autres. 28