What would be the best approach to making a plugin from a full Xcode Project? I've tried creating simple "get numbers from Objective-C" type of plugins, but this seems far more complex.
I'm trying to see if I can get the functionality of Aruts by Simon Epskamp onto a Unity game. What Aruts does is take the audio heard by the mic and output it to the headphones / speakers.
So far, what I've tried is to import IosAudioController.m, IosAudioController.h (renamed TestScript), and my own "extern" file that accesses Start and Stop functionality from said files.
While it does run without errors, all I'm hearing are rapid-firing clicking sounds, and per the comments, the function recordingCallback is not being called.
I also tried to take bits of the Aruts main.m and tried to stick it into the Unity project's main.mm, to no success. I'll update this with code when I can get my hands on it.
EDIT:
Here's my TestExtern.h:
#import <Foundation/Foundation.h>
#import "TestScript.h"
void TestStartAudioRec() {
[iosAudio start];
}
void TestStopAudioRec() {
[iosAudio stop];
}
Here's the Unity project's main.mm:
#include "RegisterMonoModules.h"
#include "RegisterFeatures.h"
#include <csignal>
#import "TestScript.h"
static const int constsection = 0;
void UnityInitTrampoline();
const char* AppControllerClassName = "UnityAppController";
int main(int argc, char* argv[])
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
iosAudio = [[TestScript alloc] init];
#autoreleasepool
{
UnityInitTrampoline();
UnityParseCommandLine(argc, argv);
RegisterMonoModules();
NSLog(#"-> registered mono modules %p\n", &constsection);
RegisterFeatures();
std::signal(SIGPIPE, SIG_IGN);
int retval = UIApplicationMain(argc, argv, nil, [NSString stringWithUTF8String:AppControllerClassName]);
NSLog(#"After retval");
[pool release];
return retval;
}
return 0;
}
#if TARGET_IPHONE_SIMULATOR && TARGET_TVOS_SIMULATOR
#include <pthread.h>
extern "C" int pthread_cond_init$UNIX2003(pthread_cond_t *cond, const pthread_condattr_t *attr)
{ return pthread_cond_init(cond, attr); }
extern "C" int pthread_cond_destroy$UNIX2003(pthread_cond_t *cond)
{ return pthread_cond_destroy(cond); }
extern "C" int pthread_cond_wait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex)
{ return pthread_cond_wait(cond, mutex); }
extern "C" int pthread_cond_timedwait$UNIX2003(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{ return pthread_cond_timedwait(cond, mutex, abstime); }
#endif // TARGET_IPHONE_SIMULATOR && TARGET_TVOS_SIMULATOR
Perhaps there's something wrong with it. Compare it with the main.m for Aruts:
#import <UIKit/UIKit.h>
#import "IosAudioController.h"
int main(int argc, char *argv[]) {
NSLog(#"main");
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
iosAudio = [[IosAudioController alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
Any suggestions?
Related
If I remember correctly, back in the days Xcode used to generate the main function something like this (at least for iOS applications):
int main(int argc, char * argv[]) {
#autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]);
}
}
I.e. UIApplicationMain function was wrapped inside of #autoreleasepool. However I noticed that currently Xcode no longer does that:
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
#autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}
So, UIApplicationMain is outside of the autorelease pool by default nowadays. Is there any official changelog/paper that explains why the latter implementation is now preferred over the former one?
I am trying to implement a simple SuperpoweredAdvancedAudioPlayer in swift. I successfully modified the SuperpoweredCrossExample project so that playerA plays the song on starting the application.
ViewController.mm now looks like this:
#import "ViewController.h"
#import "SuperpoweredAdvancedAudioPlayer.h"
#import "SuperpoweredFilter.h"
#import "SuperpoweredRoll.h"
#import "SuperpoweredFlanger.h"
#import "SuperpoweredIOSAudioIO.h"
#import "SuperpoweredSimple.h"
#import <stdlib.h>
#define HEADROOM_DECIBEL 3.0f
static const float headroom = powf(10.0f, -HEADROOM_DECIBEL * 0.025);
/*
This is a .mm file, meaning it's Objective-C++.
You can perfectly mix it with Objective-C or Swift, until you keep the member variables and C++ related includes here.
Yes, the header file (.h) isn't the only place for member variables.
*/
#implementation ViewController {
SuperpoweredAdvancedAudioPlayer *playerA;
SuperpoweredIOSAudioIO *output;
float *stereoBuffer, volA;
unsigned int lastSamplerate;
}
void playerEventCallbackA(void *clientData, SuperpoweredAdvancedAudioPlayerEvent event, void *value) {
if (event == SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess) {
ViewController *self = (__bridge ViewController *)clientData;
self->playerA->setBpm(126.0f);
self->playerA->setFirstBeatMs(353);
self->playerA->setPosition(self->playerA->firstBeatMs, false, false);
};
}
// This is where the Superpowered magic happens.
static bool audioProcessing(void *clientdata, float **buffers, unsigned int inputChannels, unsigned int outputChannels, unsigned int numberOfSamples, unsigned int samplerate, uint64_t hostTime) {
__unsafe_unretained ViewController *self = (__bridge ViewController *)clientdata;
if (samplerate != self->lastSamplerate) { // Has samplerate changed?
self->lastSamplerate = samplerate;
self->playerA->setSamplerate(samplerate);
};
bool silence = !self->playerA->process(self->stereoBuffer, false, numberOfSamples, self->volA);
if (!silence) SuperpoweredDeInterleave(self->stereoBuffer, buffers[0], buffers[1], numberOfSamples); // The stereoBuffer is ready now, let's put the finished audio into the requested buffers.
return !silence;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self f];
}
- (void) f {
volA = 1.0f * headroom;
if (posix_memalign((void **)&stereoBuffer, 16, 4096 + 128) != 0) abort(); // Allocating memory, aligned to 16.
playerA = new SuperpoweredAdvancedAudioPlayer((__bridge void *)self, playerEventCallbackA, 44100, 0);
playerA->open([[[NSBundle mainBundle] pathForResource:#"lycka" ofType:#"mp3"] fileSystemRepresentation]);
output = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)self preferredBufferSize:12 preferredMinimumSamplerate:44100 audioSessionCategory:AVAudioSessionCategoryPlayback channels:2 audioProcessingCallback:audioProcessing clientdata:(__bridge void *)self];
[output start];
playerA->play(false);
}
- (void)dealloc {
delete playerA;
free(stereoBuffer);
#if !__has_feature(objc_arc)
[output release];
[super dealloc];
#endif
}
- (void)interruptionStarted {}
- (void)recordPermissionRefused {}
- (void)mapChannels:(multiOutputChannelMap *)outputMap inputMap:(multiInputChannelMap *)inputMap externalAudioDeviceName:(NSString *)externalAudioDeviceName outputsAndInputs:(NSString *)outputsAndInputs {}
- (void)interruptionEnded { // If a player plays Apple Lossless audio files, then we need this. Otherwise unnecessary.
playerA->onMediaserverInterrupt();
}
#end
I am trying to use the same code in swift following the same method used in SuperpoweredFrequencies project to import c++ files in swift.
Superpowered.h:
#import <UIKit/UIKit.h>
#interface Superpowered: NSObject
-(void) f;
#end
Superpowered.mm:
#import "Superpowered.h"
#import "Superpowered/Headers/SuperpoweredAdvancedAudioPlayer.h"
#import "Superpowered/Headers/SuperpoweredFilter.h"
#import "Superpowered/Headers/SuperpoweredRoll.h"
#import "Superpowered/Headers/SuperpoweredFlanger.h"
#import "Superpowered/SuperpoweredIOSAudioIO.h"
#import "Superpowered/Headers/SuperpoweredSimple.h"
#import <stdlib.h>
#define HEADROOM_DECIBEL 3.0f
static const float headroom = powf(10.0f, -HEADROOM_DECIBEL * 0.025);
/*
This is a .mm file, meaning it's Objective-C++.
You can perfectly mix it with Objective-C or Swift, until you keep the member variables and C++ related includes here.
Yes, the header file (.h) isn't the only place for member variables.
*/
#implementation Superpowered {
SuperpoweredAdvancedAudioPlayer *playerA;
SuperpoweredIOSAudioIO *output;
float *stereoBuffer, volA;
unsigned int lastSamplerate;
}
void playerEventCallbackA(void *clientData, SuperpoweredAdvancedAudioPlayerEvent event, void *value) {
if (event == SuperpoweredAdvancedAudioPlayerEvent_LoadSuccess) {
Superpowered *self = (__bridge Superpowered *)clientData;
self->playerA->setBpm(126.0f);
self->playerA->setFirstBeatMs(353);
self->playerA->setPosition(self->playerA->firstBeatMs, false, false);
};
}
// This is where the Superpowered magic happens.
static bool audioProcessing(void *clientdata, float **buffers, unsigned int inputChannels, unsigned int outputChannels, unsigned int numberOfSamples, unsigned int samplerate, uint64_t hostTime) {
__unsafe_unretained Superpowered *self = (__bridge Superpowered *)clientdata;
if (samplerate != self->lastSamplerate) { // Has samplerate changed?
self->lastSamplerate = samplerate;
self->playerA->setSamplerate(samplerate);
};
bool silence = !self->playerA->process(self->stereoBuffer, false, numberOfSamples, self->volA);
if (!silence) SuperpoweredDeInterleave(self->stereoBuffer, buffers[0], buffers[1], numberOfSamples); // The stereoBuffer is ready now, let's put the finished audio into the requested buffers.
return !silence;
}
- (void)f {
volA = 1.0f * headroom;
if (posix_memalign((void **)&stereoBuffer, 16, 4096 + 128) != 0) abort(); // Allocating memory, aligned to 16.
playerA = new SuperpoweredAdvancedAudioPlayer((__bridge void *)self, playerEventCallbackA, 44100, 0);
playerA->open([[[NSBundle mainBundle] pathForResource:#"lycka" ofType:#"mp3"] fileSystemRepresentation]);
output = [[SuperpoweredIOSAudioIO alloc] initWithDelegate:(id<SuperpoweredIOSAudioIODelegate>)self preferredBufferSize:12 preferredMinimumSamplerate:44100 audioSessionCategory:AVAudioSessionCategoryPlayback channels:2 audioProcessingCallback:audioProcessing clientdata:(__bridge void *)self];
[output start];
playerA->play(false);
}
- (void)dealloc {
delete playerA;
free(stereoBuffer);
#if !__has_feature(objc_arc)
[output release];
[super dealloc];
#endif
}
- (void)interruptionStarted {}
- (void)recordPermissionRefused {}
- (void)mapChannels:(multiOutputChannelMap *)outputMap inputMap:(multiInputChannelMap *)inputMap externalAudioDeviceName:(NSString *)externalAudioDeviceName outputsAndInputs:(NSString *)outputsAndInputs {}
- (void)interruptionEnded { // If a player plays Apple Lossless audio files, then we need this. Otherwise unnecessary.
playerA->onMediaserverInterrupt();
}
#end
Project-Bridging-Header.h:
#import "Superpowered.h"
Controller.swift:
override func viewDidLoad() {
super.viewDidLoad()
let s = Superpowered();
s.f();
}
When running the app it crashes and gives the following error:
let s = Superpowered(); should be declared outside viewDidLoad(). Declaring it as an instance variable solved the problem.
I'm intentionally causing an EXC_BAD_ACCESS. By triggering a write to an NSObject in a read-only virtual memory page. Ideally, I'd like to catch EXC_BAD_ACCESS, mark the virtual memory page as read-write and have execution continue as it normally would have. Is this even possible? The code I've written to cause the EXC_BAD_ACCESS is below.
WeakTargetObject.h (ARC)
#interface WeakTargetObject : NSObject
#property (nonatomic, weak) NSObject *target;
#end
WeakTargetObject.m (ARC)
#implementation WeakTargetObject
#end
main.m (MRR)
- (void)main {
char *mem = NULL;
vm_allocate(mach_task_self(), (vm_address_t *)&mem, vm_page_size, VM_FLAGS_ANYWHERE);
NSLog(#"mem: %p", mem);
WeakTargetObject *weakTargetObject = objc_constructInstance([WeakTargetObject class], (void *)mem);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *target = [[NSObject alloc] init];
weakTargetObject.target = target;
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
NSLog(#"expect non-nil. weakTargetObject.target: %#", weakTargetObject.target);
[pool drain];
vm_protect(mach_task_self(),
(vm_address_t)mem,
vm_page_size,
1,
VM_PROT_READ);
// triggers EXC_BAD_ACCESS when objc runtime
// tries to nil weakTargetObject.target
[weakTargetObject release];
NSLog(#"expect nil. weakTargetObject.target: %#", weakTargetObject.target);
}
I found a darwin-dev post that has the answer!
WARNING
This answer has a major downside. My debugger wouldn't work in any thread other than the mach exception thread. Putting a breakpoint in any other thread caused Xcode5 to hang. I had to force-quit it. Inside my catch_exception_raise function, it worked fine. I asked the LLDB folks about this.
END WARNING
This code is the skeleton of the answer. It will infinite loop, because (according to the follow-up) you need to do something to make the error recoverable. In my case, I need to mark the page as read-write.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include <pthread.h>
#include <assert.h>
#include <mach/mach.h>
kern_return_t
catch_exception_raise(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code_vector,
mach_msg_type_number_t code_count)
{
fprintf(stderr, "catch_exception_raise %d\n", exception);
return KERN_SUCCESS; // loops infinitely...
}
void *exception_handler(void *arg)
{
extern boolean_t exc_server();
mach_port_t port = (mach_port_t) arg;
mach_msg_server(exc_server, 2048, port, 0);
abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns)
}
void setup_mach_exception_port()
{
static mach_port_t exception_port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
pthread_t returned_thread;
pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port);
}
void test_crash()
{
id *obj = NULL;
*obj = #"foo";
}
int main(int argc, char** argv)
{
setup_mach_exception_port();
test_crash();
return 0;
}
This is my new code that works:
WeakTargetObject.h (ARC)
#interface WeakTargetObject : NSObject
#property (nonatomic, weak) NSObject *target;
#end
WeakTargetObject.m (ARC)
#implementation WeakTargetObject
#end
main.m (MRR)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include <pthread.h>
#include <assert.h>
#include <mach/mach.h>
static char * mem = NULL;
kern_return_t
catch_exception_raise(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code_vector,
mach_msg_type_number_t code_count)
{
fprintf(stderr, "catch_exception_raise %d, mem: %p\n", exception, mem);
kern_return_t success = vm_protect(mach_task_self(),
(vm_address_t)mem,
vm_page_size,
0,
VM_PROT_DEFAULT);
fprintf(stderr, "switched to read-write: %d\n", success);
return KERN_SUCCESS;
}
void *exception_handler(void *arg)
{
extern boolean_t exc_server();
mach_port_t port = (mach_port_t) arg;
mach_msg_server(exc_server, 2048, port, 0);
abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns)
}
void setup_mach_exception_port()
{
static mach_port_t exception_port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
pthread_t returned_thread;
pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port);
}
- (void)main {
setup_mach_exception_port();
vm_allocate(mach_task_self(), (vm_address_t *)&mem, vm_page_size, VM_FLAGS_ANYWHERE);
NSLog(#"mem: %p", mem);
WeakTargetObject *weakTargetObject = objc_constructInstance([WeakTargetObject class], (void *)mem);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *target = [[NSObject alloc] init];
weakTargetObject.target = target;
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
NSLog(#"expect non-nil. weakTargetObject.target: %#", weakTargetObject.target);
[pool drain];
vm_protect(mach_task_self(),
(vm_address_t)mem,
vm_page_size,
// zero means don't set VM_PROT_READ as the maximum protection
// one means DO set VM_PROT_READ as the maximum protection
// we want zero because the if VM_PROT_READ is the maximum protection
// we won't be able to set it to VM_PROT_DEFAULT later
0,
VM_PROT_READ);
// triggers EXC_BAD_ACCESS when objc runtime
// tries to nil weakTargetObject.target
[weakTargetObject release];
NSLog(#"expect nil. weakTargetObject.target: %#", weakTargetObject.target);
}
I am trying to make an iOS app without using xibs or storyboards. So my main.m
looks like this:
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
[pool release];
return retVal;
}
If I check value of [AppDelegate class] it is (null).
So the question is, why?
Also if i check [NSString class] or other class from standard library it performs normally.
I thought that file is not in Compile sources list, but it's there. This problem only occurs in one project.
Check if you have initialized assigned some controller to your window's root controller in - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions.
Check out this answer.
I've noticed that there is a different way in Xcode 4.2 to start the main function:
int main(int argc, char *argv[])
{
#autoreleasepool {
return UIApplicationMain(argc, argv, nil,
NSStringFromClass([PlistAppDelegate class]));
}
}
and
int main(int argc, char *argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int retVal = UIApplicationMain(argc, argv, nil, nil);
[pool release];
return retVal;
}
Does anybody know the difference between those two?
The first one is using ARC, which is implemented in iOS5 and above to handle memory management for you.
On the second one, you're managing your own memory and creating an autorelease pool to handle every autorelease that happens inside your main function.
So after reading a bit on what's new on Obj-C with iOS5 it appears that the:
#autoreleasepool {
//some code
}
works the same as
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// some code
[pool release];
with the difference that the last one would throw an error on ARC.
EDIT:
The first one is using ARC or not.