I am working with the MusicPlayer API and I am trying to solve a problem I am having with user callback.
I have user events that are placed at every note event of a MIDI. When the notes are played, these user events pass the integer value of the note to a user callback function as *inEventData:
void noteUserCallback (void *inClientData, MusicSequence inSequence, MusicTrack inTrack, MusicTimeStamp inEventTime, const MusicEventUserData *inEventData, MusicTimeStamp inStartSliceBeat, MusicTimeStamp inEndSliceBeat)
{
UserEvent* event = (UserEvent *)inEventData;
UInt32 size = event->length;
UInt32 note = event->playedNote;
UInt32 timestamp = event->tStamp;
NSLog(#"Size: %lu Note: %lu, Timestamp: %lu", size, note, timestamp);
switch (note) {
case 60:
[whiteKey60 setImage:highlightA];
break;
default:
break;
}
}
Based upon these notes, I would like to makes changes to the UI. Namely, I have a number of UIImageViews that I would like to be able to update with different images based upon the value of the note (see above in switch statement).
The user callback function is associated with the sequence like this:
MusicSequenceSetUserCallback(sequence, noteUserCallback, NULL);
The third parameter of the above function connected with the void *inClientData parameter of the user callback function.
My problem is, that when I try to access the IBOutlets of my view from within the user callback function, I cannot. To fix this, I tried to pass the view controller into the MusicSequenceSetUserCallback function like this:
MusicSequenceSetUserCallback(sequence, noteUserCallback, self);
The problem I am having is that this inClientData parameter is of type void and I cannot pass in a ViewController* object. I tried simply creating a bridge when I set the callback function and then bridging back again within the user callback function but it didn't seem to work.
If anyone has any thoughts on how I could accomplish this. Even if it's a completely different methods, I would really appreciate it.
Thanks.
EDIT
I tried the following:
MusicSequenceSetUserCallback(sequence, noteUserCallback, (__bridge_retained void *)self);
and
void noteUserCallback (void *inClientData, MusicSequence inSequence, MusicTrack inTrack, MusicTimeStamp inEventTime, const MusicEventUserData *inEventData, MusicTimeStamp inStartSliceBeat, MusicTimeStamp inEndSliceBeat)
{
PracticeViewController* pvc = (__bridge_transfer PracticeViewController *)inClientData;
[pvc.whiteKey21 setImage:pvc.highlightA];
UserEvent* event = (UserEvent *)inEventData;
UInt32 size = event->length;
UInt32 note = event->playedNote;
UInt32 timestamp = event->tStamp;
NSLog(#"Size: %lu Note: %lu, Timestamp: %lu", size, note, timestamp);
switch (note) {
case 60:
break;
default:
break;
}
}
Unfortunately, it gives me this error: Thread 13 AURemoteIO::IOThread: EXC_BAD_ACCESS (code=2, address=0x10)
EDIT
Changing both bridges to __bridge silences the error but it is still not working properly; the call to change in the image in whiteKey60 does not work. It isn't a problem with the UIImageView connection because doing the same in viewDidLoad works fine.
What seems to be going on, to me anyways, is that self isn't making it to the function or perhaps its breaking the connection when it's passed?
Take a look at the declaration for MusicSequenceSetUserCallback again:
OSStatus MusicSequenceSetUserCallback (
MusicSequence inSequence,
MusicSequenceUserCallback inCallback,
void *inClientData
);
inClientData's type isn't void -- that wouldn't make sense, variables in C can never have a type of void. Rather, it's void * -- roughly, a pointer to something unspecified.
You need to cast the variable to a void * on the way in, and from a void * on the way out. Assuming that self is a MyClass *, then:
MusicSequenceSetUserCallback(sequence, noteUserCallback, (void *)self);
and in the callback:
void noteUserCallback (void *inClientData, MusicSequence inSequence, MusicTrack inTrack, MusicTimeStamp inEventTime, const MusicEventUserData *inEventData, MusicTimeStamp inStartSliceBeat, MusicTimeStamp inEndSliceBeat)
{
MyClass* self = (MyClass *)inClientData;
...
}
Related
i'm writing an iPhoneApp that will record sound from a device through the audio Jack, I tried compiling my code but it keep saying
'Cannot initialize a variable of type'CFDictionaryRef'(aka 'const_CFDictionary*') with an lvalue of type 'const void*'
Here is part of my Code:
#pragma mark Audio session callbacks_______________________
// Audio session callback function for responding to audio route changes. If playing
// back application audio when the headset is unplugged, this callback pauses
// playback and displays an alert that notifies the user that power has been terminated.
//
// The system takes care of iPod audio pausing during route changes--this callback
// is not involved with pausing playback of iPod audio.
void audioRouteChangeListenerCallback (
void *inUserData,
AudioSessionPropertyID inPropertyID,
UInt32 inPropertyValueSize,
const void *inPropertyValue
) {
// ensure that this callback was invoked for a route change
if (inPropertyID != kAudioSessionProperty_AudioRouteChange) return;
// This callback, being outside the implementation block, needs a reference to the
// MainViewController object, which it receives in the inUserData parameter.
// You provide this reference when registering this callback (see the call to
// AudioSessionAddPropertyListener).
MaxViewController *controller = (__bridge MaxViewController *) inUserData;
// if application sound is not playing, there's nothing to do, so return.
if (controller.sound.playing == 0 ) {
NSLog (#"Audio route change while application audio is stopped.");
return;
} else {
// Determines the reason for the route change, to ensure that it is not // because of a category change.
CFDictionaryRef routeChangeDictionary = inPropertyValue;
CFNumberRef routeChangeReasonRef =
CFDictionaryGetValue (
routeChangeDictionary,
CFSTR (kAudioSession_AudioRouteChangeKey_Reason)
);
SInt32 routeChangeReason;
I've searched every possible result here in stackoverflow and elsewhere but it has proven abortive. Any help would be greatly appreciated.
Thanks in advance.
this takes my head in. I read audio from the iPod Library for analysis of the audio samples and I can do what I want, the buffer is always leaking, I get a Low Memory Warning and the app is killed.
I tried all suggestions but to no success. The code below is incorporated in a static library and reading the audio works fine, just the buffer gets never released. I use ARC and also tried NOT to call CFRelease but same thing ... thanks for any suggestion, I am completely stuck!!!
- (NSInteger) getMP3Samples:(SInt16*)address{
AudioBufferList audioBufferList;
if (_assetReader == nil) {
return 0;
}
_mp3Control.currentSampleBufferCount = 0;
CMSampleBufferRef nextBuffer =[_assetReaderOutput copyNextSampleBuffer];
// Is the Song ended
if (nextBuffer == nil){
if ([_assetReader status] == AVAssetReaderStatusCompleted) {
[_assetReader cancelReading];
}
_assetReader = nil;
_assetReaderOutput = nil;
return _mp3Control.currentSampleBufferCount;
}
CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer(
nextBuffer,
NULL,
&audioBufferList,
sizeof(audioBufferList),
NULL,
NULL,
kCMSampleBufferFlag_AudioBufferList_Assure16ByteAlignment,
&_mp3Control.blockBuffer);
if (nextBuffer) {
CMSampleBufferInvalidate(nextBuffer);
CFRelease(nextBuffer);
nextBuffer=NULL;
}
for (int b=0; b < audioBufferList.mNumberBuffers; b++) {
memcpy((void *)(address+_mp3Control.currentSampleBufferCount),(void *)audioBufferList.mBuffers[b].mData,audioBufferList.mBuffers[b].mDataByteSize);
_mp3Control.currentSampleBufferCount+=audioBufferList.mBuffers[b].mDataByteSize;
}
///
/// Return samples and not bytes!!
///
return _mp3Control.currentSampleBufferCount/2;
}
Are you using & releasing the block buffer returned by CMSampleBufferGetAudioBufferListWithRetainedBlockBuffer in the (not posted) calling code?
If you are not releasing the object stored in &_mp3Control.blockBuffer after calling getMP3Samples:, this could be your memory management problem. (Core Foundation-style objects don't participate in ARC)
You could also run your code through the Allocation & Leaks Instruments to see further details (I am just guessing here :) ).
(This is a work in progress. I wonder if someone could to improve it)
in Objective C, it's easy to resolve a hostname with NSHost.
[[NSHost hostWithName:#"www.google.com"] address]
Sadly iOS (iPhone) contains only a private version of NSHost.
I found many ways of doing this with other Objects or methods, but all of them got only IPv4 addresses in the results. So here is for the moment the only efficient method I have found.
I first tried to use the asynchronous CFHostStartInfoResolution as did bdunagan, but failed to adapt it to IPv6.
Some of you will appreciate to get a method working, so here is one, but if you know a way which would be Asynchronous I would appreciate to learn about it... cause for the moment I use a Popup to alert about the next freeze that could occur with slow cellular connection
/**
Give the IPs corresponding to a Hostname
Sometime only 1 IPv4 is shown even if there's more.
Sometime only 1 IPv6 is shown even if there's more.
Certainly due to iOS Memory optimisation when locally cached
#author Christian Gonzalvez, http://wiki.gonzofamily.com
#param hostName A hostname
#return an Array of NSString of all the corresponding IP addresses. The first
is the Canonical name, the following are IPs (all NSString)
*/
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
const char* hostnameC = [hostname UTF8String];
struct addrinfo hints, *res;
struct sockaddr_in *s4;
struct sockaddr_in6 *s6;
int retval;
char buf[64];
NSMutableArray *result; //the array which will be return
NSMutableArray *result4; //the array of IPv4, to order them at the end
NSString *previousIP = nil;
memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = PF_UNSPEC;//AF_INET6;
hints.ai_flags = AI_CANONNAME;
//AI_ADDRCONFIG, AI_ALL, AI_CANONNAME, AI_NUMERICHOST
//AI_NUMERICSERV, AI_PASSIVE, OR AI_V4MAPPED
retval = getaddrinfo(hostnameC, NULL, &hints, &res);
if (retval == 0)
{
if (res->ai_canonname)
{
result = [NSMutableArray arrayWithObject:[NSString stringWithUTF8String:res->ai_canonname]];
}
else
{
//it means the DNS didn't know this host
return nil;
}
result4= [NSMutableArray array];
while (res) {
switch (res->ai_family){
case AF_INET6:
s6 = (struct sockaddr_in6 *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s6->sin6_addr), buf, sizeof(buf))
== NULL)
{
NSLog(#"inet_ntop failed for v6!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
case AF_INET:
s4 = (struct sockaddr_in *)res->ai_addr;
if(inet_ntop(res->ai_family, (void *)&(s4->sin_addr), buf, sizeof(buf))
== NULL)
{
NSLog(#"inet_ntop failed for v4!\n");
}
else
{
//surprisingly every address is in double, let's add this test
if (![previousIP isEqualToString:[NSString stringWithUTF8String:buf]]) {
[result4 addObject:[NSString stringWithUTF8String:buf]];
}
}
break;
default:
NSLog(#"Neither IPv4 nor IPv6!");
}
//surprisingly every address is in double, let's add this test
previousIP = [NSString stringWithUTF8String:buf];
res = res->ai_next;
}
}else{
NSLog(#"no IP found");
return nil;
}
return [result arrayByAddingObjectsFromArray:result4];
}
NB: I noticed that most of the time only 1 IPv6 is returned, I suspect it's due to iOS Memory optimisation when locally cached. if you run this method again and again, sometime you have 3 IPv6, but then you have only 1 IPv4.
If you want a method to run on a background thread, the simplest way is to use performSelectorInBackground:withObject:; this is an instance method of NSObject, so any object can use it without any extra work (including, interestingly enough, class objects, which is good in this case because this is a class method):
[[self class] performSelectorInBackground:#selector(addressesForHostName:)
withObject:theHostName];
Inside the method, you will need to set up an autorelease pool for the thread. You will also need some kind of callback method set up to get the return value back to your main thread. Make sure that you don't try to do any GUI activity on the background thread. It's only safe to do that on the main thread.
+ (NSArray *)addressesForHostname:(NSString *)hostname
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Do your stuff...
// Wait until done to allow memory to be managed properly
// If we did not do this, the array might be deallocated
// before the main thread had a chance to retain it
[self performSelectorOnMainThread:#selector(addressesCallback:)
withObject:[result arrayByAddingObjectsFromArray:result4]
waitUntilDone:YES];
// Inside a class method, self refers to the class object.
[pool drain];
}
If you were not on the main thread to begin with, or if you needed more control, you could also look into NSOperation, which is more powerful and therefore requires more work. It's still easier than explicit thread management, though!
Hope that solves your problem. It sounded like you have this method doing what you need, you just need it to not block the main thread.
thanks to Josh I could do it, but here is what I had to do :
Instead of calling directly
self.ipAddressesString = [CJGIpAddress addressesForHostname:#"www.google.com"];
I call
[self resolveNow:#"www.google.com"];
And create 3 new methods:
- (void)resolveNow:(NSString *)hostname
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
[self performSelectorInBackground:#selector(hostname2ipAddresses:)
withObject:hostname];
}
- (void)hostname2ipAddresses:(NSString *)hostname
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
//Here is my previous lonely line !! safely started in an other thread
self.ipAddressesString = [CJGIpAddress addressesForHostname:hostname];
[self performSelectorOnMainThread:#selector(resolutionDidFinish)
withObject:nil
waitUntilDone:YES];
[pool drain];
}
- (void)resolutionDidFinish
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
//My STUFF with self.ipAddressesString (now filled)
}
Edit:
In practice I use all of that in a Model, so I had a crash when I close the View before the end of the resolution
So in the view I added in dealloc what is necessary to avoid a crash
- (void)dealloc
{
self.model.delegate = nil;
[super dealloc];
}
Then - in the model - I test delegate before doing anything with it.
Hey. I have a very simple text output to buffer system which will crash randomly. It will be fine for DAYS, then sometimes it'll crash a few times in a few minutes. The callstack is almost exactly the same for other guys who use higher level controls:
http://discussions.apple.com/thread.jspa?messageID=7949746
iPhone app crashed: Assertion failed function evict_glyph_entry_from_strike, file Fonts/CGFontCache.c
It crashes at the line (below as well in drawTextToBuffer()):
[nsString drawAtPoint:CGPointMake(0, 0) withFont:clFont];
I have the same call of "evict_glyph_entry_from_cache" with the abort calls immediately following it.
Apparently it happens to other people. I can say that my NSString* is perfectly fine at the time of the crash. I can read the text from the debugger just fine.
static CGColorSpaceRef curColorSpace;
static CGContextRef myContext;
static float w, h;
static int iFontSize;
static NSString* sFontName;
static UIFont* clFont;
static int iLineHeight;
unsigned long* txb; /* 256x256x4 Buffer */
void selectFont(int iSize, NSString* sFont)
{
iFontSize = iSize;
clFont = [UIFont fontWithName:sFont size:iFontSize];
iLineHeight = (int)(ceil([clFont capHeight]));
}
void initText()
{
w = 256;
h = 256;
txb = (unsigned long*)malloc_(w * h * 4);
curColorSpace = CGColorSpaceCreateDeviceRGB();
myContext = CGBitmapContextCreate(txb, w, h, 8, w * 4, curColorSpace, kCGImageAlphaPremultipliedLast);
selectFont(12, #"Helvetica");
}
void drawTextToBuffer(NSString* nsString)
{
CGContextSaveGState(myContext);
CGContextSetRGBFillColor(myContext, 1, 1, 1, 1);
UIGraphicsPushContext(myContext);
/* This line will crash. It crashes even with constant Strings.. At the time of the crash, the pointer to nsString is perfectly fine. The data looks fine! */
[nsString drawAtPoint:CGPointMake(0, 0) withFont:clFont];
UIGraphicsPopContext();
CGContextRestoreGState(myContext);
}
It will happen with other non-unicode supporting methods as well such as CGContextShowTextAtPoint(); the callstack is similar with that as well.
Is this any kind of known issue with the iPhone? Or, perhaps, can something outside of this cause be causing an exception in this particular call (drawAtPoint)?
void selectFont(int iSize, NSString* sFont)
{
iFontSize = iSize;
clFont = [UIFont fontWithName:sFont size:iFontSize];
iLineHeight = (int)(ceil([clFont capHeight]));
}
After calling this function, clFont will be autoreleased, so there's no guarantee that a context irrelevant to this function can still have a valid clFont. You need to -retain an instance if you plan to use it later.
void selectFont(int iSize, NSString* sFont)
{
iFontSize = iSize;
[clFont release];
clFont = [[UIFont fontWithName:sFont size:iFontSize] retain];
iLineHeight = (int)(ceil([clFont capHeight]));
}
I would like to have my error handling code behave differently if it is running under the debugger. Specifically, if I am running on a handset, not attached to a debugger and fail an assertion I want to send the error to my server. When I am under gdb, I want to break into the debugger.
Although I can imagine how Apple would write the code, I can't find any documentation of a runtime way to test for the presence of the debugger.
The method described here worked fine for me
I tested by placing it in -(void)viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
int mib[4];
size_t bufSize = 0;
int local_error = 0;
struct kinfo_proc kp;
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[3] = getpid();
bufSize = sizeof (kp);
if ((local_error = sysctl(mib, 4, &kp, &bufSize, NULL, 0)) < 0) {
label.text = #"Failure calling sysctl";
return;
}
if (kp.kp_proc.p_flag & P_TRACED)
label.text = #"I am traced";
else
label.text = #"I am not traced";
}
Why not redefine assert to do what you want, when not compiled for debug?
Another option, is to create your own assert function, where you can then add a breakpoint on loading into GDB.
The assert prototype is
void assert(int expression);
void assert(int expression)
{
if( !expression )
{
// enable break point here
// log to server
}
}
or add the break point into the log to server code.