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
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);
}
}];
}
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];
}
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?
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.
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);
}