如何使用AVPlayer以不同于系统音量的iPod音乐库播放音轨

如何以不同于系统音量的音量播放iPod音乐库中的音乐(如用户定义的播放列表等)?

这适用于任何试图以不同于系统音量的音量播放ipod音乐库中的音乐/播放列表的人。 在那里有几个post说, [MPMusicPlayerController applicationMusicPlayer]可以做到这一点,但我发现,任何时候我改变applicationMusicPlayer的音量,系统音量也会改变。

使用AVAudioPlayer类还有一种更为复杂的播放音乐的方法,但是它要求您将音乐文件从ipod库复制到应用程序包,当您播放dynamic内容(如用户生成的播放列表)时,这会变得非常棘手。 这种技术确实可以让你访问字节,如果你想对数据进行处理(比如DJ应用程序),这种方法也是可行的。 链接到这里的解决scheme。

我所使用的解决scheme使用AVPlayer类,有几个好的职位,如何做到这一点。 这篇文章基本上是我在Stackoverflow和其他地方find的几种不同解决scheme的组合。

我有以下框架链接:

AVFoundation
媒体播放器
AudioToolbox
CoreAudio的
CoreMedia

(我不确定是否所有这些都是关键的,但这就是我所拥有的,我也实现了一些OpenAL的东西,我没有在下面的代码中显示)

 // Presumably in your SoundManage.m file (or whatever you call it) ... #import <CoreAudio/CoreAudioTypes.h> #import <AudioToolbox/AudioToolbox.h> @interface SoundManager() @property (retain, nonatomic) AVPlayer* audioPlayer; @property (retain, nonatomic) AVPlayerItem* currentItem; @property (retain, nonatomic) MPMediaItemCollection* currentPlaylist; @property (retain, nonatomic) MPMediaItem* currentTrack; @property (assign, nonatomic) MPMusicPlaybackState currentPlaybackState; @end @implementation SoundManager @synthesize audioPlayer; @synthesize currentItem = m_currentItem; @synthesize currentPlaylist; @synthesize currentTrack; @synthesize currentPlaybackState; - (id) init { ... //Define an AVPlayer instance AVPlayer* tempPlayer = [[AVPlayer alloc] init]; self.audioPlayer = tempPlayer; [tempPlayer release]; ... //load the playlist you want to play MPMediaItemCollection* playlist = [self getPlaylistWithName: @"emo-pop-unicorn-blood-rage-mix-to-the-max"]; if(playlist) [self loadPlaylist: playlist]; ... //initialize the playback state self.currentPlaybackState = MPMusicPlaybackStateStopped; //start the music playing [self playMusic]; ... } //Have a way to get a playlist reference (as an MPMediaItemCollection in this case) - (MPMediaItemCollection*) getPlaylistWithName:(NSString *)playlistName { MPMediaQuery* query = [[MPMediaQuery alloc] init]; MPMediaPropertyPredicate* mediaTypePredicate = [MPMediaPropertyPredicate predicateWithValue: [NSNumber numberWithInteger: MPMediaTypeMusic] forProperty:MPMediaItemPropertyMediaType]; [query addFilterPredicate: mediaTypePredicate]; [query setGroupingType: MPMediaGroupingPlaylist]; NSArray* playlists = [query collections]; [query release]; for(MPMediaItemCollection* testPlaylist in playlists) { NSString* testPlaylistName = [testPlaylist valueForProperty: MPMediaPlaylistPropertyName]; if([testPlaylistName isEqualToString: playlistName]) return testPlaylist; } return nil; } //Override the setter on currentItem so that you can add/remove //the notification listener that will tell you when the song has completed - (void) setCurrentItem:(AVPlayerItem *)currentItem { if(m_currentItem) { [[NSNotificationCenter defaultCenter] removeObserver:self name:AVPlayerItemDidPlayToEndTimeNotification object:m_currentItem]; [m_currentItem release]; } if(currentItem) m_currentItem = [currentItem retain]; else m_currentItem = nil; if(m_currentItem) { [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMusicTrackFinished) name:AVPlayerItemDidPlayToEndTimeNotification object:m_currentItem]; } } //handler that gets called when the name:AVPlayerItemDidPlayToEndTimeNotification notification fires - (void) handleMusicTrackFinished { [self skipSongForward]; //or something similar } //Have a way to load a playlist - (void) loadPlaylist:(MPMediaItemCollection *)playlist { self.currentPlaylist = playlist; self.currentTrack = [playlist.items objectAtIndex: 0]; } //Play the beats, yo - (void) playMusic { //check the current playback state and exit early if we're already playing something if(self.currentPlaybackState == MPMusicPlaybackStatePlaying) return; if(self.currentPlaybackState == MPMusicPlaybackStatePaused) { [self.audioPlayer play]; } else if(self.currentTrack) { //Get the system url of the current track, and use that to make an AVAsset object NSURL* url = [self.currentTrack valueForProperty:MPMediaItemPropertyAssetURL]; AVAsset* asset = [AVURLAsset URLAssetWithURL:url options:nil]; //Get the track object from the asset object - we'll need to trackID to tell the //AVPlayer that it needs to modify the volume of this track AVAssetTrack* track = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; //Build the AVPlayerItem - this is where you modify the volume, etc. Not the AVPlayer itself AVPlayerItem* playerItem = [[AVPlayerItem alloc] initWithAsset: asset]; //initWithURL:url]; self.currentItem = playerItem; //Set up some audio mix parameters to tell the AVPlayer what to do with this AVPlayerItem AVMutableAudioMixInputParameters* audioParams = [AVMutableAudioMixInputParameters audioMixInputParameters]; [audioParams setVolume: 0.5 atTime:kCMTimeZero]; //replace 0.5 with your volume [audioParams setTrackID: track.trackID]; //here's the track id //Set up the actual AVAudioMix object, which aggregates effects AVMutableAudioMix* audioMix = [AVMutableAudioMix audioMix]; [audioMix setInputParameters: [NSArray arrayWithObject: audioParams]]; //apply your AVAudioMix object to the AVPlayerItem [playerItem setAudioMix:audioMix]; //refresh the AVPlayer object, and play the track [self.audioPlayer replaceCurrentItemWithPlayerItem: playerItem]; [self.audioPlayer play]; } self.currentPlaybackState = MPMusicPlaybackStatePlaying; } - (void) pauseMusic { if(self.currentPlaybackState == MPMusicPlaybackStatePaused) return; [self.audioPlayer pause]; self.currentPlaybackState = MPMusicPlaybackStatePaused; } - (void) skipSongForward { //adjust self.currentTrack to be the next object in self.currentPlaylist //start the new track in a manner similar to that used in -playMusic } - (void) skipSongBackward { float currentTime = self.audioPlayer.currentItem.currentTime.value / self.audioPlayer.currentItem.currentTime.timescale; //if we're more than a second into the song, just skip back to the beginning of the current track if(currentTime > 1.0) { [self.audioPlayer seekToTime: CMTimeMake(0, 1)]; } else { //otherwise, adjust self.currentTrack to be the previous object in self.currentPlaylist //start the new track in a manner similar to that used in -playMusic } } //Set volume mid-song - more or less the same process we used in -playMusic - (void) setMusicVolume:(float)vol { AVPlayerItem* item = self.audioPlayer.currentItem; AVAssetTrack* track = [[item.asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; AVMutableAudioMixInputParameters* audioParams = [AVMutableAudioMixInputParameters audioMixInputParameters]; [audioParams setVolume: vol atTime:kCMTimeZero]; [audioParams setTrackID: track.trackID]; AVMutableAudioMix* audioMix = [AVMutableAudioMix audioMix]; [audioMix setInputParameters: [NSArray arrayWithObject: audioParams]]; [item setAudioMix:audioMix]; } @end 

