Forum Archive

Obj_C struct OSType define problem.

wolf71

Using objc_util call objc function,need define a struct.
OSType is c_char_P ??

from objc_util import *
import ctypes

AVAudioUnit=ObjCClass('AVAudioUnit')
AVAudioUnitComponent=ObjCClass('AVAudioUnitComponent')
AVAudioUnitComponentManager=ObjCClass('AVAudioUnitComponentManager')

#
# componentType,componentSubType,componentManufacturer is OSType
#
# ref:  developer.apple.com/reference/audiotoolbox/audiocomponentdescription
#
class AudioComponentDescription(ctypes.Structure):
    _fields_=[('componentType',ctypes.c_char_p),('componentSubType',ctypes.c_char_p),('componentManufacturer',ctypes.c_char_p),('componentFlags',ctypes.c_uint32),('componentFlagsMask',ctypes.c_uint32)]

anyEffect = AudioComponentDescription('aufx','dcmp','appl',0,0)
availableEffects = AVAudioUnitComponentManager.sharedAudioUnitComponentManager().componentsMatchingDescription_(anyEffect)

Error Message:
File "/var/containers/Bundle/Application/1E0EEE41-E357-4C4B-9564-45BDC5519D55/Pythonista3.app/Frameworks/PythonistaKit.framework/pylib/site-packages/objc_util.py", line 897, in call
res = objc_msgSend(obj.ptr, sel(self.sel_name), *args)
ArgumentError: argument 3: : expected AudioComponentDescription_Structure instance instead of AudioComponentDescription

Cethric

@wolf71 objc_util dynamically creates its own copy of the AudioComponentDescription hence your ArgumentError This can be resolved either by using the objc_util version (can remberer exactly how to do that) or you specify that you want to use your copy. Eg

from objc_util import *
import ctypes

AVAudioUnit=ObjCClass('AVAudioUnit')
AVAudioUnitComponent=ObjCClass('AVAudioUnitComponent')
AVAudioUnitComponentManager=ObjCClass('AVAudioUnitComponentManager')

#
# componentType,componentSubType,componentManufacturer is OSType
#
# ref:  developer.apple.com/reference/audiotoolbox/audiocomponentdescription
#
class AudioComponentDescription(ctypes.Structure):
    _fields_=[('componentType',ctypes.c_char_p),('componentSubType',ctypes.c_char_p),('componentManufacturer',ctypes.c_char_p),('componentFlags',ctypes.c_uint32),('componentFlagsMask',ctypes.c_uint32)]

anyEffect = AudioComponentDescription('aufx','dcmp','appl',0,0)
availableEffects = AVAudioUnitComponentManager.sharedAudioUnitComponentManager().componentsMatchingDescription_(anyEffect, argtypes=[AudioComponentDescription])
wolf71

Add argtypes=[AudioComponentDescription]

But Same Error Message:

availableEffects = AVAudioUnitComponentManager.sharedAudioUnitComponentManager().componentsMatchingDescription_(anyEffect,argtypes=[AudioComponentDescription])
File "/var/containers/Bundle/Application/1E0EEE41-E357-4C4B-9564-45BDC5519D55/Pythonista3.app/Frameworks/PythonistaKit.framework/pylib/site-packages/objc_util.py", line 897, in call
res = objc_msgSend(obj.ptr, sel(self.sel_name), *args)
ArgumentError: argument 3: : expected AudioComponentDescription_Structure instance instead of AudioComponentDescription

JonB

In order to override the type detection, you need to specify both restype and argtypes.

.....(anyEffect , restype=c_void_p, argtypes=[ArgumentDescription])
wolf71

@JonB add restype, but the same error message.

OSType using ctypes.c_char_p ??

JonB

Are you using the latest beta, or the app store version?
https://github.com/omz/Pythonista-Issues/issues/68 is likely the issue. Check line 801 in objc_util.py, shuld say

    kw = {k: kwargs[k] for k in ('restype', 'argtypes') if k in kwargs}

and not

    kw = {k: kwargs[k] for k in ('restype', 'kwargs') if k in kwargs}

A workaround if you are on the app store version or an older beta would be to replace this line in an objc_util.py that you put in site packages. If you have an old beta, just upgrade to latest testflight version

I don't understand what you were asking about OSType...is that a separate question? I did not see any code that uses it.

JonB

ot, OSType is c_uint32, which is 4 chars (4 bytes). I forget the trick to get a b'abcd' style bytes converted to an integer..

wolf71

App store version.

JonB

For the fourcc code, you will want to use

ctypes.c_uint32

then, to create such an integer you would use

struct.unpack('I',b'auxf')[0]

You will neeed to patch your objc_util though, OR create an ObjCInstanceMethod manually:

manager=AVAudioUnitComponentManager.sharedAudioUnitComponentManager()

availableEffects = ObjCInstanceMethod(manager,'componentsMatchingDescription_')(anyEffect, argtypes=[AudioComponentDescription],restype=c_void_p)
wolf71

@JonB Thank you very much.

It's ok.

wolf71

@JonB

swift

