AVAudiounit: I can't play a sound with an effect like delay - ios

I'm new to objective c and try to code a programm which can play a sound with an effect on it, like a reverb or delay. Unfortunately I'm only able to play the sound, but without the effect. I'm stuck for 3 days now and can't find a solution. Does anyone can tell what I'm doing wrong?
I tried this code, but with it there's no sound at all:
- (void)viewDidLoad {
[super viewDidLoad];
AVAudioEngine *engine = [AVAudioEngine new];
AVAudioPlayerNode *playerA = [AVAudioPlayerNode new];
playerA.volume = 0.5;
NSURL *Mia1url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"MIA1" ofType:#"m4a"]];
AVAudioFile *MIA1 = [[AVAudioFile alloc] initForReading:Mia1url error:nil];
AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:MIA1.processingFormat frameCapacity:1024];
[MIA1 readIntoBuffer:buffer error:nil];
AVAudioUnitDelay *delay = [AVAudioUnitDelay new];
delay.delayTime = 100;
delay.wetDryMix = 90;
[engine attachNode:playerA];
[engine attachNode:delay];
[engine connect: playerA to: delay format:MIA1.processingFormat];
[engine connect: delay to: engine.mainMixerNode format: MIA1.processingFormat];
[playerA scheduleBuffer:buffer atTime:nil options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
[engine prepare];
[engine startAndReturnError:nil];
[playerA play];
}
after that I tried this code but the sound is only coming without the effect:
- (void)viewDidLoad {
[super viewDidLoad];
AVAudioEngine *engine = [AVAudioEngine new];
AVAudioPlayerNode *playerA = [AVAudioPlayerNode new];
playerA.volume = 0.5;
NSURL *Mia1url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"MIA1" ofType:#"m4a"]];
AVAudioFile *MIA1 = [[AVAudioFile alloc] initForReading:Mia1url error:nil];
AVAudioPCMBuffer *buffer = [[AVAudioPCMBuffer alloc] initWithPCMFormat:MIA1.processingFormat frameCapacity:1024];
[MIA1 readIntoBuffer:buffer error:nil];
AVAudioUnitDelay *delay = [AVAudioUnitDelay new];
delay.delayTime = 100;
delay.wetDryMix = 90;
[engine attachNode:playerA];
[engine attachNode:delay];
[engine connect: playerA to: delay format:MIA1.processingFormat];
[engine connect: delay to: engine.mainMixerNode format: MIA1.processingFormat];
[playerA scheduleBuffer:buffer atTime:nil options:AVAudioPlayerNodeBufferLoops completionHandler:nil];
[engine prepare];
[engine startAndReturnError:nil];
//change
self.playerA = [[AVAudioPlayer alloc] initWithContentsOfURL:Mia1url error:nil];
//change from [playerA play] to:
[self.playerA play];
}

You forgot to instantiate your objects:
//AVAudioEngine *engine; // no instance exists.
AVAudioEngine *engine = [AVAudioEngine new];
AVAudioPlayerNode *playerA = [AVAudioPlayerNode new];
...
AVAudioUnitDelay *delay = [AVAudioUnitDelay new];
...
Also, you have to initialize playerA only once and before using it.
For digging deeper you might want to checkout Apples code samples.

Related

The AVAudioEngine gets data from the network and plays it with noise or silence

