How do you record audio directly from iOS - ios

My app involves creating and recording sounds you make within the app. I have tried multiple ways of recording like AVAudioRecord. So far these only offer a way to record through the microphone but I want to record the actual sounds made within the app. How on earth do I achieve this. I've been on this for days, can someone please help me, thanks!
I also use Audio unit for playback in my application.
This is what I have so far using AVAudioRecord:
_audioController = [[AEAudioController alloc] initWithAudioDescription:[AEAudioController nonInterleavedFloatStereoAudioDescription] inputEnabled:YES];
- (void)beginRecording {
// Init recorder
self.recorder = [[AERecorder alloc] initWithAudioController:_audioController];
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
objectAtIndex:0];
NSString *filePath = [documentsFolder stringByAppendingPathComponent:#"Myaudio.aiff"];
NSLog(#"%#", filePath);
// Start the recording process
NSError *error = NULL;
if ( ![_recorder beginRecordingToFileAtPath:filePath
fileType:kAudioFileAIFFType
error:&error] ) {
// Report error
return;
}
// Receive both audio input and audio output. Note that if you're using
// AEPlaythroughChannel, mentioned above, you may not need to receive the input again.
[_audioController addInputReceiver:_recorder];
[_audioController addOutputReceiver:_recorder];
}

Related

iOS CocoaHttpServer File Download Extremely Slow

I am currently building an http server in iPhone that allows wifi speakers to download music from it. This is so far what I have done:
This is the part that initializes the http server:
- (void)startHttpServer
{
[DDLog addLogger:[DDTTYLogger sharedInstance]];
_httpServer = [[HTTPServer alloc] init];
[_httpServer setType:#"_http._tcp."];
NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
[_httpServer setDocumentRoot:documentPath];
[_httpServer setPort:9088];
[_httpServer setName:#"Music Server"];
[self startServer]; }
This is the part that gets the asset URL of the music file:
if (currentGroupIndex < currentGroupPlayList.count)
{
[self.savedIndexInfo setObject:[NSNumber numberWithInteger:currentGroupIndex] forKey:groupIDNum];
//makes sure that there is no existing duplicated url;
NSURL *oneURL = [self.savedURLInfo objectForKey:groupIDNum];
[[NSFileManager defaultManager] removeItemAtURL:oneURL error:nil];
MPMediaItem *music = [currentGroupPlayList objectAtIndex:currentGroupIndex];
NSURL *assetURL = [music valueForProperty:MPMediaItemPropertyAssetURL];
[self exportAssetAtURL:assetURL
withTitle:[NSString stringWithFormat:#"song_%lld_%ld", groupID, (long)currentGroupIndex]
groupAdress:groupIP
groupID:groupID
songDuration:[[music valueForProperty:MPMediaItemPropertyPlaybackDuration]intValue]];
}
This is the part that imports the music file and exports the output url:
- (void)exportAssetAtURL:(NSURL*)assetURL withTitle:(NSString*)title groupAdress:(NSString *)groupAdress groupID:(UInt64)groupID songDuration:(NSInteger)duration
{
NSString *ext = [TSLibraryImport extensionForAssetURL:assetURL];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSURL *outURL = [[NSURL fileURLWithPath:[documentsDirectory stringByAppendingPathComponent:title]] URLByAppendingPathExtension:ext];
// we're responsible for making sure the destination url doesn't already exist
[[NSFileManager defaultManager] removeItemAtURL:outURL error:nil];
// create the import object
TSLibraryImport* import = [[TSLibraryImport alloc] init];
[import importAsset:assetURL toURL:outURL completionBlock:^(TSLibraryImport* import)
{
if (import.status == AVAssetExportSessionStatusCompleted)
{
NSString *musicURL = [NSString stringWithFormat:#"http://%#:9088/%#.%#", [self currentIPAddress], title, ext];
NSLog(#"URL: %#", musicURL);
cntl_http_pb_pkt pb;
pb.header.packet_type = CONTROL_PACKET;
pb.header.op = CNTL_PB_HTTP_PLAY;
pb.header.body_len = sizeof(pb.body);
const char *adressChar = [groupAdress cStringUsingEncoding:NSASCIIStringEncoding];
pb.body.group_addr = inet_addr(adressChar);
pb.body.group_port = htons(INIT_GROUP_PORT);
pb.body.seq = [NSDate timeIntervalSinceReferenceDate];
self.playSeq = pb.body.seq;
const char *urlChar = [musicURL cStringUsingEncoding:NSASCIIStringEncoding];
memcpy(pb.body.url, urlChar, 200);
NSData *data = [NSData dataWithBytes:&pb length:sizeof(pb)];
[self.savedURLInfo setObject:outURL forKey:[NSNumber numberWithUnsignedLongLong:groupID]];
[self.savedPlayData setObject:musicURL forKey:[NSNumber numberWithUnsignedLongLong:groupID]];
[self performSelectorOnMainThread:#selector(sendUdpData:) withObject:data waitUntilDone:YES];
_acting = NO;
}
else
{
_acting = NO;
NSLog(#"Error importing: %#", import.error);
}
import = nil;
}];
}
My Problem:
50% of the time, its either the music file cannot be downloaded from the server, or the download speed is extremely slow (less than 10kb/s). My problem is I don't know what are the possible causes of this problem, how am I able to solve this.
Different Instances:
Sometimes at the beginning, the download speed is ok(download speed is at 200+kb/s) and then without doing anything at all, the speed would suddenly drop, making the speakers totally impossible to fetch the file from the server. Take note, there are no other devices connected to the router, only speakers and the phone.
Sometimes at the beginning, the song was not playing, and then after a couple of secs the playing would begin.
There is one time I tried connecting 8 speakers to one router, the playing was smooth and fine. But then after an hour or less, Problems would occur, like a lot of stops would occur.
Solutions I have tried:
Tried connecting all speakers under different types of routers
(TP-LINK, Tenda, Netgear, mi, etc...). above instances still occurs.
When the song stops playing or when the data received is incredibly slow, I tried relaunching the app, restarting and phone, and even restarting the router, but then speed is still very slow at the beginning.
Tried disconnecting the router from modem.
Tried connecting only four devices and one phone only.
5.Tried turning off all other wifi devices that are not even under the same network.
Tried changing the port of the http server.
When the problem occurs, I tried checking whether the connection is gone, or the http server is down. But both are ok.
Take Note:
I tried accessing the url by using a browser, the download speed was as slow as the time the speaker was not playing the song smoothly. So I guess the problem is not on the speaker.
There are no error messages returned at all when the problem occurs
No connection terminated when checked on NSLog.
My Request:
So please, if you guys have an idea of what might cause this problem please tell me, or the better, if you know the solution to this. Its really hard to determine what the problem is. Because no error messages are reflected, and I tried removing a lot things that could become possible cause to this problem, but still no good result.
Any help would be highly appreciated.

AVAudioPlayer suddenly no longer plays entire audio source

I am using an AVAudioPlayer to play audio in my app, and it worked for a while. Suddenly, it just stopped working and I cannot figure out why. I created the AVAudioPlayer at the class level in the .h file. This is the code I use to init the player
-(void)initAudio
{
NSURL *song = [[NSBundle mainBundle] URLForResource:#"Prelude" withExtension:#"m4a"];
NSError *error = nil;
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:song error:&error];
[_audioPlayer prepareToPlay];
}
I call this method in the initWithSize function. error is nil, and I have verified that the player object contains the actual audio file. The audio file plays back fine in my OS environment and I have tried adding it to the build phase compile sources just to be safe, even though beforehand it worked fine without it explicitly there. When I trigger the play event, it will play the first snippet of the audio and then nothing. I log the current time of the player and it is 0. Everything else about the app is continuing on and functions without issue. I do not interfere with the avaudioplayer object in any way except to play/pause/preparetoplay.
I am completely stumped as it worked fine and then suddenly no longer works. I have tried to clean the project, I have even dumped the derivedData.
Any ideas?
I think your player has an error when you try to play the song.
You need to check the error first.
In my opinion, it seems like OSStatus error -43.
When you update an app, file path can be changed.
For example,
old path : /var/mobile/Applications/F28E0C2C-4AE2-4C9D-906F-414AE1669D90/Documents/AAA.mp3
new path: var/mobile/Applications/6086783B-A410-42C3-9BAE-7BA755D0E528/Documents/AAA.mp3
Try this code instead:
NSError *error;
NSURL *song = [[NSBundle mainBundle] URLForResource:#"Prelude" withExtension:#"m4a"];
_audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:song error:&error];
if(error){
NSLog(#"[error localizedDescription] %#", [error localizedDescription]);
NSString *fileName = [[song absoluteString] lastPathComponent]; //this code is just example. get your fileName like "AAA.m4a"
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", docDirPath , fileName];
_audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL URLWithString:filePath] error:&error];
}
_audioPlayer.delegate = self;
[_audioPlayer prepareToPlay];
[_audioPlayer play];
}
I hope this will help you.
Cheers

AVAudioRecorder blocks AVSpeechSynthesizer

I'm using Cordova 3.2 for text to speech and speech to text. Under iOS 7, AVSpeechSynthesizer is available and works very well. Here is the critical bit of the plugin:
self.synthesizer = [[AVSpeechSynthesizer alloc] init];
self.synthesizer.delegate = self;
NSString * toBeSpoken=[command.arguments objectAtIndex:0];
NSNumber * rate=[command.arguments objectAtIndex:1];
NSString * voice=[command.arguments objectAtIndex:2];
NSNumber * volume=[command.arguments objectAtIndex:3];
NSNumber * pitchMult=[command.arguments objectAtIndex:4];
AVSpeechUtterance *utt =[[AVSpeechUtterance alloc] initWithString:toBeSpoken];
utt.rate= [rate floatValue]/4;// this is to slow down the speech rate
utt.volume=[volume floatValue];
utt.pitchMultiplier=[pitchMult floatValue];
utt.voice=[AVSpeechSynthesisVoice voiceWithLanguage:voice];
[self.synthesizer speakUtterance:utt];
The problem occurs after the text is spoken. Using the Cordova Media call to record (AVAudioRecorder) the response for voice to text conversion does something to disrupt the synthesizer output.
Some things that I've noticed during my attempts to figure this out:
Running in the simulator, there is no problem. In fact, I have to be careful to wait for the speech to end before recording, otherwise the record will pick the output through the microphone.
On the iPad 3 w/ iOS 7+, starting the recording will pause the synthesizer output until the recorded file is played back. The media reference to the file is released after the successful recording.
After recording, the synthesizer delegate receives responses:
TTSPlugin did start speaking
TTSPlugin will speak in range of speech string.
TTSPlugin will speak in range of speech string.
TTSPlugin will speak in range of speech string.
TTSPlugin did cancel speaking
Canceling the speech synthesize clears the utterance queue.
My goal is to be able to have a conversation with the app. I'm not able to find where the interference is. Help?
EDIT
I solved the problem. The culprit was AVAudioSession, which the Media plugin in Cordova was managing. I hadn't done multiple audio sources before so this was a stumper.
I added these to my TTS plugin to manage AVAudioSession and activate it as needed. Everything is fine now.
// returns whether or not audioSession is available - creates it if necessary
- (BOOL)hasAudioSession
{
BOOL bSession = YES;
if (!self.avSession) {
NSError* error = nil;
self.avSession = [AVAudioSession sharedInstance];
if (error) {
// is not fatal if can't get AVAudioSession , just log the error
NSLog(#"error creating audio session: %#", [[error userInfo] description]);
self.avSession = nil;
bSession = NO;
}
}
return bSession;
}
- (void)setAudioSession
{
if ([self hasAudioSession]) {
NSError* __autoreleasing err = nil;
NSNumber* playAudioWhenScreenIsLocked = 0;
BOOL bPlayAudioWhenScreenIsLocked = YES;
if (playAudioWhenScreenIsLocked != nil) {
bPlayAudioWhenScreenIsLocked = [playAudioWhenScreenIsLocked boolValue];
}
NSString* sessionCategory = bPlayAudioWhenScreenIsLocked ? AVAudioSessionCategoryPlayback : AVAudioSessionCategorySoloAmbient;
[self.avSession setCategory:sessionCategory error:&err];
if (![self.avSession setActive:YES error:&err]) {
// other audio with higher priority that does not allow mixing could cause this to fail
NSLog(#"Unable to play audio: %#", [err localizedFailureReason]);
}
}
}
startRecordingAudio method in Media(CDVSound.m) set AVAudioSession as "AVAudioSessionCategoryRecord".
In my case, I just I added following 1 line, it works for me.
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[self.synthesizer speakUtterance:utt];

the amazing audio engine how to apply filters to microphone input

Im trying to make karaoke app that records the background music from file and the microphone.
I also want to add filter effects to the microphone input.
i can do everything stated above using the amazing audio engine sdk but i cant figure out how to add the microphone input as a channel so i can apply filters to it (and not to the background music.)
any help would be appreciated.
my current recording code:
- (void)beginRecording {
// Init recorder
self.recorder = [[AERecorder alloc] initWithAudioController:_audioController];
NSString *documentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)
objectAtIndex:0];
NSString *filePath = [documentsFolder stringByAppendingPathComponent:#"Recording.aiff"];
// Start the recording process
NSError *error = NULL;
if ( ![_recorder beginRecordingToFileAtPath:filePath
fileType:kAudioFileAIFFType
error:&error] ) {
// Report error
return;
}
// Receive both audio input and audio output. Note that if you're using
// AEPlaythroughChannel, mentioned above, you may not need to receive the input again.
[_audioController addInputReceiver:_recorder];
[_audioController addOutputReceiver:_recorder];
}
You can separate your your back ground music and your mic by using different channels and then you can apply the filter to your mic channel only.
first declare a channel group in the header file
AEChannelGroupRef _group;
then simply add the player that you are using for recorded file to this group
[_audioController addChannels:#[_player] toChannelGroup:_group ];
and then add the filter to this group only
[_audioController addFilter:_reverb toChannelGroup:_group];
self.reverb = [[[AEAudioUnitFilter alloc] initWithComponentDescription:AEAudioComponentDescriptionMake(kAudioUnitManufacturer_Apple, kAudioUnitType_Effect, kAudioUnitSubType_Reverb2) audioController:_audioController error:NULL] autorelease];
AudioUnitSetParameter(_reverb.audioUnit, kReverb2Param_DryWetMix, kAudioUnitScope_Global, 0, 100.f, 0);
[_audioController addFilter:_reverb];
You can apply filters at the time of playing the recorded audio.

MPMoviePlayerViewController hanging at loading screen

I have code that records the screen in my app, and then I want to be able to play it back. I have gotten so far as to get to the black loading screen with the rewind, play and fast forward buttons, but it hangs at loading without playing anything.
This code plays the clip:
-(IBAction)playMovie:(id)sender
{
MPMoviePlayerViewController *movieViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:locationOfVideoURL];
movieViewController.moviePlayer.movieSourceType = MPMovieSourceTypeFile;
[self presentMoviePlayerViewControllerAnimated:movieViewController];
}
I believe the problem has something to do with the locationOfVideoURL variable. I am able to print it and it seems like it SHOULD work.
Here it is printed out:
file://localhost/Users/myusername/Library/Application%20Support/iPhone%20Simulator/5.1/Applications/[left out, personal info]/Documents/Videoclip.mp4
Any ideas? Any help would be greatly appreciated! thank you
EDIT: Location of video clip:
NSString *outputPath = [[NSString alloc] initWithFormat:#"%#/%#", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0], #"Videoclip.mp4"];
NSURL *outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
NSLog(#"Completed recording, file is stored at: %#", outputURL);

Resources