let anyEffect = AudioComponentDescription(componentType:kAudioUnitType_Effect,componentSubType:kAudioUnitSubType_Delay,componentManufacturer:0,componentFlags:0,componentFlagsMask:0)

        let availableEffects: [AVAudioUnitComponent] = AVAudioUnitComponentManager.shared().components(matching: anyEffect)

        AVAudioUnit.instantiate(with: availableEffects[0].audioComponentDescription, options: []) {
            avAudioUnit, error in
            guard let avAudioUnit = avAudioUnit else { return }
            debugPrint(avAudioUnit.audioComponentDescription)
            self.ae.attach(avAudioUnit)

Python:
```
def AVAudioUnitComp(self,avAudioUnit,error):
self.effect = ObjCInstance(avAudioUnit)
self.engine.attachNode(self.effect)

AVAudioUnitComp1=ObjCBlock(AVAudioUnitComp,restype=None,argtypes=[c_void_p,c_void_p,c_void_p])

AVAudioUnit=ObjCClass('AVAudioUnit')
AVAudioUnitComponent=ObjCClass('AVAudioUnitComponent')
AVAudioUnitComponentManager=ObjCClass('AVAudioUnitComponentManager')

class AudioComponentDescription(ctypes.Structure):
fields=[('componentType',ctypes.c_uint32),('componentSubType',ctypes.c_uint32),('componentManufacturer',ctypes.c_uint32),('componentFlags',ctypes.c_uint32),('componentFlagsMask',ctypes.c_uint32)]

anyEffect = AudioComponentDescription(1635083896,1684237680,0,0,0)

manager=AVAudioUnitComponentManager.sharedAudioUnitComponentManager()

Return AVAudioUnitComponent[]

availableEffects = ObjCInstanceMethod(manager,'componentsMatchingDescription_')(anyEffect, argtypes=[AudioComponentDescription],restype=c_void_p)

AVAudioUnit.instantiateWithComponentDescription_options_completionHandler_(ObjCInstance(availableEffects[0].audioComponentDescription()),[],AVAudioUnitComp1)

```

Err Message:
AVAudioUnit.instantiateWithComponentDescription_options_completionHandler_(ObjCInstance(availableEffects[0].audioComponentDescription()),[],AVAudioUnitComp1)
File "/var/containers/Bundle/Application/14D01FBB-0800-4104-963B-7B7CEBA9B086/Pythonista3.app/Frameworks/PythonistaKit.framework/pylib/site-packages/objc_util.py", line 523, in new
cached_instance = _cached_instances.get(ptr)
File "/var/containers/Bundle/Application/14D01FBB-0800-4104-963B-7B7CEBA9B086/Pythonista3.app/Frameworks/PythonistaKit.framework/pylib/weakref.py", line 104, in get
wr = self.data[key]
TypeError: unhashable type

JonB

You cannot just pass a python function as a completion handler! You need to create an ObjCBlock.

JonB

Try getting rid of the ObjCInstance() -- that method returns a structure, not an object.

dgelessus

I think you also need to wrap availableEffects in an ObjCInstance. You've set the restype for the ObjCInstanceMethod to c_void_p, you need to wrap that in an ObjCInstance before you can call methods, access NSArray elements, etc.

Also, the second parameter of instantiateWithComponentDescription_options_completionHandler_ is a c_uint32. You need to pass 0, not [].

JonB

oh right, should be ObjCInstance(availableEffects)[0] ( note the obcinstance wraps available effects, not availableeffects[0].

wolf71

@JonB Using this like can pass,but the next arg error

AVAudioUnit.instantiateWithComponentDescription_options_completionHandler_(ObjCInstance(availableEffects[0].audioComponentDescription()),ns([]),AVAudioUnitComp1)

AVAudioUnit.instantiateWithComponentDescription_options_completionHandler_(ObjCInstance(availableEffects[0].audioComponentDescription()),ns([]),AVAudioUnitComp1)
File "/var/containers/Bundle/Application/1E0EEE41-E357-4C4B-9564-45BDC5519D55/Pythonista3.app/Frameworks/PythonistaKit.framework/pylib/site-packages/objc_util.py", line 523, in new
cached_instance = _cached_instances.get(ptr)
File "/var/containers/Bundle/Application/1E0EEE41-E357-4C4B-9564-45BDC5519D55/Pythonista3.app/Frameworks/PythonistaKit.framework/pylib/weakref.py", line 104, in get
wr = self.data[key]
TypeError: unhashable type

JonB

Note very carefully your first argument. look at what you are passing to ObjCInstance - it is not a pointer to an object.
availableEffects is probably a c_void_p. You need to convert that to a NSArray using ObjCInstance, before you try to index into it.

ObjCInstance(availableEffects)[0]

Then, take the resulting structure and index into it. Since that type is not a object, you won't use ObjCinstnce. Your best bet is to actually look at things in the debugger or console, so you know what you are dealing with. Also, I find it useful to break up these massive oneliners into multiple statements, so you can easier identifier your own bugs.

dgelessus

Also, as I said above, the options argument is an integer, not an array. [] and ns([]) does not work, you need to use 0 here.