Focus On Develop ๐ŸคŸ๐ŸคŸ

[Swift] Audio Recording ๋ณธ๋ฌธ

iOS [Swift]/๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!

[Swift] Audio Recording

๋ˆ„๋ฆฌ๋‹ฌ์ดํ‹€ 2021. 3. 1. 22:47

์˜ค๋Š˜์€ Swift์—์„œ AVAudioRecorder ๋ฅผ ํ™œ์šฉํ•œ ๋…น์Œ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด ๊ณต๋ถ€ํ•ด๋ณด๋ ค๊ณ  ํ•ด์š”!

๋จผ์ € ๋…น์Œ๋œ ํŒŒ์ผ์„ ๋กœ์ปฌ ๊ฒฝ๋กœ์— ์ €์žฅํ•˜๊ณ , ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ณผ์ •์ด ์žˆ์œผ๋‹ˆ FileManager๋ฅผ ๋จผ์ € ๋ณด์‹œ๊ธธ ์ถ”์ฒœ๋“œ๋ ค์š” ๐Ÿ˜Ž

 

 

๋จผ์ € Swift์—์„œ ๋ฏธ๋””์–ด(์˜์ƒ, ์˜ค๋””์˜ค ๋“ฑ)์„ ๋‹ค๋ฃฐ๋•Œ๋Š” AVFoundation๋ฅผ ํ™œ์šฉํ•ด์„œ ๋Œ€๋ถ€๋ถ„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

์˜ค๋””์˜ค์™€ ๊ด€๋ จ๋œ ์‚ฌํ•ญ์€ AVAudioRecord, AVAudioPlayer, AVAudioSession, AVAudioEngine ๋“ฑ๋“ฑ ๋งŽ์€ ํด๋ž˜์Šค๋กœ ์ง€์›์„ ํ•ด์ค˜์š”. AVFoundation์„ importํ•˜๊ณ  ์‹œ์ž‘ํ•ด์ฃผ์„ธ์š”~!


 

๋จผ์ € ๋…น์Œ์„ ์œ„ํ•ด์„œ ์˜ค๋””์˜ค ์„ธ์…˜์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.  ๐Ÿ‘‰  AVAudioSession

์˜ค๋””์˜ค ์„ธ์…˜์„ ํ†ตํ•ด ๋…น์Œ/์žฌ์ƒ ๋“ฑ์— ๋Œ€ํ•œ ๊ถŒํ•œ, ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋…น์Œ์ธ์ง€, ์–ด๋–ค ๋งˆ์ดํฌ๋ฅผ ์‚ฌ์šฉํ• ๊ฑด์ง€ ๋“ฑ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”.

๋จผ์ € ๊ถŒํ•œ์„ ์š”์ฒญํ•˜๊ธฐ ์œ„ํ•ด, info.plist์— ์˜ค๋””์˜ค ๊ด€๋ จ ํ•ญ๋ชฉ์„ ์ถ”๊ฐ€ํ•ด์ค๋‹ˆ๋‹ค.  (Privacy - Microphone Usage Description)

recordingSession = AVAudioSession.sharedInstance()

// ๊ถŒํ•œ
AVAudioSession.sharedInstance().requestRecordPermission { (accepted) in
    if accepted {
        print("permission granted")
    }
}

AVAudioSession.sharedInstance() ๋ผ๊ณ  ์˜ค๋””์˜ค ์„ธ์…˜์„ ๊ฐ€์ ธ ์˜ค๊ณ , requestRecordPermission ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์˜ค๋””์˜ค ์ ‘๊ทผ ๊ถŒํ•œ์„ ์š”์ฒญํ•˜๊ณ , ๊ถŒํ•œ์„ ์ˆ˜๋ฝํ–ˆ๋Š”์ง€ ์•„๋‹Œ์ง€๋ฅผ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์–ด์š”.

์ด๋Ÿฐ ํŒ์—…! ๋งŽ์ด ๋ณด์…จ์ฃ ~? requestRecordPermission ๋ฉ”์†Œ๋“œ๋กœ plist์˜ ์ •๋ณด๋ฅผ ์กฐํ•ฉํ•ด์„œ ์ด ํŒ์—…์„ ๋„์›Œ์ค€๊ฑฐ์—์š”!

 

