Skip to content

Commit

Permalink
⏬ Buffered stereo waveforms
Browse files Browse the repository at this point in the history
  • Loading branch information
AutumnSky1010 committed Dec 30, 2024
1 parent 4f68f19 commit 57bdb9e
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 27 deletions.
13 changes: 11 additions & 2 deletions src/SoundMaker/Sounds/Track.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,16 @@ public short[] GeneratePartialWave(int startIndex, int endIndex)
return [];
}

// 生成する波形の長さを計算する
var expectedLength = endIndex - startIndex + 1;

/** 探索用インデクス */
// 現在探索中のコンポーネント開始インデクス
int currentComponentIndex = StartIndex;

// 生成した波形データを格納するリスト
var result = new List<short>();

// 余分に生成した波形の長さ(データの先頭側)
var unnecessaryLengthFirst = 0;
foreach (var soundComponent in _soundComponents)
{
Expand All @@ -189,19 +193,22 @@ public short[] GeneratePartialWave(int startIndex, int endIndex)
break;
}

// 以下、コンポーネントが必要な範囲に含まれる場合の処理
var wave = soundComponent.GenerateWave(_format, _tempo, WaveType);
if (result.Count == 0)
{
unnecessaryLengthFirst = Math.Max(startIndex - currentComponentIndex, 0);

// currentComponentIndexがstartIndexより大きい場合は、開始位置を調製するために0-paddingする必要がある
// 開始インデクスより先に波形がある場合は0で埋めて、波形の開始位置をあわせる
if (currentComponentIndex > startIndex)
{
result.AddRange(Enumerable.Repeat<short>(0, currentComponentIndex - startIndex));
}
}
// 生成した波形を追加
result.AddRange(wave);

// 次のコンポーネントへ進む
currentComponentIndex = nextComponentIndex;

// 必要な範囲の波形をすべて取得したら終了
Expand All @@ -211,12 +218,14 @@ public short[] GeneratePartialWave(int startIndex, int endIndex)
}
}

// 最初の不要な長さをスキップする
var skipped = result.Skip(unnecessaryLengthFirst).ToArray();
if (skipped.Length <= expectedLength)
{
return skipped;
}

// 波形後ろ側の余分な長さをスキップする
return skipped.Take(expectedLength).ToArray();
}

Expand Down
91 changes: 66 additions & 25 deletions src/SoundMaker/Sounds/TrackBaseSound.cs
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,16 @@ public MonauralWave GenerateMonauralWave()
return new(wave);
}

/// <summary>
/// Generates buffered monaural waves from the tracks, starting at the specified index and using the specified buffer size. <br/>
/// 指定した開始インデックスから指定したバッファサイズを使用して、トラックからバッファリングされたモノラル波を生成するメソッド。
/// </summary>
/// <param name="startIndex">The starting index for the buffer. <br/> バッファの開始インデックス。</param>
/// <param name="bufferSize">The size of the buffer. <br/> バッファのサイズ。</param>
/// <returns>
/// An enumerable collection of buffered monaural waves. <br/>
/// バッファリングされたモノラル波の列挙可能なコレクション。
/// </returns>
public IEnumerable<MonauralWave> GenerateBufferedMonauralWave(int startIndex, int bufferSize)
{
if (_tracksTimeMap.Count == 0)
Expand Down Expand Up @@ -327,6 +337,62 @@ public StereoWave GenerateStereoWave()
return new(right, left);
}

/// <summary>
/// Generates buffered stereo waves from the tracks, starting at the specified index and using the specified buffer size. <br/>
/// 指定した開始インデックスから指定したバッファサイズを使用して、トラックからバッファリングされたステレオ波を生成するメソッド。
/// </summary>
/// <param name="startIndex">The starting index for the buffer. <br/> バッファの開始インデックス。</param>
/// <param name="bufferSize">The size of the buffer. <br/> バッファのサイズ。</param>
/// <returns>
/// An enumerable collection of buffered stereo waves. <br/>
/// バッファリングされたステレオ波の列挙可能なコレクション。
/// </returns>
public IEnumerable<StereoWave> GenerateBufferedStereoWave(int startIndex, int bufferSize)
{
if (_tracksTimeMap.Count == 0)
{
yield break;
}

// 最大の終了時インデクスを取得する
var maxEndIndex = _tracksTimeMap
.SelectMany(pair => pair.Value)
.Where(track => track.Count != 0)
.Max(track => track.EndIndex);
var concurrentTracksCount = GetMaxConcurrentTracks();

for (int seekIndex = startIndex; seekIndex <= maxEndIndex; seekIndex += bufferSize)
{
var right = new short[maxEndIndex + 1];
var left = new short[maxEndIndex + 1];
foreach (var (_, tracks) in _tracksTimeMap)
{
foreach (var track in tracks)
{
if (track.Count == 0)
{
continue;
}

var trackWave = track.GeneratePartialWave(seekIndex, seekIndex + bufferSize - 1);
if (trackWave.Length == 0)
{
continue;
}

var pan = (track.Pan + 1) / 2.0f;
for (int i = 0; i < trackWave.Length; i++)
{
left[i] += (short)(trackWave[i - track.StartIndex] * pan / concurrentTracksCount);
right[i] += (short)(trackWave[i - track.StartIndex] * (1 - pan) / concurrentTracksCount);
}
}
}

yield return new(right, left);
}
}

/// <summary>
/// Calculates the maximum number of overlapping tracks at any time.<br/>
/// 任意の時点で同時に再生されているトラック数の最大値を計算するメソッド。
Expand Down Expand Up @@ -374,31 +440,6 @@ private int GetMaxConcurrentTracks()
}


/// <summary>
/// Normalizes and clamps the wave data. <br/>
/// 波形データを正規化してクランプするメソッド。
/// </summary>
/// <param name="wave">The wave data. <br/> 波形データ。</param>
/// <returns>The normalized and clamped wave data. <br/> 正規化およびクランプされた波形データ。</returns>
private static short[] NormalizeAndClamp(long[] wave, long maxAmplitude)
{
const int MaxValue = short.MaxValue;
const int MinValue = short.MinValue;

var scaleFactor = maxAmplitude > MaxValue ? (double)MaxValue / maxAmplitude : 1.0;

var normalizedWave = new short[wave.Length];

for (int i = 0; i < wave.Length; i++)
{
var scaledSample = wave[i] * scaleFactor;
normalizedWave[i] = (short)Math.Clamp(scaledSample, MinValue, MaxValue);
}

return normalizedWave;
}


/// <summary>
/// Imports tracks into the internal map based on their start times. <br/>
/// トラックを開始時間に基づいて内部のマップにインポートするメソッド。
Expand Down

0 comments on commit 57bdb9e

Please sign in to comment.