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

Delphi Discussion :

[D6] Comment fonctionne la durée d'un fichier MIDI ?


Sujet :

Delphi

  1. #1
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut [D6] Comment fonctionne la durée d'un fichier MIDI ?
    Je suis en train de développer un petit outil de partitions (de musique), et j’essaie de générer un fichier midi pour entendre ce que je note.

    Nom : Musique.JPG
Affichages : 3410
Taille : 152,8 Ko

    En fouillant dans tout ce que j'ai pu trouver, j'obtiens un fichier midi qui fonctionne avec des notes simples.

    Quelqu'un peut-il m'expliquer comment fonctionne la durée d'une note ?

    Je n'ai pas trouvé d'information dans toutes les doc glanées sur internet, à part la liste des principales instructions.
    Il y a le note-on et le note-off, avec leurs paramètres, mais rien sur la durée.
    J'ai découvert son existence (à la durée) en décortiquant un fichier midi existant.
    Lors de mes tests, j'ai pu me rendre compte que si je ne donne pas cette information entre les instructions note-on et note-off, ça ne fonctionne plus du tout, mais je ne comprends pas pourquoi ce n'est pas un paramètre du note-on.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
       szNotes := szNotes + #0 + Chr(HexByte(MTRK_NOTE_ON + Char(nCanal))) + Note._NoteMidi + FormatTaille2(Note._nVelocite);
       szNotes := szNotes + GetDuree(Note._dDuree, Note._bPointee);      // <<-- DURÉE.
       szNotes := szNotes + Chr(HexByte(MTRK_NOTE_OFF + Char(nCanal))) + Note._NoteMidi + FormatTaille2(Note._nVelocite);
    Voici GetDuree :
    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
       function GetDuree(const dDuree: TDureeNote; const bPointee: Boolean): Char;
       begin
          case dDuree of
             dTripleCroche:
                begin
                   if bPointee then
                      Result := #3
                   else
                      Result := #2;
                end;
     
             dDoubleCroche:
                begin
                   if bPointee then
                      Result := #6
                   else
                      Result := #4;
                end;
     
             dCroche:
                begin
                   if bPointee then
                      Result := #12
                   else
                      Result := #8;
                end;
     
             dNoire:
                begin
                   if bPointee then
                      Result := #24
                   else
                      Result := #16;
                end;
     
             dBlanche:
                begin
                   if bPointee then
                      Result := #48
                   else
                      Result := #32;
                end;
     
             dRonde:
                begin
                   if bPointee then
                      Result := #96
                   else
                      Result := #64;
                end;
     
    //         dCarree:
    //            begin
    //               if bPointee then
    //                  Result := #192
    //               else
    //                  Result := #128;
    //            end;
          else
             Result := #16;
          end;
       end;


    Question subsidiaire :
    En utilisant les fonctions de MMSytem, comment donner cette durée ?
    Pour l'instant, j'ai du mettre un Sleep(500) entre le note-on et le note-off pour avoir le temps d'entendre la note.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    midiOutShortMsg(MidiOut, ($7F * $10000) + (Ord(Note.GetNoteMidi) * $100) + ($90));
    Sleep(500);
    midiOutShortMsg(MidiOut, ($7F * $10000) + (Ord(Note.GetNoteMidi) * $100) + ($80));
    Merci d'avance.


    PS :
    Je sais bien que ma question porte plus sur la structure du fichier midi que sur Delphi, mais Alcatîz m'a autorisé à poster ici.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  2. #2
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Bonjour.

    En Midi, la durée d'une note est fonction du PPQN (Pulses per quarter note) et du BPM (Beats per minute).
    Pour ton usage, je te conseille un PPQN = 96 (donc Noire=96, Croche=48, Double=24...).
    Le BPM, c'est le tempo exprimé en nombre de noires par minute.

    Exemple : quelle durée pour une noire avec un tempo de 120 ?
    BPM * PPQN = 120 * 96 = 11520 "unités de temps" par minute.
    Une minute = 60000 ms.
    Donc 60000 / 11520 = 5,208 ms.
    Si tu multiplies par la valeur de ta note : 5,208 * 96 = 499,99 ms, soit 2 noires par seconde.
    Formule : Durée note = 60000 / (BPM * PPQN)

    A ma connaissance, l'unité mmSystem n'a pas de fonctions concernant la durée.
    La durée = le temps entre Note_On et Note_off.

    Si tu ne joues que des notes séparées, tu peux régler ton sleep en te servant de la formule.
    Sinon, c'est une autre paire de manches. (créer une liste des événements midi, jouer à l'intérieur d'une boucle en utilisant p.ex gettickcount...).

    Il y a peut-être sur le web des composants MIDI facilitant la tâche. Je ne connais de nom que la librairie BASS (jamais testé).

    Bon code.

    Thierry

    [Correction de la formule] Durée:= (60000 / (BPM * PPQN)) * Type Note

  3. #3
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 072
    Points : 15 462
    Points
    15 462
    Billets dans le blog
    9
    Par défaut
    Bonjour !

    Pour les fichiers je ne connais pas la réponse. En revanche je peux confirmer que dans tous les exemples utilisant l'unité MMSystem que j'ai pu consulter, c'est toujours la procédure Sleep qui est utilisée.

    Puisque tu utilises Delphi 6, tu seras peut-être intéressé par Power Sequencer Plus.

    Bonne continuation ! Je suis curieux de voir la suite. Je m'étais intéressé au sujet il y a quelque temps et puis j'ai été pris par d'autres choses.
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  4. #4
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Déjà merci pour les explications.
    Je vais potasser ça tranquillement ce week-end, et je reviens vous tenir au courant de l'avancement ...

    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  5. #5
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Voici une petite function pour encoder un message midi.
    Plus simple que ton code : ($7F * $10000) + (Ord(Note.GetNoteMidi) * $100) + ($90)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const
       NOTE_ON = $90;
       NOTE_OFF = $80;
     
     
    function MIDIEncodeMessage(Msg, Param1, Param2: integer): integer;
    // alignement windows
    begin
       result := Msg + (Param1 shl 8) + (Param2 shl 16);
    end;
    Dans le cas d'une note :
    Msg = NOTE_ON ou NOTE_OFF
    Param1 = numéro de la note
    Param2 = la vélocité

    Cordialement
    Thierry

  6. #6
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 430
    Points
    28 430
    Par défaut
    cela fait très très longtemps que je n'ai plus touché aux fichiers MIDI...ça remonte au Turbo Pascal avec un player MIDI sur Speaker Interne ou SoundBlaster/AdLib, que de souvenirs

    j'ai regardé rapidement, je récupère des "Ticks" dans l'entête MThd en me basant sur un "Tempo" de 600...ensuite le temps d'attente c'est le "(Timer*Tempo) div Ticks" où Timer provient d'une piste...ou un truc comme ça

    le code est vieux de 14 ans MID2.PAS semble être le plus simple à suivre...

    et XMID.PAS permet de rechercher un fichier midi dans n'importe quelle fichier...à l'époque du DOS on trouvait en effet des fichiers MIDI inclus dans l’exécutable des jeux
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  7. #7
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    En Midi, la durée d'une note est fonction du PPQN (Pulses per quarter note) et du BPM (Beats per minute).
    Pour ton usage, je te conseille un PPQN = 96 (donc Noire=96, Croche=48, Double=24...).
    Le BPM, c'est le tempo exprimé en nombre de noires par minute.

    Exemple : quelle durée pour une noire avec un tempo de 120 ?
    BPM * PPQN = 120 * 96 = 11520 "unités de temps" par minute.
    Une minute = 60000 ms.
    Donc 60000 / 11520 = 5,208 ms.
    Si tu multiplies par la valeur de ta note : 5,208 * 96 = 499,99 ms, soit 2 noires par seconde.
    Formule : Durée note = 60000 / (BPM * PPQN)
    Je pense avoir à peu près compris la théorie.
    Par contre, en pratique c'est pas la même chose.

    Le tempo, je l'ai bien identifié et initialisé à 120.
    Citation Envoyé par doc
    81 Tempo Setting
    Par défaut, le tempo d'un fichier MIDI est de 120 battements par minute, donc d'une durée d'une demi-seconde par noire, soit 500.000 microsecondes (µs) codés en trois octets valant de 0 à 255.
    xFF - x51 - x03 - Octet1 - Octet2 - Octet3
    65 battements par minute= 60.000.000µs/65= 923076 (arrondi par défaut), qui vaut x0E15C4, soit les octets x0E - x15 - xC4
    Mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    const
       MTRK_TEMPO = #81;
    ...
          szTempo := FormatTaille6(60000000 div _nTempo);      szLongueurTempo := #3;      // Battements (noires) par minute.
          szMtrk1 := szMtrk1 + #0#255 + MTRK_TEMPO + szLongueurTempo + szTempo;
    Par contre, je n'ai pas trouvé l'autre valeur (PPQN).

    La seule autre valeur que j'initialise (dans l'en-tête MThd), c'est ce qui dans certaines doc est appelée rythme, et qui ne peut pas prendre 96 : c'est trop rapide.
    C'est tout juste si les noires ont le temps d'être jouées. Les valeurs raisonnables sont plutôt entre 10 et 30 (ou alors, j'ai raté quelque chose).

    Voici ce que disent les docs que j'ai pu trouver :
    Citation Envoyé par doc
    La plage d'entête :
    Jusqu'à présent invariablement de 14 octets, la première plage de donnée renseigne sur la structure du fichier et de la plus petite unité de temps considérée :

    · 2 octets - nombre division de la noire (quart de ronde):
    0AAAAAAA AAAAAAAA deux octets codent simplement (en général 120, 240, 480, 960...).
    100BBBBB CCCCCCCC codée de façon SMPTE. Signification des deux octets:
    100BBBBB les cinq bits faibles donne 24, 25, 29 (=29,97 FPS) ou 30 images par seconde.
    CCCCCCCC nombre de subdivisions d'une image (frame).
    Citation Envoyé par doc
    <division> 2 bytes
    unit of time for delta timing. If the value is positive, then it represents the units per beat. For example, +96 would mean 96 ticks per beat. If the value is negative, delta times are in SMPTE compatible units.
    Citation Envoyé par doc
    Time DivisionThe third and final word in the MIDI header chunk is a bit more complicated than the first two. It contains the time division used to decode the track event delta times into "real" time. This value is represents either ticks per beat or frames per second. If the top bit of the word (bit mask 0x8000) is 0, the following 15 bits describe the time division in ticks per beat. Otherwise the following 15 bits (bit mask 0x7FFF) describe the time division in frames per second. Ticks per beat translate to the number of clock ticks or track delta positions (described in the Track Chunk section) in every quarter note of music. Common values range from 48 to 960, although newer sequencers go far beyond this range to ease working with MIDI and digital audio together. Frames per second is defined by breaking the remaining 15 bytes into two values. The top 7 bits (bit mask 0x7F00) define a value for the number of SMPTE frames and can be 24, 25, 29 (for 29.97 fps) or 30. The remaining byte (bit mask 0x00FF) defines how many clock ticks or track delta positions there are per frame. So a time division example of 0x9978 could be broken down into it's three parts: the top bit is one, so it is in SMPTE frames per second format, the following 7 bits have a value of 25 (0x19) and the bottom byte has a value of 120 (0x78). This means the example plays at 24 frames per second SMPTE time and has 120 ticks per frame.
    Sinon, il y a ce qui s'appelle la mesure :
    Citation Envoyé par doc
    88 Time Signature
    Définit la mesure (2/4, 3/4, 3/8, 4/4, 5/4, 7/8...)
    xFF - x58 - x04 - numérateur - dénominateur - métronome - ntc
    · numérateur: le nombre de temps
    · dénominateur: le rang de la a division de la ronde: x00=ronde, x01= blanche, x02=noire, x03=croche...
    · métronome: x18 (24) pour la noire, x30 (48) pour la croche...
    · ntc: x08, nombre de triples coches par 24 clics
    par défaut: (4/4) x04-x02-x18-x08
    Je l'ai initialisée comme ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    const
       MTRK_MESURE = #88;
       MTRK_DUREE_NOIRE = #8;      // Durée d'une noire 8/32 = 1/4.
    ...
          szMesure := #4#2#24 + MTRK_DUREE_NOIRE;      szLongueurMesure := #4;      // Métronome.         
          szMtrk1 := szMtrk1 + #0#255 + MTRK_MESURE + szLongueurMesure + szMesure + #176;
    Déjà que c'est pas simple à comprendre, la plupart des doc sont (hélas) en anglais, et c'est encore moins clair.

    La bonne nouvelle, c'est que grâce à vos explications, j'ai compris ce qu'était cette valeur delta placée devant les note-on et les note-off.
    C'est la fameuse durée avant chaque instruction. Désormais, la gestion des silences et des liaisons de prolongation fonctionne bien.
    (la gestion des divisions exceptionnelles, ça va être une autre paire de manche ...)

    Par contre, lors de la lecture de mon fichier midi généré (avec un TMediaPlayer), j’essaie de déplacer en même temps un curseur de lecture sur ma partition (avec un TTimer), mais c'est toujours pas bon.
    Je n'arrive pas à lui donner le bon intervalle (ou alors, il y a une faille ailleurs):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
       Timer.Interval := Cardinal(60000 div ((PageControl.ActivePage as TOngletMusique)._nTempo * (PageControl.ActivePage as TOngletMusique)._nRythme) div 8);



    Citation Envoyé par Roland Chastain Voir le message
    Pour les fichiers je ne connais pas la réponse. En revanche je peux confirmer que dans tous les exemples utilisant l'unité MMSystem que j'ai pu consulter, c'est toujours la procédure Sleep qui est utilisée.
    D'accord, je continu comme ça.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
          midiOutShortMsg(MidiOut, EncodeMessageMIDI2(NOTE_ON, Ord(Note.GetNoteMidi), $7F));
          Sleep(Trunc((60000 / ((PageControl.ActivePage as TOngletMusique)._nTempo * (PageControl.ActivePage as TOngletMusique)._nRythme)) * Ord(Note.GetDureeMidi)));
          midiOutShortMsg(MidiOut, EncodeMessageMIDI2(NOTE_OFF, Ord(Note.GetNoteMidi), $7F));



    Citation Envoyé par ThWilliam Voir le message
    Voici une petite function pour encoder un message midi.
    Plus simple que ton code : ($7F * $10000) + (Ord(Note.GetNoteMidi) * $100) + ($90)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
       const
          NOTE_ON = $90;
          NOTE_OFF = $80;
     
       function MIDIEncodeMessage(Msg, Param1, Param2: integer): integer;
       // alignement windows
       begin
          result := Msg + (Param1 shl 8) + (Param2 shl 16);
       end;
    Dans le cas d'une note :
    Msg = NOTE_ON ou NOTE_OFF
    Param1 = numéro de la note
    Param2 = la vélocité
    C'est effectivement plus lisible, mais je ne comprends pas mieux.
    Tout ce qui est manipulation de bits, je n'y connais rien. Le code que j'utilisais, je l'ai trouvé et utilisé tel quel.
    Je devine que ça mets les informations bout à bout, mais je ne sais pas comment. En tout cas, ça marche.




    Citation Envoyé par Paul TOTH Voir le message
    J'ai regardé rapidement, je récupère des "Ticks" dans l'entête MThd en me basant sur un "Tempo" de 600...ensuite le temps d'attente
    c'est le "(Timer*Tempo) div Ticks" où Timer provient d'une piste...ou un truc comme ça.
    Ca correspond à quoi ce "tick" ?
    Citation Envoyé par Paul TOTH Voir le message
    Le code est vieux de 14 ans MID2.PAS semble être le plus simple à suivre ...
    Simple n'est pas le mot que j'aurais employé.
    C'est trop optimisé pour moi. Des sous-titres seraient les bienvenus.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  8. #8
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Bonjour.

    Concernant la fonction MIDIEncodeMessage : elle ne fait que coder 3 valeurs byte dans un integer (la valeur de Msg, Param1, Param2 va de 0 à 127). La manière d'organiser les octets dépend de l'OS et du CPU. Windows encode "à l'envers".
    Exemple : un TColor est en fait une variable entière sur 4 octets : 1 octet pour le rouge (0 à 255), 1 pour le vert, 1 pour le bleu + 1 (inutilisé, mais pourrait servir de couche alpha pour la transparence).Mais au lieu de faire RRVVBBAA, Windows fait AABBVVRR. C'est de l'alignement Little Endian. Voir http://fr.wikipedia.org/wiki/Endianness

    Concernant le PPQN, il ne s'agit pas de le trouver mais bien de le déterminer toi-même : c'est la valeur d'une noire.
    Les valeurs courantes sont 48, 96, 192.
    En prenant un PPQN de 96 :
    noire = 96
    noire pointée = 96 + (96/2) = 144
    croche = 96/2 = 48
    double croche = 96/4 = 24
    triple croche = 96/8 = 12
    quadruple croche = 96/16 = 6
    quintuple croche = 96/32 = 3
    Donc tu as une "précision maximale" de 1/3 de quintuple croche, ce qui parait énorme, mais qui n'est pas encore suffisant si l'on veut donner un côté humain : l'homme ne joue jamais pile dans les temps, c'est ce qui fait la beauté de la musique (compare une batterie acoustique avec une boite à rythme !!!).
    Plus le PPQN est élevé, plus la précision est grande. Mais dans ton cas, 96 est suffisant.

    Tu peux pour ta facilité créer des constantes

    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
    const
       PPQN = 96;
       noire = 96;
       noirep = 144;
       blanche = 192;
       croche = 48;
       //...
     
    var
      Tempo: integer;
       Note: byte;
       Duree: integer;
     
    Tempo:= 120;
    // joue une noire hauteur 60
    Note:= 60;
    Duree:= noire;
    midiOutShortMsg(MidiOut, MidiEncodeMessage(NOTE_ON, Note, 127));
    Sleep((60000 / (Tempo * PPQN)) * Duree);
    midiOutShortMsg(MidiOut, MidiEncodeMessage(NOTE_OFF, Note, 127));
    Pour déplacer le curseur sur ta partition, il faut jouer les nodes midi dans un thread synchronisé avec une procedure.
    Je me souviens avoir fait jadis un code de ce genre. Si cela t'intéresse, dis-le moi.

    Une question : veux-tu aller jusqu'à créer un véritable fichier midi ? si oui, on sort du cadre des Sleep...

    Cordialement
    Thierry

  9. #9
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    Pour déplacer le curseur sur ta partition, il faut jouer les notes midi dans un thread synchronisé avec une procédure.
    Je me souviens avoir fait jadis un code de ce genre. Si cela t'intéresse, dis-le moi.

    Une question : veux-tu aller jusqu'à créer un véritable fichier midi ? si oui, on sort du cadre des Sleep...
    C'est vrai que je n'ai pas tout expliqué clairement.
    A l'origine, je voulais me faire un petit outils pour composer des partitions et les imprimer (parce que tracer des partitions à la règle sur une feuille, c'est dur ).
    Ensuite, je me suis dit que ça serait bien de pouvoir entendre la mélodie : donc, je génère à la volée un fichier midi que je donne à manger à un TMediaPlayer (ça marche bien pour des notes simples).
    Ensuite, pendant que je pose mes notes, je me suis dit que ça serait bien d'entendre ces notes au moment où je les pose, au lieu d'être obligé de jouer toute la mélodie (à travers le fichier midi).
    Donc, c'est là que j'ai découvert l'existence des fonctions midiOutShortMsg pour jouer la note cliquée. A part la durée que je dois peaufiner, ça fonctionne, mais dès la fin du Sleep, l'instruction note-off tombe comme un couperet. Pour les notes brèves, l'arrêt est sec, contrairement avec un fichier midi qui gère bien les durées.

    Le curseur de lecture se déplace sur ma partition (graphique) pendant que le fichier midi est joué. Si je maitrisais les durées, ça serait à peu prêt synchrone.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  10. #10
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Pour les notes brèves, l'arrêt est sec, contrairement avec un fichier midi qui gère bien les durées.
    Si tu avais un fichier midi au tempo 120 qui se termine par une triple croche, l'arrêt serait tout aussi sec.

    Si je maitrisais les durées, ça serait à peu prêt synchrone.
    Tu sembles maitriser les durées, puisque tu arrives à créer un fichier midi à partir de ta partition (si le code est de toi, chapeau !!!).
    Je n'aime pas le terme "à peu prêt", pas pcq cela devrait être "à peu près", mais bien pcq cela devrait être tout à fait synchrone !
    Pour cela, l'emploi d'un TMediaPlayer n'est pas la bonne formule.
    Je te suggère, pour l'écoute de ta partition, de "bricoler" ton propre lecteur simplement avec les fonctions Api.
    Pour cela, comme je te l'ai déjà mentionné, le play se fait dans un thread séparé qui est synchronisé avec une procédure de ton thread principal (qui, p.ex, placerait un repère rouge sur la note jouée).

    Il y a quelques années, lorsque je donnais des cours de guitare à des débutants, j'ai fait sous Delphi7 un programme permettant d'entrer les notes à partir d'un manche de guitare virtuel.
    Lors de la lecture (player "bricolage maison"), les notes jouées sont dessinées sur le manche de façon synchrone.
    Ensuite, je sortais la partition avec Lylipond (logiciel de "programmation" de partition à partir d'un simple fichier texte) et éventuellement le fichier midi avec ce même logiciel.

    Voici un exemple de ce que cela donne. C'est un fichier midi, mais tout à fait conforme à mon player.
    Tu remarqueras les accords, ainsi que les notes basses "tenues".
    Yesterday.zip

    Cordialement
    Thierry

  11. #11
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    Si tu avais un fichier midi au tempo 120 qui se termine par une triple croche, l'arrêt serait tout aussi sec.
    J'ai pas testé. Je testerais pour comparer.

    Citation Envoyé par ThWilliam Voir le message
    Tu sembles maitriser les durées, puisque tu arrives à créer un fichier midi à partir de ta partition (si le code est de toi, chapeau !!!).
    Il est de moi.
    Mais, j'ai miséré pendant des semaines.

    Citation Envoyé par ThWilliam Voir le message
    Je n'aime pas le terme "à peu prêt", pas pcq cela devrait être "à peu près", mais bien pcq cela devrait être tout à fait synchrone !
    Pour cela, l'emploi d'un TMediaPlayer n'est pas la bonne formule.
    Je te suggère, pour l'écoute de ta partition, de "bricoler" ton propre lecteur simplement avec les fonctions Api.
    Pour cela, comme je te l'ai déjà mentionné, le play se fait dans un thread séparé qui est synchronisé avec une procédure de ton thread principal (qui, p.ex, placerait un repère rouge sur la note jouée).
    Bon, je vais creuser dans cette direction, et voir si je peux faire autant de choses avec les API qu'en midi.
    Je regarde ça ce week-end, et je te redis.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  12. #12
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    Pour les notes brèves, l'arrêt est sec, contrairement avec un fichier midi qui gère bien les durées.
    Si tu avais un fichier midi au tempo 120 qui se termine par une triple croche, l'arrêt serait tout aussi sec.
    J'ai fait l'essai, et le fichier midi lisse les notes, même celles qui sont brèves. Alors qu'avec les API, la triple-croche est tellement brève, qu'elle n'est même pas jouée.
    Pour te donner un aperçu, je suis passé sur mon ancien PC, équipé d'une vraie carte son (audigy player) pour enregistrer le rendu avec les API. Et là, surprise : les notes n'étaient plus hachées. Apparemment, la gestion midi nécessite plus qu'un simple chipset son.
    (je n'ai pas trouvé comment mettre des fichiers en pièces-jointe; à moins que les fichiers son soient interdits)

    Citation Envoyé par ThWilliam Voir le message
    Pour déplacer le curseur sur ta partition, il faut jouer les nodes midi dans un thread synchronisé avec une procedure.
    ...
    Je te suggère, pour l'écoute de ta partition, de "bricoler" ton propre lecteur simplement avec les fonctions Api.
    Bon j'ai fait ça (vite fait). La synchronisation (visuelle) du curseur est bonne.
    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
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    procedure TThreadJoueNote.JouerNote;
       function EncodeMessageMIDI1(const nMsg, nParam1: Integer): Integer;
       begin
          Result := nMsg + (nParam1 shl 8);
       end;
     
       function EncodeMessageMIDI2(const nMsg, nParam1, nParam2: Integer): Integer;
       begin
          Result := nMsg + (nParam1 shl 8) + (nParam2 shl 16);
       end;
    const
       INSTRUMENT = $C0;
       NOTE_ON = $90;
       NOTE_OFF = $80;
    var
       nDuree: Cardinal;
       Res: DWORD;
       MidiOut: hMidiOut;
       szMsg: String;
    begin
       // Si silence. 
       if _Silence <> nil then
       begin
          nDuree := 0;
          case _Silence.GetDuree of
             dHuitiemeDeSoupir:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN div 8));
             dQuartDeSoupir:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN div 4));
             dDemiSoupir:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN div 2));
             dSoupir:
                nDuree := ((60000 div (_nTempo * PPQN)) * PPQN);
             dDemiPause:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN * 2));
             dPause:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN * 4));
             dBatonDePause:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN * 8));
          end;
     
          Sleep(nDuree);
       end
       else
       begin
          // Ouverture du port Midi.
       //   Res := midiOutOpen(@MidiOut, MIDI_MAPPER, Handle, 0, CALLBACK_WINDOW);
          Res := midiOutOpen(@MidiOut, MIDI_MAPPER, 0, 0, CALLBACK_NULL);
          if Res <> 0 then
          begin
             case Res of
                MIDIERR_NODEVICE:
                   szMsg := 'Erreur :  pas de port MIDI trouvé !';
                MMSYSERR_ALLOCATED:
                   szMsg := 'Erreur :  la ressource spécifiée est déjà allouée !';
                MMSYSERR_BADDEVICEID:
                   szMsg := 'Identifiant d''élément en dehors des bornes !';
                MMSYSERR_INVALPARAM:
                   szMsg := 'Erreur :  poiteur ou structure invalide !';
                MMSYSERR_NOMEM:
                   szMsg := 'Erreur :  le système ne peut pas allouer de mémoire, ou la mémoire est verrouillée !';
                MIDIERR_NOMAP:
                   szMsg := 'Midi mapper non trouvé';
             else
                szMsg := 'Erreur inconnue !';
             end;
     
             Application.MessageBox(PChar(szMsg), 'Erreur', MB_OK);
             Terminate;
             Exit;
          end;
     
          // Instrument.
          midiOutShortMsg(MidiOut, EncodeMessageMIDI1(INSTRUMENT, _nInstrumentMidi));
     
          // Joue la note.
          midiOutShortMsg(MidiOut, EncodeMessageMIDI2(NOTE_ON, Ord(_Note.GetNoteMidi), $7F));
          nDuree := 0;
          case _Note.GetDuree of  
             dTripleCroche:
                nDuree := ((60000 div (_nTempo * PPQN)) * ((PPQN div 8) + IfThen(_Note._bPointee, (PPQN div 16))));
             dDoubleCroche:
                nDuree := ((60000 div (_nTempo * PPQN)) * ((PPQN div 4) + IfThen(_Note._bPointee, (PPQN div 8))));
             dCroche:
                nDuree := ((60000 div (_nTempo * PPQN)) * ((PPQN div 2) + IfThen(_Note._bPointee, (PPQN div 4))));
             dNoire:
                nDuree := ((60000 div (_nTempo * PPQN)) * (PPQN + IfThen(_Note._bPointee, (PPQN div 2))));
             dBlanche:
                nDuree := ((60000 div (_nTempo * PPQN)) * ((PPQN * 2) + IfThen(_Note._bPointee, PPQN)));
             dRonde:
                nDuree := ((60000 div (_nTempo * PPQN)) * ((PPQN * 4) + IfThen(_Note._bPointee, (PPQN * 2))));
             dCarree:
                nDuree := ((60000 div (_nTempo * PPQN)) * ((PPQN * 8) + IfThen(_Note._bPointee, (PPQN * 4))));
          end;
          Sleep(nDuree);
          midiOutShortMsg(MidiOut, EncodeMessageMIDI2(NOTE_OFF, Ord(_Note.GetNoteMidi), $7F));
     
          // Fermeture du port Midi.
          midiOutShortMsg(MidiOut, $00007BB0);
          midiOutClose(MidiOut);
       end;
     
       Terminate;
    end;
    Faut que je regarde si je peux faire aussi bien qu'avec le fichier midi; voire mieux, car je n'ai pas encore trouvé comment jouer deux notes en même temps, ni comment jouer plusieurs instruments en même temps (dans un fichier midi). J'ai essayé de décortiquer ton fichier midi (Yesterday.mid), mais les note-on et note-off ne sont pas employés de la façon que je connais.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  13. #13
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Bonjour.

    Pour jouer plusieurs notes en même temps, il ne faut pas utiliser Sleep (sauf si les notes sont de durée égale).

    Si cela peut t'aider, voici le code (unit ThMidi) que j'avais pondu pour l'écoute des notes. J'ai fait une petite démo pour que tu comprennes le principe.
    Bien évidemment, tu devras trouver un moyen auto d'ajouter les notes d'après ta partition.

    Demo TMidi.zip

    Je pars d'un PPQN de 96.
    Pour la durée, je multiplie par 10. Donc une noire dure 960 ms, c'est à peu près un tempo de 60bmp.
    Ce qui, pour un 4/4 nous donne une mesure = 3840 ms.
    Durée que je raccourcis ou augmente selon le tempo souhaité.


    Cordialement
    Thierry

  14. #14
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    Pour jouer plusieurs notes en même temps, il ne faut pas utiliser Sleep (sauf si les notes sont de durée égale).
    Effectivement, sans sleep, mais avec une boucle d'attente comme tu fais, ça va beaucoup mieux.

    Citation Envoyé par ThWilliam Voir le message
    Si cela peut t'aider, voici le code (unit ThMidi) que j'avais pondu pour l'écoute des notes. J'ai fait une petite démo pour que tu comprennes le principe.
    Ça m'a beaucoup aidé.
    Au début, je ne comprenais pas, et c'est en faisant par moi-même que j'ai compris tes choix et la logique utilisée.
    Maintenant, j'arrive à jouer les notes de ma partition avec les API, et le curseur est bien évidemment synchrone.

    Nom : Musique.JPG
