[WWDC23] Create a great ShazamKit experience (1)

beeeen:D·2023년 9월 18일

WWDC

목록 보기
1/2
post-thumbnail

다들 Shazam에 대해 알고 계신가요 ??
저는 아이폰 사용하면서 노래 찾을 때 사용한답니다 :)
없어서는 안되는 기능 Top 5에 들 정도 . . ?

Shazam 음악 인식으로 찾은 곡들이 97곡이나 되네요 ㅎ.ㅎ
아주 유용하게 쓰는 중입니다.

이렇게 사용하다가, 문득 어떻게 동작하는지 궁금해졌고 바로 WWDC 영상을 찾아봤어요.
이번 23년 WWDC에서도 ShazamKit 내용을 다뤘더라구요.
그래서 바로 영상을 켰고 조금 오래 걸리긴 했지만 내용 정리도 함께 해봤습니다 :D

📀 [WWDC23] Create a great ShazamKit experience






ShazamKit ?

앱에서 오디오 인식을 가능하게 하는 프레임워크

음악 리스트나 미리 녹음되어 있는 커스텀 목록에서, 인식된 오디오와 매칭되는 오디오를 찾을 수 있게 도와주는 프레임워크


ShazamKit 동작 간단히 알아보기

Signature라고 불리는 형태로 Audio Coverting
-> 매칭 시작
-> Audio Buffer Stream 또는 Signature DataShazamKit Session으로 전달
-> Shazam 목록 또는 Custom 목록에서 Signature Data와 매칭되는 오디오 찾기
-> 매칭된 경우, Session은 매칭 오디오의 메타 데이터를 포함한 Media Item 반환
-> 앱에 Media Item 표시


💡 Signature 파일은 Irreversible하다?

위에서 살짝 언급한 Signature 파일은 Irreversible 합니다!
돌이킬 수 없다, 즉 Signature 파일을 원본 파일로 재구성할 수 없다는 뜻입니다.
유저 프라이버시를 지키기 위함이라고 하네요.

💡 Catalog가 뭐지?

Signature 데이터를 묶어놓은 그룹

관련있는 미디어끼리 묶어놓은 것이 Catalog인 것이죠.

오디오 인식으로 만들어낸 Signature가 Catalog에 있는 Reference Signature의 일부와 충분히 일치하면 => Matching !
식당에서 나오는 음악과 같이, 시끄러운 공간에서 발생하는 음악 인식도 가능합니다.






여기까지 ShazamKit에 대해 간략하게 알아봤고 이제 23년에 새롭게 바뀐 부분을 한 번 살펴봅시다 🚀

  1. 오디오 녹음 파트에서 새롭게 바뀐 점
  2. Shazam Library API에 새롭게 추가된 기능
  3. ShazamKit 이용한 데모 앱 살펴보기


ShazamKit으로 오디오 인식하는 과정 OverView

  1. 사용자에게 마이크 접근 요청을 보낸다.
  2. 허용된 이후, 녹음을 시작한다.
  3. 녹음된 오디오 버퍼를 ShazamKit으로 보낸다.
  4. 매칭 성공한 경우, 결과 확인하기

영상에서 데이비드가 ShazamKit 활용한 데모 앱 하나를 보여줬는데요.
데모 앱은 마이크로 인식한 노래에 유행하는 댄스를 찾아주는 앱입니다.

동작 과정을 러프하게 살펴보면,

  1. "Learn the Dance"라는 버튼을 눌러 녹음 활성화
  2. ShazamKit은 무슨 노래인지 찾기
  3. 앱은 그 노래에 맞는 적절한 댄스 비디오를 찾기

어떻게 구현했는지 코드로 살펴봅시다 👩🏻‍💻


마이크 접근 허용 요청하기

info.plist에 마이크 사용 설명을 추가해야 사용자에게 마이크 접근 요청을 할 때 사용할 수 있어요.

오디오 엔진 구성하기

초기화 단계에서, 오디오 엔진을 구성하고 설정하는 메소드가 있어요.
PCMbuffer를 받기 위해 tap을 설치하고 오디오 엔진을 준비합니다.

