[MacRuby] #733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called
#733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called -----------------------------------+---------------------------------------- Reporter: nagachika00@… | Owner: lsansonetti@… Type: defect | Status: new Priority: blocker | Milestone: Component: MacRuby | Keywords: -----------------------------------+---------------------------------------- I'm trying to output arbitrary waveform data to audio device with CoreAudio Framework, and confront a callback function problem. I attach sample scripts. At first, I have to apply a small dirty patch to MacRuby to avoid {{{"unrecognized runtime type `?'"}}} exception. The function AudioDeviceStart/Stop take an argument has type `"^?"`, but MacRuby doesn't allow that type. I attach the patch too. In my sample, register Proc as callback function with AudioDeviceCreateIOProcID() seems success. Then call AudioDeviceStart() to start playback, but the Proc object seems never called. The sample script intends play silent waveform, so make no sound is OK, but log message to stdout with Kernel#p method isn't available. The callback function in CoreAudio is called from CoreAudio internal thread. Is that the cause of the problem? {{{ $ macruby -I. coreaudio_sample.rb Default Output Device ID = 268 #<Pointer:0x2002565a0> start sleep macruby(19434,0x103cc7000) malloc: *** auto malloc[19434]: error: GC operation on unregistered thread. Thread registered implicitly. Break on auto_zone_thread_registration_error() to debug. stop }}} -- Ticket URL: <http://www.macruby.org/trac/ticket/733> MacRuby <http://macruby.org/>
#733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called -----------------------------------+---------------------------------------- Reporter: nagachika00@… | Owner: lsansonetti@… Type: defect | Status: new Priority: blocker | Milestone: Component: MacRuby | Keywords: -----------------------------------+---------------------------------------- Comment(by martinlagardette@…): The problem comes from multiple things actually: - The CoreAudio BridgeSupport file is incorrect. The `AudioDeviceCreateIOProcID` function is said to receive `^?` (pointer to something unknow), when it shall say a pointer to a function, because `AudioDeviceIOProcID` is actually typedef-ed to a function pointer. - MacRuby's BridgeSupport parser doesn't support pointers to function pointers. I have a local patch that fixes that, and might commit later, but: - MacRuby doesn't know yet how to convert a C function pointer to a Ruby Proc. I'll try to see if I can do that. In any case, even if I fix the two MacRuby's problems, CoreAudio's BridgeSupport is not correct and will have to be modified manually for it to work. -- Ticket URL: <http://www.macruby.org/trac/ticket/733#comment:1> MacRuby <http://macruby.org/>
#733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called -----------------------------------+---------------------------------------- Reporter: nagachika00@… | Owner: lsansonetti@… Type: defect | Status: new Priority: blocker | Milestone: Component: MacRuby | Keywords: -----------------------------------+---------------------------------------- Comment(by nagachika00@…): Replying to [comment:1 martinlagardette@…]: Thank you for your inspection the problem. I have two questions.
- MacRuby doesn't know yet how to convert a C function pointer to a Ruby Proc. I'll try to see if I can do that. Did you mean that MacRuby cannot make callback Proc Object for function pointer returned from last argument of AudioDeviceCreateIOProcID()? The signature of AudioDeviceCreateIOProcID() is as below. {{{ AudioDeviceCreateIOProcID( AudioDeviceID inDevice, AudioDeviceIOProc inProc, void *inClientData, AudioDeviceIOProcID *outIOProcID) }}} AudioDeviceIOProcID is typedef-ed to AudioDeviceIOProc. I tried to experiment in C program to examine relationship between second and forth argument of AudioDeviceCreateIOProcID().
{{{ $ cat test.m --- snip --- status = AudioDeviceCreateIOProcID(devID, IOProc, NULL, &procID); printf("proc ID = %p\n", (void *)procID); printf("IOProc = %p\n", (void*)IOProc); --- snip --- $ gcc -o test test.m -framework CoreAudio $ ./test proc ID = 0x100111340 IOProc = 0x100000ae8 }}} The pointer to function passed to second argument 'IOProc' is differ to returned AudioDeviceIOProcID 'procID'. Surely call 'procID' as function cause bus error. When I call AudioDeviceStart() with 'procID', CoreAudio start to invoke callback function 'IOProc'. So I think CoreAudio can call Ruby Proc object via stub callback function passed to second argument. Is this my misconception?
In any case, even if I fix the two MacRuby's problems, CoreAudio's BridgeSupport is not correct and will have to be modified manually for it to work. If I want to distribute my MacRuby Application, can I include modified version of BridgeSupport file with it, and let MacRuby use it instead of system's one?
-- Ticket URL: <http://www.macruby.org/trac/ticket/733#comment:2> MacRuby <http://macruby.org/>
#733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called -----------------------------------+---------------------------------------- Reporter: nagachika00@… | Owner: lsansonetti@… Type: defect | Status: new Priority: blocker | Milestone: Component: MacRuby | Keywords: -----------------------------------+---------------------------------------- Comment(by martinlagardette@…): Replying to [comment:2 nagachika00@…]:
Did you mean that MacRuby cannot make callback Proc Object for function pointer returned from last argument of AudioDeviceCreateIOProcID()? [...] So I think CoreAudio can call Ruby Proc object via stub callback function passed to second argument. Is this my misconception?
I'm not sure I understand exactly what you're asking, so let me know if I'm not correctly answering: In its current implementation, when calling Obj-C / C methods, MacRuby converts ruby values (Fixnum, Strings, etc.) to C values (long, char *, etc.). When the needed C value is a function pointer, it will check if the argument is a Proc. If it is, it creates a "trampoline" function that will be passed to the C function, and the trampoline function simply "calls" (executes) the Proc. However, the contrary is not implemented. Aka, if a function returns a function pointer, or take a pointer to a function pointer, MacRuby does not know, yet, how to take this function pointer and make it a Ruby value (ideally a Proc). That is the current problem. When you pass the Pointer to the `AudioDeviceCreateIOProcID`, the pointer is filled with a function pointer. However, once the function returns, MacRuby has to translate this pointer to function pointer to some ruby value that can be used. And for now, we don't know how to do that :-).
If I want to distribute my MacRuby Application, can I include modified version of BridgeSupport file with it, and let MacRuby use it instead of system's one?
You can load custom bridge support files, I guess you could do that before loading the framework (if I remember correctly, similar bridgesupport files are not loaded twice). I'll need to make sure though, but I think it's possible. -- Ticket URL: <http://www.macruby.org/trac/ticket/733#comment:3> MacRuby <http://macruby.org/>
#733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called -----------------------------------+---------------------------------------- Reporter: nagachika00@… | Owner: lsansonetti@… Type: defect | Status: new Priority: blocker | Milestone: Component: MacRuby | Keywords: -----------------------------------+---------------------------------------- Comment(by nagachika00@…): Replying to [comment:3 martinlagardette@…]:
I'm not sure I understand exactly what you're asking, so let me know if I'm not correctly answering: I'm sorry for my ambiguous question. I understand the limitation of current MacRuby about function pointer returned from C/Obj-C functions. But in this case, I think the limitation is not matter because returned function pointer (procID) is not called by application, but just passed to AudioDeviceStart(). I wonder if the definition of AudioDeviceIOProcID is mistake in design of API. It is just an 'ID', not really function pointer (it is never called).
I guess the cause of problem is... 1. The callback function is called from CoreAudio's internal new thread. The thread may be not ready for exception or some other preparation for execute MacRuby method (I'm not sure any preparations are needed). 2. The trampoline function's lifetime maybe limited until return of call C/Obj-C function which Proc object is passed. If trampoline function is sweeped after call of AudioDeviceCreateIOProcID(), AudioDeviceStart() cannot call it. I'm afraid I have a terrible misunderstanding...
You can load custom bridge support files, Thank you! I'll try it :)
-- Ticket URL: <http://www.macruby.org/trac/ticket/733#comment:4> MacRuby <http://macruby.org/>
#733: Proc as callback function for CoreAudio(AudioDeviceCreateIOProcID) isn't called -----------------------------------+---------------------------------------- Reporter: nagachika00@… | Owner: lsansonetti@… Type: defect | Status: new Priority: blocker | Milestone: Component: MacRuby | Keywords: -----------------------------------+---------------------------------------- Comment(by martinlagardette@…): If you look at the CoreAudio `AudioHardware.h` header file: {{{ #!c /*! @typedef AudioDeviceIOProc @abstract An AudioDeviceIOProc is called by an AudioDevice to provide input data read from the device and collect output data to be written to the device for the current IO cycle. [...] */ typedef OSStatus (*AudioDeviceIOProc)( AudioDeviceID inDevice, const AudioTimeStamp* inNow, const AudioBufferList* inInputData, const AudioTimeStamp* inInputTime, AudioBufferList* outOutputData, const AudioTimeStamp* inOutputTime, void* inClientData); /*! @typedef AudioDeviceIOProcID @abstract An AudioDeviceIOProcID represents both an IOProc and the client data that goes with it. Once created, an AudioDeviceIOProcID can be used everywhere one would use a regular IOProc. The purpose for an AudioDeviceIOProcID is to allow a client to register the same function pointer as an IOProc with a device multiple times provided */ typedef AudioDeviceIOProc AudioDeviceIOProcID; /* [...] */ extern OSStatus AudioDeviceCreateIOProcID( AudioDeviceID inDevice, AudioDeviceIOProc inProc, void* inClientData, AudioDeviceIOProcID* outIOProcID) }}} As you can see, `AudioDeviceCreateIOProcID`'s 4th argument is a pointer to `AudioDeviceIOProcID`, which itself definitely is a function pointer. The thing is, a pointer to a function pointer, here, is behaving a little as if it was a return value. Even if we don't call this function pointer directly, because we just pass it around other functions, well, we cannot know that. Once the function returns, we *have* to convert every "returned" variable to Ruby values so that they can be used around your code, whatever you do with it. -- Ticket URL: <http://www.macruby.org/trac/ticket/733#comment:5> MacRuby <http://macruby.org/>
participants (1)
-
MacRuby