Volume Slider in swift - ios

I am trying to create a slider that controls volume of 4 audio files.
Here is my code for the audio:
var ButtonAudioURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("audio1", ofType: "mp3")!)
var firstplayer = AVAudioPlayer()
var ButtonAudioURL2 = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("audio2", ofType: "mp3")!)
var secondplayer = AVAudioPlayer()
var ButtonAudioURL3 = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("audio3", ofType: "mp3")!)
var thirdplayer = AVAudioPlayer()
var ButtonAudioURL4 = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("audio4", ofType: "mp3")!)
var forthplayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
firstplayer = AVAudioPlayer(contentsOfURL: ButtonAudioURL, error: nil)
secondplayer = AVAudioPlayer(contentsOfURL: ButtonAudioURL2, error: nil)
thirdplayer = AVAudioPlayer(contentsOfURL: ButtonAudioURL3, error: nil)
forthplayer = AVAudioPlayer(contentsOfURL: ButtonAudioURL4, error: nil)
I have the audio to play when each button is touched. But I just need to get a slider to control the volume of each audio file from each button.
How should i do this? I have looked at MPVolumeView class reference on developer.apple, but I am still confused and in the developer.apple reference for MPVolumeView, it talks about AirPlay, can I disable that?
I just need a slider to control the volume.

You're close!
Drag in a slider into your storyboard, and create an IB outlet and IB action for it.
In the IB action code, use firstPlayer.volume = sliderOutlet.value
Since both range from 0 to 1 as default, they are on the same scale and so can be used like this without scaling. You may need to declare the AVAudioPlayers more globally and have a way of seeing which button was pressed (e.g. a variable that stores the name of the button pressed). That way, you change the correct volume using 4 if statements and don't end up changing the volume for a player that has not been initialized yet.
Let me know if this works!

If you use Swift 2 Try with this:
#IBOutlet weak var volumen: UISlider!
var reproductor = AVAudioPlayer()
after:
#IBAction func sliderValueChanged(sender: UISlider) {
let selectedValue = Float(sender.value)
reproductor.volume = selectedValue
}
and try your slider volume during the playing sound. That is all. I've helped.

I am using Xcode 6. I am building a similar example and I dragged from the slider in the storyboard to my viewController and created an Outlet
#IBOutlet weak var volumeControl: UISlider!
after that I created an action
#IBAction func adjustVolume(sender: AnyObject) {
if audioPlayer != nil {
audioPlayer?.volume = volumeControl.value
}
}
and it is working for me. You can create 4 sliders and assign them to each instance of AudioPlayer you created.
In this link you can find an example of Playing an audio on iOS8 using AVAudioPlayer http://www.techotopia.com/index.php/Playing_Audio_on_iOS_8_using_AVAudioPlayer

Related

`audioPlayerDidFinishPlaying` Not Being Called [duplicate]

This question already has an answer here:
AVAudioPlayer.play() does not play sound
(1 answer)
Closed 3 years ago.
Having an issue where audioPlayerDidFinishPlaying isn't getting called...
I've looked at some other SO threads:
Swift: AudioPlayerDidFinished will not be called
AVAudioPlayerDelegate doesn't call the method
...but still having the same issue.
I've set my ViewController to conform to AVAudioPlayerDelegate:
class ViewController: UIViewController, AVAudioPlayerDelegate {
var audioEngine = AVAudioEngine()
...
}
And create the player here; setting the delegate right after creating the player itself:
func startAudioEngine(audioFileURL: URL) {
var player: AVAudioPlayer?
do {
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(contentsOf: audioFileURL, fileTypeHint: AVFileType.mp3.rawValue)
guard let player = player else { return }
player.delegate = self
player.prepareToPlay()
player.play()
} catch let error {
print(error.localizedDescription)
}
}
Your problem is twofold:
var player: AVAudioPlayer? is declared local to func startAudioEngine(), so as soon as the function exists, you lose reference to player.
You are setting the delegate on the optionally bound player
Change to this (or something like this):
class ViewController: UIViewController, AVAudioPlayerDelegate {
var audioEngine = AVAudioEngine()
var player: AVAudioPlayer?
}
Then after you assign player, declare your delegate self.player?.delegate = self. Make sure to include self. so that the delegate is set on the class property and not the optional binding;