๊ถŒํ•œ์„ ์–ป์—ˆ์œผ๋‹ˆ ๋…น์Œ์„ ํ•ด๋ณผ๊ฑฐ์—์š”.

do {
        try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord)
        try recordingSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)
        let filename = getDirectory().appendingPathComponent("\(tag).m4a")
        audioRecoder = try AVAudioRecorder(url: filename, settings: settinigs)
        audioRecoder.record()
    } catch {
        print(" fail to record ")
    }

์•„๊นŒ ์œ„์—์„œ ์˜ค๋””์˜ค ์„ธ์…˜์€ ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋…น์Œ์ธ์ง€, ์–ด๋–ค ๋งˆ์ดํฌ์ธ์ง€๋„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค๊ณ  ํ–ˆ์ฃ ?

์ด๋ ‡๊ฒŒ AVAudioSession.Category์—์„œ ์–ด๋–ค ์ข…๋ฅ˜๋ฅผ ๋…น์Œํ• ๊ฑด์ง€(Ambient, play, playAndRecord, playback ๋“ฑ) ๋ฅผ ์„ค์ •ํ•˜๊ณ , setCategory() ๋ฉ”์†Œ๋“œ๋กœ ์„ค์ •์„ ์ ์šฉ์‹œ์ผœ์ค˜์š”. ์šฐ๋ฆฌ๋Š” ๋…น์Œํ•˜๊ณ  ์žฌ์ƒํ• ๊ฑฐ๋ผ์„œ playAndRecord๋ฅผ ์ง€์ •ํ•ด์คฌ์–ด์š”.

try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playAndRecord)

๋‹ค์Œ์€ ์–ด๋–ค ๋งˆ์ดํฌ๋ฅผ ์‚ฌ์šฉํ• ๊ฑด์ง€๋ฅผ ์ •ํ•ด์ค„๊ฑฐ์—์š”. AVAudioSession.PortOverride.speaker ์ด๋ ‡๊ฒŒ ์Šคํ”ผ์ปค๋ฅผ ์‚ฌ์šฉํ•˜๊ฒ ๋‹ค๊ณ  overrideOutputAudioPort() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์„ค์ •ํ•ด์ค๋‹ˆ๋‹ค.

try recordingSession.overrideOutputAudioPort(AVAudioSession.PortOverride.speaker)

๊ทธ๋ฆฌ๊ณ  ๋…น์ŒํŒŒ์ผ์ด ์ €์žฅ๋  ์œ„์น˜์™€ ํŒŒ์ผ๋ช…์„ ์ •ํ•ด์ค์‹œ๋‹ค.

getDirectory()๋Š” ๊ฐ„๋‹จํžˆ FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] ๋กœ์ปฌ ๊ฒฝ๋กœ๋ฅผ ๊ฐ€์ ธ์˜ค๋„๋ก ํ–ˆ์–ด์š”. FileManager๋Š” ์—ฌ๊ธฐ์„œ ๋”ฐ๋กœ ์ž‘์„ฑํ–ˆ์œผ๋‹ˆ ํŒจ์Šคํ•ฉ๋‹ˆ๋‹ค!

let filename = getDirectory().appendingPathComponent("\(tag).m4a")

๋งˆ์ง€๋ง‰์œผ๋กœ ๋…น์Œ ํ’ˆ์งˆ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์–ด์š”.

๋‚ด๋ถ€ ํฌ๋งท์€ ์–ด๋–ป๊ฒŒ ํ• ๊ฑด์ง€, ์ฃผํŒŒ์ˆ˜(?) ํ—ค๋ฅด์ธ ๋Š” ์–ด๋–ป๊ฒŒ ํ• ๊ฑด์ง€, ํ’ˆ์งˆ์€ ์–ด๋–ป๊ฒŒ ํ• ๊ฑด์ง€ ๋“ฑ์„ ์ •ํ•ด์ค„ ์ˆ˜ ์žˆ์–ด์š”.

