Novocaine : Saved audio file does not work - ios

I output an m4a file from Novocaine's sample project. But I cannot open the file in iTunes. The file might be corrupted.
NSArray *pathComponents = [NSArray arrayWithObjects:
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject],
#"My Recording.m4a",
nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
NSLog(#"URL: %#", outputFileURL);
self.fileWriter = [[AudioFileWriter alloc]
initWithAudioFileURL:outputFileURL
samplingRate:self.audioManager.samplingRate
numChannels:self.audioManager.numInputChannels];
__block int counter = 0;
self.audioManager.inputBlock = ^(float *data, UInt32 numFrames, UInt32 numChannels) {
[wself.fileWriter writeNewAudio:data numFrames:numFrames numChannels:numChannels];
counter += 1;
if (counter > 800) { // roughly 5 seconds of audio
wself.audioManager.inputBlock = nil;
}
};
I did not change the code, just removed comment-out.
If you know possible solutions or suggestions, I was wondering if you could share me.
Novocaine https://github.com/alexbw/novocaine

This issue is discussed here:
https://github.com/alexbw/novocaine/issues/59
The original code terminates the recording with this line (inside a handler block):
wself.audioManager.inputBlock = nil;
I got it working by adding this line immediately after:
[wself.fileWriter stop];
The stop method of AudioFileWriter is private, so it is also necessary to add it to the public interface.

Related

Objective-c how to add filter to an existing video like Instagram Application?

I'm trying to add filter to video after recording it, just like Instagram. After searching for a way I found GPUImage, but that didn't solve it since in this code the video is being written to directory before displaying it (causing delay):
//Setting path for temporary storing the video in document directory
NSURL *movieURL = [self dataFilePath: #"tempVideo.mp4"]; // url where we want to save our new edited video
Is there a way to preview the filtered video before saving it in order not to take time? If so, how it is done?
Also, I found in the apple documentation about CIFilter but still can not find a way that states how to add filters to the video.
In addition, that there are some codes but written in swift.
Thanks in advance.
I couldn't find a way to stop the delay that is happing with the GPUImage. So I tried working with the SCRecorder.
What I have done is:
[_player setItemByUrl:videoURL];
instead of:
[_player setItemByAsset:_recordSession.assetRepresentingSegments];
by this way I'm able to play and add filter to an existing video just like Instagram.
To export the video with the chosen filter I used this code:
- (void)saveToCameraRoll {
NSString *fileName = videoURL.lastPathComponent;
NSArray *dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [dirPaths objectAtIndex:0];
NSString *videoPath = [NSString stringWithFormat:#"%#/%#",docsDir,fileName];
NSURL *urlPath = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#", videoPath]];
NSURL *assetUrl = urlPath;
AVAsset *asset = [AVAsset assetWithURL:assetUrl];
SCFilter *exportFilter = [self.filterSwitcherView.selectedFilter copy];
SCAssetExportSession *exportSession = [[SCAssetExportSession alloc] initWithAsset:asset];
NSURL *urlFile = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#.mov",docsDir,fileName]];
exportSession.outputUrl = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#",urlFile]];
filterURL = exportSession.outputUrl;
exportSession.videoConfiguration.filter = exportFilter;
exportSession.videoConfiguration.preset = SCPresetHighestQuality;
exportSession.audioConfiguration.preset = SCPresetHighestQuality;
exportSession.videoConfiguration.maxFrameRate = 35;
exportSession.outputFileType = AVFileTypeMPEG4;
exportSession.delegate = self;
exportSession.contextType = SCContextTypeAuto;
self.exportSession = exportSession;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
if (exportSession.error == nil) {
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
[exportSession.outputUrl saveToCameraRollWithCompletion:^(NSString * _Nullable path, NSError * _Nullable error) {
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
if (error == nil) {
//Success
}
}];
} else {
NSLog(#"Error: %#", exportSession.error);
}
}];
}

How do you record audio directly from 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];
}

Multiple MIDI sounds with MusicDeviceMIDIEvent

