midi port in c++

Discussion in 'Effects and the DSP' started by stylus02, Jan 18, 2008.

  1. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    hi all,

    since i made a lot of tests with my dsp synthesizer and the audigys soundfont sampler, i asked me, how good an own midi input could be in c++.

    i mean, what can i expect? (before i start working on it)

    actually the midi timing feels like a chewing rubber. i know that there ever were midi timing problems in kx (in contrast to the original creative drivers) but i hope there is a way to improve this.

    thanks, stylus
     
  2. Lex Nahumury

    Lex Nahumury DH Senior Member

    Joined:
    Jan 5, 2003
    Messages:
    1,944
    Likes Received:
    6
    Trophy Points:
    0
    ?? I already answered that here;
    http://www.driverheaven.net/effects-dsp/152712-log_dane-exp_dane-c-2.html#post1117475

    No there were/are not!
    There is a "soundfont attack" issue and a "sysex issue" but "kX MIDI IN Port" works just as fine as Creative driver.

    As I already explained before; "KX MIDI Automation" is to slow for your application,
    but opening "kX MIDI IN Port" (or any other midi port) through WMM API works fast enough for triggering synths.

    However, if you want to playback from a sequencer, you must install MidiYoke's virtual midi port. (kinda same as Hubi's).
    Such virtual midi ports add overhead so timing may suffer on busy sequences.
     
  3. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    hi lex,

    a thread on midi timing could interest many people. we have now a few instruments running on our cards and it's called "toy's are us". the emu dsp is a 32 bit audio processor. the audio signal processing capabilities are "state of the art". i can't agree with a designation "toy".

    ok, you have made a midi port in c++ for your synths and it's usable for triggering from external sequencers. that's what i wanted to hear. virtual midi routing like hubi's stuff is widely accepted by musicians. thus I can live. :)

    stylus
     
  4. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    time for questions.

    i tried out a low level midi callback function written in "c". (Low Level MIDI API)
    it returns short (notes, controller, pitch..) and long midi messages (sysex) from a midi input. quite good..
    but how do i set dsp registers with these values? let's say there is a hex value for midi databyte 1 (note) and it should be written to dsp register "DATA_01_P" to process in a plugin.

    stylus
     
  5. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    With the set_dsp_register function from the kX SDK (but I suspect you already know this, so I am unsure what you are really asking).
     
    Last edited: May 6, 2008
  6. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    yes i know, but it doesn't work.

    void abc ()
    {
    set_dsp_register(DATA_01_P, 0xccccccc); // 0.1 or any value
    }

    error C2065: 'set_dsp_register' : undeclared identifier
     
  7. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Did you include the kX SDK headers (see stdafx.h from the demo plugin for the typical includes for a plugin), and link with the kX libs (kxapi.lib and kxgui.lib (if you are using the kxgui API))?

    BTW: You should also add the \h and \lib directories (from the kX SDK) to your project.
     
    Last edited: May 6, 2008
  8. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    i used the demo.dsw with modification.
    here is the myname.cpp:

    #include "stdafx.h"
    #include "myname.h"
    #include "da_myname.cpp"

    // Get DSP Microcode
    //----------------------------------------------------
    int imynamePlugin::request_microcode()
    {
    {
    publish_microcode(myname); // ? don't understood
    }
    return 0;
    }
    // plugin description
    //--------------------------------------------------
    char *imynamePlugin::get_plugin_description(int id)
    {
    plugin_description(myname);

    }
    // set register
    //-----------------------------------------------------
    void abc ()
    {
    set_dsp_register(DATA_01_P, 0xccccccc); // 0.1 or any value
    }

    the demo.cpp looks little different:

    ...
    int iDemoPlugin::set_param(int ndx, kxparam_t value) // member function?
    {
    _params[ndx] = value;

    switch (ndx)
    {
    case VOL1_ID:
    // vol1 parameter directly corresponds to the vol1 DSP register [0x8002]
    set_dsp_register(VOL1_P, value); // i only want this :)
    break;
    ...
     
  9. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    set_dsp_register is part of the plugin class, so you cannot call it from a global/static function.

    I would probably do something like this instead:
    (psuedocode)
    Code:
    // somewhere in your plugin code
    midiInOpen(&m_hMidiIn, m_nPortMidiIn, (DWORD_PTR)MidiInProc, (DWORD_PTR)this, CALLBACK_FUNCTION); // pass plugin pointer to callback function
    ...
    
    void CALLBACK MidiInProc(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
    {
        CMyPlugin * pPlugin = (CMyPlugin *)dwInstance;
        pPlugin->OnMidiIn(hMidiIn, wMsg, dwParam1, dwParam2);  // pass data to plugin function
    }
    ...
    
    void CMyPlugin::OnMidiIn(HMIDIIN hMidiIn, UINT wMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)  // member function
    {
        // filter data and use however you want
        switch (wMsg)
        {
            ...
            case MIM_DATA:
            {
                union 
                { 
                    DWORD_PTR pData; 
                    DWORD dwData; 
                    BYTE bData[4]; 
                } uMidiData; 
    
                uMidiData.pData = dwParam1;            
    
                switch (uMidiData.bData[0] & 0xF0)
                {
                    case 0x80:  // Note Off
                        // any calculations, etc
                        set_dsp_register...
                        break;
                    case 0x90:  // Note On    
                        ...
                }            
            }
            break;
            ...
        }
    }
    
     
    Last edited: May 6, 2008
  10. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    this doesn't look easy because my small c/c++ knowledge. (still doing some tutorials ) :)
     
  11. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    It can be confusing (callbacks in particulair), but take it a step at a time and lookup things that you do not understand (and if that doesn't help, ask about it).

    You do not necessarily need to understand all the code to use it (but of course it is better if you do).

    For processing the MIDI data, you can use whatever methods you are more familair with, the above is just an example (BTW: In the example,
    uMidiData.bData[0] would be the status byte, uMidiData.bData[1] would be the 1st data byte, and uMidiData.bData[2] would be the 2nd data byte)
    . The main idea was just to pass the data back to the plugin so that you can use plugin functions with it.
     
    Last edited: May 6, 2008
  12. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    thank you. let's see what i can do with it.
     
  13. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    have played around but no success. i did mix my midi callback code with the demo.cpp created some global variables which should hold now any mididata and tried to point on itusing something like that:

    int midi_data_02 // global
    ...
    midi_data_02=((dwParam1>>16) & 0x000000FF);//assignment in the callback function
    ...
    set_dsp_register(VOL1_P, *(&
    midi_data_02)*(0x1000000)); // point on any midi_data in the member function of the demo.cpp
    ...
    //blabla 8)

    russ, i understand now a little bit more why/how data has to pass from one function to another but i fear have not the staying power to write a running code in the near future. there are to many problems for an implementation. i need for instance a data window in the .kxl to check what i'm doing. don't know if the debugger could help..
    no idae if the midi callback works after putting it inside the demo.cpp.
    also the class concept is not yet fully understood (c++ basics). maybe we could find a common solution based on your idea above.

    stylus
     
    Last edited: May 18, 2008
  14. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    ok, maybe i should specify what it would be great to have for the time being. we have a running midi callback function , the kx demo workspace and our dsp (microcode) keyboard unit.
    we want manipulate 2 registers in the kbu. one register should have a state 0 or 1 - our gate, which could get data from note volume. the second is the note register, which also ranges from 0..1 and gets the data from midi data byte 1. so far so good.
    we can set the right midi input device in the callback function after displaying it with a separately .exe also available here: Low Level MIDI API
    we don't need much filtering status bytes and sysex or do error treatment. simply set dsp registers with note and gate would be nice.

    stylus
     
    Last edited: May 18, 2008
  15. Lex Nahumury

    Lex Nahumury DH Senior Member

    Joined:
    Jan 5, 2003
    Messages:
    1,944
    Likes Received:
    6
    Trophy Points:
    0
    To trace debug messages you could use kX message formatter function void debug(wchar_t *__format,...);
    (iirc see debug.h).
    Then You could use Debugview DebugView for Windows
    to view the output.

    So in your code simply write debug code like debug("Value= %d string=%s", some_value, "some_string");
    Start DebugView, load your plugin and voila,..examine the output.

    Usualy it's good practise to enclose the debug code lines in conditional compile like;

    #ifdef DEBUG_MY_STUFF
    debug("Value= %d string=%s", some_value, "some_string");
    #endif

    that way you can turn all dbg msg on/off with 1 define.


    Hmm,..I don't know what you are doing in the rest of your code but looking at your code snippet
    I wunder if and when your set_dsp_register(VOL1_P, ...); is called.

    Anyway, apart from that, I think you don't see the full potential of the C/C++ kxl extension.
    The idea is to move ALL parameter computation from .da to .kxl.
    A basic example;

    - in your midi callback handler you process incoming midi events.
    Let's say you recieve a NoteOn;
    from that midi data you then compute the new Pitch coeff for your DSP Oscilator(s),
    send that pitch coeff through set_dsp_register(),
    send an ADSR trigger through set_dsp_register().

    Note that all those pitch, filterCutOff and keyboard computations are now done in C/C++.
    that is much easier and will safe you alot of DSP gprs/instructions.


    The basic idea is; (pseudo code)

    void iSynthPlugin::midiCallbackHandler(MIDIMessage mididata)
    {
    if(mididate==NoteOn || mididate==NoteOFF)
    call midi_note_on_off(mididata)
    if(mididate==Controldata)
    call midi_control_data(mididata)
    if(mididate==PitchBend)
    call midi_PitchBend(mididata)
    // etc. etc.
    };

    void iSynthPlugin::midi_note_on_off(MIDIMessage mididata)
    {
    // here you have to run a full keyboard schedular to
    // implement a 'real' analog keyboard with porta, glide, keymodes etc.
    // but basicly its the same as;

    // Turn off previous note
    set_dsp_register(R_ADSR_TRIG, ...);

    // compute new pitch coeff IN C/C++
    set_dsp_register(R_PITCH_COEF, ...);

    // compute new Filter coeffS IN C/C++
    set_dsp_register(R_FILTER_COEF, ...);
    etc. etc.

    // Turn on note
    set_dsp_register(R_ADSR_TRIG, ...);
    };

    BTW: you really should use the mechanisme Russ already described to you to implement the above.
    There is no way around that (like you just tried) since the Callback must be able to notify your plugin when a midi msg arrives. Hence it must have access to a member function in your plugin class.
     
    Last edited: May 19, 2008
  16. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    thanks lex. for some time i asked me what the debug.h is for. the debugview works. so it can be very useful.

    stylus
     
  17. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    I created a basic plugin (limited functionality, no GUI, no error checking, etc) that you can use for example (or as a starting point, etc).

    It uses the default port for MIDI input and listens for Note On / Note Off messages on Channel 1 of that port, and sets DSP registers with calculated CV and Gate values, etc.

    (temporary link - removed)

    -Russ
     
    Last edited: May 20, 2008
  18. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    russ, after compiling the sourcecode i got an error "syntax error on DWORD_PTR". i don't know what the _PTR means because we have a DWORD datatype which stands for a 32 bit unsigned long.
    all i had to do was to substitute DWORD_PTR with DWORD in the callback function.
    now it works fine. we have a fast midi response in a compiled .kxl what makes the synthesizer more professional and its just great.

    i also want answer lex because the idea of the separation of midinote, controller or sysex etc. for the moment i don't want compile a lot of modules kxm 1xx..kxm9xx.
    so i had in mind to channel trough the midi data from our keyboard unit to a virtual midi out like "midi yoke out 1" or similar. from there the kx automation could get the controller data. this would give a good compatibility with all driver version. hope this works.

    stylus
     
    Last edited: May 20, 2008
  19. Russ

    Russ Well-Known Member

    Joined:
    Jan 17, 2005
    Messages:
    5,722
    Likes Received:
    13
    Trophy Points:
    48
    Regarding DWORD_PTR, it sounds as though you do not have any recent Windows SDK installed. New data types were added (and functions were modified to use these new data types) for 64 bit support, etc.
     
  20. stylus02

    stylus02 New Member

    Joined:
    Jan 11, 2008
    Messages:
    283
    Likes Received:
    2
    Trophy Points:
    0
    oh, this is interesting. i use windows xp and a microsoft visual c++ 6.0. no 64 bit support.
     

Share This Page

visited