let settinigs = [
        AVFormatIDKey: Int(kAudioFormatMPEG4AAC),
        AVSampleRateKey: 12000,
        AVNumberOfChannelsKey: 1,
        AVEncoderAudioQualityKey: AVAudioQuality.high.rawValue
    ]

์ด์ œ๋Š” ์œ„์—์„œ ์„ค์ •ํ–ˆ๋˜ ์ •๋ณด๋กœ ๋…น์Œ๊ธฐ๋งŒ ์„ธํŒ…ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

audioRecoder = try AVAudioRecorder(url: filename, settings: settinigs)
audioRecoder.record()

record() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์‹ค์ œ ๋…น์Œ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ์–ด์š”. ๋ฐ˜๋Œ€๋กœ ๋…น์Œ์„ ์ค‘์ง€ํ•˜๋Š”๊ฑด stop() ๋ฉ”์†Œ๋“œ๋ž๋‹ˆ๋‹ค.

 

๋…น์Œ์„ ํ–ˆ์œผ๋‹ˆ ์žฌ์ƒ๋„ ํ•ด๋ณผ๊นŒ์š”? ์žฌ์ƒ์€ ์ •๋ง๋กœ ๊ฐ„๋‹จํ•ด์š”.

let filePath = getDirectory().appendingPathComponent("\(tag).m4a")
do {
    audioPlayer = try AVAudioPlayer(contentsOf: filePath)
    audioPlayer.play()
} catch {
    print("faild to play file")
}

์•„๊นŒ ์ €์žฅํ–ˆ๋˜ ๊ฒฝ๋กœ์—์„œ ํŒŒ์ผ์„ ๊ฐ€์ ธ์™€์„œ, AVAudioPlayer์˜ play() ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด ์žฌ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

 

 

์•„์ฃผ ๊ฐ„๋‹จํ•œ ์˜ˆ์ œ๋กœ ์˜ค๋””์˜ค ๋…น์Œ/์žฌ์ƒ์„ ํ•œ๋ฒˆ ์•Œ์•„๋ณด์•˜๋Š”๋ฐ์š”!

์•„๋ž˜์ฒ˜๋Ÿผ 4๊ฐ€์ง€ ๋…น์Œ์ด ๊ฐ€๋Šฅํ•˜๊ณ , ๊พน ๋ˆ„๋ฅด๋ฉด ๋…น์Œ์‹œ์ž‘, ์™„๋ฃŒ๋˜๋ฉด play๊ฐ€ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“  ์‹ค์Šต ์•ฑ๋„ ๋งŒ๋“ค์–ด ๋ณด์•˜์–ด์š”.

์ „์ฒด ์†Œ์Šค์— ๋Œ€ํ•œ ์˜ˆ์ œ๋Š” ์—ฌ๊ธฐ์„œ ๋‹ค์šด๋กœ๋“œ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๐Ÿ‘‰ github.com/Nurys-KT/RecodeApp

 

์Œ์„ฑ ๋ณ€์กฐ๋‚˜, ํšจ๊ณผ๋ฅผ ๋”ํ•˜๊ณ  ์‹ถ์€ ๋ถ„์€ AVAudioEngine, AVAudioUnitEffect์„ ์ถ”๊ฐ€๋กœ ๊ณต๋ถ€ํ•ด๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค!

 

๊ทธ๋Ÿผ ์˜ค๋Š˜๋„ ๋ˆ„๊ตฐ๊ฐ€์—๊ฒŒ ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ž˜์š” ๐Ÿ™

'iOS [Swift] > ๊ธฐ์ดˆ๋ฅผ ํƒ„ํƒ„ํžˆ!' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Swift] Singleton Pattern (์‹ฑ๊ธ€ํ†ค ํŒจํ„ด)  (0) 2021.03.16
[Swift] FileManager  (0) 2021.03.02
[Swift] CaseIterable  (0) 2021.02.27
[Swift] typealias  (0) 2021.02.26
[Swift] Codable  (0) 2021.02.26
Comments