Updating Labels on a second View Controller

I have several different ViewControllers set up in my project. The first one acts as a 'landing' page and and on pressing a button the view is directed to a following ViewController.
There are various buttons and labels on there that I want to use to provide information and run a method. The buttons all work ok, but I have followed every tutorial I can to get the labels to update based on a method, and I can't seem to get them to do so.
I know the methods are called correctly as I have put print statements in there to check.
Basic idea is, program plays a series of beeps separated by a delay (bleep_time), this changes each level (bleep_level) and there are several steps (bleep_step) in each level. Ive simplified the arrays containing this data to save space.
I have successfully created a number of tutorial projects and the labels all update correctly, but they only use one ViewController.
Is the issue I am facing due to using 2 ViewControllers?
ViewController 1
import UIKit
class Bleep_Test_Menu: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
View Controller 2
import UIKit
import AVFoundation
class Bleep_Test_Level_1: UIViewController {
// Global Variables
var audioPlayer: AVAudioPlayer?
// Bleep test delays
var bleep_time = [1,2,3,4]
// Levels of bleep test
var bleep_level = [1,2,3,4]
// Level Label
#IBOutlet weak var bleepTestLevel1Level: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
// Function to run repeated bleeps
#IBAction func bleepTestLevel1Start(_ sender: Any) {
var i = 0
tracker = false
let length = bleep_time.count
while i < length {
var bleeplevel = bleep_level[i]
bleepTestLevel1Level.text = "\(bleeplevel)"
playSaveSound()
sleep(UInt32(bleep_time[i]))
i += 1
}
}
// Function to play sounds
func playSaveSound(){
let path = Bundle.main.path(forResource: "Sound1.wav", ofType: nil)!
let url = URL(fileURLWithPath: path)
do {
//create your audioPlayer in your parent class as a property
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer?.play()
} catch {
print("couldn't load the file")
}
}
}
The only error I keep getting in the console is this;
"Tests[53616:891478] - changing property contentsGravity in transform-only layer, will have no effect"
But I can't seem to find anything that relates to my issue.

Swift in iOS with AVAudioPlayer: Singleton not working the way I hoped it would

I am using a standard Master-Detail project, listing songs in the Master and playing them in Detail. Each song has up to four parts playing simultaneously, with independent volume control, so I have four AVAudioPlayer objects in Detail, each with a slider with an IBOutlet and an IBAction to implement the volume control.
The problem is that when you click on a song (in the list on the Master), the previous song doesn't stop. Both songs play, although the volume controls now only control the most recent song. This can go on for any number of songs.
I want to get rid of the currently playing song when a new song is clicked on.
I thought I might be able to accomplish this by creating the players inside a Singleton, in such a way that there would only ever be four players. Since, according to the documentation, each player can only play one sound file at a time, I hoped the previous sound file would stop playing when the new one started. But it's not working. The same behavior described above is still happening: multiple songs can play simultaneously, with the volume controls only controlling the most recent song. Any suggestions would be greatly appreciated.
Here is the code for the Singleton:
import Foundation
import AVFoundation
class FourPlayers {
static let audioPlayers = [one, two, three, four]
static let one = AVAudioPlayer()
static let two = AVAudioPlayer()
static let three = AVAudioPlayer()
static let four = AVAudioPlayer()
private init() {} //This prevents others from using the default '()' initializer for this class.
}
(Initially, I had just made audioPlayers static, but when that didn't work, I decided to make each individual player static as well.)
Then, in the DetailViewController:
var audioPlayers = FourPlayers.audioPlayers
Here's code for one of the four volume controls:
#IBOutlet weak var vol1: UISlider!
#IBAction func volAdjust1(sender: AnyObject) {
audioPlayers[0].volume = vol1.value
}
Playing a song looks like this (the audioFiles array is populated when the song info is passed in from the Master):
var audioFiles = []
func playAudioFiles() {
var i = 0
for _ in audioFiles {
audioPlayers[i].play()
i+=1
}
}
This is the code telling the players which file to play:
func prepareAudioFiles () {
var i = 0;
for audioFile in audioFiles {
let s = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(audioFile as? String, ofType: "mp3")!)
do {
audioPlayers[i] = try AVAudioPlayer(contentsOfURL:s)
} catch {
print("Error getting the audio file")
}
audioPlayers[i].prepareToPlay()
self.audioPlayers[i].delegate = self
}
}
It appears the the volume control is only adjusting the first player (index 0)
#IBAction func volAdjust1(sender: AnyObject) {
audioPlayers[0].volume = vol1.value
}
maybe you could add a variable to keep track of the currently playing index
var currentlyPlayingIndex = 0
#IBAction func volAdjust1(sender: AnyObject) {
audioPlayers[currentlyPlayingIndex].volume = vol1.value
}
And update it when needed
When you play the audio you could use the same variable
func playAudioFiles() {
var i = 0
for _ in audioFiles {
if i == currentlyPlayingIndex {
audioPlayers[i].play()
} else {
audioPlayers[i].stop()
}
i+=1
}
}
The problem is the following line:
var audioPlayers = FourPlayers.audioPlayers
You are actually creating a new copy of the FourPlayers.audioPlayers array into the audioPlayers variable. So you are not reusing the AVAudioPlayers but creating independent ones for each song.
See the "Assignment and Copy Behavior for Strings, Arrays, and Dictionaries" section in the Swift Programming Documentation, Classes and Structures
You could use the static variable directly in your code instead of making a copy, for example:
#IBOutlet weak var vol1: UISlider!
#IBAction func volAdjust1(sender: AnyObject) {
FourPlayers.audioPlayers[0].volume = vol1.value
}
and:
var audioFiles = []
func playAudioFiles() {
var i = 0
for _ in audioFiles {
FourPlayers.audioPlayers[i].play()
i+=1
}
}
and update the rest of your code accordingly.

