Publicité

Annonce

Réduire
Aucune annonce.

Communication MIDI en C#

Réduire
X
 
  • Filtre
  • Heure
  • Afficher
Tout nettoyer
nouveaux messages

  • #16
    Bon, j'ai retrouvé du très vieux code (2000 ?), en C++ (un embryon d'un truc appelé "Triton Explorer").
    Testé à l'époque : j'envoyais un sysex pour explorer le réseau MIDI, et je détectais un Triton.

    Donc les sysexes fonctionnent correctement avec ce code.

    Attention: bien allouer / désallouer des buffers pour les sysexes avec HeapAlloc() / HeapFree() !!! (en relisant mon code de débutant en C++ (snif), j'ai l'impression qu'un new/delete devraient aussi convenir, mais que je passais des buffers provenant de la pile, ce qui fait planter le pilote MIDI une fois qu'on a quitté la fonction, allez savoir pourquoi :oups)

    D'ici quelques mois, j'aurai réutilisé ce fragment, ou pas (je pense plutôt passer par quelque chose de mieux testé, tel RTMidi)

    Code:
    #ifndef __MIDIDRIVER_H__
    #define __MIDIDRIVER_H__
    
    #include <mmsystem.h>
    
    #include "charlib.h"
    
    class CMidiOut {
    private:
      int iMidiDevice;
      HMIDIOUT handle;
    
      void Open(int iMidiOutDevice);
      void Close(void);
    public:
      CMidiOut(int iMidiOutDevice);
      ~CMidiOut(void);
    
      operator bool(void) { return (handle != 0); }
      bool SendSysex(const CChunk<unsigned char> *C);
    };
    
    const int maxsysx = 100;
    
    class CMidiIn {
    private:
      int iMidiDevice;
      HMIDIOUT handle;
      MIDIHDR midiHdr;
      CChunkList<unsigned char> cursysex;
    public:
      CChunkList<unsigned char> sysexs;
      CMidiIn(int bufsize = 1024);
      ~CMidiIn();
      void Start(int iMidiDevice);
      void Stop(void);
    
      friend void CALLBACK midiCallback(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2);
    };
    
    CChunk<unsigned char>* SysexDialog(int iMidiOutDevice, int iMidiInDevice, CChunk<unsigned char> *SysexOut, unsigned timeout = 200);
    bool TestSysexConnection(int iMidiOutDevice, int iMidiInDevice, unsigned timeout = 200);
    
    #endif
    Code:
    #include <vcl.h>
    
    #include "MidiDriver.h"
    #include "sysex.h"
    
    //---------------------------------------------------------------------------
    
    void PrintMidiOutErrorMsg(unsigned long err)
    {
      char buffer[1024];
    
      switch (midiOutGetErrorText(err, buffer, 1024)) {
        case 0:
          ShowMessage(buffer);
          break;
        case MMSYSERR_BADERRNUM:
          ShowMessage("Strange error number returned!");
          break;
        case MMSYSERR_INVALPARAM:
          ShowMessage("Specified pointer is invalid!");
          break;
        default:
          ShowMessage("Unable to allocate/lock memory!");
      }
    }
    
    //---------------------------------------------------------------------------
    
    CMidiOut::CMidiOut(int iMidiOutDevice) {
      Open(iMidiOutDevice);
    }
    
    CMidiOut::~CMidiOut() {
      Close();
    }
    
    void CMidiOut::Open(int iMidiOutDevice) {
      unsigned err;
      err = midiOutOpen(&handle, iMidiDevice = iMidiOutDevice, 0, 0, CALLBACK_NULL);
      if (err)
        PrintMidiOutErrorMsg(err);
    }
    
    void CMidiOut::Close(void) {
      if (handle) {
        unsigned err;
        err = midiOutClose(handle);
        if (err)
          PrintMidiOutErrorMsg(err);
      }
      handle = 0;
    }
    
    bool CMidiOut::SendSysex(const CChunk<unsigned char> *C) {
      MIDIHDR midiHdr;
      bool res = false;
      unsigned err;
    
      if (C && C->len) {
        midiHdr.lpData = C->buf;
        midiHdr.dwBufferLength = C->len;
        midiHdr.dwFlags = 0;
    
        err = midiOutPrepareHeader(handle, &midiHdr, sizeof(MIDIHDR));
        if (err)
          PrintMidiOutErrorMsg(err);
        else {
          midiOutLongMsg(handle, &midiHdr, sizeof(MIDIHDR));
          while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR)))
          ;
          res = true;
        }
      }
      
      return res;
    }
    
    //---------------------------------------------------------------------------
    
    void PrintMidiInErrorMsg(unsigned long err)
    {
      char buffer[1024];
    
      switch (midiInGetErrorText(err, buffer, 1024)) {
        case 0:
          ShowMessage(buffer);
          break;
        case MMSYSERR_BADERRNUM:
          ShowMessage("Strange error number returned!");
          break;
        case MMSYSERR_INVALPARAM:
          ShowMessage("Specified pointer is invalid!");
          break;
        default:
          ShowMessage("Unable to allocate/lock memory!");
      }
    }
    
    //---------------------------------------------------------------------------
    
    void CALLBACK midiCallback(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
      switch (uMsg) {
        case MIM_LONGDATA: {
          unsigned err;
          LPMIDIHDR lpMidiHeader  = (LPMIDIHDR)dwParam1;
          unsigned char *ptr = (unsigned char *)(lpMidiHeader->lpData);
          unsigned bytes = lpMidiHeader->dwBytesRecorded;
          if (bytes) {
            CMidiIn *recorder = (CMidiIn*) dwInstance;
            recorder->cursysex.Add(new CChunk<unsigned char>(bytes, ptr));
    
            if (ptr[--bytes] == 0xf7) {
              recorder->sysexs.Add(recorder->cursysex.Compact());
              recorder->cursysex.Clear();
            }
    
            lpMidiHeader->dwBytesRecorded = 0;
            if (0 != (err = midiInAddBuffer(handle, lpMidiHeader, sizeof(MIDIHDR))))
              PrintMidiInErrorMsg(err);
          }
    
          break;
        }
        case MIM_OPEN:
          break;
        case MIM_CLOSE:
          break;
        case MIM_ERROR:
          break;
        case MIM_LONGERROR:
          break;
        case MIM_MOREDATA:
          break;
      }
    }
    
    //---------------------------------------------------------------------------
    
    CMidiIn::CMidiIn(int bufsize) {
      handle = 0;
      midiHdr.dwFlags = 0;
      midiHdr.lpData = (char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufsize);
      midiHdr.dwBufferLength = bufsize;
      cursysex.Clear();
    };
    
    CMidiIn::~CMidiIn() {
      Stop();
      HeapFree(GetProcessHeap(), 0, midiHdr.lpData);
    };
    
    void CMidiIn::Start(int iMidiDevice) {
      long err;
      sysexs.Clear();
      cursysex.Clear();
      if ((err = midiInOpen(&handle, iMidiDevice, (DWORD)midiCallback, (DWORD)this, CALLBACK_FUNCTION | MIDI_IO_STATUS)) == 0) {
        midiInPrepareHeader(handle, &midiHdr, sizeof(MIDIHDR));
        midiInAddBuffer(handle, &midiHdr, sizeof(MIDIHDR));
        midiInStart(handle);
      }
      else
        PrintMidiInErrorMsg(err);
    }
    
    void CMidiIn::Stop(void) {
      long err;
      if (handle) {
        if ((err = midiInReset(handle)) != 0)
          PrintMidiInErrorMsg(err);
        midiInUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR));
        if ((err = midiInClose(handle)) != 0)
          PrintMidiInErrorMsg(err);
        handle = 0;
      }
    }
    
    //---------------------------------------------------------------------------
    
    void CALLBACK midiCallbackForDialog(HMIDIIN handle, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2) {
      if (uMsg == MIM_LONGDATA) {
        unsigned err;
        LPMIDIHDR lpMidiHeader  = (LPMIDIHDR)dwParam1;
        unsigned char *ptr = (unsigned char *)(lpMidiHeader->lpData);
        unsigned bytes = lpMidiHeader->dwBytesRecorded;
        if (bytes) {
          CChunkList<unsigned char> *cursysex = (CChunkList<unsigned char>*) dwInstance;
          cursysex->Add(new CChunk<unsigned char>(bytes, ptr));
          lpMidiHeader->dwUser++;
    
          if (ptr[--bytes] != 0xf7) {
            lpMidiHeader->dwBytesRecorded = 0;
            if (0 != (err = midiInAddBuffer(handle, lpMidiHeader, sizeof(MIDIHDR))))
              PrintMidiInErrorMsg(err);
          }
        }
      }
    }
    
    CChunk<unsigned char>* SysexDialog(int iMidiOutDevice, int iMidiInDevice, CChunk<unsigned char> *SysexOut, unsigned timeout) {
      CChunkList<unsigned char> cursysex;
      CMidiOut midiout(iMidiOutDevice);
    
      if (midiout && SysexOut) {
        long err;
        unsigned long start;
        HMIDIOUT handle;
        LPMIDIHDR midiHdr = (LPMIDIHDR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MIDIHDR));
        midiHdr->dwFlags = 0;
        midiHdr->dwUser = 0; /* used for "Got first Long_message" */
        midiHdr->lpData = (char*) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024);
        midiHdr->dwBufferLength = 1024;
    
        if ((err = midiInOpen(&handle, iMidiInDevice, (DWORD)midiCallbackForDialog, (DWORD)&cursysex, CALLBACK_FUNCTION | MIDI_IO_STATUS)) == 0) {
          midiInPrepareHeader(handle, midiHdr, sizeof(MIDIHDR));
          midiInAddBuffer(handle, midiHdr, sizeof(MIDIHDR));
          midiInStart(handle);
          midiout.SendSysex(SysexOut);
          start = GetTickCount();
          while (midiHdr->dwFlags & MHDR_INQUEUE) {
            if (!midiHdr->dwUser && ((GetTickCount() - start) > timeout))
              break;
            SleepEx(100, TRUE);
          }
          if ((err = midiInReset(handle)) != 0)
            PrintMidiInErrorMsg(err);
          midiInUnprepareHeader(handle, midiHdr, sizeof(MIDIHDR));
          if ((err = midiInClose(handle)) != 0)
            PrintMidiInErrorMsg(err);
        }
        else
          PrintMidiInErrorMsg(err);
    
        HeapFree(GetProcessHeap(), 0, midiHdr->lpData);
        HeapFree(GetProcessHeap(), 0, midiHdr);
      }
    
      return cursysex.Compact();
    }
    
    bool TestSysexConnection(int iMidiOutDevice, int iMidiInDevice, unsigned timeout) {
      bool res = false;
      if ((iMidiOutDevice > -2) && (iMidiInDevice > -2)) {
        CChunk<unsigned char> *cursysex = SysexDialog(iMidiOutDevice, iMidiInDevice, SysEx::DeviceInquiry, timeout);
        res = (cursysex->len > 0);
        delete cursysex;
      }
      return res;
    }
    Dernière modification par alchemist, 26 octobre 2010, 22h14.
    Hello, World!

    Commentaire


    • #17
      Envoyé par iaoranaemaeva Voir le message
      Effectivement, c'est rare. Je ne l'ai jamais fait. Xavier indique de bonnes pistes (comme d'hab :8)).

      Sinon, as-tu regardé cet article de stackoverfow.com :

      Getting signals from a MIDI port in C#

      Si tu trouves une solution, ça m'intéresse (pour le futur :roll.

      PS : en attendant de trouver LA solution élégante, pourquoi ne pas commencer par le polling, ça te permettrait de valider la liaison montante ?

      Alain
      J'ai regardé ton lien et ... qu'est-ce que je suis bête

      J'ai fait comme-ci MidiInStart et MidiInStop servaient à lancer une séquence:roll:

      Merci, je continuerai demain.


      Edit: j'écrivais pendant que tu postais ton message alchemist, mais midiOutGetDevCaps ne devait pas suffire pour savoir si c'était un triton? ( en théorie parce que pour mon PC1X il me dit -1 comme ID de produit lol)


      Par contre j'ai rien réglé comme buffer, c'est juste pour les Sysex qu'ils sont utile? là je récupère bien quelle note est enclenchée, quel est la position de la molette (quand j'y touche)...
      Dernière modification par novaXire, 26 octobre 2010, 22h23.
      http://www.luxaeternaband.com/

      Matos : Kurzweil PC3K8

      Ma video pour le Kurzweil Forte Video Contest : https://www.youtube.com/watch?v=u_Fvc7UWGyI

      Commentaire


      • #18
        Envoyé par novaXire Voir le message
        (..) mais midiOutGetDevCaps ne devait pas suffire pour savoir si c'était un triton?
        Oui : midiOutGetDevCaps implémente le dialogue Identity Request / Identity Reply (et bien plus) sans avoir à mettre les mains dans le cambouis des SysEx :8).

        (en théorie parce que pour mon PC1X il me dit -1 comme ID de produit lol)
        Si tu cherches un instrument d'un constructeur et ou d'une famille donnée d'instrument, c'est fiable, si tu as pu tester le dialogue avec l'instrument ou les instruments !

        Par contre, à l'aveuglette, il peut y avoir des surprises, d'après mes lointains souvenirs (12 ans, comme Xavier :roll. C'est là que le client te tombe dessus : " ton *$@%& de logiciel ne reconnaît pas mon clavier XYZ " :oups

        j'ai rien réglé comme buffer, c'est juste pour les Sysex qu'ils sont utile? là je récupère bien quelle note est enclenchée, quel est la position de la molette (quand j'y touche)...
        Probablement parce que ce sont des messages courts (commande avec au plus 2 data bytes et un status byte), mais il paraît plus prudent de prévoir un buffer quand même.
        Dernière modification par iaorana, 27 octobre 2010, 02h05. Motif: ajout

        Commentaire


        • #19
          Hello,

          Les 2 buffers sont indispensables pour les messages longs (sysexes), les autres messages passent par les deux paramètres de la callback.

          Le but était aussi d'éditer le Triton, donc j'avais besoin de sysexes
          Hello, World!

          Commentaire


          • #20
            Hello,

            Les 2 buffers sont indispensables pour les messages longs (sysexes), les autres messages passent par les deux paramètres de la callback.

            Le but était aussi d'éditer le Triton, donc j'avais besoin de sysexes
            Hello, World!

            Commentaire


            • #21
              Envoyé par alchemist Voir le message
              Les 2 buffers sont indispensables pour les messages longs (sysexes), les autres messages passent par les deux paramètres de la callback.
              Ah ! Voilà :8)

              Commentaire


              • #22
                Sinon, pour du code bien testé, allez voir RTMidi, il implémente tout ce qu'il faut comme il faut


                Et laissez tomber HeapAlloc, utilisez juste les allocateurs de votre système (et surtout pas la pile), RTMidi utilise new[] et delete[].
                Hello, World!

                Commentaire


                • #23
                  Tu as l'air de bien l'aimer ce RtMidi !
                  Bon, n'ampèche qu'il va falloir que je le teste un jour, ça m'éviterais peut-être quelques prises de tête avec alsa (j'expérimente parfois la programmation midi sous linux).

                  Pour info, VMPK doit utiliser RTMidi si je ne me trompe pas :8)
                  Musicien, bricoleur et geek fou furieux 8)

                  Commentaire


                  • #24
                    Je pense que je rencontre le problème de buffer :oups

                    Sur midiOut pas de problème, j'ai envoyé 600 notes et aucun problème, mais en MidiIn, au bout d'environ 80 notes que je joue sur le synthé, plantage, et mes try catch ne choppe rien:?


                    Edit : rien de tout ça lol, j'affichais les données que je recevais dans une textbox multiligne, théoriquement la limite de caractères est de 32K environ, là j'avais pas 1K et ça plantais:mefie
                    Dernière modification par novaXire, 28 octobre 2010, 22h22.
                    http://www.luxaeternaband.com/

                    Matos : Kurzweil PC3K8

                    Ma video pour le Kurzweil Forte Video Contest : https://www.youtube.com/watch?v=u_Fvc7UWGyI

                    Commentaire


                    • #25
                      Envoyé par novaXire Voir le message
                      Edit : rien de tout ça lol, j'affichais les données que je recevais dans une textbox multiligne, théoriquement la limite de caractères est de 32K environ, là j'avais pas 1K et ça plantais:mefie
                      Ouf ! :8)

                      Pour info : en fouinant, je suis tombé sur ce code C#. Il s'occupe entre autres du GC (utilisation de la Méthode KeepAlive).

                      Commentaire


                      • #26
                        Oui j'ai donner un lien vers ce code la page précédente. Mais j'avais pas fait gaffe au KeepAlive je ne connaissait pas:super
                        http://www.luxaeternaband.com/

                        Matos : Kurzweil PC3K8

                        Ma video pour le Kurzweil Forte Video Contest : https://www.youtube.com/watch?v=u_Fvc7UWGyI

                        Commentaire


                        • #27
                          Les try/catch ne prendront rien, car les API Win32 n'enverront pas d'exceptions .Net, et que si ça plante, ce sera à très très bas niveau, celui du pilote. Donc blocages de la machine, qu'il faut débrancher.
                          Hello, World!

                          Commentaire


                          • #28
                            Envoyé par alchemist Voir le message
                            Les try/catch ne prendront rien, car les API Win32 n'enverront pas d'exceptions .Net, et que si ça plante, ce sera à très très bas niveau, celui du pilote. Donc blocages de la machine, qu'il faut débrancher.
                            Pour ma part, et pour plus de sûreté, je demande à EDT d'arrêter la centrale électrique de l'île :8) ;

                            Zut, ça ne marche pas : il y a une batterie dans un portable

                            Commentaire


                            • #29


                              Alors, NovaXire, est-ce que ça fonctionne ?
                              Hello, World!

                              Commentaire


                              • #30
                                Sinon, pour du code bien testé, allez voir RTMidi, il implémente tout ce qu'il faut comme il faut
                                Tout ce qu'il faut on est bien d'accord mais comme il le faut... Il suffit de voir les exemples à base de goto et de exit, des try/catch partout (une seule pair aurait bien suffit) ... Et puis j'ai aussi été déçu par la source elle même : certaines erreurs sont simplement envoyé à cerr et je ne commenterais même pas la classe RtError!

                                Tout cela pour dire que je vais continuer à chercher, même si c'est exactement ça que je voulais.

                                Commentaire

                                Chargement...
                                X