Affichages : 1477
Taille : 278,6 Ko
    J'ai également réussi à lire deux notes en même temps, avec les API, et avec un fichier midi.

    Maintenant, il faut que j'arrive à bien gérer les liaisons de prolongation, pour avoir des notes d'accompagnement qui durent le temps qu'il faut.
    Et après, je vais attaquer le fait de jouer plusieurs instruments en même temps.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  15. #15
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    Et après, je vais attaquer le fait de jouer plusieurs instruments en même temps.
    Et dire qu'au départ, tu ne voulais entendre que la mélodie !

    Pour la création du fichier midi : si tu as envie de partager ton code, n'hésite pas à déposer la source sur ce site.

  16. #16
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par ThWilliam Voir le message
    Et dire qu'au départ, tu ne voulais entendre que la mélodie !
    Bin oui, mais la mélodie est parfois composées de plusieurs notes en même temps, et parfois même d'un accompagnement d'un autre instrument. Alors j'ai pas le choix ...

    Citation Envoyé par ThWilliam Voir le message
    Pour la création du fichier midi : si tu as envie de partager ton code, n'hésite pas à déposer la source sur ce site.
    Ça me dérange pas. C'est effectivement pas évident de trouver des infos précises et détaillées sur le format midi.
    Par contre, je ne garanti pas que mon code soit un modèle de ce qu'il faut faire (mais il marche ).
    Je le met à la suite de mon post, ou il y a un endroit plus approprié ?
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  17. #17
    Membre chevronné

    Homme Profil pro
    au repos
    Inscrit en
    Février 2014
    Messages
    429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : au repos

    Informations forums :
    Inscription : Février 2014
    Messages : 429
    Points : 1 884
    Points
    1 884
    Par défaut
    ou il y a un endroit plus approprié ?
    Forum Delphi: rubrique Télécharger.

  18. #18
    Modérateur
    Avatar de tourlourou
    Homme Profil pro
    Biologiste ; Progr(amateur)
    Inscrit en
    Mars 2005
    Messages
    3 858
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 61
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Biologiste ; Progr(amateur)

    Informations forums :
    Inscription : Mars 2005
    Messages : 3 858
    Points : 11 301
    Points
    11 301
    Billets dans le blog
    6
    Par défaut
    Bonjour, et merci pour le partage.

    La rubrique Contribuez est idéale pour ce faire !
    Delphi 5 Pro - Delphi 11.3 Alexandria Community Edition - CodeTyphon 6.90 sous Windows 10 ; CT 6.40 sous Ubuntu 18.04 (VM)
    . Ignorer la FAQ Delphi et les Cours et Tutoriels Delphi nuit gravement à notre code !

  19. #19
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Citation Envoyé par tourlourou Voir le message
    Bonjour, et merci pour le partage.

    La rubrique Contribuez est idéale pour ce faire !
    C'est fait :
    http://www.developpez.net/forums/d14...d-liste-notes/
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

  20. #20
    Expert éminent
    Avatar de Lung
    Profil pro
    Analyste-programmeur
    Inscrit en
    Mai 2002
    Messages
    2 664
    Détails du profil
    Informations personnelles :
    Âge : 43
    Localisation : France, Haute Savoie (Rhône Alpes)

    Informations professionnelles :
    Activité : Analyste-programmeur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2002
    Messages : 2 664
    Points : 6 965
    Points
    6 965
    Par défaut
    Nom : # Musique liaison.jpg
