Hi,
could someone of you tell me how is it possible to transmit some audio- and midi-date from my x86 processor (working with visual studio c++) to my LPC-USB-Device? Is there also a specific headerfile / library like the hid.h / hid.lib (using WriteFile() ReadFile() methods)?
My USB-Audio-Device-Class is already successfull installed and registrated in the windows device manager.
best regards Jens
DirectX 8.1 DirectSound was easier than current DirectShow or Core Audio
DirectSound - Playing Sounds msdn.microsoft.com/.../ms804971.aspx
Find DirectX 8.1 SDK (dx81sdk_full.exe) on web for the example C:\Program Files\Microsoft DirectX 8.1 SDK\samples\Multimedia\DirectSound\PlaySound\
A typical code sequence is, g_pSoundManager = new CSoundManager(); g_pSoundManager->Create( &g_pSound, fileName, ... ) // Load wave file g_pSound->Play() // play the wave file
Playing MIDI Files msdn.microsoft.com/.../dd743676(v=vs.85).aspx
MIDI player on MS Windows SDK msdn.microsoft.com/.../bb980924 C:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\audio\midiplyr\
For more practical code reference, Audacity source code audacity.sourceforge.net/.../source
Tsuneo
Hi Tsuneo,
thanks for these information. At the moment, I'm not able to get the usb-audio-messages form the LPC device into my x86 visual c++ program. These packets are not recoginzed so far. Using a usb sniffer, I can see that the usb-audio packets were sent from the LPC to the x86 controller (e.g. message: 0x01 0x02 0x03 0x03 0x04).
My init code in my windows-application is the following:
HMIDIIN hMidiDevice = NULL;; DWORD nMidiPort = 0; UINT nMidiDeviceNum; MMRESULT rv; PrintMidiDevices(); nMidiDeviceNum = midiInGetNumDevs(); if (nMidiDeviceNum == 0) { fprintf(stderr, "midiInGetNumDevs() return 0..."); return -1; } rv = midiInOpen(&hMidiDevice, nMidiPort, (DWORD)(void*)MidiInProc, 0, CALLBACK_FUNCTION); if (rv != MMSYSERR_NOERROR) { fprintf(stderr, "midiInOpen() failed...rv=%d", rv); return -1; } midiInStart(hMidiDevice);
My device is recognized and I'm able to start the recording session (midiInStart).
Here's a small snippet from my usb descriptors:
USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_MIDISTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x06, /* iInterface */ /* MIDI streaming interface descriptor */ 0x07, 0x24, 0x01, WBVAL(0x0100), /* 1.00 */ /* bcdADC */ WBVAL(size), /* MIDI IN Jack Descriptor */ 0x06, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x02, /* bDescriptorSubtype: MIDI_IN_JACK subtype */ 0x01, /* bJackType: EMBEDDED.*/ 0x01, /* bJackID */ 0x00, /* iJack */ /* MIDI OUT Jack Descriptor */ 0x09, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x03, /* bDescriptorSubtype: MIDI_OUT_JACK subtype */ 0x01, /* bJackType: EMBEDDED.*/ 0x03, /* bJackID */ 0x01, /* bNrInputPins: Number of Input Pins of this Jack */ 0x02, /* BaSourceID(1) */ 0x01, /* BaSourcePin(1) */ 0x00, /* iJack: unused */ /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ 0x00, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */ /* Class-specific MS Bulk IN Endpoint Descriptor */ 0x05, /* bLength */ 0x25, /* bDescriptorType: CS_ENDPOINT descriptor */ 0x01, /* bDescriptorSubtype: MS_GENERAL subtype */ 0x01, /* bNumEmbMIDIJack */ 0x03, /* BaAssocJackID(1) */ /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ 0x00, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */ /* Class-specific MS Bulk OUT Endpoint Descriptor */ 0x05, /* bLength */ 0x25, /* bDescriptorType: CS_ENDPOINT descriptor */ 0x01, /* bDescriptorSubtype: MS_GENERAL subtype */ 0x01, /* bNumEmbMIDIJack */ 0x01, /* BaAssocJackID(1) */
I hope you will see where the problem is and how I can easily solve it.
By the way, I also tried to send a well-known midi message to my application (note on: 0x90, 0x3C, 0x40), but my callback function won't be called.
I recognized that the midi commands are a little bit different, when they are send by usb. But if I send the note on message event from the first embedded jack (0x19 0x90 0x3C 0x40) I also didn't receive the pkt by the callback function or by the software MIDI-Ox.
Your MIDI descriptors have a couple of troubles.
BaSourceID(1) field of the MIDI OUT jack refers to a MIDI IN Jack of ID 2. But this MIDI IN Jack is not defined.
/* MIDI OUT Jack Descriptor */ 0x09, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x03, /* bDescriptorSubtype: MIDI_OUT_JACK subtype */ 0x01, /* bJackType: EMBEDDED.*/ 0x03, /* bJackID */ 0x01, /* bNrInputPins: Number of Input Pins of this Jack */ 0x02, /* BaSourceID(1) */ // <-------- 0x01, /* BaSourcePin(1) */ 0x00, /* iJack: unused */
An embedded MIDI IN Jack for USB-MIDI converter is defined, but no MIDI OUT Jack is connected to this MIDI IN.
Maybe, you've deleted "external" MIDI Jacks from the standard MIDI descriptors. But it destroys the MIDI patchboard topology. Recover these MIDI Jack descriptors.
MIDI Jack descriptors describe connection topology between MIDI elements, USB-MIDI converter, and external MIDI Jacks on the device.
USB-MIDI converter patch board Bulk IN endpoint <--- embedded MIDI OUT Jack <---------- external MIDI IN Jack or embedded element Bulk OUT endpoint ---> embedded MIDI IN Jack ----------> external MIDI OUT Jack or embedded element
If you delete the external MIDI Jacks, you have to declare embedded MIDI elements, instead.
here's my full usb-midi-descriptor. In my earlier message, I send only a part of this.
USB_INTERFACE_DESC_SIZE, USB_INTERFACE_DESCRIPTOR_TYPE, 0x00, 0x00, 0x00, USB_DEVICE_CLASS_AUDIO, AUDIO_SUBCLASS_AUDIOCONTROL, AUDIO_PROTOCOL_UNDEFINED, 0x00, /* Audio Control Interface */ AUDIO_CONTROL_INTERFACE_DESC_SZ(1),/* bLength */ AUDIO_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ AUDIO_CONTROL_HEADER, /* bDescriptorSubtype */ WBVAL(0x0100), /* 1.00 */ /* bcdADC */ WBVAL( /* wTotalLength */ AUDIO_CONTROL_INTERFACE_DESC_SZ(1) ), 0x01, /* bInCollection */ 0x01, /* baInterfaceNr */ USB_INTERFACE_DESC_SIZE, /* bLength */ USB_INTERFACE_DESCRIPTOR_TYPE, /* bDescriptorType */ 0x01, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x02, /* bNumEndpoints */ USB_DEVICE_CLASS_AUDIO, /* bInterfaceClass */ AUDIO_SUBCLASS_MIDISTREAMING, /* bInterfaceSubClass */ AUDIO_PROTOCOL_UNDEFINED, /* bInterfaceProtocol */ 0x06, /* iInterface */ /* MIDI streaming interface descriptor */ 0x07, 0x24, 0x01, WBVAL(0x0100), /* 1.00 */ /* bcdADC */ WBVAL( /* wTotalLength */ 2* 0x06 + 2* 0x09 + AUDIO_STANDARD_ENDPOINT_DESC_SIZE + 0x05 + AUDIO_STANDARD_ENDPOINT_DESC_SIZE + 0x05 ), /* MIDI IN Jack Descriptor */ 0x06, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x02, /* bDescriptorSubtype: MIDI_IN_JACK subtype */ 0x01, /* bJackType: EMBEDDED.*/ 0x01, /* bJackID */ 0x00, /* iJack */ /* MIDI IN Jack Descriptor */ 0x06, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x02, /* bDescriptorSubtype: MIDI_IN_JACK subtype */ 0x02, /* bJackType: EXTERNAL.*/ 0x02, /* bJackID */ 0x00, /* iJack */ /* MIDI OUT Jack Descriptor */ 0x09, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x03, /* bDescriptorSubtype: MIDI_OUT_JACK subtype */ 0x01, /* bJackType: EMBEDDED.*/ 0x03, /* bJackID */ 0x01, /* bNrInputPins: Number of Input Pins of this Jack */ 0x02, /* BaSourceID(1) */ 0x01, /* BaSourcePin(1) */ 0x00, /* iJack: unused */ /* MIDI OUT Jack Descriptor */ 0x09, /* bLength */ 0x24, /* bDescriptorType: CS_INTERFACE descriptor */ 0x03, /* bDescriptorSubtype: MIDI_OUT_JACK subtype */ 0x02, /* bJackType: EXTERNAL.*/ 0x04, /* bJackID */ 0x01, /* bNrInputPins: Number of Input Pins of this Jack */ 0x01, /* BaSourceID(1) */ 0x01, /* BaSourcePin(1) */ 0x00, /* iJack: unused */ /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_IN(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ 0x00, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */ /* Class-specific MS Bulk IN Endpoint Descriptor */ 0x05, /* bLength */ 0x25, /* bDescriptorType: CS_ENDPOINT descriptor */ 0x01, /* bDescriptorSubtype: MS_GENERAL subtype */ 0x01, /* bNumEmbMIDIJack */ 0x03, /* BaAssocJackID(1) */ /* Endpoint - Standard Descriptor */ AUDIO_STANDARD_ENDPOINT_DESC_SIZE, /* bLength */ USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType */ USB_ENDPOINT_OUT(1), /* bEndpointAddress */ USB_ENDPOINT_TYPE_BULK, /* bmAttributes */ WBVAL(64), /* wMaxPacketSize */ 0x00, /* bInterval */ 0x00, /* bRefresh */ 0x00, /* bSynchAddress */ /* Class-specific MS Bulk OUT Endpoint Descriptor */ 0x05, /* bLength */ 0x25, /* bDescriptorType: CS_ENDPOINT descriptor */ 0x01, /* bDescriptorSubtype: MS_GENERAL subtype */ 0x01, /* bNumEmbMIDIJack */ 0x01, /* BaAssocJackID(1) */
and here's my usb-midi-tx-call (in the LPC processor) to send a pkt by usb:
unsigned char midi_testdata[4]; /* midi note on event */ midi_testdata[0] = 0x19; midi_testdata[1] = 0x90; midi_testdata[2] = 0x3C; midi_testdata[3] = 0x40; USB_WriteEP(0x81, (unsigned char *) &midi_testdata[0], 4* sizeof(unsigned char));
ok, I found the first error in my code:
unsigned char midi_testdata[4]; /* midi note on event */ midi_testdata[0] = 0x09; /* <--- first jack is 0 and not 1 */ midi_testdata[1] = 0x90; midi_testdata[2] = 0x3C; midi_testdata[3] = 0x40; USB_WriteEP(0x81, (unsigned char *) &midi_testdata[0], 4* sizeof(unsigned char));
With this modification, I'm able to receive this midi message. But at the moment, I'm not able to receive sysex-messages, fullframe-miditimecode-message or anything else?? Is is also possible to receive my own midi-message (with user-specific bytes)?
unsigned char midi_testdata[10]; /* midi timecode quarter frame message (not recognized by my windows prog)..... */ midi_testdata[0] = 0x02; midi_testdata[1] = 0xF1; midi_testdata[2] = 0x04; USB_WriteEP(0x81, (unsigned char *) &midi_testdata[0], 3* sizeof(unsigned char)); /* SysEx message (not recognized by my windows prog)..... */ midi_testdata[0] = 0x0F; midi_testdata[1] = 0x00; midi_testdata[2] = 0x01; midi_testdata[3] = 0x02; midi_testdata[4] = 0x03; midi_testdata[5] = 0xF7; USB_WriteEP(0x81, (unsigned char *) &midi_testdata[0], 6* sizeof(unsigned char)); /* is it also possible to write a user-specific midi message with 5bytes of data??? */ midi_testdata[0] = 0x01; /* <- user-defined message */ midi_testdata[1] = 0x00; midi_testdata[2] = 0x01; midi_testdata[3] = 0x02; midi_testdata[4] = 0x03; midi_testdata[5] = 0x04; USB_WriteEP(0x81, (unsigned char *) &midi_testdata[0], 6* sizeof(unsigned char));
You would be better to read "4 USB-MIDI Event Packets" section (p16) again, on the USB MIDI spec, to know the rule of USB MIDI packets. www.usb.org/.../midi10.pdf
1) USB MIDI packets always consist of 4 bytes, one byte header and three bytes MIDI message - Longer MIDI message (than 3 bytes) is split into multiple packets. - Unused bytes must be padded with zeros. 2) The upper nibble (4bits) of the header shows cable number. - in your case, its 0 3) The lower nibble of the header is a code index, which describes following 1-3 bytes Refer to "Table 4-1: Code Index Number Classifications"
a) midi timecode quarter frame message - require one byte header b) 6 bytes SysEx message is split into two packets (3bytes, each). One byte header precedes each 3bytes. They are sent over twice USB_WriteEP() calls.
"Table 4-2: Examples of Parsed MIDI Events in 32-bit USB-MIDI Event Packets"(p17) on the USB MIDI spec will help you to understand USB MIDI packet better.
I got the sysex messages (with 6bytes) working. But how can I determine (know) that I can already send a second usb-message, including the last three bytes of the sysex message?
Is there an interrupt-possibility or a method where I can set a flag which indiciates that another usb-pkt if available could be send?
/* first sysex-message */ USB_WriteEP(0x81, (unsigned char *) &midi_testdata[0], 4* sizeof(unsigned char)); delay_ms(1000); /* second sysex message */ USB_WriteEP(0x81, (unsigned char *) &midi_testdata[4], 4* sizeof(unsigned char));
Ah, I remember. A bulk transaction can hold multiple USB-MIDI packets (4 bytes). A single bulk transaction (ie. single USB_WriteEP() call) sends up to 64 bytes. 16 USB-MIDI packets can be put into a 64 bytes buffer sequentially, and this buffer is sent to host at a time. Therefore, you may send two USB-MIDI packets, which holds SysEx message, in single USB_WriteEP() call. > Is there an interrupt-possibility or a method where I can set a flag which indiciates that another usb-pkt if available could be send?
There are two ways to know if the bulk IN endpoint is available or not. As the background, the bulk endpoints of NXP LPC USB MCUs has double buffers. You may put two transactions to these endpoints, at a time.
a) Using a counter We place a counting semaphore as a global variable. This variable is initialized at USB_Configure_Event() At the transaction completion (USB_EndPointX() call back), this variable increments.
usbuser.c U8 bulk_EP_TX_counter = 0; // a global variable to hold available number of transactions #if USB_CONFIGURE_EVENT void USB_Configure_Event (void) { if (USB_Configuration) { /* Check if USB is configured */ bulk_EP_TX_counter = 2; // two transactions are available } } #endif void USB_EndPoint2 (U32 event) { switch (event) { case USB_EVT_IN: // a bulk IN transaction completes ++bulk_EP_TX_counter; // we may put another transaction break; } }
In your code, the variable decrements.
if ( bulk_EP_TX_counter ) { --bulk_EP_TX_counter; // decrement the semaphore USB_WriteEP( 0x82, buffer, number_of_bytes_to_send ); }
b) Using SIE command "Select Endpoint" command returns the endpoint status. FE bit (Full/Empty: bit0) of this status indicates availability of either of endpoint buffers. SIE command takes more execution time than above method.
#define BULK_IN_EP 0x82 ep_status = RdCmdDat( CMD_SEL_EP(EPAdr( BULK_IN_EP )) );
Umm.. You are using an interrupt EP (0x81), instead of a bulk EP. It's OK, but interrupt EPs have just single buffer.