안드로이드로 앱 만들고 배운 점을 기록한 기술일지

Emili5·2022년 5월 17일
0

졸업프로젝트로 '음성인식 기반 음정교정 서비스'를 제작 중에 있으며, 현재 서비스에 필요한 기능과 화면은 마무리 단계에 있다.

저번 포스팅에서는 본격적인 기능 구현에 앞서 저희 서비스의 가장 중요한 기능인 음성인식에 필요한 음성인식을 지원하는 java기반 TarsosDSP 라이브러리에 대해 소개하였다.

이번 포스팅은 저번 포스팅 이후 많은 시간이 지난 만큼 그 동안 구현한 기능과 화면을 제작하면서 필요한 기술들에 대해 소개하는 내용으로 구성하겠다.

우리팀 개발순서는 크게

  1. Pitch Detection 기능- TarsosDSP 라이브러리를 이용하여 사용자에게 받은 음성 주파수를 옥타브로 바꾸고,정확하게 음성을 인식하는지 확인

  2. scoring Algorithm- 사용자에게 받은 음성을 기반으로 소절별 음정과 박자 점수를 계산하는 점수 계산 알고리즘 제작

  3. Android Studio로

    회원가입 및 로그인

    음정 테스트

    노래 목록

    노래 연습

    나의 기록 확인

    취약 소절 연습

    결과 확인

    음정,박자교정

화면을 제작하였다.

Pitch shifting 기능은 안드로이드 화면제작이후 마무리하기로 하였고, 현재 진행중에 있다.

저는 기능에서는 박자점수 계산 알고리즘을 구현하였고, 화면 제작에서는 나의 기록 확인과 결과 확인 화면을 제작하였다.

그럼 제가 맡았던 역할을 위주로 고민했던 흔적, 새로 배운 점들이 담긴 포스팅 시작하겠습니다!!💨💨

🎶 박자 계산 알고리즘 만들기

(이 부분은 기술적인 내용보다는 최적의 점수 계산을 위한 알고리즘 코드를 구상하고 코드를 구현하는 과정에서 어떤 어려움이 있었고 어떤 문제를 발견해서 해결할 수 있었는지에 관한 내용으로 봐주시면 됩니다^^)

사용자가 부른 노래의 음정과 박자에 대한 점수를 계산하기 위해서는 기존 음원의 음정과 박자와의 비교가 필요하다.

firebase에 기존 음원의 음정과 박자 정보를 넣어놓고 사용자가 부른 음성에 대한 음정과 박자 데이터를 실시간으로 firebase에 노래별로 넣어놓고 다시 안드로이드로 가져와 소절별로 두 데이터를 비교하는 과정으로 진행하였다.

🤦‍♀️그러나 진행과정에서 생각보다 고려해야 할 사항이 많다는 것을 알게 되었다.

소절 단위로 음정과 박자 점수를 따로 계산하기 위해 한 소절이 끝나면 소절 Index를 증가하여 소절을 구분하고, 소절마다의 시작시간과 끝시간에 대해서도 같은 방식으로 증가시켜 그 안에서 한 Note당 시작시간과 끝시간을 사용자 음성이 해당 시간의 오차범위 안에 들어왔으면 점수를 주기로 하였다.

구상 후 코드를 짜고 테스트를 해봤을 때는 점수가 계속 0점이 나와 데이터를 수정해보기도 하고 관련 코드를 수정하는 작업을 며칠 고민하며 진행하였다.
알고보니 점수를 계산하는 함수에서 한 소절이 끝나고 다음 소절로 갈 때 점수가 초기화가 되기도 하였고, 제대로 된 사용자 음성과 완전한 데이터를 넣지 않아 이를 인식하지 못하는 경우 등 여러가지가 혼재되어 긴 코드 속에서 문제점들을 조금씩 개선하고
계속 테스트 해보며 확인하는 수 밖에 없었다.

그렇게 해서 firebase에서 collection의 map과 문자열, Document에서 데이터를 가져와 음정과 박자를 소절별로 기존 음원의 시작시간과 끝시간을 사용자 데이터와 비교하여 최대한 정확히 점수를 계산하는 알고리즘을 구현할 수 있었다.

🎤나의 기록 화면과 취약소절 연습화면 제작

안드로이드 스튜디오에서 Activity에 버튼이나 텍스트뷰를 추가해서 화면을 구성하는 연습을 해봤었지만 지금 당장 내가 구현해야 하는 화면은 그런 단순한 화면 구성이 아니었다.

만들어야 하는 나의 기록화면 프로토타입이다.

이런 식으로 firebase에 노래별로 노래이름, 가수이름, 점수 데이터를 불러와서 리스트 형태로 보여주는 화면을 만들고 싶었다.
내가 잘 못 찾는 것도 있는 것 같긴한데,, firestore에서 리스트뷰로 가져오는 설명이 나와있는 블로그가 생각보다 많이 없어서 이렇게 정리해놓으면 좋을 것 같긴 했다.