I want to use the AVAudioEngine to play the data from the network, but when I get the data from the network and use scheduleBuffer to add the data, there will be noise or the sound will simply break
//init
- (void)viewDidLoad {
[super viewDidLoad];
[self initWebSocket:server_ip];
self.engine = [[AVAudioEngine alloc] init];
self.playerNode = [[AVAudioPlayerNode alloc] init];
[self.engine attachNode:self.playerNode];
self.format = [[AVAudioFormat alloc] initWithCommonFormat:AVAudioPCMFormatFloat32
sampleRate:(double)48000.0
channels:(AVAudioChannelCount)1
interleaved:NO];
AVAudioMixerNode *mainMixer = [self.engine mainMixerNode];
[self.engine connect:self.playerNode to:mainMixer format:self.format];
if (!self.engine.isRunning) {
[self.engine prepare];
NSError *error;
BOOL success;
success = [self.engine startAndReturnError:&error];
NSAssert(success, #"couldn't start engine, %#", [error localizedDescription]);
}
[self.playerNode play];
}
// get data from network
- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(id)message{
NSData *data = message;
AVAudioPCMBuffer *pcmBuffer = [[AVAudioPCMBuffer alloc]
initWithPCMFormat:self.format
frameCapacity:(uint32_t)(data.length)
/self.format.streamDescription->mBytesPerFrame];
pcmBuffer.frameLength = pcmBuffer.frameCapacity;
[data getBytes:*pcmBuffer.floatChannelData length:data.length];
[self.playerNode scheduleBuffer:pcmBuffer completionHandler:nil];
}
You set 4 times longer than data length to the frameLength
Your Audio format uses float (32bit).
But NSData includes UInt8 (8bit) data stream.
So, pcmBuffer.frameLength must be data.length/4.

AVPlayer crashes after multiple time play video from local directory

When i click my local video url and present Viewcontroller and Play video in AVPlayer.Play 14-15 times and Player crash.Avplayer showing like
- (void)viewDidLoad
{
[self PlayVideoinPlayer:_videoURL];
}
-(void)PlayVideoinPlayer:(NSString *)URL
{
NSURL *fileURL = [NSURL fileURLWithPath:URL];
_Avcontroller=[[AVPlayerViewController alloc]init];
asset = [AVURLAsset URLAssetWithURL:fileURL options:nil];
anItem = [AVPlayerItem playerItemWithAsset:asset];
_avPlayer = [AVPlayer playerWithPlayerItem:anItem];
[_avPlayer addObserver:self forKeyPath:#"status" options:0 context:nil];
self.Avcontroller.view.frame = self.view.bounds;
[self.Avcontroller setPlayer:_avPlayer];
_Avcontroller.videoGravity=AVLayerVideoGravityResizeAspectFill;
[self.view addSubview:self.Avcontroller.view];
[self.view addSubview:self.Avcontroller.view];
[_avPlayer play];
CMTime interval = CMTimeMake(1, 1800);
__strong __typeof(self) weakself = self;
playbackObserver = [_avPlayer addPeriodicTimeObserverForInterval:interval queue:dispatch_get_main_queue() usingBlock: ^(CMTime time) {
CMTime endTime = CMTimeConvertScale (_avPlayer.currentItem.asset.duration, _avPlayer.currentTime.timescale, kCMTimeRoundingMethod_RoundHalfAwayFromZero);
if (CMTimeCompare(endTime, kCMTimeZero) != 0)
{
// double normalizedTime = (double) avPlayer.currentTime.value / (double) endTime.value;
//NSLog(#"--------->>>>%#",playbackObserver);
}
int CurrentSecond=[[weakself getStringFromCMTime:_avPlayer.currentTime] intValue];
_avPlayer.rate=1.0f;
}
Anyone have solution.? Please help me.Thanks

Playing a video recorded with AVFoundation

I would like to use the same button to start and stop recording. I would like to use another button to play back the recording. Here is what I have:
- (IBAction)recordVideo:(id)sender {
if(!self.movieOutput.isRecording) {
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"output.mp4"];
NSFileManager *manager = [[NSFileManager alloc] init];
if ([manager fileExistsAtPath:outputPath])
{
[manager removeItemAtPath:outputPath error:nil];
}
[self.movieOutput startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputPath]
recordingDelegate:self];
Float64 maximumVideoLength = 5; //Whatever value you wish to set as the maximum, in seconds
int32_t prefferedTimeScale = 30; //Frames per second
CMTime maxDuration = CMTimeMakeWithSeconds(maximumVideoLength, prefferedTimeScale);
self.movieFileOutput.maxRecordedDuration = maxDuration;
self.movieFileOutput.minFreeDiskSpaceLimit = 1024*1024;
}
else
{
[self.movieOutput stopRecording];
}
- (void) captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL
fromConnections:(NSArray *)connections error:(NSError *)error
{
NSLog(#"Recording to file ended");
[_captureSession stopRunning];
}
Then to play:
- (IBAction)playVideo:(id)sender {
NSURL *fileURL = [NSURL URLWithString:#"outputPath"];
self.avPlayer = [AVPlayer playerWithURL:fileURL];
AVPlayerLayer *movieLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
self.avPlayer.actionAtItemEnd = AVPlayerActionAtItemEndNone;
movieLayer.frame = self.cameraView.bounds;
movieLayer.videoGravity = AVLayerVideoGravityResize;
[self.cameraView.layer addSublayer:movieLayer];
[_avPlayer play];
When I run and press the playback button I get no errors and I see no avplayer.
You are recording and saving file in temporary directory
NSString *outputPath = [NSTemporaryDirectory()stringByAppendingPathComponent:#"output.mp4"];
and trying to play from bundle path.Use the same path to play recording also.
First, check Is your video is recorded and saved properly or not.From your code, the video is saved Temporary directory.Check the video at the Path.If it is exist or not.
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"output.mp4"];
NSLog(#"%#", outputPath);
In your code, you are trying to play video from outPutPath, which is not defined and initialize in your code.If you have defined outPutPath as property or variable, then you need to initialise _outPutPath, with the same path you save the video.
NSString *outputPath = [NSTemporaryDirectory()stringByAppendingPathComponent:#"output.mp4"];
_outputPath = outputPath;
To Play Video Try this,
if ([[NSFileManager defaultManager]fileExistsAtPath: _ouputPath]) {
AVAsset *asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:_ouputPath]];
_avPlayer = [[AVPlayer alloc]initWithPlayerItem:[[AVPlayerItem alloc]initWithAsset:asset]];
AVPlayerLayer *movieLayer = [AVPlayerLayer playerLayerWithPlayer:self.avPlayer];
movieLayer.frame = self.cameraView.bounds;
[self.cameraView.layer addSublayer:movieLayer];
[self.avPlayer play];
}
Replace your this line :
NSURL *url = [NSURL fileURLWithPath:filePath];
with this: 
NSURL *url=[NSURL URLWithString:filePath];
 & then try.

Black Screen when attempting to use MPMoviePlayer

I am using MPMoviePlayer to display a video from an external URL onto my iPhone App, however when I run the App a black screen is all that shows.
Here is the URL I am using:
2015-04-27 00:11:29.655 Floadt[21069:2598414] https://scontent.cdninstagram.com/hphotos-xaf1/t50.2886-16/11179443_819874424728492_389701720_n.mp4
Here is my code to try to setup MPMoviePlayer:
if (entry[#"videos"] != nil) {
NSLog(#"There is a Video: %#", entry[#"videos"]);
NSString *urlString = entry[#"videos"][#"standard_resolution"][#"url"];
NSLog(urlString);
NSURL *url = [NSURL URLWithString:urlString];
MPMoviePlayerController *player = [[MPMoviePlayerController alloc] initWithContentURL: url];
[player prepareToPlay];
[player.view setFrame: CGRectMake(10, 65, 299, 299)];
[cell.contentView addSubview: player.view];
player.shouldAutoplay = YES;
[player play];
}
You need to retain your instance to MPMoviePlayerController i.e. as a property or an instance variable. The reference to the movie player is lost if you do not retain.
When we try to load the video from URL initially it will display blank screen only. MPMoviePlayerController will take some time to load the video from url.So we can display first frame of the video till the video loads. For this need to import two frameworks.
1.AVFoundation
2.AssetsLibrary
Using these two we can display first frame of video into UIImageView as follows:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
url=[NSURL URLWithString:#"https://scontent.cdninstagram.com/hphotos-xaf1/t50.2886-16/11179443_819874424728492_389701720_n.mp4"];
AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
if ([[avAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0)
{
AVAssetImageGenerator *imageGenerator =[AVAssetImageGenerator assetImageGeneratorWithAsset:avAsset];
Float64 durationSeconds = CMTimeGetSeconds([avAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error;
CMTime actualTime;
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:kCMTimeZero actualTime:&actualTime error:&error];
if (halfWayImage != NULL)
{
NSString *actualTimeString = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
NSString *requestedTimeString = (NSString *)CFBridgingRelease(CMTimeCopyDescription(NULL, midpoint));
NSLog(#"Got halfWayImage: Asked for %#, got %#", requestedTimeString, actualTimeString);
UIImage *img=[UIImage imageWithCGImage:halfWayImage];
_imgVw.image=img;
}
}
UITapGestureRecognizer *tap=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(tapped)];
[_imgVw addGestureRecognizer:tap];
}
-(void)tapped
{
MPMoviePlayerController *movPlayer=[[MPMoviePlayerController alloc] init];
[movPlayer setContentURL:url];
[movPlayer setMovieSourceType:MPMovieSourceTypeFile];
[movPlayer.view setFrame:CGRectMake(0, 0, _imgVw.frame.size.width, 250)];
[movPlayer prepareToPlay];
movPlayer.controlStyle = MPMovieControlStyleNone;
movPlayer.fullscreen = NO;
movPlayer.shouldAutoplay=YES;
[movPlayer setScalingMode:MPMovieScalingModeAspectFill];
[_imgVw addSubview:movPlayer.view];
[movPlayer play];
}
Here i am taking UIImageView view for playing the video. In viewDidLoad i am loading the 1st frame and giving tap gesture to the UIImageView. When i tapped the ImageView then i am playing the video.

How to stop AVPlayer Ios and remove the periodicTimeObserverForInterval

I'm having a little difficulty stopping AVPlayer.
I have a method that records and plays music simultaneously. I'm using AVPlayer to play the music because I want to use the addPeriodicTimeObserverForInterval Function. I have it set up as follows:
- (IBAction) recordVoice:(id)sender {
if(!recorder.isRecording){
//set up the file name to record to
NSString *recordingLocation = [self createFileName];
recordingName = recordingLocation;
NSArray *pathComponents = [NSArray arrayWithObjects:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject],
recordingLocation, nil];
NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
recordingURL = outputFileURL;
// Setup audio session
session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker
error:nil];
// Define the recording settings to record as m4a
NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
[recordSetting setValue:[NSNumber numberWithInt:kAudioFormatMPEG4AAC] forKey:AVFormatIDKey];
[recordSetting setValue:[NSNumber numberWithFloat:44100.0] forKey:AVSampleRateKey];
[recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
// initiate and prepare the recorder
recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:NULL];
recorder.delegate = self;
recorder.meteringEnabled = YES;
[recorder prepareToRecord];
[session setActive:YES error:nil];
[recorder record];
// find which song to play and initiate an AVPlayer to play it
NSString *playerLocation = self.TitleLabel.text;
NSString *path = [[NSBundle mainBundle] pathForResource:playerLocation ofType:#"m4a"];
player = [[AVPlayer alloc] initWithURL:[NSURL fileURLWithPath:path]];
lastTime = nil;
//check where the player is at and update the song lines accordingly
[player addPeriodicTimeObserverForInterval:CMTimeMake(3, 10) queue:NULL usingBlock:^(CMTime time){
NSTimeInterval seconds = CMTimeGetSeconds(time);
for (NSDictionary *item in robotR33) {
NSNumber *time = item[#"time"];
if ( seconds > [time doubleValue] && [time doubleValue] >= [lastTime doubleValue] ) {
lastTime = #(seconds);
NSString *str = item[#"line"];
[self nextLine:str];
};
}
}];
[player play];
[_recordButton setImage:[UIImage imageNamed:#"micRecording.gif"] forState:UIControlStateNormal];
}
else{
[recorder stop];
player = nil;
[session setActive:NO error:nil];
}
}
If the recorder is not recording I set up both a new recorder AVAudioRecorder and an AVPlayer. In the AVPlayer I set up an AddPeriodicTimeObserverForInterval which updates the UI based on the position of the player.
If the recorder is recording I stop the recorder and I set the player to nil. This stops the audio from playing but I notice that the addPeriodicTimeObserverInterval is still running because the UI continues to update. Should I destroy the AVPlayer altogether and if so how should I do that? Many thanks in advance.
Also as an aside, I have a warning inside the addPeriodicTimeObserverForInterval block. I am looping over an Array called robotR33. Xcode tells me that 'Capturing self strongly in this block is likely to lead to a retain cycle". Could this be part of my problem?
When finished playing the observer needs to be removed from the player.
Adding [player removeTimeObserver:self.timeObserver] works.

Resources