请原谅你看到的任何错误 – 让我知道在评论中,我会解决它们。 否则,如果有人遇到同样的挑战,我希望这会有所帮助!

其实我find了一个非常简单的方法,通过从MPMusicPlayer加载iPod URL ,然后通过AVAudioPlayer进行播放。

 // Get-da iTunes player thing MPMusicPlayerController* iTunes = [MPMusicPlayerController iPodMusicPlayer]; // whazzong MPMediaItem *currentSong = [iTunes nowPlayingItem]; // whazzurl NSURL *currentSongURL = [currentSong valueForProperty:MPMediaItemPropertyAssetURL]; info( "AVAudioPlayer playing %s", [currentSongURL.absoluteString UTF8String] ) ; // mamme AVAudioPlayer NSError *err; avAudioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:currentSongURL error:&err] ; if( err!=nil ) { error( "AVAudioPlayer couldn't load %s", [currentSongURL.absoluteString UTF8String] ) ; } avAudioPlayer.numberOfLoops = -1; //infinite // Play that t [avAudioPlayer prepareToPlay] ; [avAudioPlayer play]; [avAudioPlayer setVolume:0.5]; // set the AVAUDIO PLAYER's volume to only 50%. This // does NOT affect system volume. You can adjust this music volume anywhere else too.