I have an iOS app using SwiftUI. It handles a few sound files and performs some audio recording. This is the function doing the recording work:
func recordAudio(to file: String, for duration: TimeInterval) {
let audioSession:AVAudioSession = AVAudioSession.sharedInstance()
do {try audioSession.setCategory(.playAndRecord, mode: .default)
try audioSession.setActive(true)
let audioFilename = getDocumentsDirectory().appendingPathComponent(file+".m4a"),
audioURL = URL(fileURLWithPath: audioFilename),
settings = [
AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100,
AVNumberOfChannelsKey: 2,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
audioRecorder = try AVAudioRecorder(url: audioURL, settings: settings)
audioRecorder.delegate = self
audioRecorder.record(forDuration: TimeInterval(2.0))
} catch let error as NSError {
print("Failed -- Recording !!! -> \(error)")
}
}
At this point, it basically works, but there is a strange behaviour that I neither understand nor like.
Here is the problem:
When I start the app and play a sound file, the volume is right for my taste.
Then without ever adjusting the volume I perform some recording (using the function above).
Finally after the recording is done, I go back to the file I played just before and play it again; the volume has mysteriously gone down, without me knowing why.
Is there something in my function that could explain that?
Or some other cause that someone could think of?
If I restart the app, the volume automatically goes back to normal.
For information, I am using iOS 14.4.2 and Xcode 12.4.
The audio session will be a decreased volume during playback after recording in .playAndRecord mode. After recording, explicitly set to something like .playback to get the volume you're expecting.
Related
I'm getting a click sound just when starting the recording in the app. I have no clue where it comes from, thinking it might come from the tap on the button but that seems far fetched.
This is the code that I'm using:
private var recorderSettings: [String: Any] {
[
AVFormatIDKey: NSNumber(value: kAudioFormatMPEG4AAC),
AVSampleRateKey: 44100.0,
AVNumberOfChannelsKey: 1,
AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
]
}
audioRecorder = try AVAudioRecorder(url: fileUrl, settings: recorderSettings)
try audioSession.setCategory(.playAndRecord, mode: .default, options: [])
audioRecorder?.isMeteringEnabled = true
audioRecorder?.record()
meteringTimer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true, block: { (timer) in
self.audioRecorder?.updateMeters()
self.soundSamples[self.currentSample] = self.audioRecorder?.averagePower(forChannel: 0) ?? 0
self.currentSample = (self.currentSample + 1) % self.numberOfSamples
})
A "click" is usually an indication of either an impulse audio value (e.g., a single 1 in a stream of 0's) or a significant non-linearity (e.g., either jumping into or out of a full-volume signal from 0). It's probably not the actual sound of a key or mouse click! (Experiment: put a fixed delay between the click time and the recording onset, and see if the click remains audible.)
If the situation is one where Apple doesn't have built in functions to deal with this common problem, perhaps you can start your recording by gradually fading up the volume at the onset. A fade up over the course of 1/50th of a second might be sufficient for avoiding audible signal discontinuities. I often use fades of 1/40th of a second or less on 44100 fps data as an alternative to going from volume A to volume B instantaneously in my Java audio coding. (In your case, volume A is 0 and volume B is your recording volume.)
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).
I'm trying to build an iOS application using Swift 4 that would play different sounds. Part of what I need to do is to limit the duration of the sound file played depending on some settings.
What I'd like to know is if it's possible to set the duration of the sound prior to playing it so it can stop after the set duration. If so, how?
I'm currently using Swift's AVAudioPlayer but I don't know if it can do it. My current code is shown below:
// resName set depending on settings
url = Bundle.main.url(forResource: resName, withExtension: "mp3")
do{
audioPlayer = try AVAudioPlayer(contentsOf: url!)
audioPlayer.prepareToPlay()
audioPlayer.currentTime = 0
} catch let error as NSError{
print(error.debugDescription)
}
audioPlayer.play()
Thanks in advance for the help :)
Well, since you are in control of playing the sounds, one way how to deal with it would be running a Timer when you start playing that would after the given time period stop playing:
let timeLimit = 1.6 // get it from settings
player.play()
Timer.scheduledTimer(withTimeInterval: timeLimit, repeats: false) { (timer) in
player.stop()
}
And in case you need to cancel it, you can keep a reference to the timer and cancel it using timer.invalidate().
As an audio engineer, I recommend editing your audio to be exactly how you want it to avoid any unwanted artifacts that can happen from coding.
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.
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