Affichages : 1347
Taille : 173,9 Ko

    Bon, j'ai enfin (presque) réussi à jouer une mélodie et son accompagnement. J'ai mis du temps avant de comprendre qu'il fallait d'abord les gérer séparément.
    Par contre, les API ne veulent pas jouer les notes inférieures à la blanche dans l'accompagnement. J'ai transcrit l'algorithme dans le fichier midi, et lui me joue bien la mélodie et l'accompagnement (toutes les notes). Par contre, les liaisons de prolongations ne fonctionnent plus ...
    Mais, je pense que je tiens le bon bout.
    L'urgent est fait, l'impossible est en cours, pour les miracles prévoir un délai. ___ Écrivez dans un français correct !!

    C++Builder 5 - Delphi 6#2 Entreprise - Delphi 2007 Entreprise - Delphi 2010 Architecte - Delphi XE Entreprise - Delphi XE7 Entreprise - Delphi 10 Entreprise - Delphi 10.3.2 Entreprise - Delphi 10.4.2 Entreprise - Delphi 11.1 Entreprise
    OpenGL 2.1 - Oracle 10g - Paradox - Interbase (XE) - PostgreSQL (15.4)

Discussions similaires

  1. Comment fonctionne le fichier java.policy
    Par iaiiai dans le forum Langage
    Réponses: 1
    Dernier message: 07/11/2012, 20h29
  2. Réponses: 5
    Dernier message: 22/01/2007, 17h54
  3. [TP]comment creer une disquette bootable (les fichiers)
    Par ludovic5532 dans le forum Turbo Pascal
    Réponses: 5
    Dernier message: 25/10/2003, 18h46
  4. Réponses: 5
    Dernier message: 20/08/2002, 18h01

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