//

//  ViewController.swift

//  Audio

//

//  Created by MacBookPro on 2017. 11. 28..

//  Copyright © 2017년 MacBookPro. All rights reserved.

//

//오디오를 재생하려면 헤더 파일과 델리게이트가 필요하다.

import UIKit

import AVFoundation

class ViewController: UIViewController,AVAudioPlayerDelegate, AVAudioRecorderDelegate{

    

    

    //변수 및 상수

    var audioPlayer : AVAudioPlayer!    //avaudioplayer인스턴스 변수

    var audioFile : URL!                // 재생할 오디오의 파일명 변수

    let MAX_VOLUME : Float = 10.0       //최대 불륨, 실수형 상수

    var progressTimer : Timer!          //타이머를 위한 변수

    

    let timePlayerSelector:Selector = #selector(ViewController.updatePlayTime)

    let timeRecordSelector:Selector = #selector(ViewController.updateRecordTime)

    

    //재생 아웃렛 변수

    @IBOutlet weak var pvProgressPlay: UIProgressView!

    @IBOutlet weak var lbCurrentTime: UILabel!

    @IBOutlet weak var lbEndTime: UILabel!

    

    @IBOutlet weak var btnPlay: UIButton!

    @IBOutlet weak var btnPause: UIButton!

    @IBOutlet weak var btnStop: UIButton!

    @IBOutlet weak var slVolume: UISlider!

    

    

    

    //녹음 아웃렛 변수

    

    @IBOutlet weak var btnRecord: UIButton!

    @IBOutlet weak var lbRecordTime: UILabel!

    

    var audioReorder : AVAudioRecorder!

    var isRecorderMode = false //현재는 재생 모드

    

    

    override func viewDidLoad() {

        super.viewDidLoad()

        

        

        selectAudioFile()

        

        

        if (!isRecorderMode) {

            

            //오디오를 초기화 하는 함수

            initPlay()

            //녹음 버튼과 녹음 재생시간은 비활성화

            btnRecord.isEnabled = false

            lbRecordTime.isEnabled = false

        }else{

            

            initRecord()

            

        }

        

        

    }

    

    //녹음기능 초기화 함수