I am creating MIDI sounds with MusicDeviceMIDIEvent and it's perfectly fine, I use some aupreset files I've created. One for each instrument.
My code is basically the example from apple: LoadPresetDemo. All I use is:
- (void)loadPreset:(id)sender instrumentName:(const char *)instrumentName {
NSString *presetURL1 = [NSString stringWithCString:instrumentName encoding:NSUTF8StringEncoding];
NSString *path = [[NSBundle mainBundle] pathForResource:presetURL1 ofType:#"aupreset"];
NSURL *presetURL = [[NSURL alloc] initFileURLWithPath: path];
if (presetURL) {
NSLog(#"Attempting to load preset '%#'\n", [presetURL description]);
}
else {
NSLog(#"COULD NOT GET PRESET PATH!");
}
[self loadSynthFromPresetURL: presetURL];
}
To load my aupreset file, then:
- (void) noteOn:(id)sender midiNumber:(int)midiNumber {
UInt32 noteNum = midiNumber;
UInt32 onVelocity = 127;
UInt32 noteCommand = kMIDIMessage_NoteOn << 4 | 0;
OSStatus result = noErr;
require_noerr (result = MusicDeviceMIDIEvent (self.samplerUnit, noteCommand, noteNum, onVelocity, 0), logTheError);
logTheError:
if (result != noErr) NSLog (#"Unable to start playing the low note. Error code: %d '%.4s'\n", (int) result, (const char *)&result);
}
to play the note of the previously loaded aupreset, and:
- (void) noteOff:(id)sender midiNumber:(int)midiNumber
when I want it to stop.
Now I would like to play one note of each instrument simultaneously. What is the easiest way of doing this?

MusicPlayer Delegate, Music Player Did End Playing File Notification or Warning

I am writing an app that will play MIDI files from parsed URL's. Currently, my code downloads the file then plays it using MusicPlayer and MusicSequence.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL), ^(void) {
NSData *midData = [[NSData alloc] initWithContentsOfURL:playURL];
NSString *resourceDocPath = [[NSString alloc] initWithString:[[[[NSBundle mainBundle] resourcePath] stringByDeletingLastPathComponent] stringByAppendingPathComponent:#"Documents"]];
NSString *filePath = [resourceDocPath stringByAppendingPathComponent:#"part.mid"];
[midData writeToFile:filePath atomically:YES];
NSLog(#"Downloaded");
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *url = [NSURL fileURLWithPath:filePath];
MusicSequence s;
NewMusicSequence(&s);
MusicSequenceFileLoad(s, (__bridge CFURLRef)(url), 0, 0);
NewMusicPlayer(&midPlayer);
MusicPlayerSetSequence(midPlayer, s);
MusicPlayerPreroll(midPlayer);
MusicPlayerStart(midPlayer);
MusicTrack t;
MusicTimeStamp len;
UInt32 sz = sizeof(MusicTimeStamp);
MusicSequenceGetIndTrack(s, 1, &t);
MusicTrackGetProperty(t, kSequenceTrackProperty_TrackLength, &len, &sz);
midPlaying = YES;
});
});
What I'm looking for is a notification when the file reached its end, similar to something like AVPlayer:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(itemDidFinishPlaying:) name:AVPlayerItemDidPlayToEndTimeNotification object:playerItem];
Is this possible? I've been looking through Apple's documentation and cannot find anything.
Documentation: MusicSequence Reference, MusicPlayer Reference
I don't recall ever seeing any built in way to do that but you can easily implement it through polling playback status on your own. CADisplayLink is a built in timer you might want to use if you are updating a progress bar or doing other screen drawing (or are just lazy like me).
Being confronted to the same problem and having searched around, I don't think there's a way of doing that.

Playing midi files with MusicPlayer & Music Sequence

I've successfully gotten iOS to play a .mid (midi) file with a soundfont sample using the following code:
-(void) playMusic:(NSString*) name
{
NSString *presetURLPath = [[NSBundle mainBundle] pathForResource:#"GortsMiniPianoJ1" ofType:#"SF2"];
NSURL * presetURL = [NSURL fileURLWithPath:presetURLPath];
[self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)3];
NSString *midiFilePath = [[NSBundle mainBundle] pathForResource:name ofType:#"mid"];
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];
NewMusicPlayer(&musicPlayer);
if (NewMusicSequence(&musicSequence) != noErr)
{
[NSException raise:#"play" format:#"Can't create MusicSequence"];
}
if(MusicSequenceFileLoad(musicSequence, (CFURLRef)midiFileURL, 0, 0 != noErr))
{
[NSException raise:#"play" format:#"Can't load MusicSequence"];
}
MusicPlayerSetSequence(musicPlayer, musicSequence);
MusicSequenceSetAUGraph(musicSequence, _processingGraph);
MusicPlayerPreroll(musicPlayer);
MusicPlayerStart(musicPlayer);
}
However, the problem comes when I then try to play a second file when the first is still playing.
I've tried many variations. Firstly, the above code will play both tracks simultaneously. Or, I've tried:
DisposeMusicPlayer(musicPlayer);
DisposeMusicSequence(musicSequence);
Before the NewMusicPlayer(&musicPlayer), but this produces a weird version of the tune with only sporadic notes being played.
I'd love to simply call this method, and the next track to be played.
Ok, I found the answer on how to properly dispose of a MusicPlayer and MusicSequence.
-(void) stop
{
OSStatus result = noErr;
result = MusicPlayerStop(musicPlayer);
UInt32 trackCount;
MusicSequenceGetTrackCount(musicSequence, &trackCount);
MusicTrack track;
for(int i=0;i<trackCount;i++)
{
MusicSequenceGetIndTrack (musicSequence, i, &track);
result = MusicSequenceDisposeTrack(musicSequence, track);
}
result = DisposeMusicPlayer(musicPlayer);
result = DisposeMusicSequence(musicSequence);
result = DisposeAUGraph(_processingGraph);
}

Resources