private func configureAudioEngine() {
	let inputNode = audioEngine.inputNode
    let recordingFormat = inputNode.outputFormat(forBus: .zero)
    inputNode.installTap(onBus: .zero, bufferSize: 8192, format: recordingFormat) { [weak self] (buffer, time) in 
    	self?.session.matchStreamingBuffer(buffer, at: time)
    }
    
    audioEngine.prepare()
}



매칭 시작하기

match 메소드는 "Learn the Dance" 버튼(음성 인식을 시작하는 버튼)을 누르면 호출됩니다.

마이크 접근 요청
-> 허용
-> 녹음 시작을 위해 오디오 엔진 호출
-> UI에게 매칭 시작 알림 던지기
-> session.results 호출해서 매칭 결과의 비동기 시퀀스 기다리기

⭕️ 매칭 성공 : Match Object 보여주기
❌ 매칭 실패 : No Match나 Error 케이스 다루기

func match() async {
	let granted = await AVAudioApplication.requestRecordPermission()
    
    guard granted else {
    	print("No recording permission granted ...")
        return
    }
    
    do {
    	try audioEngine.start()
    } catch {
    	print("Failed to start audio engine")
        return
    }
    
    isMatching = true
    
    for await result in session.results {
    	switch result {
        case .match(let match):
        	Task { @MainActor in
            	self.currentMatchResult = MatchResult(match: match)
            }
        case .noMatch(_):
        	print("No match")
            endSession()
        case .error(let error, _):
        	print("Error \(error.localizedDescription)")
            endSession()
        }
        stopRecording()
    }
}




오디오 엔진을 멈추는 함수는 아래와 같습니다.

func stopRecording() {
	gurad audioEngine.isRunning else { return }
    audioEngine.stop()


이렇게 복잡한 레코딩, 매칭 과정을 쉽게 만들어주는 API가 있습니다.

바로 SHManagedSession ! !

  • recording 자동 관리 (오디오 버퍼 셋팅과 같은 번거로운 작업 없이)
  • 쉬운 셋업과 사용

두 가지뿐인데 아주 메리트 있어 보이죠 ?

그리고 이 세션은 꼭 마이크 접근 허용을 꼭 받아야 해요 ! 받지 않으면 사용할 수 없답니다.
제일 처음에 했던 info.plist 기억하시죠?
그 단계를 꼭 꼭 해주셔야 합니다.



실제로 API를 어떻게 사용하는지 살펴볼까요?

아래와 같이 SHManagedSession 인스턴스를 하나 생성해줍니다.

let managedSession = SHManagedSession()

Session의 result()를 호출해서 결과 값을 기다립니다.
이후 값이 들어오면, 그 값에 따라 Switch문이 동작하는데요.
match, noMatch, error 이렇게 총 세 가지 상태를 가지고 있습니다.

let result = await managedSession.result()

// result 값에 따라 switch문 동작
switch result {
	case .match(let match):
    	print("Match found. MediaItemsCount: \(match.mediaItems.count)")
    case .noMatch(_):
    	print("No match found")
    case .error(_, _):
    	print("An error occurred")
}




💡 만약 좀 길게 recording session을 가져가고 많은 결과를 받고 싶을 땐 어떻게 할까요?

managedSession에 있는 async sequence result 속성을 이용하면 됩니다.
방금 전에 본 것처럼 시퀀스로부터 받은 결과를 사용할 수 있으며 긴 시간동안 오디오 녹음이 가능해요 !

for await result in managedSession.results {
	switch result { ... }
}




마지막으로 매칭을 멈출 수 있는 메소드도 있어요.
managedSession의 cancel()을 호출함으로써, 현재 진행 중인 매치를 취소하고 녹음을 중단할 수 있습니다.

managedSession.cancel()




managedSession을 사용하면, 짧은 코드로 녹음을 시작하고 매칭 결과를 받아볼 수 있는 큰 메리트를 느낄 수 있어요 !




내용이 많아서 나눠서 작성하도록 하겠습니다 😊
다음 글에서 봐요 🚀

profile
Appel Developer Academy @ Postech | iOS developer 👩🏻‍💻

0개의 댓글