    func selectAudioFile() -> Void {

        

        //재생모드

        if !isRecorderMode {

                    audioFile = Bundle.main.url(forResource: "music", withExtension: "mp3")

        }else{//녹음모드

           //녹음모드일 때는 새 파일인 recordFile.m4a가 생성 된다.

           let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]

           audioFile = documentDirectory.appendingPathComponent("recordFile.m4a")

           

            

            

        }

    }

    //녹음을 위한 초기화 함수 : 음질은 최대, 비트율 320kbps, 오디오 채널은 2, 샘플율은 44,100hz

    func initRecord() -> Void {

        

        let recordSettings = [

            AVFormatIDKey : NSNumber(value : kAudioFormatAppleLossless as UInt32),

            AVEncoderAudioQualityKey : AVAudioQuality.max.rawValue,

            AVEncoderBitRateKey : 320000,

            AVNumberOfChannelsKey : 2,

            AVSampleRateKey : 44100.0] as [String : Any]

        

        do {

            // selectAudioFile 함수에서 저장한 audioFile을 url로 하는 audioRecorder 인스턴스를 생성

            audioReorder = try AVAudioRecorder(url: audioFile, settings: recordSettings)

        } catch let error as NSError {

            print("error-initRecord:\(error)")

        }

        audioReorder.delegate = self

        //박자관련

        audioReorder.isMeteringEnabled = true

        

        audioReorder.prepareToRecord()

        

        slVolume.value = 1.0

        audioPlayer.volume = slVolume.value

        lbEndTime.text = convertNSTimeInterval2String(0)

        lbCurrentTime.text = convertNSTimeInterval2String(0)

        //버튼 모드 비활성화

        setPlayButton(false, pause: false, stop: false)

        

        let session = AVAudioSession.sharedInstance()

        

        do {

            try session.setCategory(AVAudioSessionCategoryPlayAndRecord)

        } catch let error as NSError {

            print("error-setcategory : \(error)")

        }

        

        do {

            try session.setActive(true)

        } catch let error as NSError {

            print("error-setActive : \(error)")

        }

        

    }


    //audioFile을 url로 하는 audioplayer 인스턴스 생성

    // 입력파라미터인 오디오 파일이 없을 때에 대비하여 try catch문 사용한다.

    func initPlay(){

        

        //에러 처리

        do {

            audioPlayer = try AVAudioPlayer(contentsOf: audioFile) //오류발생 가능 함수

        } catch let error as NSError { //오류타입

            print("error-initplay : \(error)")  //오류타입에 대한 처리 구문

        }

        

        //슬라이더의 최대 불륨을 지정

        slVolume.maximumValue =  MAX_VOLUME

        //슬라이더의 불륨을 1.0으로 초기화

        slVolume.value = 1.0

        //프로그레스 뷰의 진행을 0으로 초기화

        pvProgressPlay.progress = 0

        

        //audioplayer의 델리게이트를 self로 한다.

        audioPlayer.delegate = self

        //prepareToplay 실행한다.

        audioPlayer.prepareToPlay()

        //오디오 플레이어의 블륨 초기화 한다.

        audioPlayer.volume = slVolume.value

        

        //오디오 파일의 재생시간인 audioplayer.duration값을 이 함수를 이용해서 텍스트에 출력

        lbEndTime.text = convertNSTimeInterval2String(audioPlayer.duration)

        //lbcurrentTime텍스트에는 이 함수를 이용해서 00:00이 출력되도록 한다.

        lbCurrentTime.text = convertNSTimeInterval2String(0)

        

        //play 버튼 활성화

        //btnPlay.isEnabled = true

        //btnPause.isEnabled = false

        //btnStop.isEnabled = false

        

        

        setPlayButton(true, pause: false, stop: false)

    }

    //

    func setPlayButton(_ play:Bool, pause:Bool, stop:Bool) -> Void {

        btnPlay.isEnabled = play

        btnPause.isEnabled = pause

        btnStop.isEnabled = stop

    }

    

    //00:00형태로 바꾸기 위해 timeinterval 값을 받아 문자열로 돌려보내는 함수

    func convertNSTimeInterval2String(_ time:TimeInterval) -> String {

        //재생시간의 매개변수인 time값을 60으로 나눈 몫을 정수 값으로 변환하여 상수 min에 초기화

        let min = Int(time/60)

        //time값을 60으로 나눈 나머지 값을 정수 값으로 변환하여 상수 sec 값에 초기화 한다.

        let sec = Int(time.truncatingRemainder(dividingBy: 60))

        //이 두 값을 이용해서 "%02d:%02d" 형태의 문자열로 변환하여 상수에 초기화

        let strTime = String(format: "%02d:%02d",min,sec)

        

        return strTime

        

    }

    

    

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


    //재생 버튼

    @IBAction func btnPlayAudio(_ sender: UIButton) {

        

        //오디오 재생

        audioPlayer.play()

        //재생버튼 비활성화 나머지 버튼 활성화

        setPlayButton(false, pause: true, stop: true)

        //프로그레스 타이머 설정

        progressTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: timePlayerSelector, userInfo: nil, repeats: true)

        

    }

    

    

    //앞에서 만든 타이머에 의해 0.1초 간격으로 이 함수가 실행되는데, 그때마다 재생시간을 라벨과 프로그래스바에 보여준다.

    @objc func updatePlayTime() -> Void {

        lbCurrentTime.text = convertNSTimeInterval2String(audioPlayer.currentTime)

        pvProgressPlay.progress = Float(audioPlayer.currentTime/audioPlayer.duration)

    }

    

    //일시정지버튼

    @IBAction func btnPauseAudio(_ sender: UIButton) {

        audioPlayer.pause()

        setPlayButton(true, pause: false, stop: true)

    }

    

    //정지버튼

    @IBAction func btnStopAudio(_ sender: UIButton) {

        audioPlayer.stop()

        

        //오디오를 정지하고 재생하면 다시 처음부터 재생, 시간 초기화

        audioPlayer.currentTime = 0

        lbCurrentTime.text = convertNSTimeInterval2String(0)

        setPlayButton(true, pause: false, stop: false)

        progressTimer.invalidate()

        

    }

    

    //음량 슬라이더 액션 함수

    @IBAction func slChangeVolume(_ sender: UISlider) {

        

        audioPlayer.volume = slVolume.value

    }

    

    //오디오 재생이 끝나면 맨 처음 상태로 돌아가도록 지정 하는 함수

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {

        progressTimer.invalidate()

        //재생버튼 활성화 나머지 버튼 비활성화

        setPlayButton(true, pause: false, stop: false)

    }

    

    

    //녹음 스위치

    @IBAction func swRecordMode(_ sender: UISwitch) {

        

        //스위치가 녹음 모드일 때

        if sender.isOn {

            

            //오디오 재생 중지, 현재시간 00:00으로 설정, record값 참으로 설정 녹음 버튼과 시간 활성화

            audioPlayer.stop()

            audioPlayer.currentTime = 0

            lbRecordTime!.text = convertNSTimeInterval2String(0)

            isRecorderMode = true

            btnRecord.isEnabled = true

            lbRecordTime.isEnabled = true

        }else{

            //재생모드일 때, 레코드모드 값을 거짓으로 바꾸고, 녹음 버튼과 시간을 비활성화, 녹음 시간 0

            

            isRecorderMode = false

            btnRecord.isEnabled = false

            lbRecordTime.isEnabled = false

            lbRecordTime.text = convertNSTimeInterval2String(0)

        }

        

        //이 함수를 호출해서 오디오 파일을 선택하고 , 모드에 따라서 초기화 할 함수 호출

        selectAudioFile()

        

        if !isRecorderMode {

            initPlay()

        }else {

        

            initRecord()

            

        }

        

    }

    //녹음하기 버튼 함수

    @IBAction func btnRecord(_ sender: UIButton) {

        

        if sender.titleLabel?.text == "record"{

            audioReorder.record()

            sender.setTitle("stop", for: UIControlState())

            progressTimer=Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: timeRecordSelector, userInfo: nil, repeats: true)

        }else {

            

            audioReorder.stop()

            progressTimer.invalidate() //녹음이 정지되면 타이머 무효화

            sender.setTitle("record", for: UIControlState())

            btnPlay.isEnabled = true

            initPlay()

        }

        

    }

    

    @objc func updateRecordTime(){

        lbRecordTime.text = convertNSTimeInterval2String(audioReorder.currentTime)

    }

}


  1. 한상민 2019.01.17 14:10

    개발자님 한가지 질문드리겠습니다
    스위프트 xcode 9 swift 4버젼에서 avplayer을 이용하여 서버에서 url 받고 이를 실행 시키고 싶은데
    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" 이런 형태의 오디오 url은 받아 오는데
    "https://s3carpum.s3.us-east-2.amazonaws.com/ce5e57608a0d5c335d5c0311096c8d0c067b11e0caaf730793a3919967fee20fd33f3383d41a5094930c7e88d45b877a.mp4" 이런 형태의 url은 재생을 못 시킵니다.
    안드로이드에서는 받아와서 재생이 되는데 스위프트에서는 재생이 안됩니다.
    첫번째 url은 어플 실행시키면 권한에 대해서 알림이 오는데 두번째 url 은 권한에 대해서 알림이 오지도 조차 않습니다
    혹시 스위프트에서 는 특정 오디오url 만 가능합니까?

    • 길위의 개발자 2019.01.17 14:27 신고

      안녕하세요. 첫번째 url은 작동이 되는데.. 아래 url 들어가보니 스트리밍이 안되는데 지금 서버가 꺼져있나요?

      https://www.s3carpum.s3.us-east-2.amazonaws.com/ce5e57608a0d5c335d5c0311096c8d0c067b11e0caaf730793a3919967fee20fd33f3383d41a5094930c7e88d45b877a.mp4

    • 길위의 개발자 2019.01.17 14:29 신고

      audioFile = Bundle.main.url(forResource: "music", withExtension: "mp3")
      서버가 작동중이라면, file 확장자도 한번 확인해주세요

  2. 한상민 2019.01.17 15:05

    빠른 답변 감사합니다!!
    서버는 항상 켜져 있었어요 .
    파일 확장자 바꾸는거 통해서 한 번 해보겠습니다

  3. 한상민 2019.01.17 15:22

    https://www.s3carpum.s3.us-east-2.amazonaws.com/ce5e57608a0d5c335d5c0311096c8d0c067b11e0caaf730793a3919967fee20fd33f3383d41a5094930c7e88d45b877a.mp4

    이거를 아예 mp3로 바꾸어서 올려서 해봤는데도 안되요 ㅠㅠ

    "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3" 요거는 되는데
    이러면 서버측 문제일까요?? 아니면 로컬 측 문제일까요??
    혹시 몰라서 코드까지 올려드렸습니다


    var player:AVPlayer?
    var playerItem:AVPlayerItem?
    func setvoice(input : String){
    if input == ""{
    print("record is nil")
    viewcontract?.setrecordnull()
    return
    }
    else{
    print("redcord is not nil")
    var url = URL.init(string: input)
    let playerItem:AVPlayerItem = AVPlayerItem(url: url!)
    player = AVPlayer(playerItem: playerItem)

    viewcontract?.setrecordon()
    return
    }
    }
    func playButtonTapped()
    {

    if player?.rate == 0
    {
    viewcontract?.setrecordstop()
    self.viewcontract?.Toastview(option : 2)
    player!.play()

    } else {

    viewcontract?.setrecordplaying()
    player!.pause()

    }
    }

    • 한상민 2019.01.17 15:23

      input은 url 들어오는 자리입니다

    • 한상민 2019.01.17 15:24

      viewcontract?.~~~~~ 이런형식은 viewcontroller 버튼 이미지 바꿔주는 함수이고여

+ Recent posts