Flow Control in Swift - AVAudioPlayer

I wrote an Iphone Swift app that plays a series of sounds in a random order using AVAudioPlayer-- for now a pop sound, a horn sound and a gong. It works when you hit the play button, except....
However, when I hit the stop button nothing happens- it doesn't respond (The stop does work if I have just one sound). I believe it is due to my flow control. If I did not put in the 'while soundPlayer.playing == true {}', the code would "fly" through the sound and not wait for it to finish.
How can I modify the code so the the sound plays to completion before going to the next sound? And also allow the stop button to be functional? See Code and screen shot below. (Actually Stack overflow will not allow me to post an image since I am so new)
//
// ViewController.swift
// InsultSchool2 Swift
//
// Created by JR on 12/3/14.
// Copyright (c) 2014 JR. All rights reserved.
//
import UIKit
import AVFoundation
//---------------------------------------------------
var soundPlayer:AVAudioPlayer = AVAudioPlayer()
// used to try to do some kind of flow control. May not be needed if better way is found.
var stopFlag:Bool = false
//-----------------------------------------------------
class ViewController: UIViewController {
#IBOutlet weak var valueSliderTime: UISlider!
#IBOutlet weak var valueLabelTime: UILabel!
#IBOutlet weak var valueStepperVolume: UIStepper!
#IBOutlet weak var valueLabelVolume: UILabel!
//------------------------------------------------------
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//Displays an initial Volume value in volume label on load
//Convert to an int. Otherwise you get a weird value when getting close to zero
//Multiply by 10 so the int works. Otherwise you would int'ing a number between 0.0 and 1.0.
// "\()" is a shorthand to convert whatever to a string
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//------------------------------------------------------
#IBAction func buttonStop(sender: UIBarButtonItem) {
NSLog("Enter Button Stop")
//?1 If a sound is not playing and stop is hit, then it crashes
//?2 the stop button does not work with the whlle loop below
soundPlayer.stop()
}
#IBAction func sliderTime(sender: UISlider) {
valueLabelTime.text = "\(Int(valueSliderTime.value))"
}
#IBAction func stepperVolume(sender: UIStepper) {
//Converted to an int. Otherwise you get a weird value when getting close to zero
valueLabelVolume.text = "\(Int(valueStepperVolume.value * 10))"
}
#IBAction func buttonPlay(sender: UIBarButtonItem) {
NSLog("Enter Button Start")
var soundArray:[String] = ["Sound0", "Sound1", "Sound2"]
// Randomizes a number to indicate which random sound to play in the array
/* Number is from 0 to number in the (). Don't add one or 0 will never play. Go one more than the numbers in the array. For example if you have 3 items in the array go to 3. THis will go from 0 to 2 (ie., 3 items)*/
// Reference----- var soundRandomNumber:Int = Int(arc4random_uniform(3))
var soundRandomNumber:Int
soundRandomNumber = Int(arc4random_uniform(3))
//Creates a random number to wait between sounds based on the slider value.
//arc4random requires a UInt32 (Unsigned is a positive number).
//_uniform is slightly more random than without the Uniform
//The documentation says to use Int otherwise.
println(Int(valueSliderTime.value))
NSLog("valueSliderTime.value")
var waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
println(waitTimeRandomNumber)
NSLog("waitTimeRandomNumber")
// Constructs a string with the random number for the URL
//var soundFile:String = soundArray[soundRandomNumber]
var soundFile:String
soundFile = soundArray[soundRandomNumber]
//Reference---- var soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
var soundURL:NSURL!
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
//?3 How do I set up a loop or control that works until the stop button is pressed?
while stopFlag == false{
NSLog("inside while")
println(stopFlag)
//?4 Is the below correct? The actual volume does not seem to change though the .volume does
soundPlayer.volume = Float(valueStepperVolume.value)
println(Float(valueStepperVolume.value))
NSLog("Float(valueStepperVolume.value)")
println(soundPlayer.volume)
NSLog("soundPlayer.volume")
soundRandomNumber = Int(arc4random_uniform(3))
soundFile = soundArray[soundRandomNumber]
soundURL = NSBundle.mainBundle().URLForResource(soundFile, withExtension:"mp3")
soundPlayer = AVAudioPlayer(contentsOfURL: soundURL, error: nil)
soundPlayer.prepareToPlay()
soundPlayer.play()
//?5 How do I make the player not blow through the sound and wait until is finished
while soundPlayer.playing == true {
}
//?6 How can i make a random timer that waits for a random time before relooping?
waitTimeRandomNumber = Int(arc4random_uniform(UInt32(valueSliderTime.value)))
}// End of while loop
} //ends playButton IBAction
//?7 How to allow this app to play over other music in another player
}
use the repeat while statement instead to control the flow:
here is a link to apple's developers reference:
https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ControlFlow.html
repeat {
// move up or down for a snake or ladder
square += board[square]
// roll the dice
if ++diceRoll == 7 { diceRoll = 1 }
// move by the rolled amount
square += diceRoll
} while square < finalSquare
print("Game over!")