구글링을 통해 위와 같은 형태를 만들기 위해서는 ListView를 이용하여 구현해야 함을 알게 되었다.그 과정에서 데이터를 가져와서 ListView와 연결해주는 Adpater라는 새로운 개념도 등장한다.
그럼 이제부터 내가 ListView를 통해 위와 같은 화면을 어떻게 만들게 되었는지 설명하겠다.

참고)안드로이드 커스텀 뷰에 대한 개념과 만드는 방법에 대해 도움을 받았던 블로그 주소이다.
https://recipes4dev.tistory.com/43?category=605791

안드로이드 ListView란?

ListView는 사용자가 정의한 데이터 목록을 아이템 단위로 구성하여 화면에 출력하는 ViewGroup의 한 종류로, ListView의 아이템들은 세로 방향으로 나열되며, ListView내 아이템은 단순 Text에서 Image, Button 등 여러 View의 조합으로 구성되는 Custom형태가 될 수도 있다.

Adpater

ListView에 데이터를 추가해서 화면에 표시하기 위해서는 Adpater를 사용해야 한다. Adapter에서 받은 데이터로 View를 생성하여 이렇게 생성되는 View가 ListView내 하나의 아이템 영역에 표시된다.

위와 같은 구조도를 보면 더 이해가 쉬울 것이다.

가장 먼저 화면에 보여줄 xml파일을 만드는데,listview 전체가 보여지는 화면과 ListView내 하나의 View를 구성하는 xml파일을 만들어야 한다.


activity_my_record.xml



myrecordlistview_item.xml

ListView내 한 View에서 Adapter로 받아와야 하는 데이터는 노래제목, 가수이름, 점수이므로 다음과 같이 Custom을 구성하였다.


CustomUserSongListDto.java

앞에서도 말했듯이 Adapter는 데이터를 가져와야 하며 firestore에서 가져오기 위해

로 연결한다.

처음에는 위 블로그에 나와있는대로 Adapter는 연결역할을 하고 Activity에서 값을 받아오는 코드를 작성했었는데, 멘토님께 여쭤보니 Activity는 화면에 보여지는 것만 써야하며, 데이터를 가지고 오는 것은 모두 Adpater에서 가져와야 한다고 하셨다.
그래서 Activity에서 firebase로부터 사용자가 부른 노래 정보를 가져오는 함수인 getUserSongList()를 Adpater로 옮겨 다음과 같이 코드를 작성하였다.



UserSongListViewAdapter.java


MyRecordActivity.java


adapter를 Activity와 연결시켜준다.

이 코드는 adpater에 값을 추가하는 시간보다 firestore에서 값을 가져오는 시간의 딜레이가 더 커서 NullPointException이 생겨 이를 막기 위해 apdater에 값을 추가하는 addItem()에 Millisec 2500초를 준 것이다.

이렇게 해서 원하던 화면 구조를 만들 수 있었다.

결과화면도 이와 같은 ListView를 이용하여 구현하는 거라 방식은 비슷해서 금방 구현할 수 있었다.

사용자가 노래를 부르고 점수 알고리즘으로 계산된 점수를 기준으로 취약소절로 분류도된 소절을 인덱스로 저장하였다. 그럼 취약 소절 연습화면에서는 firestore에서 사용자가 부른 노래 리스트에 저장된 취약 소절의 인덱스를 새로운 배열에 저장하고 이 값들을 인덱스로 하여 해당 음원에서 인덱스로 저장된 가사들을 가져와서 리스트 형태로 보여줘야 한다. 바로 이 부분이 내가 구현한 부분이며 중간에 오류를 만났지만 잘 해쳐나가며 원하는 뷰를 만들 수 있었다.👍

😎Fragment 관련 이슈

취약 소절 연습 화면은 취약 소절이 있는 리스트뷰의 버튼을 누르면 해당 소절이 있던 자리에 실시간으로 연습하는 화면이 나오도록 구상하였다. 취약 소절이 있는 리스트가 있던 부분만 연습 화면으로 바뀌고 나머지는 기존 화면과 같도록 만들기 위해 Activity를 사용하려면 기존에 있던 부분을 다시 만들어야 하므로 비효율적이었고, 분명히 구현할 수 있는 방법이 있을 것이라고 생각해 구글링을 하였다.

찾아보니 화면 전체인 Activity가 아닌, 화면의 일부인 Fragment로 화면의 일부분만 바꿀 수 있었다. Activity처럼 Fragment도 이미 지원하고 있기 때문에 Activity와 거의 똑같은 로직으로 구현하면 된다.

이와 관련하여 멘토링 시간을 활용해서 멘토님께 다음 화면을 만들 때
Fragment 여러개 + Activity 1개와 Activity 여러 개 중 어떤 방식으로 만드는지 여쭤보니 보통은 Fragment로 구현하지만,전체적인 코드의 로직은 거의 똑같기 때문에 현재 더 익숙한 것으로 구현하면 된다고 말씀해 주셨다.

결국 익숙한 Activity로 구현하였지만, 하단에 네비게이션 바를 만들려면 Fragment에 익숙해져야 하므로 나중에 Fragment로도 구현해보려고 한다.😊

0개의 댓글