AVAudioPlayer playing Sound with very low volume in iPhone 6 and 6+ - ios

I am playing a sound using AVAudioPlayer.
The sound volume in iPods (iOS7 & iOS8 both) is good.
But when I play same sound in iPhones the sound is playing with very low volume.
Here is my code:
var audioPlayer: AVAudioPlayer?
var soundURL:NSURL? = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("sound", ofType: "mp3")!)
audioPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
audioPlayer?.prepareToPlay()
audioPlayer?.volume = 1
audioPlayer?.play()
I've already included AudioToolbox.framework library in my project.
How can I improve sound in iPhone6 and 6+ ?
EDIT
Recently i noticed that automatically the sound was increased for couple of seconds,but don't know what's wrong happening.?

The volume is low because the sound is coming via the top speaker in iPhone(used for calling).
You have to override the AVAudioSession out put port to the main speaker.(bottom speaker)
This is the code that Route audio output to speaker
AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker, error: &error)
Swift 3.0:
do {
try AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.speaker)
} catch _ {
}

Solution for Swift 4
try? session.setCategory(.playAndRecord, mode: .default, policy: .default, options: .defaultToSpeaker)
P.S. For full class you can check my gist

I think if you set the volume of AVAudioPlayer to 1, then the problem is on the system volume.
You may use a different audio category when playing your sound and the volume is different in different category or different audio route(like native speaker, headset or bluetooth) The default category in your app will be ambient, but the Music app's is playback.
You can try to adjust your volume when you are playing your sound rather than before it's played.

you need to maximize the media volume as well.
extension MPVolumeView {
var volumeSlider:UISlider {
self.showsRouteButton = false
self.showsVolumeSlider = false
self.hidden = true
var slider = UISlider()
for subview in self.subviews {
if subview.isKindOfClass(UISlider){
slider = subview as! UISlider
slider.continuous = false
(subview as! UISlider).value = AVAudioSession.sharedInstance().outputVolume
return slider
}
}
return slider
}
}
then change the volume like this :
func setMediaVolume(volume : Float) {
MPVolumeView().volumeSlider.value = volume
}
in your case, setMediaVolume(1) right before you play your audio.
also make sure that the current audio route is to the Speaker and not the earpiece, you can do that using
try! AVAudioSession.sharedInstance().overrideOutputAudioPort(AVAudioSessionPortOverride.Speaker)

I also experienced this in the swift playground, the volume is much smaller than the sound in the original file. Just increase the volume.
audioPlayer?.volume = 3

Related

How can I play audio through non-apple bluetooth earphones without distortion

