Page 1 of 2 1 2 LastLast
Results 1 to 15 of 18

Thread: MIDI tijdcode als 'Quarter frame' verzenden.

  1. #1
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536

    MIDI tijdcode als 'Quarter frame' verzenden.

    Voor het testen van een MIDI timecode reader (MTC) wil ik een test programmaatje maken.
    De tijd die gebruikt kan worden is gewoon de tijd van de PC klok. ('Now')
    De implementatie van MIDI is voor een groot deel al geregeld en wellicht bruikbaar door middel van 'MIDI I/O Components 3.0' door David Churcher.
    Maar het lukt mij niet om 2-byte MIDI pakketjes te verzenden via dit component, waarschijnlijk omdat het nooit voor MIDI timecode gemaakt is.
    (Het zijn immers geen 'note' of 'sysex' datapakketjes.)
    Gedurende mijn tests gebruikte ik de procedure 'PutShort' voor het verzenden van de databytes.
    Meer over MIDI timecode hier: https://web.archive.org/web/20120212...t/tech/mtc.htm
    Is er wellicht iemand die eerder met dit bijltje gehakt heeft en hier iets meer over weet? Suggesties zijn zeer welkom!
    Voor elk probleem is een oplossing!

  2. #2
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Ik vrees dat mijn vraagstelling iets te specifiek is.
    Laat ik het anders formuleren: hoe kan ik twee 'willekeurige' bytes naar een MIDI poort sturen?
    Voor elk probleem is een oplossing!

  3. #3

  4. #4
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Hartelijk dank! Ik ga hier eens op zitten broeden, want volgens mij zag ik in de gauwigheid al wat interessante informatie.
    Voor elk probleem is een oplossing!

  5. #5
    Geen idee of je het al gevonden had maar zag deze code in een link van [Reidinga]
    PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte): Output a short
    MIDI message. Handy when you can't be bothered to build a TMyMidiEvent.
    If the message you're sending doesn't use Data1 or Data2, set them to 0.
    https://bitbucket.org/h4ndy/midiio-d...lt/MidiOut.pas

  6. #6
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Ja, inderdaad, daar ben ik mee aan de slag gegaan.
    Het punt is dat ik twee bytes moet verzenden als 'quarter frame'. (In de praktijk: 241 dec. of $F1 hex. + een tweede byte.)
    Volgens de documentatie kun je dan als 'derde' byte (=data2) een '0' verzenden.
    Of nog meer 'basic':
    Code:
    thisMsg := DWORD(MidiMessage) Or
      	      (DWORD(Data1) shl 8) Or
    	      (DWORD(Data2) shl 16);
    
    FError := midiOutShortMsg(FMidiHandle, thisMsg);
    Het merkwaardige is dat als ik dit doe er daadwerkelijk data wordt verzonden, maar met een geheel onverwacht resultaat.
    De 4 hoogste bits van een 'quarter frame' (data1 dus) bepalen welke informatie er verzonden wordt (frames, seconden, minuten, uren), terwijl de laagste 4 bits de waarde weergeven. (En dat dan ook nog gesplitst in bijvoorbeeld seconden x 1 en seconden x 10, als afzonderlijke 'quarter frames'.)
    Maar we blijven puzzelen!
    Voor elk probleem is een oplossing!

  7. #7
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Het probleem is ook een beetje dat ik niet precies kan controleren wat er nou verzonden wordt.
    Die 241 (dec) of $F1 (hex) klopt, want anders zou een MTC reader niet reageren.
    Maar het is kennelijk het volgende byte dat niet correct overgedragen wordt. In elk geval niet zoals het zou moeten...
    Nu heb ik al geprobeerd de MIDI uitgang met een kabeltje naar een MIDI ingang te leiden en dan met een MIDI monitorprogrammaatje te bekijken wat daar binnenkomt, maar de meeste MIDI monitor applicaties crashen op MIDI Time Code... Ik kan nog eens proberen met een oscilloscoop het MIDI signaal te bekijken, misschien dat ik daar wat wijzer van kan worden.
    Voor elk probleem is een oplossing!

  8. #8
    is er niet toevallig een wireshark plugin voor?

    Sommige dso's en logic analyzers hebben ook protocol decoders.

  9. #9
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Inmiddels ben ik verder zoekend een interessant ogend artikel tegengekomen: http://www.giordanobenicchi.it/midi-tech/lowmidi.htm
    Hierin wordt een mogelijkheid beschreven een single byte naar een MIDI poort te sturen.
    Op een forum (https://forum.juce.com/t/how-to-send...ne-byte/2662/5) heeft iemand de code geïsoleerd weergegeven:
    Code:
    #include <windows.h>
    #include <stdio.h>
    #include <conio.h>
    #include <mmsystem.h>
    
    unsigned char	Vol[] = {0xf1};
    
    
    void PrintMidiOutErrorMsg(unsigned long err)
    {
    #define BUFFERSIZE 120
    	char	buffer[BUFFERSIZE];
    	
    	if (!(err = midiOutGetErrorText(err, &buffer[0], BUFFERSIZE)))
    	{
    		printf("%s\r\n", &buffer[0]);
    	}
    	else if (err == MMSYSERR_BADERRNUM)
    	{
    		printf("Strange error number returned!\r\n");
    	}
    	else
    	{
    		printf("Specified pointer is invalid!\r\n");
    	}
    }
    
    int main(int argc, char **argv)
    {
    	HMIDIOUT		handle;
    	MIDIHDR			midiHdr;
    	UINT			err;
    
    	if (!(err = midiOutOpen(&handle, 0, 0, 0, CALLBACK_NULL)))
    	{
    		midiHdr.lpData = (LPBYTE)&Vol[0];
    		midiHdr.dwBufferLength = sizeof(Vol);
    		midiHdr.dwFlags = 0;
    		err = midiOutPrepareHeader(handle,  &midiHdr, sizeof(MIDIHDR));
    
    	    if (!err)
    		{
    	        /* Output the SysEx message. Note that this could return immediately if
    			the device driver can output the message on its own in the background.
    			Otherwise, the driver may make us wait in this call until the entire
    			data is output
    			*/
    		    err = midiOutLongMsg(handle, &midiHdr, sizeof(MIDIHDR));
    			if (err)
    			{
    				PrintMidiOutErrorMsg(err);
    			}
    
    			
    			while (MIDIERR_STILLPLAYING == midiOutUnprepareHeader(handle, &midiHdr, sizeof(MIDIHDR)))
    			{
    				Sleep(1000);
    			}
    		}
    		else
    		{
    			PrintMidiOutErrorMsg(err);
    		}
    
    		midiOutClose(handle);
    	}
    	else
    	{
    		printf("Error opening default MIDI Out device!\r\n");
    		PrintMidiOutErrorMsg(err);
    	}
    
    	return(0);
    }
    Op zich ziet dit er veelbelovend uit. De complicatie is echter dat ik onvoldoende thuis ben in de C-varianten om dit om te zetten naar Delphi.
    Dus als iemand deze 'vertaalslag' zou aandurven (het meeste is commentaar) zou ik zéér dankbaar zijn en ga ik daar eens mee experimenteren.
    Ook heb ik het MIDI signaal van een MTC timecode generator op de oscilloscoop bekeken en dan zie je duidelijk de 241(dec) of $F1 (hex) die onveranderd blijft en het tweede byte dat continu staat te 'reutelen'.
    Voor elk probleem is een oplossing!

  10. #10
    voor wat betreft die eerste functie via midioutshortmsg
    https://docs.microsoft.com/en-us/win...idioutshortmsg
    This function is used to send any MIDI message except for system-exclusive or stream messages.

    This function might not return until the message has been sent to the output device. You can send short messages while streams are playing on the same device (although you cannot use a running status in this case).
    en je kan in de functie zelf nog even controleren of die tweede byte wel echt ook aankomt zoals bedacht. Zie geen rare code behalve dat het casten naar dword van de bytes niet echt nodig was.

    die tweede (c) routine is anders en is wel geschikt om sysex messages te sturen.

    Geen idee wat sysex msg zijn en of dat ook is wat je wilt versturen

    edit:

    Nog even verder graven http://midi.teragonaudio.com/tech/mtc.htm
    The most important message is the Quarter Frame message (which is not a SysEx message)
    Last edited by Miep; 02-May-20 at 12:01. Reason: weer wat geleerd :)

  11. #11
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    SysEx (System Exclusive) bestaat uit een serie bytes waarbij het eerste byte altijd 240 (dec.) is.
    Een SysEx eindigt altijd met een 0 als laatste byte. De lengte kan variabel zijn.
    Midi Time Code (MTC) 'quarter frame' messages beginnen altijd met 241 (dec.) als eerste byte, gevolgd door een tweede byte waar de tijdinformatie in zit.
    (De hoogste 4 bits geven aan of het om frames, seconden, minuten of uren gaat en de laagste 4 bits bevatten de waarde.)
    Ik heb zonder mijn ervaring in de C-talen een poging gedaan de code die ik gaf te 'vertalen' naar Delphi, en het goede is dat de boel niet crashed en er zo te zien daadwerkelijk iets verzonden wordt. Of het ook echt werkt moet ik nog uitproberen...
    Voor elk probleem is een oplossing!

  12. #12
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Inmiddels heb ik mijn geknutsel getest, maar het blijkt niet te werken.
    Op de oscilloscoop zie ik iets dat lijkt op een startbit, maar zonder daarop volgende data.
    Ik denk (sterker: ik weet het wel zéker!) dat ik een grote fout heb gemaakt bij de omzetting van C naar Delphi. (Ik weet namelijk hoegenaamd niets van de C talen...)
    Dus beste forumleden, tik me maar op de vingers!!!
    Dit is de code die ik toegevoegd heb aan MidiOut.pas van David Churcher:

    Code:
    function TMidiOutput.SendSingleByte(HMIDIOUT: THandle; B: Byte): Integer;
    var Mymidihdr: TMyMidiHdr;
    begin
      MyMidiHdr := TMyMidiHdr.Create(128);
      MyMidiHdr.hdrPointer^.lpData := @B;
      MyMidiHdr.hdrPointer^.dwBufferLength := SizeOf(B);
      MyMidiHdr.hdrPointer^.dwFlags := 0;
      MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr);
      FError := midiOutPrepareHeader(FMidiHandle,MyMidiHdr.hdrPointer, SizeOf(TMIDIHDR));
      if Ferror > 0 then
        raise EMidiOutputError.Create(MidiOutErrorString(FError));
      FError := midiOutLongMsg(FMidiHandle, MyMidiHdr.hdrPointer, SizeOf(TMIDIHDR));
      if Ferror > 0 then
        raise EMidiOutputError.Create(MidiOutErrorString(FError));
    end;
    En ja, ik wéét dat de functie geen return waarde heeft, daar ben ik nog niet aan toe gekomen...
    Voor elk probleem is een oplossing!

  13. #13
    als ik snel kijk is het verschil dat jouw code maar 1 byte stuurt en de c code kan een x hoeveelheid bytes sturen

    daarnaast heeft de c code de MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); niet. Komt ook een beetje raar over een verwijzing naar zichzelf of is dat bedoelt als soort linked list maar dan nog is verwijzing naar zichzelf raar?

    ook een create van 128 bytes, die maakt toch een buffer van 128 bytes aan terwijl je het daarna overruled met B niet correct of maakt die create niet een hdrpointer met een buffer van 128 bytes?

  14. #14
    Senior Member Ruud123's Avatar
    Join Date
    Sep 2007
    Location
    Leerdam
    Posts
    536
    Inderdaad, die
    Code:
    MyMidiHdr := TMyMidiHdr.Create(128);
    zou net zo goed kunnen zijn:
    Code:
    MyMidiHdr := TMyMidiHdr.Create(SizeOf(B));
    maar dat verandert de werking niet echt.
    En:
    Code:
    { Store the MyMidiHdr address in the header so we can find it again quickly
     MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr);
    heb ik letterlijk overgenomen uit bestaande code. Volgens mij doet dat weinig kwaad.
    Feit blijft dat het niet werkt...
    Voor elk probleem is een oplossing!

  15. #15
    Ik zie MyMidiHdr, TMyMidiHdr en ook nog Sizeof(TMIDIHDR).
    Ik zie geen verband tussen TMyMidiHdr en TMIDIHDR).
    Compileert je code wel?
    Verder: als B een Byte is, is SizeOf(B) dan wel zinvol?

Page 1 of 2 1 2 LastLast

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •