88
99import { getLast } from "@/sing/utility" ;
1010
11+ /**
12+ * mipmapのレベル。バケツサイズと、各バケツのmin/max値を保持する。
13+ */
1114type WaveformMipmapLevel = {
1215 readonly bucketSize : number ;
1316 readonly minBuckets : Float32Array ;
@@ -16,7 +19,7 @@ type WaveformMipmapLevel = {
1619
1720/**
1821 * 波形ピークのmipmap。
19- * levels[0]が最も細かいレベルで 、以降はバケツサイズの昇順に並ぶ。
22+ * 先頭のレベルが最も細かいレベルで 、以降はバケツサイズの昇順に並ぶ。
2023 */
2124export type WaveformPeaks = readonly WaveformMipmapLevel [ ] ;
2225
@@ -28,25 +31,32 @@ export type ResampledPeaks = {
2831 readonly maxValues : Float32Array ;
2932} ;
3033
31- const BASE_BUCKET_SIZE = 16 ;
32-
3334/**
3435 * AudioBufferのch0からピークのmipmapを生成する。
35- * 最も細かいレベルのバケツサイズは BASE_BUCKET_SIZE で、
36- * 以降は2倍ずつ大きなバケツサイズのレベルを、バケツ数が1個になるまで積み上げる。
36+ *
37+ * @param audioBuffer ピークを計算する音声データ。ch0のみ使用される。
38+ * @param minBucketSize 最小のバケツサイズ。1以上の整数でなければならない。
39+ * @returns 生成されたピークのmipmap。レベルはバケツサイズで昇順にソートされている。
3740 */
38- export function generateWaveformPeaks ( audioBuffer : AudioBuffer ) : WaveformPeaks {
41+ export function generateWaveformPeaks (
42+ audioBuffer : AudioBuffer ,
43+ minBucketSize : number ,
44+ ) : WaveformPeaks {
45+ if ( ! Number . isInteger ( minBucketSize ) || minBucketSize < 1 ) {
46+ throw new Error ( "minBucketSize must be a positive integer." ) ;
47+ }
48+
3949 const channel = audioBuffer . getChannelData ( 0 ) ;
4050 const numSamples = channel . length ;
4151
4252 // レベル0: サンプルから直接バケツ化
43- const baseNumBuckets = Math . ceil ( numSamples / BASE_BUCKET_SIZE ) ;
53+ const baseNumBuckets = Math . ceil ( numSamples / minBucketSize ) ;
4454 const baseMinBuckets = new Float32Array ( baseNumBuckets ) ;
4555 const baseMaxBuckets = new Float32Array ( baseNumBuckets ) ;
4656
4757 for ( let bucketIndex = 0 ; bucketIndex < baseNumBuckets ; bucketIndex ++ ) {
48- const start = bucketIndex * BASE_BUCKET_SIZE ;
49- const end = Math . min ( start + BASE_BUCKET_SIZE , numSamples ) ;
58+ const start = bucketIndex * minBucketSize ;
59+ const end = Math . min ( start + minBucketSize , numSamples ) ;
5060 let min = channel [ start ] ;
5161 let max = channel [ start ] ;
5262 for ( let i = start + 1 ; i < end ; i ++ ) {
@@ -64,13 +74,13 @@ export function generateWaveformPeaks(audioBuffer: AudioBuffer): WaveformPeaks {
6474
6575 const levels : WaveformMipmapLevel [ ] = [
6676 {
67- bucketSize : BASE_BUCKET_SIZE ,
77+ bucketSize : minBucketSize ,
6878 minBuckets : baseMinBuckets ,
6979 maxBuckets : baseMaxBuckets ,
7080 } ,
7181 ] ;
7282
73- // 上位レベル : バケツ数が1個になるまで2バケツずつまとめる
83+ // レベル1以降 : バケツ数が1個になるまで2バケツずつまとめる
7484 while ( getLast ( levels ) . minBuckets . length > 1 ) {
7585 const prev = getLast ( levels ) ;
7686 const prevNumBuckets = prev . minBuckets . length ;
@@ -106,12 +116,15 @@ export function generateWaveformPeaks(audioBuffer: AudioBuffer): WaveformPeaks {
106116 * 各区間に対応するサンプル範囲を境界配列で指定して、ピークをリサンプルする。
107117 * 区間ごとに最適なmipmapレベルを選択するため、サンプル幅が不均等でも適切な解像度になる。
108118 *
109- * @param binBoundarySamples 長さ `numBins + 1` の配列。
119+ * @param peaks リサンプル元のピークmipmap。
120+ * レベルはバケツサイズで昇順にソートされていなければならない。
121+ * @param binBoundarySamples 各区間の境界をサンプル位置で表した配列。
110122 * `binBoundarySamples[i]` 以上 `binBoundarySamples[i + 1]` 未満が区間 `i` のサンプル範囲。
123+ * 配列の長さは 区間の数 + 1 で、2以上でなければならない。
111124 * 各値は音声データ先頭基準のサンプル位置で、単調非減少でなければならない。
112- * 負の値や `numSamples` を超える値も許容され 、範囲外は0埋めになる。
125+ * サンプル数を超える値や負の値も許容され 、範囲外は0埋めになる。
113126 * 幅0の区間は点として扱い、その位置のサンプルを含むバケツの値になる。
114- * 長さは2以上でなければならない 。
127+ * @returns 各区間のmin/max値 。
115128 */
116129export function resamplePeaks (
117130 peaks : WaveformPeaks ,
0 commit comments