Android에서 음성 파일에서 음악의 BPM을 살펴 보았습니다.
5 일째 기사입니다!
만든 앱에서 음악의 BPM을 알고 싶었기 때문에
음성 파일에서 음악의 BPM을 조사하는 프로그램을 작성해 보았습니다.
기사로 써 보았지만 모르는 곳도 많기 때문에
이상한 곳이 있을지도 모릅니다.
거기에 지적해 주시면 기쁩니다 m(_ _)m
절차
라고 순서대로 했습니다!
여기의 사이트를 거의 거의 참고로 했습니다.
htp://hp.ゔ c와 r. 이. jp / 아우테 rs / ゔ 046927 / m포 / m포. HTML
자세한 곳을 알고 싶은 분은 이쪽을 봐 주세요.
이번에 이용한 음원
이번 음원은 다음과 같은 메트로놈의 소리를 이용했습니다.
작성한 코드
음성 데이터 디코딩
MediaCodec라고 하는 저레벨의 미디어 코딕(엔코더/디코더)에 액세스 할 수 있는 api를 이용해 디코드를 실시했습니다.
디코더 만들기
for (int i = 0; i < extractor.getTrackCount(); i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
// 音楽のデータなら
if (mime.startsWith("audio/")) {
extractor.selectTrack(i);
try {
decoder = MediaCodec.createDecoderByType(mime);
} catch (IOException e) {
e.printStackTrace();
}
decoder.configure(format, null, null, 0);
break;
}
}
디코딩
decoder.start();
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
boolean isDecoding = true;
while (!Thread.interrupted()) {
if (isDecoding) {
int inIndex = decoder.dequeueInputBuffer(TIMEOUT_US);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
// 終了
Log.d(TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
decoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isDecoding = false;
} else {
// 次へ
decoder.queueInputBuffer(inIndex, 0, sampleSize, extractor.getSampleTime(), 0);
extractor.advance();
}
}
}
int outIndex = decoder.dequeueOutputBuffer(info, TIMEOUT_US);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.d(TAG, "New format " + decoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.d(TAG, "time out");
break;
default:
ByteBuffer buffer = outputBuffers[outIndex];
final byte[] chunk = new byte[info.size];
buffer.get(chunk);
buffer.clear();
soundDataList.add(chunk);
decoder.releaseOutputBuffer(outIndex, true);
break;
}
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d(TAG, "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
break;
}
}
decoder.stop();
decoder.release();
extractor.release();
디코딩된 데이터를 샘플당 저장
이번에는 샘플링 속도가 48000이므로
초당 48000개의 샘플이 들어가게 됩니다.
이번에는 비트/샘플이 16bit이므로
2바이트 분씩
samples[]
에 격납해 갑니다 int c = 0;
for (int i = 0; i < soundDataList.size(); i++) {
byte[] chunk = (byte[]) soundDataList.get(i);
for (int j = 0; j < chunk.length; j = j + 2) {
int value = 0;
value = (value << 8) + (chunk[j]);
value = (value << 8) + (chunk[j + 1]);
samples[c] = value;
c++;
}
}
프레임당 음량 구하기
1프레임의 샘플수는 참고 사이트와 같이 512로 하고 있습니다.
// フレームの数
int n = c / FRAME_LEN;
// フレームごとの音量を求める
double[] vols = new double[n];
for (int i = 0; i < n; i++) {
double vol = 0;
for (int j = 0; j < FRAME_LEN; j++) {
int sound = samples[i * FRAME_LEN + j];
vol += Math.pow(sound, 2);
}
vol = Math.sqrt((1.0 / FRAME_LEN) * vol);
vols[i] = vol;
}
인접한 프레임의 음량의 증가분을 구한다
// 隣り合うフレームの音量の増加分を求める
double[] diffs = new double[n];
for (int i = 0; i < n - 1; i++) {
double diff = vols[i] - vols[i + 1];
if (diff > 0) {
diffs[i] = diff;
} else {
diffs[i] = 0;
}
}
아래 그림은 볼륨의 증가분의 일부를 그래프로 한 것입니다.
가로가 시간축으로 세로가 음량의 증가분
어떤 템포가 일치하는지 묻는다.
증가량의 시간 변화의 주파수 성분을 구하여
double s = (double) sampleRate / FRAME_LEN;
double[] a = new double[240 - 60 + 1];
double[] b = new double[240 - 60 + 1];
double[] r = new double[240 - 60 + 1];
for (int bpm = 60; bpm <= 240; bpm++) {
double aSum = 0;
double bSum = 0;
double f = (double) bpm / 60;
for (int i = 0; i < n; i++) {
aSum += diffs[i] * Math.cos(2.0 * Math.PI * f * i / s);
bSum += diffs[i] * Math.sin(2.0 * Math.PI * f * i / s);
}
double aTmp = aSum / n;
double bTmp = bSum / n;
a[bpm - 60] = aTmp;
b[bpm - 60] = bTmp;
r[bpm - 60] = Math.sqrt(Math.pow(aTmp, 2) + Math.pow(bTmp, 2));
}
가장 일치하는 배열의 인덱스를 찾습니다.
int maxIndex = -1;
// 一番マッチするインデックスを求める
double dy = 0;
for (int i = 1; i < 240 - 60 + 1; ++i) {
double dyPre = dy;
dy = r[i] - r[i - 1];
if (dyPre > 0 && dy <= 0) {
if (maxIndex < 0 || r[i - 1] > r[maxIndex]) {
maxIndex = i - 1;
}
}
}
실제 BPM은
maxIndex + 60
입니다.(maxIndex는 배열의 요소 수이기 때문에)
실행한 결과 120이라는 값이 나왔습니다.
이것의 소스는 github에 올리고 있습니다.
https://魏Tub. 작은 m/벌레 C431 페r/벌레 cBpm
마지막으로
BPM을 살펴보십시오.
딱 120이라는 값이 나와서 놀랐습니다.
마찬가지로 템포 100 메트로놈의 음원에서도 시도해 보았습니다.
딱 맞는 값이 나왔습니다.
이번에는 메트로놈의 음원이라고합니다.
템포가 알기 쉬운 음원을 이용했습니다.
아직 음악 데이터로 실제로 BPM을 사용할 수 없습니다.
채널 수가 다르기 때문에, 또 세세한 곳이 다른 것일까라고 생각합니다.
음악에서도 실제로 BPM을 취할 수 있도록 가고 싶습니다.
2채널의 경우 어떤 식으로 BPM을 조사하면 좋을지 등
아는 분, 어드바이스 받을 수 있으면 기쁩니다.
그럼!
참고
MediaCodec 문서
htps : //에서 ゔぇぺぺr. 안 d로이 d. 코 m/레후오렌세/안 d로이 d/메아아/메아아코에서 c. HTML
MediaCodec 문서 번역
ぃ tp // 코 m / 지금 / ms / bd9d49cfb1f73383 또는 12
디코드시에 참고로 한 코드
htps : // 기주 b. 코 m / 타에 후 v / 메아 아코데세 mp ぇ
htps : // 기주 b. 코 m / ょ ぇ 시오 / 메아 아코로 c에서도
C/C++ 언어로 음성 파일의 템포 해석을 실시하는 샘플 프로그램
htp://hp.ゔ c와 r. 이. jp / 아우테 rs / ゔ 046927 / m포 / m포. HTML
메트로놈의 음원 획득
htp : ///메 t로노메 r. 코m/
Reference
이 문제에 관하여(Android에서 음성 파일에서 음악의 BPM을 살펴 보았습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/music431per/items/8d687b49afee0d7ccfdf텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)