AVFoundation Value of option type 'String?' not unwrapped, App Crashes, won't play sound

I'm trying to play a simple sound when I push a button. xcode is forcing me to use a "!" for my NSURL for the audio file, I followed other tutorials for how to play a sound in swift and for some reason I keep getting this error, why!? Code:
import UIKit
import AVFoundation
class ViewController: UIViewController {
var player = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
let path = NSBundle.mainBundle().pathForResource("buttonSound", ofType: "mp3")
let fileURL = NSURL(fileURLWithPath: path!)
player = AVAudioPlayer(contentsOfURL: fileURL, error: nil)
player.prepareToPlay()
}
#IBAction func button21(sender: AnyObject) {
player.play()
}
}
When I do add the "!", the app will load without crashing, when I press the button the app crashes and the log outputs "fatal error: unexpectedly found nil while unwrapping an Optional value"
Good to see your problem got solved. Just adding to the conversation:
I had the same issue as you and I followed the advice given by other people here, but it didn't get rid of my problem (unexpectedly found nil while unwrapping an Optional value). I'm using Xcode 6.1 and the thing that solved it for me was deleting the file and adding it again except this time, instead of dragging the file in, I did it by right-clicking the project folder and selecting the "Add Files to your_app's_name" option.
import UIKit
import AVFoundation
class ViewController: UIViewController {
var myPlayer = AVAudioPlayer()
var yourSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("alarm2", ofType: "mp3")!)
func prepareYourSound() {
myPlayer = AVAudioPlayer(contentsOfURL: yourSound, error: nil)
myPlayer.prepareToPlay()
}
override func viewDidLoad() {
super.viewDidLoad()
prepareYourSound()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func playMySound(sender: AnyObject) {
myPlayer.play()
}
}
HERE is the project sample link
Sample Project
A bit old now, but I had the same problem recently.
The way I fixed it was to select my file (an mp3, too) and go into the File Inspector and make sure the Target Membership was checked for my project.

Resources