I am trying to create a TTS to file in Objc.
Since iOS13 can write it to a file.
But I'm stuck with writeUtterance:toBufferCallback.
Do someone has an exemple with this function in objc?
[synth speakUtterance:utterance];
Referring to the potential answer in Swift, this would be the Objective-C implementation
AVSpeechSynthesizer *synthesizer = [[AVSpeechSynthesizer alloc] init];
AVSpeechUtterance *utterance = [[AVSpeechUtterance alloc] initWithString:#"test 123"];
AVSpeechSynthesisVoice *voice = [AVSpeechSynthesisVoice voiceWithLanguage:#"en-US"];
[utterance setVoice:voice];
__block AVAudioFile *output = nil;
[synthesizer writeUtterance:utterance
toBufferCallback:^(AVAudioBuffer * _Nonnull buffer) {
AVAudioPCMBuffer *pcmBuffer = (AVAudioPCMBuffer*)buffer;
if (!pcmBuffer) {
NSLog(#"Error");
return;
}
if (pcmBuffer.frameLength != 0) {
//append buffer to file
if (output == nil) {
output = [[AVAudioFile alloc] initForWriting:[NSURL fileURLWithPath:#"test.caf"]
settings:pcmBuffer.format.settings
commonFormat:AVAudioPCMFormatInt16
interleaved:NO error:nil];
}
[output writeFromBuffer:pcmBuffer error:nil];
}
}];
Related
I'm working on an app for iPad iOS 8, and I need to make my app wait for an answer from:
[directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error) {}]
This method is inside 3 loops. I tried dispatch_semaphore_t, but the app can't continue after this line:
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
and gets stuck. I tried with dispatch_group_t and got the same result.
I guess that I'm doing something wrong, but I don't know what. I tried to search SO for similar problems and but found nothing. Can someone explain how I could accomplish this?
-(void)setTimesMissions {
for (Driver *d in self.dataList) {
for (Period *p in d.periods) {
for (Mission *m in p.missions) {
MKDirections *directions = ....
// HERE i want the for loop stop until this completionHandler finish
[directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error) {
//and when he finish here continue
}];
}
}
}
}
Call your method in dispatch_async block.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[youClassInstance setTimesMissions];
});
And use dispatch_semaphore_wait in your loop
- (void)setTimesMissions {
Mission *home = [[Mission alloc]init];
Mission *next = [[Mission alloc]init];
for (Driver *d in self.dataList) {
home.clientLat = d.centralPointLat;
home.clientLon = d.centralPointLon;
home.clientPaddres = d.centralAddress;
for (Period *p in d.periods) {
home.times = [[NSMutableArray alloc]init];
if ([p.periodIx isEqualToString:self.thisPeriodIX]) {
for (Mission *m in p.missions) {
Mission *source = home;
Mission *destination = m ;
MKPlacemark *placemarkSource = [[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake([source.clientLat doubleValue], [source.clientLon doubleValue]) addressDictionary:nil] ;
MKMapItem *mapItemSource = [[MKMapItem alloc] initWithPlacemark:placemarkSource];
MKPlacemark *placemarkDestination = [[MKPlacemark alloc] initWithCoordinate:CLLocationCoordinate2DMake([destination.clientLat doubleValue], [destination.clientLon doubleValue])addressDictionary:nil] ;
MKMapItem *mapItemDestination = [[MKMapItem alloc] initWithPlacemark:placemarkDestination];
MKDirectionsRequest *directionsRequest = [[MKDirectionsRequest alloc] init];
[directionsRequest setSource:mapItemSource];
[directionsRequest setDestination:mapItemDestination];
directionsRequest.transportType = MKDirectionsTransportTypeAutomobile;
[directionsRequest setRequestsAlternateRoutes:NO];
MKDirections *directions = [[MKDirections alloc] initWithRequest:directionsRequest];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block double timeTo;
[directions calculateETAWithCompletionHandler:^(MKETAResponse *response, NSError *error) {
if ( response.expectedTravelTime) {
timeTo = response.expectedTravelTime;
double ans = timeTo;
Time *t = [[Time alloc]init];
t.ix = m.serviceIX;
t.time = ans;
[home.times addObject:t];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
}
dispatch_async(dispatch_get_main_queue(), ^{
// code that should be executed on main queue
});
if (next.clientPaddres) {
home = next;
}
}
}
}
}
For a list of URLs I need to load the photos with ALAssetsLibrary:assetForURL, and this within one method.
Since this method works async but it is not iterating over the passed list of URLs, as we all know.
I found this snippet (which should work):
- (void)loadImages:(NSArray *)imageUrls loadedImages:(NSArray *)loadedImages callback: (void(^)(NSArray *))callback
{
if (imageUrls == nil || [imageUrls count] == 0) {
callback(loadedImages);
}
else {
NSURL *head = [imageUrls head];
__unsafe_unretained id unretained_self = self;
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
[library assetForURL:head resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *assetRepresentation = asset.defaultRepresentation;
UIImage *image = [UIImage imageWithCGImage:assetRepresentation.fullResolutionImage scale:assetRepresentation.scale orientation:(UIImageOrientation)assetRepresentation.orientation];
[unretained_self loadImages:[imageUrls tail] loadedImages:[loadedImages arrayByAddingObject:image] callback:callback];
} failureBlock:^(NSError *error) {
[unretained_self loadImages:[imageUrls tail] loadedImages:loadedImages callback:callback];
}];
}
}
How do I write the method definition in the form (above all the callback)
void loadImages(NSArray *imageUrls, NSArray *loadedImages, ...) ?
How do I call this method from another method (again mainly the callback part) ?
Can the callback be in the calling method or a 3rd method needed for this? and how does this method need to be written?
I have found the snippet here: http://www.calebmadrigal.com/functional-programming-deal-asynchronicity-objective-c/
Use NSThread to call the loadImages method.
NSMutableArray *imageCollection = [NSThread detachNewThreadSelector:#selector (loadImages:)
toTarget:self
withObject:imageUrlsCollection];
- (NSMutableArray *)loadImages:(NSArray *)imageUrls
{
ALAssetsLibrary* library = [[ALAssetsLibrary alloc] init];
NSMutableArray *loadedImages = [[NSMutableArray alloc] init];
#try
{
for(int index = 0; index < [imageUrls count]; index++)
{
NSURL *url = [imageUrls objectAtIndex:index];
[library assetForURL:url resultBlock:^(ALAsset *asset) {
ALAssetRepresentation *assetRepresentation = asset.defaultRepresentation;
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithCGImage:assetRepresentation.fullResolutionImage scale:assetRepresentation.scale orientation:(UIImageOrientation)assetRepresentation.orientation];
[loadedImages addObject:image];
});
} failureBlock:^(NSError *error) {
NSLog(#"Failed to get Image");
}];
}
}
#catch (NSException *exception)
{
NSLog(#"%s\n exception: Name- %# Reason->%#", __PRETTY_FUNCTION__,[exception name],[exception reason]);
}
#finally
{
return loadedImages;
}
}
Note: With ARC,take care about invalid attempt to access ALAssetPrivate past the lifetime of its owning ALAssetsLibrary issue
Here is the fix :)
I think this is a really bad way to check user access to the resources.
Is there better way to get user access?
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// create library
ALAssetsLibraryAssetForURLResultBlock resultblock = ^(ALAsset *myasset)
{
accessGiven = YES;
return ;
};
void (^assetGroupEnumberatorFailure)(NSError *) = ^(NSError *error) {
accessGiven = NO;
return;
};
// create 2 blocks
NSURL *url = [[NSURL alloc] initWithString:#" "];
[library assetForURL:url resultBlock:resultblock failureBlock:assetGroupEnumberatorFailure];
// use empty url to check acces..
[library release];
iOS 6.0 has better way to do that.
[ALAssetsLibrary authorizationStatus];
Method Description
Authorization Status Enum
I have developed in C but am quite new to Objective-C and iPhone app development. I am working on an app that needs to strip the punctuation off a string. The function works but when I analyse the code it flags up some issues around one of the NSstrings I am using.
I don't understand why and therefore don't know how to fix it.
The code for the main function along with the analyser warning is:
- (IBAction)doIt {
NSString *start_punct = [[NSString alloc] init];
NSString *end_punct = [[NSString alloc] init];
NSString *actual_word = [[NSString alloc] init];
outputTextTextView.text = translatedText; //potential leak of an object alloctated on line xx and stored into 'actual word'
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count of an object that is not owned at this point by the caller
[end_punct release]; end_punct = nil;
[actual_word release]; actual_word = nil; //this causes a crash
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len];
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
}
The code for the getStartPunct and getEndPunct functions is below
+(NSString*) getStartPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* start_str = nil;
NSRange firstAlphanumCharFromStart = [inputString rangeOfCharacterFromSet:[NSCharacterSet alphanumericCharacterSet]];
if (firstAlphanumCharFromStart.location != NSNotFound) {
start_str = [inputString substringWithRange: NSMakeRange(0, firstAlphanumCharFromStart.location)];
*length = firstAlphanumCharFromStart.length;
*rangeStart = firstAlphanumCharFromStart.location;
} //if
if (start_str == nil) {
*length=0;
*rangeStart=0;
}
return start_str;
} //getStartPunct
+(NSString*) getEndPunct:(NSString*) inputString
start:(NSInteger*)rangeStart
len:(NSInteger*)length {
NSString* end_str = nil;
NSInteger rnge = inputString.length;
NSCharacterSet* CS = [NSCharacterSet alphanumericCharacterSet];
NSRange firstNonAlphanumCharFromEnd = [inputString rangeOfCharacterFromSet:CS options:NSBackwardsSearch];
if (firstNonAlphanumCharFromEnd.location != NSNotFound) {
end_str = [inputString substringWithRange: NSMakeRange(firstNonAlphanumCharFromEnd.location+1, rnge - firstNonAlphanumCharFromEnd.location-1)];
*length = firstNonAlphanumCharFromEnd.length;
*rangeStart = firstNonAlphanumCharFromEnd.location;
} //if
if (end_str == nil) {
*length=0;
*rangeStart=0;
}
return end_str;
} //getEndPunct
Can someone see what the issue is? I'm sure it is something very basic..
Many Thanks in advance!
Thanks for all the responses so far.
adpalumbo you are right, I had paste the elements in the wrong order. The correct order is below and I have changed the initialization as suggested by Alex Nichol.
This has fixed 1 of the warning but the others (as shown below) still remain and I don't understand why 'start_punct' and 'end_punct' are behaving differently
- (IBAction)doIt {
NSString *start_punct = nil;
NSString *end_punct = nil;
NSString *actual_word = nil;
start_punct = [MainViewController getStartPunct:word start:&start_range_start len:&start_range_len]; // method returns objective with +0 retain count
end_punct = [MainViewController getEndPunct:word start:&end_range_start len:&end_range_len];
actual_word = [word substringWithRange: NSMakeRange(start_range_start,(end_range_start-start_range_start)+1)];
[translatedText release]; translatedText = nil;
[start_punct release]; start_punct = nil; //incorrect decrement of reference count
[end_punct release]; end_punct = nil;
//[actual_word release]; actual_word = nil; //possible abend
}
I'm really desperate on this one. I'm trying to make a Framework which you can search and play YouTube videos with. But while testing it, I'm running in to a big problem.
In the search operation I'm adding YTVideos (a subclass of NSObject) to a NSMutableArray. When I loop thru it in the main(), I'm getting nil-objects:
Method
- (NSArray *)videosInRange:(NSRange)range {
if(range.length > 50) {
[NSException raise:#"Range lenth > 50"
format:#"The range of -videosInRange: can't be bigger than 50"];
return nil;
}
if((range.location + range.length) > 999) {
[NSException raise:#"Range to big"
format:#"The given range was to big (%d, %d)", range.location, range.length];
return nil;
}
NSString *searchURLString = [[self feedURL] absoluteString];
searchURLString = [searchURLString stringBySettingURLAttribute:#"start-index" value:[NSString stringWithFormat:#"%d",range.location + 1]];
searchURLString = [searchURLString stringBySettingURLAttribute:#"max-results" value:[NSString stringWithFormat:#"%d",range.length]];
NSLog(#"%#",searchURLString);
NSURL *url = [NSURL URLWithString:searchURLString];
NSXMLDocument *xmlDoc = [[NSXMLDocument alloc] initWithContentsOfURL:url
options:0
error:NULL];
if(!xmlDoc)
return nil;
NSArray *videoElements = [[xmlDoc rootElement] elementsForName:#"entry"];
NSMutableArray *videos = [[NSMutableArray alloc] initWithCapacity:[videoElements count]];
register int i;
for(i = 0; i < [videoElements count]; i++) {
NSAutoreleasePool *addPool = [[NSAutoreleasePool alloc] init];
YTVideo *vid = [[YTVideo alloc] initWithXMLElement:[videoElements objectAtIndex:i]];
[videos addObject:vid];
[vid release];
[addPool drain];
}
NSArray *retValue = [NSArray arrayWithArray:videos];
[videos release];
return retValue;
}
main()
int main(int argc, const char *argv[]) {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
YTSearchFeed *feed = [[YTSearchFeed alloc] initWithSearch:#"Eminem"];
long long results = [feed videoCount];
NSLog(#"%lld videos for search", results);
long long i = 0;
while(results != 0) {
int length = (results >= 50) ? (50) : (results);
NSArray *videos = [feed videosInRange:NSMakeRange(i, length)];
NSLog(#"L: %d", [videos count]);
int z;
for(z = 0; z < [videos count]; z++, i++) {
YTVideo *vid = [videos objectAtIndex:z];
NSString *title = [vid title];
NSLog(#"%d: %#", i+1, title);
}
results -= length;
}
[pool drain];
return NSApplicationMain(argc, argv);
}
I hope someone can take the time to look at this, and if you need anymore information, just ask.
Thank you in advance,
ief2
EDIT: YTVideo
- (id)initWithXMLElement:(NSXMLElement *)element {
self = [super init];
if(self != nil) {
_XMLElement = [element copy];
}
return self;
}
- (NSString *)title {
if(!_title) {
NSString *str = [[[self XMLElement] firstElementWithName:#"title"] stringValue];
_title = [[str stringByDecodingHTMLEntities] retain];
}
return [[_title copy] autorelease];
}
I get the title (and other video information) only when it's requested. the -stringByDecodingHTMLEntities works fine (Category on NSString).
I've rewritten the code and initialized all instance variables in the -initmethod