UISlider and playing sound in iOS - ios

I'm trying to use a UISlider to play sound in a fashion which emulates how a violin bow would play on a string. At current, I can get sound playing from the slider however the functionality isn't correct.
-(IBAction) slider
{
NSString* path = [[NSBundle mainBundle] pathForResource:#"im_on" ofType:#"mp3" ];
if (song) [song release];
song = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path] error:NULL];
[song play];
}
This is code and there is not much else to it. I have linked this method in IB to a slider which is triggered on the event Value Changed. When set to continuous = YES, when the slider begins to slide, the sound plays. However if it continues to slide the sound will stop until the slider stops where it then play again. When set to continuous = NO, the sound does not play when the value changes but when the touch is lifted. the sound continues to play while the value is hanging after that.
Is there a way to have the sound continue to play until the slider stops/changes direction/touch is released? i.e override all but the first value changed sent event until x condition is met.

Related

MPMusicPlayerController won't seek to given playbackTime

I want MPMusicPlayerController.applicationMusicPlayer() instance to start playing from a specific starting time:
applicationMusicPlayer.setQueueWithStoreIDs([String(track.id)])
applicationMusicPlayer.currentPlaybackTime = 10.0
print(applicationMusicPlayer.currentPlaybackTime) // will print 10.0
But as soon as the player starts playing the item, it will reset its currentPlaybackTime to zero and will start from the beginning:
applicationMusicPlayer.play()
print(applicationMusicPlayer.currentPlaybackTime) // will print 0.0
I thought maybe it's because I set the playback to the player that has just been created and is not ready yet, but neither .prepareToPlay() or .isPreparedToPlay() help me with that situation.
I even tried to wait for a few seconds and only then start the playback from the position that I've set. No success at all, extremely frustrating.
Maybe it is somehow connected to the fact that I'm playing songs from Apple Music directly? I can't switch to AVPlayer 'cause I have to play music from Apple Music.
Would appreciate any thoughts and help!
UPDATE: I found the method beginSeekingBackward at the MPMediaPlayback and it says that if the content is streamed, it won't have any effect:
https://developer.apple.com/documentation/mediaplayer/mpmediaplayback/1616248-beginseekingbackward?language=objc
Seems like only built-in Music app has a control over streaming music playback?
I had just run into this exact problem. I managed to get around it with the follow code.
It creates a background thread that continuously checks the currentPlaybackTime of the player. As soon as currentPlaybackTime is not the time I set it to be I set currentPlaybackTime back to what I wanted.
It feels like a terrible hack but it's working for me so far.
MPMusicPlayerController *player = [MPMusicPlayerController systemMusicPlayer];
player.currentPlaybackTime = _startTime;
[player play];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0ul);
dispatch_async(queue, ^{
while(true) {
if (player.currentPlaybackTime != _startTime) {
player.currentPlaybackTime = _startTime;
break;
}
}
});

How to Speed up the audio Playing in MpMoviePlayerController

I am using MPMoviePlayerController in my app to play the video and audios. I want to give an option to user to play the audio/video slower/faster then the normal speed i.e 0.5x (slower then normal ), 1x (normal speed), 2x (double speed then normal.).
I want to know is there any way that i can speed up/down the MPMoviePlayerController streaming so that user can have options to listen/view the audio/video at slower/faster speed.
i found the solution to this problem myself.
When you use MPMoviePlayerController and make its instance then you have a property of MpMoviePlayerController as currentPlaybackRate. It is set to 1.0 by default means normal playing. If you sets its value to 1.5 or 2.0 then it will play the currently playing audio at that speed.
See the following code.
MPMoviePlayerController *moviePlayer = [MPMoviePlayerController alloc]init];
moviePlayer.movieSourceType = MPMovieSourceTypeStreaming;
moviePlayer.contentURL = #"http://someduumyUrl.com" ;
moviePlayer.controlStyle = MPMovieControlStyleDefault;
[moviePlayer prepareToPlay];
moviePlayer.currentPlaybackRate = 2.0 // will play the audio at double speed.

Control volume of one channel in objective-c