I have an iPhone app that plays back prerecorded video clips. The audio sounds fine from the phone speaker or applepods, but when I listen through bluetooth connected headphones/speakers that are not apple it sounds terribly distorted. I have tried to use AVAUDIOSESESSION to fix the problem, but no luck. This is the code I tried (from another similar stack overflow answer):
var error: NSError?
var success: Bool?
override func viewDidLoad() {
super.viewDidLoad()
// so we can play the audio undistorted through bluetooth headphones:
do {
try AVAudioSession.sharedInstance().setCategory(.playAndRecord,
mode: .default, options: [.mixWithOthers, .allowAirPlay,
.allowBluetoothA2DP,.defaultToSpeaker])
try AVAudioSession.sharedInstance().setActive(true)
} catch let error1 as NSError {
error = error1
success = false
}
if !success! {
print("Failed to set audio session category. Error: \(error!)")
}
I am a first time developer so I need things explained very simply and from the basics up. Thank you so much.
It was an embarrassing error. I hope no one else makes it.
The range of AVPlayer's volume must be between 0.0 and 1.0. If you set the volume louder than this (I had player.volume = 7, instead of player.volume = 0.7) you will get distortion on non-apple bluetooth headphones for some reason (apple earphones and the internal speaker accommodates for this error).

How to dynamically change players in AudioKit output

I'm implementing an app utilizing AudioKit that allows you to play a large number of audio files and switch between them at any time. Additionally we need one audio file to play on loop for ambient noise.
What is the correct way to dynamically change players in AudioKit's output? Or what's the proper way to implement this behavior?
AudioKit requires you set its output which can be an AKPlayer that plays one audio file or an AKMixer given an array of AKPlayers for example. You cannot change the output once AudioKit has been started as I saw previously. So currently my approach is to use AKMixer to play two AKPlayers - one for the current file and one for the ambient noise. When the user taps the 'Next' button I stop() AudioKit, recreate an AKMixer with a new player for the next song's audio file and the ambient noise player, assign that to output, start() AudioKit, and play both players. This results in undesirable behavior because playback of the ambient noise is stopped when switching songs resulting in a brief pause.
If this is the correct approach, how can we update the output without stopping AudioKit? I wondered if you can initialize the mixer with an array of players that's a strongly held property and simply add/remove players in the array. But that doesn't work - starting a new player throws an error player started when in a disconnected state because this node is not attached to the output.
I've created a sample project to demonstrate this behavior. When launched the app plays drums as ambient noise and waves for the current track. When you tap Next it'll switch to the next track (which in this demo code is just the same audio file) and you can hear the drums stop and then resume which is undesirable. I've included the project's code below:
final class Maestro: NSObject {
static let shared = Maestro()
private var audioPlayer: AKPlayer?
private var ambientPlayer: AKPlayer = {
let player = AKPlayer(url: Bundle.main.url(forResource: "drums", withExtension: "wav")!)!
player.isLooping = true
return player
}()
private var mixer: AKMixer?
private let audioFileURL = Bundle.main.url(forResource: "waves", withExtension: "mp3")!
func play() {
playNewPlayer(fileURL: audioFileURL)
}
func next() {
//In the real app we'd play the next audio file in the playlist but for the demo we'll just play the same file
playNewPlayer(fileURL: audioFileURL)
}
private func playNewPlayer(fileURL: URL) {
audioPlayer?.stop()
audioPlayer = nil
do {
try AudioKit.stop()
} catch {
print("Maestro AudioKit.stop error: \(error)")
}
audioPlayer = AKPlayer(url: fileURL)!
mixer = AKMixer([audioPlayer!, ambientPlayer])
AudioKit.output = mixer
do {
try AudioKit.start()
} catch {
print("Maestro AudioKit.start error: \(error)")
}
if ambientPlayer.isPlaying {
//need to resume playback from current position
let pos = ambientPlayer.currentTime
ambientPlayer.stop()
ambientPlayer.play(from: pos)
} else {
ambientPlayer.play()
}
audioPlayer?.play()
}
}

AVAudioPlayer will not play audio for one mp3 but will for others?

All of my other audio files play perfectly through the audio player. Most of the files are less than 5 seconds long, and the longest file that still plays is 23 seconds long. For some reason my 1:15 long file "sortSong" is completely silent when it is called.
Here is the code for my audio player:
import Foundation
import AVFoundation
class AudioPlayer {
static let sharedInstance = AudioPlayer()
enum Sound: String {
case sortSongPlay = "sortSong"
case centerButtonRelease = "centerButtonRelease"
case buttonTap = "tapSound"
static var all: [Sound] {
return [.centerButtonRelease, .buttonTap, sortSongPlay]
}
var url: URL {
return URL.init(fileURLWithPath: Bundle.main.path(forResource: self.rawValue, ofType: "mp3")!)
}
}
private var soundMap = [String: SystemSoundID]()
init() {
for sound in Sound.all {
let soundUrl = sound.url
var soundId: SystemSoundID = 0
AudioServicesCreateSystemSoundID(soundUrl as CFURL, &soundId);
soundMap[sound.rawValue] = soundId
}
}
func play(sound: Sound) {
AudioServicesPlaySystemSound(soundMap[sound.rawValue]!)
}
}
The sound is played when this function is called in my view controller.
func successfulSort() {
AudioPlayer.sharedInstance.play(sound: .sortSongPlay)
rotateSortImageOne()
rotateSortImageTwo()
}
This is the action that calls the successfulSort func
if inputText == "hello" && seedArray == [1, 4, 1] {
successfulSort()
}
If I simply change the case sortSongPlay to = "shortSortSong" (the 23 second version) it plays just fine.
All of my sound files have their target memberships checked for this project file, and all of the files have the correct path. The audio will play in the interface builder if I press the play button. There are no compiler errors or warnings, the app never crashes, the audio for sortSong simply isn't playing when it is called in the app.
This is a link containing examples I have tried on my project. The first two sounds play silently in the app while the shorter sounds all play perfectly. https://soundcloud.com/spiffystache
What is causing this to be silent?
You should put a correct title on your question.
Your code does not use AVAudioPlayer, but uses System Sound Services.
The documentation clearly states its limitation:
You can use System Sound Services to play short (30 seconds or
shorter) sounds.
I have never checked if its limitation is exactly 30 seconds, but it matches the description in your question.
Consider using AVAudioPlayer (as in the title) to play longer sound.

AVPlayer : change rate with NO pitch correction?

I am using the rate property of AVPlayer to change the playback speed of an audio sample. It seems to always apply pitch correction, but I want no pitch correction so that when it speeds up it gets higher like a record or an old tape player. Is there any way to shut off pitch correction altogether in AVPLayer?
I am currently using Swift 3, but Objective C answers are welcome, too.
Not sure if this is possible using an AVPlayer, but if you're just using it to play audio you can easily do this with an AVAudioEngine:
var audioPlayer = AVAudioPlayerNode()
var engine = AVAudioEngine()
var speedControl = AVAudioUnitVarispeed()
// engine setup:
do {
let file = try AVAudioFile(forReading: "myFile.mp3")
engine.attach(audioPlayer)
engine.attach(speedControl)
engine.connect(audioPlayer, to: speedControl, format: nil)
engine.connect(speedControl, to: engine.mainMixerNode, format: nil)
audioPlayer.scheduleFile(file, at: nil)
try engine.start()
} catch {
print(error)
}
// changing rate without pitch correction:
speedControl.rate = 0.91
Actually, this is possible with AVPlayer --
let player = AVPlayer(url: fileURL)
// to turn off pitch correction:
player.currentItem?.audioTimePitchAlgorithm = .varispeed
Using AVAudioUnitTimePitch is what you need. Changing rate property (default is 1.0) will change speed of audio without pitch changing.

how do I set my Audio Session Default Behavior to Playback in swift?

My app has a "Click" sound functionality. I used the
import AVFoundation
then the following function to run the "Click" sound:
var audioPlayer = AVAudioPlayer()
func playSound() {
var soundPath = NSBundle.mainBundle().pathForResource("tick", ofType: "wav")
var soundURL = NSURL.fileURLWithPath(soundPath!)
self.audioPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
self.audioPlayer.play()
}
Now if the user is running a music player, my app causes the music player to stop. I read about the Audio Session Default Behavior in the documentation, but I don't know how to apply it.
Can you please help?
Thank you!
If you are wondering the syntax for swift 2, here it is:
let audioSession = AVAudioSession.sharedInstance()
do {
try audioSession.setCategory(AVAudioSessionCategoryPlayAndRecord, withOptions: .DuckOthers)
} catch {
print("AVAudioSession cannot be set: \(error)")
}
Depending on what you want the app to behave, i.e, how your app's sound effect or music should interact with other app's background audio session, you might need to tweak both the audio session category and categoryOption.
If you just want to play the sound effect, like "tick" sound, then, AVAudioSessionCategoryAmbient and DuckOthers should be used respectively, for example:
let audioSession = AVAudioSession.sharedInstance()
var error: NSErrorPointer = nil
audioSession.setCategory(AVAudioSessionCategoryAmbient, withOptions: .DuckOthers, error: error)
However, I suppose you are actually trying to play a sound effect, in this case, the AudioServices API is a more suitable choice. You can check func AudioServicesPlaySystemSound(inSystemSoundID: SystemSoundID) in AudioToolbox framework for more details.
Another common scenario. If you want to have your app to play audio exclusively, even if there're other app's playing the music in the background, you need to set the category to AVAudioSessionCategorySoloAmbient, for example:
let audioSession = AVAudioSession.sharedInstance()
var error: NSErrorPointer = nil
audioSession.setCategory(AVAudioSessionCategorySoloAmbient, error: error)
I hope you've got what you're looking for.

Resources