I want to implement the UI slider and adjust the volume.

Asked 1 years ago, Updated 1 years ago, 81 views

Extensions based on the contents of the current textbook (adjust the volume of the UI slider sound source)
is about to be implemented.

The problem I want to solve is that I can adjust the volume after playing music (running the play() method).
When I try to adjust the volume on the slider before playing music, the error Thread1:EXC_BAD_ACCESS(code=1, address=0x48) appears and the process stops.

I spent half a day trying to solve the error, but I can't find a clue to the solution with my current
I asked you a question.
I would appreciate it if someone could let me know.

The code is reprinted below.
I will add any missing information.

import UIKit
import AVFoundation
import MediaPlayer

classViewController:UIViewController {

    override func viewDidLoad(){
        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from anib.
    }

    override funcdidReceiveMemoryWarning(){
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func prepareToPlay()->Bool{
        return true
    }

    // Specifying the cymbal sound source file
    let cymbalPath=Bundle.main.bundleURL.appendingPathComponent("cymbal.mp3")
    // Generate a player instance for cymbals
    varcymbalPlayer=AVAudioPlayer()

    // Specify the guitar sound source file
    let guitarPath=Bundle.main.bundleURL.appendingPathComponent("guitar.mp3")
    // Generate a player instance for cymbals
    varguitarPlayer=AVAudioPlayer()

    // Specifying background music source files
    letbackMusicPath=Bundle.main.bundleURL.appendingPathComponent("backmusic.mp3")
    // Generate background music player instances
    varbackMusicPlayer=AVAudioPlayer()

    @ IBAction function(_sender:Any){
        soundPlayer (player: & cymbalPlayer, path: cymbalPath, count:0)
    }

    @ IBAction funcguitar(_sender:Any){
            soundPlayer(player:&guitarPlayer, path:guitarPath, count:1)
        }

    @ IBAction funcplay(_sender:Any){
        soundPlayer(player:&backMusicPlayer, path:backMusicPath, count:-1)
    }

    //    What to do when the stop button is tapped?
    @ IBAction func stop(_sender:Any){
           soundPlayer(player:&backMusicPlayer, path:backMusicPath, count:0)
            backMusicPlayer.stop()
    }

    @IBOutlet weak var volume:UISlider!

    @ IBAction func volumeController(_sender:UISlider){

        backMusicPlayer.volume=volume.value
    }

    // Common player playback processing
    fileprivate func soundPlayer(player:inoutAVAudioPlayer, path:URL, count:Int){
        do{
            player=try AVAudioPlayer(contentsOf:path, fileTypeHint:nil)
            player.play()
        } catch{
            print("Error Occurred!")
        }
    }
 }

swift4

2022-09-30 19:51

2 Answers

These three lines are the worst points you can see from the code you are currently posting.

var cymbalPlayer=AVAudioPlayer()

    varguitarPlayer=AVAudioPlayer()

    varbackMusicPlayer=AVAudioPlayer()

Each statement is a syntax that declares instance properties and initializes it with AVAudioPlayer().However, the behavior of the initializer AVAudioPlayer.init() without arguments is not mentioned at all in the Apple AVAudioPlayer official document.

In other words, there is no guarantee that an instance created with the syntax AVAudioPlayer() will work. Apple documents may not be flattering enough, so you may have to use poorly documented features, but at least AVAudioPlayer should not use the syntax AVAudioPlayer(/code>).

I don't know long ago, but on iOS from at least 4-5 years ago, no matter what method you try to get an instance created with AVAudioPlayer(), the app always crashes.

As for the fix, if you cannot create the appropriate instance when declaring the property, you should use the Optional type instead of forcing a strange instance.This allows the rest of the code to determine whether the value is nil or not "before the appropriate instance is created."

class ViewController:UIViewController {

    // ... (no changes)

    // Specifying the cymbal sound source file
    let cymbalPath=Bundle.main.bundleURL.appendingPathComponent("cymbal.mp3")
    // Player properties for cymbals
    varcybalPlayer —AVAudioPlayer ?//<-

    // Specify the guitar sound source file
    let guitarPath=Bundle.main.bundleURL.appendingPathComponent("guitar.mp3")
    // Player properties for cymbals
    varguitarPlayer —AVAudioPlayer ?//<-

    // Specifying background music source files
    letbackMusicPath=Bundle.main.bundleURL.appendingPathComponent("backmusic.mp3")
    // Back music player properties
    varbackMusicPlayer —AVAudioPlayer ?//<-

    @ IBAction function(_sender:Any){
        soundPlayer (player: & cymbalPlayer, path: cymbalPath, count:0)
    }

    @ IBAction funcguitar(_sender:Any){
        soundPlayer(player:&guitarPlayer, path:guitarPath, count:1)
    }

    @ IBAction funcplay(_sender:Any){
        soundPlayer(player:&backMusicPlayer, path:backMusicPath, count:-1)
    }

    //    What to do when the stop button is tapped?
    @ IBAction func stop(_sender:Any){
        // ↓ No need to assign a new player to stop
        // soundPlayer(player:&backMusicPlayer, path:backMusicPath, count:0)
        US>backMusicPlayer ?.stop()//<-
    }

    @IBOutlet weak var volume:UISlider!

    @ IBAction func volumeController(_sender:UISlider){

        backMusicPlayer ?.volume=volume.value//<-
    }

    // Common player playback processing
    fileprivate func soundPlayer(player:inoutAVAudioPlayer?, path:URL, count:Int) {//<- `AVAudioPlayer?` instead of `AVAudioPlayer`
        do{
            player=try AVAudioPlayer(contentsOf:path, fileTypeHint:nil)
            player?play()//<-
        } catch{
            print("Error Occurred!", error) // < - Throwing the error makes debugging difficult
        }
    }
}

I don't know what kind of "textbook" you use, but if AVAudioPlayer() directly uses the syntax, but does not mention any of the above precautions, the author of the text is likely to have little experience in iOS development.

There seems to be a lot of trial and error, so the above correction may not be enough, but it should be necessary first.If there is anything you can't do with just this, please let me know in your comments.


2022-09-30 19:51

*The following expression is "Questioner has this kind of wrong understanding," but you may get the impression that the questioner has no idea.That's probably because the author of the textbook you're writing is misleading.Therefore, please read the expressions flexibly.

There are two possible misconceptions that the questioner may have:One for class AVAudioPlayer.The other concerns Swift and object-oriented reference types.First, let's talk about a misunderstanding about the AVAudioPlayer class.

From the name AVAudioPlayer, isn't this class associated with the metaphor of a portable music player such as a CD deck, Walkman, and iPod touch?If you insert Beethoven's Ninth Symphony into the CD deck and press the Play button, the Ninth Symphony will be played, and if you insert Mozart's Magic Flute CD, the Magic Flute will be played.Volume is set on the CD deck, and the volume will not be restored every time you change the CD.
However, the AVAudioPlayer instance is not like that, but one AVAudioPlayer instance is created for one song, one voice data.
See the Class AVAudioPlayer properties url and data sections.

See url
var url:URL?{get}

(data as well.) {get} means read-only and non-writeable properties ({getset} if writable)
In other words, if you replace the CD (if you change the URL or file path), it doesn't mean that another song will be played.Once you generate an instance by specifying the URL in Init(contentsOfurl:URL)
, you cannot change it later.

Based on the code provided, I do not believe that Swift and object-oriented instance references are correctly understood.It's a good opportunity, so let's study Swift's "class reference type."
First, Swift limits reference objects to classes only.Other structures, Enumeration, etc. are value types, not reference types.
So, what is a reference type? First, run the following code in Playground in Xcode.

//Defining Class Hoge
class Hoge {
    var name —String

    var volume —Float=0

    init(name:String){
        self.name = name
    }
}

// Instance class Hoge.
var hoge1 = Hoge(name: "Taro")
hoge1.volume=100
print(hoge1.volume)//100

var hoge0 = substituted a reference for hoge1//hoge1 for hoge0.
hoge0.volume=200
print(hoge1.volume)//200
// Both variables hoge0 and hoge1 have references to the same instance.

This is a reference-type technique in which operations (changes) to the variable hoge0 are reflected in the variable hoge1, but this alone might make you wonder, "Why?"However, considering that the reference type of this class can provide a mechanism that reflects user input on the ViewControllerA screen objects (for example, tables, collections, and so on).
At the same time, however, this mechanism means that if you substitute a reference for another class of instances for a variable that substitutes a reference for an instance of one class, you will no longer refer to an instance of the previous class.

class Hoge {
    var name —String

    var volume —Float=0

    init(name:String){
        self.name = name
    }
}

var hoge1 = Hoge(name: "Taro")
hoge1.volume=100
print(hoge1.volume)//100

var hoge2 = Hoge(name: "Hanako")
hoge2.volume=50
print(hoge2.volume)//50

The hoge1 = hoge2 // variable hoge1 is substituted by a reference to an instance of name = "Hanako" that is substituted by hoge2
print(hoge1.name)//Hanako
print(hoge1.volume)//50

I believe that the above attempts (or experiments) have shown that the code you provided does not work at all as intended.

Below, away from the class reference type, I would like to write practical programming advice as a free gift.
First, write the initialization process for the UIViewController subclass in the viewDidLoad() method.Also, in property definitions, don't feel like you've done initialization by writing the default values.
Next, take a look at each reference that you can view to help with Xcode and make sure you have a better choice than the methods you're about to use.
Please refer to the code below.

import UIKit
import AVFoundation

classViewController:UIViewController, AVAudioPlayerDelegate{

    @IBOutlet weak varplayButton:UIButton!
    @IBOutlet weak var volumeSlider:UISlider!

    varcymbalPlayer —AVAudioPlayer?

    override func viewDidLoad(){
        super.viewDidLoad()

        iflet url=Bundle.main.url(forResource: "cymbal", withExtension: "mp3"){// If the resource is not complex with tiering, resource files can be directly designated.
            cymbalPlayer=try?AVAudioPlayer(contentsOf:url)//Use simple exception handling patterns.
            cymbalPlayer ?.delegate=self
            iflet value = cymbalPlayer ?.volume {
                volumeSlider.value = value
            }
        }
    }

    @ IBAction funcplayCymbal(_sender:UIButton){
        cymbalPlayer?.play()//If the initialization process is done properly, you can play it in just one line.
        sender.isEnabled=false//Do not press the button during playback.
    }

    // When the cymbalPlayerdelegate playback is finished, return the button to press.
    funcaudioPlayerDidFinishPlaying(_player:AVAudioPlayer, successfully flag:Bool){
        playButton.isEnabled=true
    }

    // volume control
    @ IBAction func volumeLebel(_sender:UISlider){
        cymbalPlayer?.volume=sender.value
    }
}

The AVAudioPlayer sound source has an initialization method with exception handling (throws) because it is a bit unreliable, for example, data downloaded on the network or files that can be referenced by music apps. The try? syntax replaces exception handling with Optional Chaining and Optional Binding.


2022-09-30 19:51

If you have any answers or tips


© 2024 OneMinuteCode. All rights reserved.