I have an really simple audio player with AVAudioPlayer with (at this point) one song.
It is simple to control the volume of both channels (left and right) at the same time.
But is it possible to control only the left channel or only the right channel?
In this way I can set the left speaker a little louder then the right or vice versa.
The AVAudioPlayer reference should be consulted to answer this question.
You can see from the interface that you can't control the left/right channel directly, but you can set the pan from -1 (full left) to 1 (full right). Perhaps this will achieve what you are looking for?
Using core audio will be much more difficult, but it should be able to handle this. Another possibility using AVAudioPlayer is to split the sound into its separate channels and to play them separately. Then, you can set the volume separately. There is an example in the documentation of how to ensure that two sounds play at exactly the same time. (And, you'll want to set the pan for each sound separately so that each sound plays correctly.)
A less efficient method is two play two copies of the .mp3 at once. (Caveat: I haven't tested this code.)
// assumes you have url for the file you want to play
AVAudioPlayer *first = [[AVAudioPlayer alloc] initWithContentsOfURL:myUrl error:nil];
AVAudioPlayer *second = [[AVAudioPlayer alloc] initWithContentsOfURL:myUrl error:nil];
first.pan = -1; // only play on left channel
second.pan = 1; // only play on right channel
first.volume = 0.6;
second.volume = 1.0;
NSTimeInterval shortStartDelay = 0.01;
NSTimeInterval now = player.deviceCurrentTime;
[first playAtTime: now + shortStartDelay];
[second playAtTime: now + shortStartDelay];

How do I loop a video with AVFoundation without unwanted pauses at the end?

I am trying to play a video clip that loops indefinitely. I am doing this the way Apple recommends; by setting up a notification that gets triggered by AVPlayerItemDidPlayToEndTimeNotification:
#property(nonatomic) AVPlayer *videoPlayer;
#property(nonatomic) AVPlayerItem *videoPlayerItem;
-(void)loadVideo
{
NSURL *url = [[NSBundle mainBundle] URLForResource:filename withExtension:extension];
AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil];
NSString *tracksKey = #"tracks";
[asset loadValuesAsynchronouslyForKeys:#[tracksKey] completionHandler:
^{
dispatch_async(dispatch_get_main_queue(),
^{
NSError *error;
AVKeyValueStatus status = [asset statusOfValueForKey:tracksKey error:&error];
if (status == AVKeyValueStatusLoaded)
{
[self setVideoPlayerItem:[AVPlayerItem playerItemWithAsset:asset]];
[videoPlayerItem addObserver:self forKeyPath:#"status" options:0 context:&ItemStatusContext];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector (videoPlayerItemDidReachEnd:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:videoPlayerItem];
[self setVideoPlayer:[AVPlayer playerWithPlayerItem:videoPlayerItem]];
[scenePlayerView setVideoPlayer:videoPlayer];
}
});
}];
}
When triggered, this calls my method to effectively rewind and play the clip again:
-(void)videoPlayerItemDidReachEnd:(NSNotification *)notification
{
[videoPlayerItem seekToTime:kCMTimeZero];
[videoPlayer play];
}
The problem is, there is a brief but visible pause in the video playback every time it hits this method and loops back to the beginning.
The video clips are H.264 and have been tested in other players to ensure that they have no visible "skips" in their content. This is all happening under iOS6 both in the Simulator and on an iPad2 and iPad3.
What am I doing wrong?
You have two possible approaches to get a really professional looking loop with no glitches (AVPlayer by itself will not work). First, you could take your original video and decode it to a series of RGB frames (images). Then encode a new h.264 video and feed the frames into a longer h.264 video that is much longer than the first. For example, you might encode a 10 second looping clips 6 times to make a clip that last one minute or 6 * 5 to make a clip that would play for 5 minutes without glitching when the loop restarts. Then, open the longer clip and play it with AVPlayer and it will not glitch for a period of time.
The second approach would be to use an existing library that already handles seamless looping. You can take a look at my library, see my answer to basically the same question iphone-smooth-transition-from-one-video-to-another. The character in the example at the linked answer uses a repeating "loop" of one video clip that loops over and over when no buttons have been pressed.
Unfortunately it appears this actually is not currently possible using AVPlayer as it stands- there is an unavoidable hiccup upon beginning playback in an AVPlayer in every case. So "messier" hacks appear to be the only way to solve this, using multiple AVPlayers, etc.
I managed to get it working with two AVPlayer, two AVPlayerLayer added to two different views (I've tried add to the same view and the upper one get stuck once in a while), and switch alpha value between these two views.

How to reset the content URL for MPMoviePlayerController when playing a video?

I want to reset the content URL for the MPMoviePlayerController object when playing a video.
on a button click, I am setting the content URL like below,
[videoPlayer setContentURL:url];
But i am getting the "Bad Access" error.
Is there a way to change the url when a movie player is already playing a video?
It should stop the previous video and should start the video for the new url.
It means you didn't allocate memory to videoplayer
MPMoviePlayerController * videoplayer = [MPMoviePlayerController alloc] init];
[videoplayer setcontenturl: url];
it will not give you any error.

Resources