<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Ai on 얼쑤 · Team Edo</title><link>https://team-edo.github.io/categories/ai/</link><description>Recent content in Ai on 얼쑤 · Team Edo</description><generator>Hugo</generator><language>ko-kr</language><lastBuildDate>Sun, 08 Nov 2026 00:00:00 +0900</lastBuildDate><atom:link href="https://team-edo.github.io/categories/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>10초 AAC를 4.62초만 읽고 성공한 FFmpeg</title><link>https://team-edo.github.io/posts/2026/11/aac-packet-truncation/</link><pubDate>Sun, 08 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/aac-packet-truncation/</guid><description>&lt;p>얼쑤의 연주 분석 서버는 녹음을 받은 뒤 FFmpeg로 소리를 풀어 음높이와 박자를 계산합니다. 이 변환을 구현하면서 10초짜리 AAC 파일을 중간에서 잘라 봤습니다. FFmpeg는 성공을 뜻하는 종료 코드 &lt;code>0&lt;/code>을 반환했고, FFprobe는 파일 길이가 여전히 10초라고 보고했습니다. 디코딩한 샘플을 세어 보니 실제로 읽힌 소리는 &lt;strong>4.62초&lt;/strong>였습니다.&lt;/p>
&lt;h2 id="잘린-위치에-따라-결과가-달랐습니다">잘린 위치에 따라 다른 결과&lt;/h2>
&lt;p>파일의 바이트를 임의 위치에서 자르면 AAC 패킷 중간을 끊을 수 있습니다. 디코더가 불완전한 패킷을 만나면 오류를 냅니다. 오류가 난 구간을 건너뛴 채 분석을 이어가면, 중간 소리가 빠져 뒤의 음이 앞당겨질 수 있습니다. 그래서 디코딩 오류가 발생하면 바로 멈추도록 &lt;code>-xerror&lt;/code>를 사용했습니다. &lt;a href="https://github.com/FFmpeg/FFmpeg/blob/n8.0/doc/ffmpeg.texi">FFmpeg 옵션 문서&lt;/a>&lt;/p></description></item><item><title>120초 녹음 분석에 약 3GiB가 든 이유</title><link>https://team-edo.github.io/posts/2026/11/dtw-recording-memory/</link><pubDate>Sun, 08 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/dtw-recording-memory/</guid><description>&lt;p>얼쑤는 연주자가 악보보다 조금 빠르거나 느리게 불어도 같은 음끼리 비교할 수 있도록 DTW로 두 시간축을 맞춥니다. 녹음 길이 제한을 검토하면서, 계산이 끝나는 데 걸리는 시간과 함께 한 요청이 차지하는 메모리도 확인했습니다. 운영과 같은 조건에서 120초 합성 녹음을 처리하는 데 &lt;strong>17.25초&lt;/strong>가 걸렸고 프로세스 최대 메모리는 &lt;strong>3,065MiB&lt;/strong>였습니다.&lt;/p>
&lt;h2 id="같은-서버-조건에서-길이를-바꿔-봤습니다">같은 서버에서 녹음 길이 비교&lt;/h2>
&lt;p>운영과 같은 분석 이미지와 6GiB 메모리 제한으로 일회용 컨테이너를 실행했습니다. 네트워크를 끄고 합성 음계를 이어 붙여 만든 녹음을 길이별로 한 번씩 처리했습니다. 각 실행에서 짧은 입력으로 pYIN을 먼저 호출해 준비 시간을 따로 기록했습니다. 아래 시간은 준비 이후의 음높이 추정과 채점을 합한 값입니다.&lt;/p></description></item><item><title>틀린 음을 불었는데, AI는 왜 맞다고 했을까?</title><link>https://team-edo.github.io/posts/2026/11/octave-error-in-music-scoring/</link><pubDate>Thu, 05 Nov 2026 00:00:00 +0000</pubDate><guid>https://team-edo.github.io/posts/2026/11/octave-error-in-music-scoring/</guid><description>&lt;p>단소를 연습할 때 누군가 옆에서 악보를 봐준다면 어떨까요? 어느 음을 잘못 불었는지, 박자는 어디서 빨라졌는지 알려주는 사람 말입니다.&lt;/p>
&lt;p>얼쑤에서 만들던 채점 기능도 비슷한 역할을 합니다. 단소 연주를 녹음하면 악보와 비교해서 음정과 박자를 알려줍니다.&lt;/p>
&lt;p>이 기능을 시험하면서 일부러 틀린 음을 넣었습니다. 악보에 적힌 음보다 한 옥타브 높은 음이었습니다.&lt;/p>
&lt;p>그런데 채점 결과는 정답이었습니다.&lt;/p>
&lt;p>더 이상한 점도 있었습니다. 같은 높이의 음을 따로 들려주면 제대로 찾았는데, 다른 음 사이에 짧게 넣으면 놓친 겁니다.&lt;/p></description></item><item><title>문서 주소 없이 FastAPI 계약 검증</title><link>https://team-edo.github.io/posts/2026/11/generate-private-openapi-contract/</link><pubDate>Tue, 03 Nov 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/11/generate-private-openapi-contract/</guid><description>&lt;p>백엔드가 호출하는 분석 API의 요청과 응답을 한곳에서 읽을 수 있게 만들고 싶었습니다. 분석 서버는 내부에서만 호출하는 구성이라 운영에 문서 조회 주소를 열 필요는 없었습니다. 또 이미 사용 중인 응답에는 상태마다 다른 필드와 JSON 이름이 있어서, 문서를 붙이는 작업이 기존 직렬화까지 바꾸면 호출자에 영향이 갈 수 있었습니다.&lt;/p>
&lt;h2 id="스키마-생성과-문서-조회-주소를-분리했다">스키마 생성과 공개 경로 분리&lt;/h2>
&lt;p>FastAPI의 &lt;code>docs_url&lt;/code>, &lt;code>redoc_url&lt;/code>, &lt;code>openapi_url&lt;/code> 설정은 꺼 둔 채, 앱 객체의 &lt;code>app.openapi()&lt;/code>에서 문서를 생성하도록 했습니다. 개발 환경에서 JSON을 파일로 저장해 문서를 읽을 수 있습니다. HTTP 테스트에는 문서 관련 경로 세 곳이 모두 404를 반환하는지도 추가했습니다. &lt;a href="https://fastapi.tiangolo.com/tutorial/metadata/#openapi-url">FastAPI의 문서 URL 설정&lt;/a>&lt;/p></description></item><item><title>−20센트 합성음에서 나온 −15센트 추정</title><link>https://team-edo.github.io/posts/2026/10/pyin-tuning-resolution/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/pyin-tuning-resolution/</guid><description>&lt;p>얼쑤에서 백엔드의 분석 요청을 받아 음표별 판정을 돌려주는 API를 구현했습니다. 검증할 때 악보의 모든 음보다 &lt;strong>20센트 낮게&lt;/strong> 만든 합성음을 넣었습니다. 반음이 100센트이므로, 같은 간격으로 음높이만 조금 낮춘 입력입니다.&lt;/p>
&lt;p>분석은 HTTP 200과 &lt;code>DONE&lt;/code>으로 끝났습니다. 그런데 응답의 조율 보정값 &lt;code>tuningOffsetCent&lt;/code>는 &lt;strong>−15.0센트&lt;/strong>였습니다. 입력을 만들 때 정한 값과 채점기가 실제로 적용한 값 사이에 5센트 차이가 있었습니다.&lt;/p>
&lt;h2 id="보정값이-만들어지는-과정">pYIN 추정에서 조율 보정까지&lt;/h2>
&lt;p>로컬에서 실제 분석 API를 띄우고, 저장소 역할을 하는 로컬 HTTP 서버가 합성 M4A를 보내도록 했습니다. API는 오디오를 16kHz 모노로 디코딩한 뒤 pYIN으로 짧은 구간마다 기본 주파수를 추정합니다. 당시 요청에는 기준음이 없어서 황의 기준 주파수를 311.13Hz로 두고, 악보의 각 음이 거기서 몇 반음 떨어져 있는지로 목표 음높이를 구했습니다.&lt;/p></description></item><item><title>스테레오 평균 테스트가 놓친 합산</title><link>https://team-edo.github.io/posts/2026/10/stereo-rms-test-mono-reference/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/stereo-rms-test-mono-reference/</guid><description>&lt;p>얼쑤의 연주 분석은 스테레오 녹음을 16kHz 모노 PCM으로 변환한 뒤 시작합니다. 두 채널을 같은 비중으로 평균 내는 동작을 검사하려고, &lt;strong>440Hz 사인파&lt;/strong>를 담은 &lt;strong>44.1kHz·2초짜리 AAC&lt;/strong> 파일 두 개를 만들었습니다. 한 파일에는 왼쪽 채널에만 소리를 넣고, 다른 파일에는 양쪽에 같은 소리를 넣었습니다.&lt;/p>
&lt;p>처음에는 두 출력의 RMS를 나눴습니다. RMS는 샘플을 제곱해 평균을 낸 뒤 제곱근을 구한 값으로, 여기서는 디코딩한 신호의 크기를 비교하는 데 썼습니다. 왼쪽만 소리가 나는 출력의 RMS를 양쪽에 같은 소리가 나는 출력으로 나눠 &lt;strong>0.5 ± 0.05&lt;/strong>인지 확인했습니다. 리뷰어가 FFmpeg 필터를 두 채널의 &lt;strong>합&lt;/strong>으로 바꿔도 이 검사는 통과했습니다.&lt;/p></description></item><item><title>잡음 다섯 프레임은 0점일까</title><link>https://team-edo.github.io/posts/2026/10/reject-all-missed-recordings/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/reject-all-missed-recordings/</guid><description>&lt;p>얼쑤는 녹음에서 추정한 음높이를 악보와 맞춘 뒤 음표마다 판정을 내립니다. 처음에는 어느 프레임에서도 음높이를 찾지 못했을 때 &lt;code>NO_SOUND&lt;/code>로 끝냈습니다. 잡음을 음높이로 잡으면 이 검사를 통과할 수 있었습니다. 그러면 악보의 모든 음표를 놓쳤는데도 분석이 완료돼 0점이 나왔습니다.&lt;/p>
&lt;p>이 경우를 고정하려고 악보 길이에 맞춘 음높이 배열을 만들었습니다. 대부분을 무음으로 표시하고 &lt;strong>다섯 프레임&lt;/strong>에만 음높이 값을 넣었습니다. 마이크로 잡음을 녹음하는 대신, 음높이 추정 단계가 몇 프레임을 소리로 넘긴 상황을 재현한 입력입니다. 기존에는 이 배열이 &lt;code>DONE&lt;/code>과 &lt;strong>0점&lt;/strong>을 냈습니다.&lt;/p></description></item><item><title>정답인데 2~4번째 음이 박자 오류로 찍힌 이유</title><link>https://team-edo.github.io/posts/2026/10/aac-priming-timing-fixture/</link><pubDate>Wed, 28 Oct 2026 00:00:00 +0900</pubDate><guid>https://team-edo.github.io/posts/2026/10/aac-priming-timing-fixture/</guid><description>&lt;p>채점 API의 정상 경로를 검사하려고 악보와 같은 음높이·박자로 합성 연주를 만들었습니다. 합성음을 AAC로 인코딩해 분석 API에 넣자 **2~4번째 음이 &lt;code>TIMING&lt;/code>**으로 판정됐습니다. 악보와 합성기의 박자는 그대로였는데, 파일을 거친 뒤에만 음이 늦어진 것처럼 보였습니다.&lt;/p>
&lt;h2 id="합성음과-채점기-사이에-있던-시간">AAC 준비 샘플과 박자 오차&lt;/h2>
&lt;p>테스트는 FFmpeg의 표준 출력에서 조각 MP4를 받아 사용했습니다. AAC 인코더는 앞에 준비 샘플을 붙일 수 있습니다. 재생 타임라인에는 그 구간을 건너뛰는 정보가 필요한데, 이 테스트의 조각 MP4에는 그 정보가 없었습니다. 앱의 녹음 파일은 일반 MP4였습니다. &lt;a href="https://developer.apple.com/documentation/quicktime-file-format/background_aac_encoding">Apple의 AAC 인코딩 설명&lt;/a>&lt;/p></description></item></channel></rss>