IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Différentes classes ou classe template


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Inscrit en
    Janvier 2006
    Messages
    173
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 173
    Par défaut Différentes classes ou classe template
    Bonjour,

    Je travaille sur un projet musical. Ma partition se compose de groupes qui se composent eux même d'instruments. Chaque instrument est écrit sur une portée.
    J'ai créé une classe singleton partition contenant pour attributs un vecteur de pointeurs vers une classe groupes et un pointeur vers une classe instruments.

    Chaque portée se compose de symboles différents : clef, notes, silences qui sont autant de structures différentes.
    Je pensais pour chaque symbole instancier une classe. Le pb est que chaque symbole est différent et possède des attributs différents.
    Que faire :

    1/ Créer autant de classes qu'il y a de symboles : une classe pour les notes, une classe pour les clefs ...

    OU

    2/Créer une classe template


    Merci pour vos réponses

  2. #2
    Membre émérite
    Homme Profil pro
    R&D imagerie 3D / prog embarquée
    Inscrit en
    Mars 2007
    Messages
    419
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : R&D imagerie 3D / prog embarquée
    Secteur : Santé

    Informations forums :
    Inscription : Mars 2007
    Messages : 419
    Par défaut
    Salut,

    J'ai l'impression que tes classes note, clé, silence etc... n'auraient pas les mêmes fonctions. Par exemple, une note pourrait te donnée la note à l'o0ctave supérieur et inférieur, alors que ça n'a aucun sens pour un silence.
    Si j'ai bien senti le problème, le template n'est pas adapté.
    De plus, je n'ai pas l'impression que tu puisse dire qu'un silence et une note avec un type particulier de donnée qui la représente.
    Par exemple, si on parlait de fréquence (radio) on pourrait le faire. Tu pourrais dire qu'un fréquence AM est une fréquence radio en entier, alors qu'une fréquence FM serait en float.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Frequency<T>
    typedef Frequency<int> AMFrequency
    typedef Frequency<float> FMFrequeny
    Je ne vois donc pas bien comment utiliser un template dans ton cas.

  3. #3
    Inactif  


    Homme Profil pro
    Inscrit en
    Novembre 2008
    Messages
    5 288
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 49
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Secteur : Santé

    Informations forums :
    Inscription : Novembre 2008
    Messages : 5 288
    Par défaut
    Salut

    On peut toujours utiliser des templates, c'est qu'un détail d'implémentation...

    Par exemple, on peut créer une hiérarchie de classe représentant les symboles et des classes de traits pour récupérer les infos sur les symboles :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    struct silence;
    struct pause : public silence;
    struct soupir : public silence;
     
    struct note;
    struct noir : public note;
    struct blanche : public note;
     
    is_note<pause>; // retourne false
    temps<noir>; // retourne 1
    temps<pause>; // retourne 2
    On peut également utiliser des classes de police pour adapter les comportements (afficher un note, placer une clé, etc). Voir créer un DSEL pour écrire la notation de musique directement en C++... Mais je m'emballe un peu


    Sinon, djuju parle de fréquence, donc suppose que le but est de générer du son à partir des partitions. Est-ce le cas ou le but est simplement d'afficher des partitions ?

    Le plus simple, si tu débutes en C++, est probablement de créer une hiérarchie d'objets. Il faut que tu définisses correctement ce que représentent les différents objets (objet au sens musique, càd note, silence, etc, pas au sens C++), les contraintes qu'ils doivent respecter, etc

    Concernant le singleton, cela veut dire que dès le départ, tu interdis la gestion multipartitions. C'est pas une contrainte inutile ?

  4. #4
    Membre émérite
    Homme Profil pro
    R&D imagerie 3D / prog embarquée
    Inscrit en
    Mars 2007
    Messages
    419
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : R&D imagerie 3D / prog embarquée
    Secteur : Santé

    Informations forums :
    Inscription : Mars 2007
    Messages : 419
    Par défaut
    L'idée était d'avoir une classe générique de type T et T pourrait prendre struc_note, struc_rest, struc_clef ...
    Que représenterait cette classe générique ?
    Si c'est une "cellule" de ta partition (p.e. le 3ieme symbole de la ligne 1 de ma partition), ça marche bien.

  5. #5
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    A vrai dire, je crois qu'il faut faire la distinction de ce qui est "la vue" et de ta partie "métier".

    Ta partition n'est, en définitive, qu'une représentation "conventionnelle" de la mélodie, tout comme le fait de l'entendre la mélodie n'est qu'une manière différente de la représenter.

    On peut, en outre, considérer qu'un silence ou un murmure n'est jamais... qu'une note qui ne produit aucun son

    En outre, une note reste une note, mais elle a sémantique de valeur:

    En effet, s'il existe deux référentiels (d'octave et de durée), une fois que tu as déterminé la note qui doit être jouée, sa durée et son octave, la note est, globalement, constante et il est tout à fait possible d'avoir, à deux adresses mémoire différentes, deux notes dont le nom, la durée et l'octave sont identiques, et qui seraient considérées comme égales : ni le nom d'une note, ni sa durée ni son octave, ni même l'adresse mémoire à laquelle la note se trouve, ni même l'ensemble des quatres ne peuvent être considérés comme un identifiant permettant d'identifier une note "de manière unique et non ambigüe".

    Ce n'est pas parce que tu as un do "triple croche" de la troisième octave sur sur la portée du tubas que tu ne peux pas avoir un autre do "triple croche" de la troisème octave sur la portée du piano et se trouvant à une adresse mémoire tout à fait différente (à ceci près que je ne sais pas du tout si un tubas est susceptible de jouer des notes de la troisième octave )

    Une note est donc représentable par son nom qui, pour une octave donnée, correspond à une fréquence donnée, mais cela signifie que si tu as trois informations, à savoir:
    1. l'octave qui sert de "multiplicateur de fréquence" étant donné que chaque fois que tu passera d'une octave à l'autre, tu multipliera la fréquence par 2
    2. le nom de la note associé (si tu veux entendre la mélodie) à une fréquence sur l'octave que l'on considérera comme de référence (par exemple, le do de l'octave 0 est à 32,70 Hz)
    3. sa durée
    En fait, si tu fais une relation entre le nom de la note et la fréquence qui lui correspond dans l'octave 0, tu peux obtenir la fréquence de cette note pour n'importe quelle autre octave en la multipliant par le (2^numéro d'octave).

    A vrai dire, on pourrait même considérer que seule la fréquence du do de l'octave 0 est importante, étant donné qu'il y a moyen de calcuer la fréquence de toutes les notes de l'octave assez facilement...

    Mais bon, cela n'est a priori utile que pour obtenir une représentation sonore de la mélodie

    Pour ce qui est de la durée, comme elle se compte en énieme de tempo (de 1/16eme pour la quadruple corche à 4/1eme pour le carré), il est relativement aisé de trouver une logique qui permette, tant d'un point de vue visuel (la partition) que d'un point de vue sonore (la mélodie) de calculer la manière dont une note donnée doit etre représentée (jouée).

    A partir de ces trois informations, il est tout à fait possible aussi bien de représenter la mélodie sous forme d'une partition que d'en avoir une représentation sonore (en faisant la corrélation entre le nom de la note et la fréquence qui la représente).

    Il faut noter que, pour ce qui est des silences, la logique reste tout à fait valide avec, simplement une corrélation entre son nom (silence ) et une fréquence égale à 0 (correspondant à l'absence de son).

    Tu peux donc te "contenter" de trois énumérations relativement simples qui permettent de représenter l'ensemble des variations possibles, et qui serviront de base à la création d'une seule classe Note, qui ne devra absolument pas être dérivée:

    La première énumération est l'octave, qui pourrait ressembler à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum OctaveNumber
    {
        zero,
        one,
        two,
        three,
        four,
        five,
        six,
        seven, 
        octaveUnset // permettra d'avoir un constructeur par défaut
    };
    La deuxième énumération te permettrait de représenter le "nom" de la note sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    enum MusicalNoteName
    {
        silence = -1, // (*)
        do,
        dodies,
        re,
        mibemol,
        mi,
        fa,
        fadies,
        sol,
        soldies
        la,
        sibemol,
        si,
        notANote // permettra d'avoir un constructeur par défaut
    };
    (*) si l'on envisage d'utiliser la formule pour évaluer la fréquence des notes, le fait d'avoir une valeur de -1 pour le silence permet à la fois de tester cette valeur assez facilement et d'avoir un "nombre de demi-ton" entre le do et les autres notes correct

    Enfin, l'énumération de durée pourrait ressembler à quelque chose comme (**) (***)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum Duration
    {
       quadrupleCroche = 1,
       tripleCroche = 2,
       doubleCroche = 4,
       croche = 8,
       black = 16, // (**)
       white = 32,
       circle = 64,
       square = 128,
       noDuration // permet d'avoir un constructeur par défaut
    };
    (**) La durée normal d'un temps est celle de la note noire.

    Si l'on divise la valeur d'une des valeur énumérées indiquées ci dessus par la valeur de black, on obtient, bel et bien, la durée correspondante
    (***) Un silence n'étant, encore une fois, qu'une note de fréquence 0, on peut parfaitement utiliser cette énumération, même si on parle en fait de soupirs et autres termes

    Et, enfin, nous aurions donc notre classe "MusicalNote" qui ressemblerait à quelque chose comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /* comme la note a sémantique de valeur, nous laisserons le compilateur
     * générer le constructeur par copie, l'opérateur d'affectation et le
     * destructeur "classiques"
     */
    class MusicalNote
    {
        public:
            /*Le constructeur par défaut sera, essentiellement, utilisé lorsqu'il
             * s'agira de gérer des collections de notes
             */
            MusicalNote() octave_(octaveUnset), name_(notANote),duration_(noDuration) {}
            MusicalNote( OctaveNumber octave, MusicalNoteName name, Duration duration):
                octave_(octave), name_(name), duration_(duration){}
            /* les accesseurs (et uniquement eux) de tout ce beau monde */
            OctaveNumber octave() const{return octave_;}
            MusicalNoteName name() const{return name_;}
            Duration duration() const {return duration_;}
        private:
            OctaveNumber octave_;
            MusicalNoteName name_;
            Duration duration_;
    };
    Il est, maintenant, temps de nous intéresser à la portée...

    La portée représente tout ou partie de la mélodie que doit jouer un instrument donné.

    Elle est caractérisée par une clé (qui, si mes souvenirs sont bons (mais le sont-ils ) permet d'indiquer sur quelle ligne permet de représenter quelle note) et un rythme, qui correspond au nombre de battements de temps par une minute.

    (Pas taper si les termes ne sont pas bons ici, ca fait près de 30 ans que je n'ai plus eu de solfège )

    Elle est composée d'un ensemble de battements qui contiennent chacun l'ensemble des notes qui doivent être jouée durant la durée de cet "espace temporel".

    On peut considérer, vu que la plus petite durée est le 16eme de battement, qu'un battementpeut être divisé en 16/16emes et contenir autant de notes qu'il le souhaite tout en veillant à ce que la durée totale des notes qu'il contient ne dépasse pas les 16/16emes.

    A ce stade, il me semble utile et important de remarquer, même si je ne suivrai cette voie que jusqu'à un certain point, que l'on trouve deux grandes catégories d'instruments de musique, pour ce qui nous intéresse du moins:
    • les instruments, comme les guitares ou les pianos, qui permettent (mais n'en font pas une obligation) de jouer plusieurs notes en même temps et
    • les instruments, comme la flute ou la trompette, qui ne permettent de jouer qu'une seule note à la fois
    Cette constatation est importante parce que, s'il faut partir sur une hiérarchie d'héritage, ce n'est pas au niveau de la note, mais bien au niveau... de ce que j'appelle le "battement".

    Nous devrions donc avoir une classe AbstractBatment qui aurait sémantique d'entité et qui servirait de base à deux classes distinctes l'une permettant de représenter les battements qui ne permettent de jouer qu'une note à la fois (pour les flutes et autres) et ceux qui permettent d'en jouer plusieurs (pour les guitares, pianos et autres instruments à cordes).

    C'est d'autant plus vrai que, à un instant T de la mélodie, un instrument donné ne pourra jouer... qu'un ensemble donné de notes

    Il pourrait d'ailleurs être identifié de manière "unique et non ambigüe" par le numéro de la portée à laquelle il se rapporte et par son "numéro d'ordre" dans cette portée (ce serait, par exemple, le troisième tempo de la portée numéro cinq, à quoi que puisse correspondre la cinquième portée )

    Il faut aussi prendre en compte le fait que certaines notes (les blanches, les rondes et les carrés) se prolongent bien au delà des 16/16emes de battement...

    Il faudra donc envisager de pouvoir récupérer le nombre de 16emes de battements de ces notes qui doivent être "reportés" sur le battement suivant
    La classe de base (AbstractBatment, ai-je dit plus tot ) correspondrait donc à quelque chose comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    class AbstractBatment
    {
        public:
            AbstractBatment(unsigned int portee, unsigned int order, int report):
                portee_(portee), order_(order){}
            virtual ~AbstractBatment(){}
            /* "mode C++11 on" AbstractTempo ayant sémantique d'entité,
             * il n'est ni copiable ni affectable
             */
            AbstractBatment(AbstractBatment const &) = delete;
            AbstractBatment& operator= (AbstractBatment const &) = delete;
            /* utilse le pattern NVI pour autoriser l'ajout d'une note au seizieme de tempo adéquat
              */        
           void addNote(Note const & n, unsigned int seizieme)
           {
               void checkDuration(n);
               void doAddNote(n);
           }
           /* permet à l'utilisateur de récupérer la durée totale des notes du 
            * battement (en 16eme de battement)
            */
          virtual unsigned int totalDuration() const = 0;
          /* renvoie la valeur du report à utiliser pour la création du battement
           * suivant.
           * Par exemple, si on place une quadruple croche suivie d'une blanche 
           * dans le battement N, la blanche devrait se prolonger pendant les
           * 15/16emes restant du battement N et la fonction devrait donc
           *  renvoyer 17 (32 - 15) lorsque invoquée depuis le battement N,
           * et 1 (17 - 16) lorsque invoquée depuis le battement N+1 pour la 
           * création du battement N+2.
           * La première note du battement N+2 ne pouvant prendre place qu'à partir du 2eme 16ieme de ce battement 
           */
          virtual unsigned int nextReport() const = 0;
        private:
            /* délègue aux classes dérivées le fait de vérifier que l'on peut
             * rajouter la note indiquée sans dépasser la durée du tempo
             * car cette vérification peut se faire de différentes manières
             */
            virtual void checkDuration(Note const &) const = 0;
            /* délègue aux classes dérivée le fait d'ajouter la note indiquée
             * au seizieme de tempo adéquat car cet ajout peut s'effectuer
             * de différentes manières
             */
            void doAddNote(Note const & n, unsigned int seizieme) = 0;
            unsigned int portee_;
            unsigned int order_;
            unsigned int report_;
    }
    cette classe AbstractBatment sera dérivée en SingleNoteBatment (les battements qui n'autorisent qu'une seule note à la fois) et MultiNoteBatment (ceux qui autorisent de jouer plusieurs notes en même temps)...

    Parce qu'elle reste "relativement" simple, je ne m'intéresserai ici qu'à SingleNoteBatment.
    La classe SingleNoteBatment pourrait prendre une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    class SingleNoteBatment : public AbstractBatment
    {
     
        public:
            SingleNoteBatment (unsigned int portee, unsigned int order, int report):
                AbstractBatment(portee,order,report){}
            virtual ~SingleNoteBatment (){}
            SingleNoteBatment (SingleNoteBatment const &) = delete;
            SingleNoteBatment & operator= (SingleNoteBatment const &) = delete;
           /* permet de savoir le nombre de notes déjà présentes
            */
          size_t noteCount() const{return notes_.size();}
          /** permet de récupérer la note se trouvant à l'index indiqué */
          Note const & noteFromIndex(size_t index) const
          {
              assert(index < noteCount());
              return notes_[index];
          }
          virtual unsigned int totalDuration() const
          {
              unsigned int ret = report;
              for(auto n : notes_)
                  if(n.duration != noDuration)
                      ret+=n.duration;
              return ret;
          }
          virtual unsigned int nextReport() const
          {
              if(totalDuration() + report_ <16)
                  return 0;
              return totalDuration() + report -16;
          }
        private:
            virtual void checkDuration(Note const & ) const 
            {
                /* en fait, j'ai changé d'idée en cours de route, mais
                 * si on abandonne l'idée du report, il s'agit de s'assurer que
                 * la durée totale actuelle + la durée de la note transmise en
                 * argument n'excède pas 16/16emes ;)
                 */
            }
            void doAddNote(Note const & n, unsigned int seizieme)
            {
                notes_.push_back(n);
            }
            std::vector<Notes> notes_;
    };
    Je ne vais pas présenter le code de la classe MultiNoteBatment, mais, en gros, il faudra peut etre travailler avec un tableau de seize listes de notes (une liste par 16eme de battement ) et la durée totale correspondra sans doute à la somme des durées de la note de chaque liste (ou quelque chose de très similaire )

    Nous en arrivons à la notion de portée.

    Comme je l'ai indiqué plus haut, la portée est, essentiellement, caractérisée par une clé et un rythme.

    Pour ce qui est de la clé, une simple énumération proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    enum MusicalKey
    {
        solKey,
        laKey,
        utKey
    };
    devrait pouvoir faire l'affaire.

    Pour ce qui est du rythme, il ne s'agit ni plus ni moins que d'une valeur numérique entière représentant le nombre de battements par minutes (ex 60 ou 120 )selon la "vitesse" de la mélodie

    Je place volontairement le rythme au niveau de la portée parce qu'il se peut que la mélodie commence sur un rythme très lent avant d'accélérer de plus en plus pour terminer par ralentir sur la fin.

    Il ne faut cependant pas oublier qu'il est aussi nécessaire de pouvoir identifier chaque portée de "manière unique et non ambgüe" sur base de son ordre d'apparition dans la "partition globale".

    Enfin, elle doit fournir une interface qui permet de la visiter de manière constante et non constante.

    Elle aura, elle aussi, sémantique d'entité, et cela pour deux raisons :

    La première, c'est qu'elle agira, en gros, comme un conteneur de battements et qu'une classe ayant des membres qui ont sémantique d'entité a, elle-même, sémantique d'entité.

    De plus, il est important de pouvoir identifier chaque portée de manière unique et non ambigüe : deux portées se trouvant à des adresses différentes mais contienant le même nombre (mais cela, on s'y attend très fort ) de battements dont les notes correspondent exactement devat être considérées comme des portées différentes.

    Enfin, tout comme on a des battements qui ne permettent de jouer qu'une seule note à la fois et d'autres qui permettent d'en jouer plusieurs, on a des portées (pour les flutes, par exemples) composées de battements ne permettent de jouer qu'une note à la fois et d'autres (pour les guitares et les pianos, par exemple) composées de battement qui permettent de jouer plusieurs notes à la fois.

    Nous aurons donc une deuxième hiérarchie de classes basée sur une classe AbstractStaff proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    class AbstractStaff
    {
        public:
            AbstractStaff(size_t staffId, MusicalKey key, unsigned int rythme):
                id_(staffId), key_(key), rythme_(rythme){}
            virtual ~AbstractStaff()
            {
                for( auto b, batments_)
                    delete *b;
            }
            /* crée un type concret de battement, l'insère à la liste et 
             * renvoie une référence sur le battement en question
             *
             * cette fonction est virtuelle pour permettre le retour co-variant
             * et virtuelle pureparce que l'on ne sait pas pour l'instant quel sera le
             * type concret du battement
             */
            AbstractBatment & addNextBatment() = 0;
            /* renvoie une référence constante sur un battement existant
             *
             * cette fonction est virtuelle pour permettre le retour co variant
             * et virtuelle pure parce qu'on ne dispose pour l'instant pas des
             * données permettant de fournir une implémentation
             */
            virtual AbstractBatment const & batment(size_t index) const =0;
     
            /* renvoie le nombre de battement dont la portée est composée
             *
             * cette fonction est virtuelle pure parce qu'on ne dispose pour 
             * l'instant pas des données permettant de fournir une implémentation
             */
            size_t batmentCount() const =0;
            /* fournit la possibilité d'utiliser un visiteur qui modifiera la portée
             */
            virtual void accept( Visitor const &) = 0;
            /* fournit la possibilité d'utiliser un visiteur en considérant la portée
             * constante
             */
            virtual void accept(ConstVisitor const &) const = 0;
            unsigned int id() const{return id_;}
            MusicalKey key() const{return key_;}
            size_t rythme() const{return rythme_;}
        private:
    }
    Cette classe sera dérivée en SingleNoteStaff, qui manipulera exclusivement un ensemble de SingleNoteBatment et en MulpileNoteStaff qui manipulera exclusivement des MultiNoteBatment.

    Toutes deux se baseront sur le nombre de battements dont elles sont déjà composées pour déterminer le numéro d'ordre du battement à rajouter.

    La partition, enfin, qui ne devrait, au passage, pas être un singleton agira comme un conteneur un conteneur de (pointeurs sur) AbstractStaff en se basan sur le nombre de portées dont elle est déjà composée pour déterminer l'id de la nouvelle portée.

    Grâce au retour co variant et au fait que chaque type particulier de portée concrète ne manipule qu'un type de battement particulier, il n'est nécessaire de rendre que la portée "visitable".

    En effet, si on perd le fait qu'une portée particulière (par exemple, la cinquième portée dans la partition) est destinées à un piano (par exemple), il suffira d'utiliser le pattern visiteur (sous la forme constante ou non) pour récupérer l'information, et savoir que l'on travaille soit avec une référence (éventuellement constante) sur SingleNoteStaff soit avec une référence (éventuellement constante) sur MulpileNoteStaff.

    A partir de là, on aura la certitude que tous les battements que nous pourrions récupérer seront soit des SingleNoteBatment (dans le cas d'une instance de SingleNoteStaff ) soit des MultiNoteBatment (dans le cas de MulpileNoteStaff).

    Il ne restera donc à créer que quelques foncteurs spécifiques au fait que l'on travaille soit sur des battements ne permettant qu'une note à la fois soit sur des battement qui en permettent plusieurs

    Après, bien sur, il y a toute la gestion de la représentation que l'on fait de la partition...

    Mais il me semble avoir donné suffisamment d'explications pour que tu puisses déterminer les données à utiliser et la logique à suivre pour être en mesure non seulement d'offrir une vue "graphique" de ta partition mais aussi, pourquoi pas, un rendu sonore (qui n'est, je le redis, qu'une possibilité de représentation )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Ouppsss...

    Je me suis rendu compte que j'avais fait un erreur grossière dans la définition de l'énumeration MusicalNoteName.

    Il est en effet plus embêtant qu'autre chose d'avoir les notes et leurs dérivées dans la même énumération.

    Il serait donc intéressant d'avoir l'énumération MusicalNoteName sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum MusicalNoteName
    {
        silence = -1, // (*)
        do,
        re,
        mi,
        fa,
        sol,
        la,
        si,
        notANote // permettra d'avoir un constructeur par défaut
    };
    et d'avoir une énumération supplémentaire NoteModifier proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    enum NoteModifier
    {
        noModifier = 0,
        bemol,
        diese
    };
    la note en elle-même devenant
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    class MusicalNote
    {
        public:
            /*Le constructeur par défaut sera, essentiellement, utilisé lorsqu'il
             * s'agira de gérer des collections de notes
             */
            MusicalNote() octave_(octaveUnset), name_(notANote),duration_(noDuration),modifier_(noModifier) {}
            MusicalNote( OctaveNumber octave, MusicalNoteName name, Duration duration,NoteModifier modifier):
                octave_(octave), name_(name), duration_(duration), modifier_(modifier){}
            /* les accesseurs (et uniquement eux) de tout ce beau monde */
            OctaveNumber octave() const{return octave_;}
            MusicalNoteName name() const{return name_;}
            Duration duration() const {return duration_;}
            NoteModifier modifier() const{return modifier_;}
        private:
            OctaveNumber octave_;
            MusicalNoteName name_;
            Duration duration_;
            NoteModifier modfiier_;
    };
    En veillant à s'assurer dans le constructeur que le modificateur est "valide" (qu'il existe) pour la note envisagée

    De cette manière, les énumérations OctaveNumber et MusicalNoteName permettent d'évaluer la position Y de la note par rapport à la portée, l'énumération Duration pemet d'évaluer la manière dont la note est représentée (de carré à quadruple croche) et l'énumération NoteModifier permet d'évaluer s'il faut afficher ou non un modificateur de note et, si oui, lequel (bemol ou dièse )

    Le reste de l'analyse ne changeant absolument pas

    J'ai cependant également oublié d'attirer ton attention sur un fait important en ce qui concerne les battements et les portées.

    Comme ce sont des classes qui ont sémantique d'entité, il faut les rendre non copiables et non assignables, ce qui apparait dans le code par la déclaration du constructeur par copie et de l'opérateur d'affectation comme étant delete, mais qui en C++ "pré C++11" se traduit par le fait de les déclarer dans l'accessibilité privée sans les définir.

    Comme on ne peut ni copier ni affecter les instances de ces deux classes, il va de soi qu'il faudra toujours les récupérer par référence ou par référence constante

    Enfin, je me suis rendu compte du fait que j'ai considéré, dans un premier temps, que tu remplissais les portées une à une en y insérant les battement un à un avant de passer au battement suivant ou à la portée suivante sans te donner l'occasion d'y changer quoi que ce soit une fois que la portée ou le battement est rempli.

    Cette approche est peut etre correcte si tu prévois de charger une partition déjà sérialisée, mais peut sembler particulièrement restrictive si l'idée est de créer ta propre partition, en sachant que tu peux très bien vouloir modifier une portée ou un battement "n'importe quand".

    Il sera donc peut etre intéressant de rajouter quelques fonctions "qui vont bien" dans les classes de portée et de battement afin d'avoir ce genre de fonctionnalités
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre confirmé
    Inscrit en
    Janvier 2006
    Messages
    173
    Détails du profil
    Informations forums :
    Inscription : Janvier 2006
    Messages : 173
    Par défaut
    L'idée était d'avoir une classe générique de type T et T pourrait prendre struc_note, struc_rest, struc_clef ...

Discussions similaires

  1. Réponses: 2
    Dernier message: 30/12/2009, 20h44
  2. Tableau de classe utilisant les templates
    Par RaphAstronome dans le forum Collection et Stream
    Réponses: 1
    Dernier message: 22/10/2009, 16h34
  3. Réponses: 4
    Dernier message: 21/01/2009, 14h39
  4. Réponses: 1
    Dernier message: 31/07/2008, 14h30
  5. les classes et les templates dans les plugins
    Par asoka13 dans le forum C++
    Réponses: 22
    Dernier message: 24/01/2008, 17h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo