DLLCC and RtMidi (Real Time MIDI) Interface

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

DLLCC and RtMidi (Real Time MIDI) Interface

Stew MacLean

It’s been a while, but I’m back with VW working on a MIDI project (makes a change from Accounting!).

 

I’m aiming to interface to a C++ midi library written by Gary P. Scavone http://www.music.mcgill.ca/~gary/rtmidi/

via a C wrapper API written by Ben Swift https://github.com/benswift/rtmidi-c-api

 

I’ve had a little experience in compiling C and the DLLCC, but I’m running into a lot of problems. After spending days on this I’d very much appreciate any help on this!

 

The rig is: VW 7.9.1 on Mac OS X 10.7.3.

 

I’ve compiled a test midi out program in C++ and that (more or less) works fine. I’ve compiled the shared C library, and I can open it and retrieve lists of Midi ports from Smalltalk. This works most of the time with the odd failure. I’ve also been able to open ports, but this seems to always fail the first time, and work afterwards (depending on whether I use g++ or clang++ - clang++  faults). I’ve also been able to send a message, but the data being received in the API is random, and bears no relation to what I’m sending. (not sure, but maybe an encoding problem). I’m starting from the command line and writing to cout to debug.

 

I’ve tried all sorts of combinations drawn from the DLLC manual, the THAPI example, the net, Siren, and Jun.

 

The main problems I’m having is passing the portName to openOutPort, and passing the message contents to sendMessage:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){
     try {
          std::string name(portName); 
          dev->ptr->openPort(portNumber, name);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};

 

My understanding is that the DLLCC machinery should take a String and convert it to a null terminated string pointer. I’ve tried copying to heap, asFixed… etc. What is the correct way to pass this String in from Smalltalk?

 

The send message uses a relatively new type that is apparently guaranteed to be of fixed width. I just want to pass a ByteArray of the form #[144 94 127].  This gets loaded to a vector and shunted off to the C++ library. I’ve tried using char * and int * copyToHeap, creating CTypes etc. When the call works, I end up with random numbers that sometime stay the same between calls. Again this fails the first time, and then “work” subsequently.

 

rtErr sendMessage( outDevice* dev, int64_t messageLength, uint8_t* message ){
     try {
          std::vector<uint8_t> msgVector;
          for (int i = 0; i < (int)messageLength; ++i)
          {
               msgVector.push_back(message[i]);
          }
          dev->ptr->sendMessage(&msgVector);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};
 
What is the correct way of passing in a ByteArray into this function?
 
Any help appreciated!
 
Thanks,
 
Stewart

 


_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: DLLCC and RtMidi (Real Time MIDI) Interface

Stew MacLean

OK, I’ve done some further testing and it looks like my first problem is with converting the pointer passed in from DLLCC to a string within the C function. Please see below for comparative test runs – the first fails, the second works fine. This indicates that the pointer string can be dereferenced ok within C.

 

When I create a string pointer locally within the C function, it works fine. With the string pointer from DLLCC, it terminates with a “0x95ff4b12 std::__throw_logic_error(char const*) + 121”

 

Any help in explaining this behaviour appreciated….

 

1) Failing Scenario….Smalltalk code…

testOutput

 

"

| if |

RtMidiExternalInterface unloadLibraries.

(if := self new)

open.

[if

openOutputPort: 1;   “<<<fails here…”

testOutput] ensure: [interface close]

 

 

| msg msgPointer |

 

RtMidiExternalInterface unloadLibraries.

msg := #[144 94 127] copy.

msgPointer := msg gcCopyToHeap.

 

interface sendMessage: outputDevice with: msg size with: msgPointer.

 

External interface….

 

openOutPort: dev with: portNumber with: portName

<C: rtErr openOutPort(outDevice * dev, int64_t portNumber, char * portName)>

^self externalAccessFailedWith: _errorCode

 

C Code…

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){

     try {

         std::cout << "openOutPort\n";

         //const char *s = "Hello, World!";   // works ok with these

         //std::string name(s); 

         

         std::string name(portName);    //<<<<<<  fails here

         std::cout << "Pre openPort\n";

         dev->ptr->openPort(portNumber, name);

         std::cout << "Pre RTMIDI_NOERROR\n";

         return RTMIDI_NOERROR;

     }

     catch ( RtError &error ) {

          error.printMessage();

          return RTMIDI_ERROR;

     }

};

 

Console output….

 

newMidiInDevice

getInPortCount

getInPortName

getInPortName

getInPortName

newMidiOutDevice

getOutPortCount

getOutPortName

getOutPortName

openOutPort

terminate called throwing an exceptionAbort trap: 6

Error log…

 

8   libstdc++.6.dylib             0x95ff4b12 std::__throw_logic_error(char const*) + 121

9   libstdc++.6.dylib             0x9601edb5 char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) + 163

10  libstdc++.6.dylib             0x9601ee76 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) + 60

11  librtmidi.dylib               0x0a76e859 openOutPort + 121

12  com.cincom.vw7.9.1             0x00077da9 pdCallC + 2545 (unx86CallC.c:368)

13  com.cincom.vw7.9.1             0x00006a26 primCallVarArgC + 332 (exCallC.c:512)

14  com.cincom.vw7.9.1             0x0005ee2b rtPrimGlueVarArgMany + 43

 

2) This works fine…Smalltalk code…

 

testChar

 

| msg msgPointer |

 

"

100 timesRepeat: [self new testChar].

"

 

RtMidiExternalInterface unloadLibraries.

interface := RtMidiExternalInterface new.

msg := 'Take me to the C'.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

 

msg := #[144 94 127] copy.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

External interface….

 

testChar: messageLength with: message

<C: rtErr testChar(int  messageLength, char*  message)>

^self externalAccessFailedWith: _errorCode

C Code…..

 

rtErr testChar(int messageLength, char* message ){

        std::cout << "testChar\n";

        std::cout << message;

        std::cout << "\nPre for loop\n";

        for (int i = 0; i < (int)messageLength; ++i)

            { std::cout << "Each:\n";

                printf("dec: %d char: %c\n", message[i], message[i]);

            }

        return RTMIDI_NOERROR;

};

 

Console output, all good…

 

testChar

Take me to the C

Pre for loop

Each:

dec: 84 char: T

Each:

dec: 97 char: a

Each:

dec: 107 char: k

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 109 char: m

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 111 char: o

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 104 char: h

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 67 char: C

testChar

?^

Pre for loop

Each:

dec: -112 char: ?

Each:

dec: 94 char: ^

Each:

dec: 127 char: 

 

 

 

 

 

-----Original Message-----
From: [hidden email] [mailto:[hidden email]] On Behalf Of Stewart MacLean
Sent:
7 April 2013 8:12 a.m.
To: [hidden email]
Subject: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

It’s been a while, but I’m back with VW working on a MIDI project (makes a change from Accounting!).

 

I’m aiming to interface to a C++ midi library written by Gary P. Scavone http://www.music.mcgill.ca/~gary/rtmidi/

via a C wrapper API written by Ben Swift https://github.com/benswift/rtmidi-c-api

 

I’ve had a little experience in compiling C and the DLLCC, but I’m running into a lot of problems. After spending days on this I’d very much appreciate any help on this!

 

The rig is: VW 7.9.1 on Mac OS X 10.7.3.

 

I’ve compiled a test midi out program in C++ and that (more or less) works fine. I’ve compiled the shared C library, and I can open it and retrieve lists of Midi ports from Smalltalk. This works most of the time with the odd failure. I’ve also been able to open ports, but this seems to always fail the first time, and work afterwards (depending on whether I use g++ or clang++ - clang++  faults). I’ve also been able to send a message, but the data being received in the API is random, and bears no relation to what I’m sending. (not sure, but maybe an encoding problem). I’m starting from the command line and writing to cout to debug.

 

I’ve tried all sorts of combinations drawn from the DLLC manual, the THAPI example, the net, Siren, and Jun.

 

The main problems I’m having is passing the portName to openOutPort, and passing the message contents to sendMessage:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){
     try {
          std::string name(portName); 
          dev->ptr->openPort(portNumber, name);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};

 

My understanding is that the DLLCC machinery should take a String and convert it to a null terminated string pointer. I’ve tried copying to heap, asFixed… etc. What is the correct way to pass this String in from Smalltalk?

 

The send message uses a relatively new type that is apparently guaranteed to be of fixed width. I just want to pass a ByteArray of the form #[144 94 127].  This gets loaded to a vector and shunted off to the C++ library. I’ve tried using char * and int * copyToHeap, creating CTypes etc. When the call works, I end up with random numbers that sometime stay the same between calls. Again this fails the first time, and then “work” subsequently.

 

rtErr sendMessage( outDevice* dev, int64_t messageLength, uint8_t* message ){
     try {
          std::vector<uint8_t> msgVector;
          for (int i = 0; i < (int)messageLength; ++i)
          {
               msgVector.push_back(message[i]);
          }
          dev->ptr->sendMessage(&msgVector);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};
 
What is the correct way of passing in a ByteArray into this function?
 
Any help appreciated!
 
Thanks,
 
Stewart

 


_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: DLLCC and RtMidi (Real Time MIDI) Interface

Steven Kelly

This wouldn’t be a String of length divisible by 4, and an instance of a platform-specific class like MSCP1252String rather than ByteString? I reported a bug for those years back, but its AR was rejected:

 

Case 358800: AR 47137: ExternalMethod call with MSCP1252 string of size multiples of 4 is changed

 

The bug was that the string was somehow corrupted when a multiple of 4. Internally VW has string sizes padded to 4 bytes, and thus if the size is not divisible by 4 it’s easy to set the next padding byte to 0x00, to be the C string terminator. But for strings already filled to the end of their last 4 byte segment with characters, whatever VW was doing (or not doing) to add the 0x00 terminator was resulting in a corrupt string.

 

The AR was rejected because this should have been obvious to DLLCC users J. However, it seems to have been fixed at least for Windows in VW 7.7.

 

All the best,

Steve

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Stewart MacLean
Sent: Monday, April 8, 2013 2:27 PM
To: [hidden email]
Subject: Re: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

OK, I’ve done some further testing and it looks like my first problem is with converting the pointer passed in from DLLCC to a string within the C function. Please see below for comparative test runs – the first fails, the second works fine. This indicates that the pointer string can be dereferenced ok within C.

 

When I create a string pointer locally within the C function, it works fine. With the string pointer from DLLCC, it terminates with a “0x95ff4b12 std::__throw_logic_error(char const*) + 121”

 

Any help in explaining this behaviour appreciated….

 

1) Failing Scenario….Smalltalk code…

testOutput

 

"

| if |

RtMidiExternalInterface unloadLibraries.

(if := self new)

open.

[if

openOutputPort: 1;   “<<<fails here…”

testOutput] ensure: [interface close]

 

 

| msg msgPointer |

 

RtMidiExternalInterface unloadLibraries.

msg := #[144 94 127] copy.

msgPointer := msg gcCopyToHeap.

 

interface sendMessage: outputDevice with: msg size with: msgPointer.

 

External interface….

 

openOutPort: dev with: portNumber with: portName

<C: rtErr openOutPort(outDevice * dev, int64_t portNumber, char * portName)>

^self externalAccessFailedWith: _errorCode

 

C Code…

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){

     try {

         std::cout << "openOutPort\n";

         //const char *s = "Hello, World!";   // works ok with these

         //std::string name(s); 

         

         std::string name(portName);    //<<<<<<  fails here

         std::cout << "Pre openPort\n";

         dev->ptr->openPort(portNumber, name);

         std::cout << "Pre RTMIDI_NOERROR\n";

         return RTMIDI_NOERROR;

     }

     catch ( RtError &error ) {

          error.printMessage();

          return RTMIDI_ERROR;

     }

};

 

Console output….

 

newMidiInDevice

getInPortCount

getInPortName

getInPortName

getInPortName

newMidiOutDevice

getOutPortCount

getOutPortName

getOutPortName

openOutPort

terminate called throwing an exceptionAbort trap: 6

Error log…

 

8   libstdc++.6.dylib             0x95ff4b12 std::__throw_logic_error(char const*) + 121

9   libstdc++.6.dylib             0x9601edb5 char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) + 163

10  libstdc++.6.dylib             0x9601ee76 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) + 60

11  librtmidi.dylib               0x0a76e859 openOutPort + 121

12  com.cincom.vw7.9.1             0x00077da9 pdCallC + 2545 (unx86CallC.c:368)

13  com.cincom.vw7.9.1             0x00006a26 primCallVarArgC + 332 (exCallC.c:512)

14  com.cincom.vw7.9.1             0x0005ee2b rtPrimGlueVarArgMany + 43

 

2) This works fine…Smalltalk code…

 

testChar

 

| msg msgPointer |

 

"

100 timesRepeat: [self new testChar].

"

 

RtMidiExternalInterface unloadLibraries.

interface := RtMidiExternalInterface new.

msg := 'Take me to the C'.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

 

msg := #[144 94 127] copy.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

External interface….

 

testChar: messageLength with: message

<C: rtErr testChar(int  messageLength, char*  message)>

^self externalAccessFailedWith: _errorCode

C Code…..

 

rtErr testChar(int messageLength, char* message ){

        std::cout << "testChar\n";

        std::cout << message;

        std::cout << "\nPre for loop\n";

        for (int i = 0; i < (int)messageLength; ++i)

            { std::cout << "Each:\n";

                printf("dec: %d char: %c\n", message[i], message[i]);

            }

        return RTMIDI_NOERROR;

};

 

Console output, all good…

 

testChar

Take me to the C

Pre for loop

Each:

dec: 84 char: T

Each:

dec: 97 char: a

Each:

dec: 107 char: k

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 109 char: m

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 111 char: o

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 104 char: h

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 67 char: C

testChar

?^

Pre for loop

Each:

dec: -112 char: ?

Each:

dec: 94 char: ^

Each:

dec: 127 char: 

 

 

 

 

 

-----Original Message-----
From: [hidden email] [[hidden email]] On Behalf Of Stewart MacLean
Sent: 7 April 2013 8:12 a.m.
To: [hidden email]
Subject: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

It’s been a while, but I’m back with VW working on a MIDI project (makes a change from Accounting!).

 

I’m aiming to interface to a C++ midi library written by Gary P. Scavone http://www.music.mcgill.ca/~gary/rtmidi/

via a C wrapper API written by Ben Swift https://github.com/benswift/rtmidi-c-api

 

I’ve had a little experience in compiling C and the DLLCC, but I’m running into a lot of problems. After spending days on this I’d very much appreciate any help on this!

 

The rig is: VW 7.9.1 on Mac OS X 10.7.3.

 

I’ve compiled a test midi out program in C++ and that (more or less) works fine. I’ve compiled the shared C library, and I can open it and retrieve lists of Midi ports from Smalltalk. This works most of the time with the odd failure. I’ve also been able to open ports, but this seems to always fail the first time, and work afterwards (depending on whether I use g++ or clang++ - clang++  faults). I’ve also been able to send a message, but the data being received in the API is random, and bears no relation to what I’m sending. (not sure, but maybe an encoding problem). I’m starting from the command line and writing to cout to debug.

 

I’ve tried all sorts of combinations drawn from the DLLC manual, the THAPI example, the net, Siren, and Jun.

 

The main problems I’m having is passing the portName to openOutPort, and passing the message contents to sendMessage:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){
     try {
          std::string name(portName); 
          dev->ptr->openPort(portNumber, name);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};

 

My understanding is that the DLLCC machinery should take a String and convert it to a null terminated string pointer. I’ve tried copying to heap, asFixed… etc. What is the correct way to pass this String in from Smalltalk?

 

The send message uses a relatively new type that is apparently guaranteed to be of fixed width. I just want to pass a ByteArray of the form #[144 94 127].  This gets loaded to a vector and shunted off to the C++ library. I’ve tried using char * and int * copyToHeap, creating CTypes etc. When the call works, I end up with random numbers that sometime stay the same between calls. Again this fails the first time, and then “work” subsequently.

 

rtErr sendMessage( outDevice* dev, int64_t messageLength, uint8_t* message ){
     try {
          std::vector<uint8_t> msgVector;
          for (int i = 0; i < (int)messageLength; ++i)
          {
               msgVector.push_back(message[i]);
          }
          dev->ptr->sendMessage(&msgVector);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};
 
What is the correct way of passing in a ByteArray into this function?
 
Any help appreciated!
 
Thanks,
 
Stewart

 


_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: DLLCC and RtMidi (Real Time MIDI) Interface

Wallen, David
In reply to this post by Stew MacLean

In addition to Steve’s notes, hopefully some of the following rambling will be of use. In no particular order…

 

I.

msg := 'Take me to the C'.

msgPointer := msg.

interface testChar: msg size with: msgPointer.

 

I think this works because DLLCC automatically appends the null terminator when passing a Smalltalk ByteString.

(If you pass a ByteArray instead, DLLCC has no inclination to append a null, and the C function will interpret all the ensuing chars up to the first zero byte valued character, wherever that occurs, as the intended string. This can cause all sorts of trouble.)

 

> msg := #[144 94 127] copy.

> msgPointer := msg.

> 

> interface testChar: msg size with: msgPointer.

 

I don't think the above will include the null terminator, so it's good that you use the loop in this case. I'm a little surprised that the cout<< call works in the C function--perhaps the next char on the heap is a zero byte.

 

 

 

II.

> [if

> openOutputPort: 1;   “<<<fails here…”

> testOutput] ensure: [interface close]

 

I'm probably missing something, but here it looks like you're not passing the other required parameters to the call. I assume there's another Smalltalk method which fills in the missing parameters and makes the actuall DLLCC call using, say:

openOutPort: dev with: portNumber with: portName

 

Eg., you'd want something like:

   if openOutPort: 1 with: 115 with: 'abc'.

or

   if openOutPort: 1 with: 115 with: 'abc' copyToHeap.

 

(You can test this by inspecting:

                ('abc' copyToHeap) copyCStringFromHeap

Note that I wouldn't use gcCopy variants at first because they can involve garbage collection surprise issues. For example, if the DLL expects to use the pointer later, you don't want it to be GC'd. This can be tricky with thapi. Also, structs that contain gcMalloc'd objects do not (iirc) know enough to tell the GC to stay away from their member objects. So, a held reference to a struct will not prevent its internal gcMalloc'd members from disappearing. Later, replace malloc with gcMalloc and see what needs tweaking. (This development approach is okay as long as your development image can tolerate some buildup of un-freed pointers.)

 

 

III.

> msg := #[144 94 127] copy.

> msgPointer := msg gcCopyToHeap.

>

> interface sendMessage: outputDevice with: msg size with: msgPointer.

 

I’m not sure why this fails. In your C function, you appear to be successfully appending the characters to an empty Vector. Assuming that the receiver of the Vector object knows how to deal with it, things should work.

 

 

IV.

On a minor note, you might also want to check that the type, int64_t is declared to be a long long (assuming it's 64-bits).

Eg., here’s an example from MySQLInterface,

                my_ulonglong

                   <C: typedef unsigned long long my_ulonglong>

 

Hth,

-Dave

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Stewart MacLean
Sent: Monday, April 08, 2013 4:27 AM
To: [hidden email]
Subject: Re: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

OK, I’ve done some further testing and it looks like my first problem is with converting the pointer passed in from DLLCC to a string within the C function. Please see below for comparative test runs – the first fails, the second works fine. This indicates that the pointer string can be dereferenced ok within C.

 

When I create a string pointer locally within the C function, it works fine. With the string pointer from DLLCC, it terminates with a “0x95ff4b12 std::__throw_logic_error(char const*) + 121”

 

Any help in explaining this behaviour appreciated….

 

1) Failing Scenario….Smalltalk code…

testOutput

 

"

| if |

RtMidiExternalInterface unloadLibraries.

(if := self new)

open.

[if

openOutputPort: 1;   “<<<fails here…”

testOutput] ensure: [interface close]

 

 

| msg msgPointer |

 

RtMidiExternalInterface unloadLibraries.

msg := #[144 94 127] copy.

msgPointer := msg gcCopyToHeap.

 

interface sendMessage: outputDevice with: msg size with: msgPointer.

 

External interface….

 

openOutPort: dev with: portNumber with: portName

<C: rtErr openOutPort(outDevice * dev, int64_t portNumber, char * portName)>

^self externalAccessFailedWith: _errorCode

 

C Code…

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){

     try {

         std::cout << "openOutPort\n";

         //const char *s = "Hello, World!";   // works ok with these

         //std::string name(s); 

         

         std::string name(portName);    //<<<<<<  fails here

         std::cout << "Pre openPort\n";

         dev->ptr->openPort(portNumber, name);

         std::cout << "Pre RTMIDI_NOERROR\n";

         return RTMIDI_NOERROR;

     }

     catch ( RtError &error ) {

          error.printMessage();

          return RTMIDI_ERROR;

     }

};

 

Console output….

 

newMidiInDevice

getInPortCount

getInPortName

getInPortName

getInPortName

newMidiOutDevice

getOutPortCount

getOutPortName

getOutPortName

openOutPort

terminate called throwing an exceptionAbort trap: 6

Error log…

 

8   libstdc++.6.dylib             0x95ff4b12 std::__throw_logic_error(char const*) + 121

9   libstdc++.6.dylib             0x9601edb5 char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) + 163

10  libstdc++.6.dylib             0x9601ee76 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) + 60

11  librtmidi.dylib               0x0a76e859 openOutPort + 121

12  com.cincom.vw7.9.1             0x00077da9 pdCallC + 2545 (unx86CallC.c:368)

13  com.cincom.vw7.9.1             0x00006a26 primCallVarArgC + 332 (exCallC.c:512)

14  com.cincom.vw7.9.1             0x0005ee2b rtPrimGlueVarArgMany + 43

 

2) This works fine…Smalltalk code…

 

testChar

 

| msg msgPointer |

 

"

100 timesRepeat: [self new testChar].

"

 

RtMidiExternalInterface unloadLibraries.

interface := RtMidiExternalInterface new.

msg := 'Take me to the C'.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

 

msg := #[144 94 127] copy.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

External interface….

 

testChar: messageLength with: message

<C: rtErr testChar(int  messageLength, char*  message)>

^self externalAccessFailedWith: _errorCode

C Code…..

 

rtErr testChar(int messageLength, char* message ){

        std::cout << "testChar\n";

        std::cout << message;

        std::cout << "\nPre for loop\n";

        for (int i = 0; i < (int)messageLength; ++i)

            { std::cout << "Each:\n";

                printf("dec: %d char: %c\n", message[i], message[i]);

            }

        return RTMIDI_NOERROR;

};

 

Console output, all good…

 

testChar

Take me to the C

Pre for loop

Each:

dec: 84 char: T

Each:

dec: 97 char: a

Each:

dec: 107 char: k

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 109 char: m

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 111 char: o

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 104 char: h

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 67 char: C

testChar

?^

Pre for loop

Each:

dec: -112 char: ?

Each:

dec: 94 char: ^

Each:

dec: 127 char: 

 

 

 

 

 

-----Original Message-----
From: [hidden email] [mailto:[hidden email]] On Behalf Of Stewart MacLean
Sent: 7 April 2013 8:12 a.m.
To: [hidden email]
Subject: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

It’s been a while, but I’m back with VW working on a MIDI project (makes a change from Accounting!).

 

I’m aiming to interface to a C++ midi library written by Gary P. Scavone http://www.music.mcgill.ca/~gary/rtmidi/

via a C wrapper API written by Ben Swift https://github.com/benswift/rtmidi-c-api

 

I’ve had a little experience in compiling C and the DLLCC, but I’m running into a lot of problems. After spending days on this I’d very much appreciate any help on this!

 

The rig is: VW 7.9.1 on Mac OS X 10.7.3.

 

I’ve compiled a test midi out program in C++ and that (more or less) works fine. I’ve compiled the shared C library, and I can open it and retrieve lists of Midi ports from Smalltalk. This works most of the time with the odd failure. I’ve also been able to open ports, but this seems to always fail the first time, and work afterwards (depending on whether I use g++ or clang++ - clang++  faults). I’ve also been able to send a message, but the data being received in the API is random, and bears no relation to what I’m sending. (not sure, but maybe an encoding problem). I’m starting from the command line and writing to cout to debug.

 

I’ve tried all sorts of combinations drawn from the DLLC manual, the THAPI example, the net, Siren, and Jun.

 

The main problems I’m having is passing the portName to openOutPort, and passing the message contents to sendMessage:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){
     try {
          std::string name(portName); 
          dev->ptr->openPort(portNumber, name);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};

 

My understanding is that the DLLCC machinery should take a String and convert it to a null terminated string pointer. I’ve tried copying to heap, asFixed… etc. What is the correct way to pass this String in from Smalltalk?

 

The send message uses a relatively new type that is apparently guaranteed to be of fixed width. I just want to pass a ByteArray of the form #[144 94 127].  This gets loaded to a vector and shunted off to the C++ library. I’ve tried using char * and int * copyToHeap, creating CTypes etc. When the call works, I end up with random numbers that sometime stay the same between calls. Again this fails the first time, and then “work” subsequently.

 

rtErr sendMessage( outDevice* dev, int64_t messageLength, uint8_t* message ){
     try {
          std::vector<uint8_t> msgVector;
          for (int i = 0; i < (int)messageLength; ++i)
          {
               msgVector.push_back(message[i]);
          }
          dev->ptr->sendMessage(&msgVector);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};
 
What is the correct way of passing in a ByteArray into this function?
 
Any help appreciated!
 
Thanks,
 
Stewart

 


_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc
Reply | Threaded
Open this post in threaded view
|

Re: DLLCC and RtMidi (Real Time MIDI) Interface

Stew MacLean

Hi Guys,

 

It wasn’t the bug “ExternalMethod call with MSCP1252 string of size multiples of 4 is changed”.

 

It was actually David’s “minor note” that put me right, re the int64_t type. I document it below for any other DLLCC newbies

 

I was focusing on the wrong parameter, as the String was manifesting the error. I think the pointer to the string was misaligned due to the previous parameter declaration being a pointer type, and DLLCC was just passing the integer. In order to make it work as is, I guess I should have passed a pointer to the integer?

 

I’m curious, should DLLCC have parsed int64_t  as David suggested?:

 

my_ulonglong

<C: typedef unsigned long long my_ulonglong>

 

Having subsequently browsed through the DLLCCTest classes (which doesn’t appear to work) I don’t see any of the “uintN_t” types. I guess DLLCC is showing signs of age?

 

Thanks heaps for the help – I can now send midi data with no errors. Next challenge is to get midi in via the callback to work.

 

Cheers,

 

Stewart

 

===============================================================================================

The interface builder had parsed:

 

Header file:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName );

 

c wrapper function:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){

     try {

          std::string name(portName); 

          dev->ptr->openPort(portNumber, name);

          return RTMIDI_NOERROR;

     }

     catch ( RtError &error ) {

          error.printMessage();

          return RTMIDI_ERROR;

     }

};

 

Type Declaration generated:

 

int64_t

<C: typedef void * int64_t>

 

DLL procedure:

 

openOutPort: dev with: portNumber with: portName

<C: rtErr openOutPort(outDevice * dev, int64_t portNumber, char * portName)>

^self externalAccessFailedWith: _errorCode

 

 

Called with:  outputPortNumber = 1, no copying to heap or making pointers

 

openOutputPort: outputPortNumber

 

| return msg msgPointer |

msg := 'An Output Port Name 64'.

msgPointer := msg.

 

(return := interface 

openOutPort: outputDevice

with: outputPortNumber

with: msgPointer) asInteger = 0 

ifFalse: [return inspect. self error: 'Open Output Port Failed'].

 

Gave system exceptions on the first string function in the c routine.

 

The fix was to use plan old unsigned int instead (which is actually what the C++ RtMidi function that gets called uses!)

 

-----Original Message-----
From: Wallen, David [mailto:[hidden email]]
Sent:
9 April 2013 7:45 a.m.
To: [hidden email]; [hidden email]
Subject: RE: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

In addition to Steve’s notes, hopefully some of the following rambling will be of use. In no particular order…

 

I.

msg := 'Take me to the C'.

msgPointer := msg.

interface testChar: msg size with: msgPointer.

 

I think this works because DLLCC automatically appends the null terminator when passing a Smalltalk ByteString.

(If you pass a ByteArray instead, DLLCC has no inclination to append a null, and the C function will interpret all the ensuing chars up to the first zero byte valued character, wherever that occurs, as the intended string. This can cause all sorts of trouble.)

 

> msg := #[144 94 127] copy.

> msgPointer := msg.

>

> interface testChar: msg size with: msgPointer.

 

I don't think the above will include the null terminator, so it's good that you use the loop in this case. I'm a little surprised that the cout<< call works in the C function--perhaps the next char on the heap is a zero byte.

 

 

 

II.

> [if

> openOutputPort: 1;   “<<<fails here…”

> testOutput] ensure: [interface close]

 

I'm probably missing something, but here it looks like you're not passing the other required parameters to the call. I assume there's another Smalltalk method which fills in the missing parameters and makes the actuall DLLCC call using, say:

openOutPort: dev with: portNumber with: portName

 

Eg., you'd want something like:

   if openOutPort: 1 with: 115 with: 'abc'.

or

   if openOutPort: 1 with: 115 with: 'abc' copyToHeap.

 

(You can test this by inspecting:

                ('abc' copyToHeap) copyCStringFromHeap

Note that I wouldn't use gcCopy variants at first because they can involve garbage collection surprise issues. For example, if the DLL expects to use the pointer later, you don't want it to be GC'd. This can be tricky with thapi. Also, structs that contain gcMalloc'd objects do not (iirc) know enough to tell the GC to stay away from their member objects. So, a held reference to a struct will not prevent its internal gcMalloc'd members from disappearing. Later, replace malloc with gcMalloc and see what needs tweaking. (This development approach is okay as long as your development image can tolerate some buildup of un-freed pointers.)

 

 

III.

> msg := #[144 94 127] copy.

> msgPointer := msg gcCopyToHeap.

>

> interface sendMessage: outputDevice with: msg size with: msgPointer.

 

I’m not sure why this fails. In your C function, you appear to be successfully appending the characters to an empty Vector. Assuming that the receiver of the Vector object knows how to deal with it, things should work.

 

 

IV.

On a minor note, you might also want to check that the type, int64_t is declared to be a long long (assuming it's 64-bits).

Eg., here’s an example from MySQLInterface,

                my_ulonglong

                   <C: typedef unsigned long long my_ulonglong>

 

Hth,

-Dave

 

From: [hidden email] [mailto:[hidden email]] On Behalf Of Stewart MacLean
Sent:
Monday, April 08, 2013 4:27 AM
To: [hidden email]
Subject: Re: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

OK, I’ve done some further testing and it looks like my first problem is with converting the pointer passed in from DLLCC to a string within the C function. Please see below for comparative test runs – the first fails, the second works fine. This indicates that the pointer string can be dereferenced ok within C.

 

When I create a string pointer locally within the C function, it works fine. With the string pointer from DLLCC, it terminates with a “0x95ff4b12 std::__throw_logic_error(char const*) + 121”

 

Any help in explaining this behaviour appreciated….

 

1) Failing Scenario….Smalltalk code…

testOutput

 

"

| if |

RtMidiExternalInterface unloadLibraries.

(if := self new)

open.

[if

openOutputPort: 1;   “<<<fails here…”

testOutput] ensure: [interface close]

 

 

| msg msgPointer |

 

RtMidiExternalInterface unloadLibraries.

msg := #[144 94 127] copy.

msgPointer := msg gcCopyToHeap.

 

interface sendMessage: outputDevice with: msg size with: msgPointer.

 

External interface….

 

openOutPort: dev with: portNumber with: portName

<C: rtErr openOutPort(outDevice * dev, int64_t portNumber, char * portName)>

^self externalAccessFailedWith: _errorCode

 

C Code…

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){

     try {

         std::cout << "openOutPort\n";

         //const char *s = "Hello, World!";   // works ok with these

         //std::string name(s); 

         

         std::string name(portName);    //<<<<<<  fails here

         std::cout << "Pre openPort\n";

         dev->ptr->openPort(portNumber, name);

         std::cout << "Pre RTMIDI_NOERROR\n";

         return RTMIDI_NOERROR;

     }

     catch ( RtError &error ) {

          error.printMessage();

          return RTMIDI_ERROR;

     }

};

 

Console output….

 

newMidiInDevice

getInPortCount

getInPortName

getInPortName

getInPortName

newMidiOutDevice

getOutPortCount

getOutPortName

getOutPortName

openOutPort

terminate called throwing an exceptionAbort trap: 6

Error log…

 

8   libstdc++.6.dylib             0x95ff4b12 std::__throw_logic_error(char const*) + 121

9   libstdc++.6.dylib             0x9601edb5 char* std::string::_S_construct<char const*>(char const*, char const*, std::allocator<char> const&, std::forward_iterator_tag) + 163

10  libstdc++.6.dylib             0x9601ee76 std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) + 60

11  librtmidi.dylib               0x0a76e859 openOutPort + 121

12  com.cincom.vw7.9.1             0x00077da9 pdCallC + 2545 (unx86CallC.c:368)

13  com.cincom.vw7.9.1             0x00006a26 primCallVarArgC + 332 (exCallC.c:512)

14  com.cincom.vw7.9.1             0x0005ee2b rtPrimGlueVarArgMany + 43

 

2) This works fine…Smalltalk code…

 

testChar

 

| msg msgPointer |

 

"

100 timesRepeat: [self new testChar].

"

 

RtMidiExternalInterface unloadLibraries.

interface := RtMidiExternalInterface new.

msg := 'Take me to the C'.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

 

msg := #[144 94 127] copy.

msgPointer := msg.

 

interface testChar: msg size with: msgPointer.

External interface….

 

testChar: messageLength with: message

<C: rtErr testChar(int  messageLength, char*  message)>

^self externalAccessFailedWith: _errorCode

C Code…..

 

rtErr testChar(int messageLength, char* message ){

        std::cout << "testChar\n";

        std::cout << message;

        std::cout << "\nPre for loop\n";

        for (int i = 0; i < (int)messageLength; ++i)

            { std::cout << "Each:\n";

                printf("dec: %d char: %c\n", message[i], message[i]);

            }

        return RTMIDI_NOERROR;

};

 

Console output, all good…

 

testChar

Take me to the C

Pre for loop

Each:

dec: 84 char: T

Each:

dec: 97 char: a

Each:

dec: 107 char: k

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 109 char: m

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 111 char: o

Each:

dec: 32 char:  

Each:

dec: 116 char: t

Each:

dec: 104 char: h

Each:

dec: 101 char: e

Each:

dec: 32 char:  

Each:

dec: 67 char: C

testChar

?^

Pre for loop

Each:

dec: -112 char: ?

Each:

dec: 94 char: ^

Each:

dec: 127 char: 

 

 

 

 

 

-----Original Message-----
From: [hidden email] [mailto:[hidden email]] On Behalf Of Stewart MacLean
Sent:
7 April 2013 8:12 a.m.
To: [hidden email]
Subject: [vwnc] DLLCC and RtMidi (Real Time MIDI) Interface

 

It’s been a while, but I’m back with VW working on a MIDI project (makes a change from Accounting!).

 

I’m aiming to interface to a C++ midi library written by Gary P. Scavone http://www.music.mcgill.ca/~gary/rtmidi/

via a C wrapper API written by Ben Swift https://github.com/benswift/rtmidi-c-api

 

I’ve had a little experience in compiling C and the DLLCC, but I’m running into a lot of problems. After spending days on this I’d very much appreciate any help on this!

 

The rig is: VW 7.9.1 on Mac OS X 10.7.3.

 

I’ve compiled a test midi out program in C++ and that (more or less) works fine. I’ve compiled the shared C library, and I can open it and retrieve lists of Midi ports from Smalltalk. This works most of the time with the odd failure. I’ve also been able to open ports, but this seems to always fail the first time, and work afterwards (depending on whether I use g++ or clang++ - clang++  faults). I’ve also been able to send a message, but the data being received in the API is random, and bears no relation to what I’m sending. (not sure, but maybe an encoding problem). I’m starting from the command line and writing to cout to debug.

 

I’ve tried all sorts of combinations drawn from the DLLC manual, the THAPI example, the net, Siren, and Jun.

 

The main problems I’m having is passing the portName to openOutPort, and passing the message contents to sendMessage:

 

rtErr openOutPort( outDevice* dev, int64_t portNumber, char* portName ){
     try {
          std::string name(portName); 
          dev->ptr->openPort(portNumber, name);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};

 

My understanding is that the DLLCC machinery should take a String and convert it to a null terminated string pointer. I’ve tried copying to heap, asFixed… etc. What is the correct way to pass this String in from Smalltalk?

 

The send message uses a relatively new type that is apparently guaranteed to be of fixed width. I just want to pass a ByteArray of the form #[144 94 127].  This gets loaded to a vector and shunted off to the C++ library. I’ve tried using char * and int * copyToHeap, creating CTypes etc. When the call works, I end up with random numbers that sometime stay the same between calls. Again this fails the first time, and then “work” subsequently.

 

rtErr sendMessage( outDevice* dev, int64_t messageLength, uint8_t* message ){
     try {
          std::vector<uint8_t> msgVector;
          for (int i = 0; i < (int)messageLength; ++i)
          {
               msgVector.push_back(message[i]);
          }
          dev->ptr->sendMessage(&msgVector);
          return RTMIDI_NOERROR;
     }
     catch ( RtError &error ) {
          error.printMessage();
          return RTMIDI_ERROR;
     }
};
 
What is the correct way of passing in a ByteArray into this function?
 
Any help appreciated!
 
Thanks,
 
Stewart

 


_______________________________________________
vwnc mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/vwnc