質問するログイン新規登録

質問編集履歴

1

前提にスレッドに関する情報を追記、またソースコードを現象を再現できるより簡易なものに変更しました。

2021/01/26 11:33

投稿

hibin0bin0
hibin0bin0

スコア2

title CHANGED
File without changes
body CHANGED
@@ -1,9 +1,11 @@
1
1
  ### 前提・実現したいこと
2
+ ※スレッドに関して追記しました。
2
3
 
3
4
  自作ツールでNaudioを使って音声データを扱っています。
5
+ メインスレッドで音声を読み込んでから、
4
- A・音声データの読み込み直後
6
+ A・音声データの読み込み直後(別スレッド)
5
- B・Aの後にFFTの計算時
7
+ B・Aの後にFFTの計算時(別スレッド)
6
- C・波形データの描画
8
+ C・波形データの描画(メインスレッド)
7
9
  主にこの3つのタイミングでAudioFileReader.Readを行っています。
8
10
  wav/mp3などの音声データでは問題ないのですが
9
11
  mp4のような動画データから音声を読む際に、
@@ -32,13 +34,33 @@
32
34
 
33
35
  ### 該当のソースコード
34
36
 
35
- Aのソースコード
37
+ ※追記 Winforms Form1クラピペできる簡易版のスクリプトはこちらです:
36
38
  ```C#
37
- //AudioFileReaderリスト:元データ、波形描画用ピークファイルtasks個分、再生用ストリームを追加していく
39
+ public partial class Form1 : Form
40
+ {
38
- public List<AudioFileReader> streams = new List<AudioFileReader>();
41
+ List<AudioFileReader> streams = new List<AudioFileReader>();
42
+ int ch;
43
+ long fullsamples;
44
+ WaveFormat waveformat;
45
+ long fullms;
46
+ int block = 128;
47
+ int fftLength = 128;
48
+
49
+ public Form1()
50
+ {
51
+ InitializeComponent();
39
52
 
53
+ streams.Add(new AudioFileReader(/*任意のメディアファイル*/));
54
+ ch = streams[0].WaveFormat.Channels;
55
+ fullsamples = (int)(streams[0].Length / streams[0].BlockAlign);
56
+ waveformat = streams[0].WaveFormat;
57
+ fullms = (int)(fullsamples * 1000 / waveformat.SampleRate);
58
+
40
- //backgroundworkerのDoWork
59
+ backgroundWorker1.RunWorkerAsync();
60
+ }
61
+
62
+ //Aの処理
41
- public void CreatePeakFiles(object sender, DoWorkEventArgs e)
63
+ private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
42
64
  {
43
65
  BackgroundWorker control = (BackgroundWorker)sender;
44
66
 
@@ -47,15 +69,15 @@
47
69
  int block = this.block * 2;
48
70
  float[] sample = new float[block * ch];
49
71
 
50
- for (int x = 0; x < tasks; x++)
72
+ for (int x = 0; x < 3; x++)
51
73
  {
52
- var writer = new WaveFileWriter("tempshrink" + x + ".wav", streams[x].WaveFormat);
74
+ var writer = new WaveFileWriter("tempshrink" + x + ".wav", streams[x].WaveFormat);
53
75
  streams[x].Position = 0;
54
76
 
55
- int read = streams[x].Read(sample, 0, block * ch); //ここのReadは問題なし
77
+ int read = streams[x].Read(sample, 0, block * ch);
56
78
  while (read != 0)
57
79
  {
58
- for(int b =0; b < block; b++)
80
+ for (int b = 0; b < block; b++)
59
81
  {
60
82
  for (int c = 0; c < ch; c++)
61
83
  {
@@ -75,28 +97,20 @@
75
97
  //書き込み
76
98
  for (int i = 0; i < write.Length; i++) writer.WriteSample(write[i]);
77
99
 
78
- //キャンセル確認
79
- if (control.CancellationPending)
80
- {
81
- e.Cancel = true;
82
- return;
83
- }
84
- control.ReportProgress((int)(streams[x].Position * 100 / streams[x].Length), $"ピークを取得しています...");
85
-
86
- read = streams[x].Read(sample, 0, block * ch); //ここも問題なし
100
+ read = streams[x].Read(sample, 0, block * ch);
87
101
  }
88
102
  writer.Dispose();
89
103
  streams.Add(new AudioFileReader("tempshrink" + x + ".wav"));
90
- Console.WriteLine($"tempshrink{x}ピーク算出終了:" + watch.ElapsedMilliseconds.ToString());
91
- watch.Restart();
92
104
  }
93
- streams.Add(new AudioFileReader(soundpath)); //再生用
105
+ }
94
106
 
107
+ private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
108
+ {
109
+ backgroundWorker2.RunWorkerAsync();
95
110
  }
96
- ```
111
+
97
- Bのソースコード
112
+ //Bの処理
98
- ```C#
99
- public void CalculateFFT(object sender, DoWorkEventArgs e)
113
+ private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
100
114
  {
101
115
  BackgroundWorker control = (BackgroundWorker)sender;
102
116
 
@@ -108,7 +122,7 @@
108
122
  int stride = (yoko % 4 == 0) ? yoko : (yoko / 4 + 1) * 4;
109
123
  byte[] result = new byte[stride * tate];
110
124
  byte[][] spec = new byte[yoko][];
111
- int m = (int)Math.Log(fftLength, 2);
125
+ int m = (int)Math.Log(fftLength, 2);
112
126
  int bundle = 10;
113
127
  Complex[] buffer = new Complex[fftLength];
114
128
  int count = 0;
@@ -116,17 +130,15 @@
116
130
  int readoncelength = fftLength * ch * bundle;
117
131
  float[] sample = new float[readoncelength];
118
132
 
119
- while (reader.Read(sample, 0, readoncelength)!=0) //問題なし
133
+ while (reader.Read(sample, 0, readoncelength) != 0)
120
134
  {
121
-
122
-
123
135
  for (int i = 0; i < bundle; i++)
124
136
  {
125
137
  int timex = count * bundle + i;
126
138
  if (timex >= yoko) break;
127
139
  for (int r = 0; r < fftLength; r++)
128
140
  {
129
- int index =(i * fftLength + r) * ch;
141
+ int index = (i * fftLength + r) * ch;
130
142
  if (ch == 2) sample[index] = (sample[index] + sample[index + 1]) / 2;
131
143
 
132
144
  buffer[r].X = sample[index] * (float)FastFourierTransform.HammingWindow(r, fftLength);
@@ -150,74 +162,31 @@
150
162
  }
151
163
  }
152
164
 
153
-
154
- //キャンセル確認
155
- if (control.CancellationPending)
156
- {
157
- e.Cancel = true;
158
- return;
159
- }
160
- control.ReportProgress((int)(reader.Position * 100 / reader.Length), $"スペクトルを計算しています...");
161
165
  count++;
162
166
  }
163
- ```
164
- Cの中で呼び出している関数
165
- ```C#
166
- public float[] GetNextPeak(int id, long samplesperpeak, bool withrms = false)
167
- {
168
- int channels = streams[id].WaveFormat.Channels;
169
- float[] samples = new float[samplesperpeak * channels];
170
167
 
171
- //ここでなぜか例外発生
168
+ e.Result = result;
172
- //id は波形の拡縮によってストリームを切り替え、0の時は元データのストリーム
173
- //0以外はwavファイルなのでMediaFoundationReaderは使用せず
174
- //mp4を読み込んだあとのid=0の時のみ、この例外が発生
169
+ }
175
- int read = streams[id].Read(samples, 0, samples.Length);
176
170
 
177
-
171
+ private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
172
+ {
178
- if (read == 0) return null;
173
+ timer1.Enabled = true;
174
+ }
179
175
 
180
- //結果を入れる配列...LMax/RMax/Lmin/Rmin
181
- float[] result = new float[channels * 2];
182
- if (withrms) result = new float[channels * 2 + channels];
183
- //初期化;
176
+ //Tickで描画させているという体
184
- for(int i = 0; i < result.Length; i++)
177
+ private void timer1_Tick(object sender, EventArgs e)
185
- {
178
+ {
186
- result[i] = 0f;
179
+ float[] samples = new float[2];
187
- }
188
180
 
181
+ //ここで例外発生
189
- for(int i=0;i<read / channels; i++)
182
+ while (streams[0].Read(samples, 0, 2)!=0)
190
183
  {
191
- for(int ch = 0; ch < channels; ch++)
192
- {
193
- if (i == 0)
194
- {
195
- result[ch] = samples[channels * i + ch];
196
- result[ch + channels] = samples[channels * i + ch];
197
- }
198
- else
199
- {
200
- result[ch] = Math.Max(result[ch], samples[channels * i + ch]);
201
- result[ch + channels] = Math.Min(result[ch + channels], samples[channels * i + ch]);
202
- }
203
- if (withrms) result[channels * 2 + ch] += (float)Math.Pow(samples[channels * i + ch], 2);
204
184
 
205
- }
206
185
  }
207
-
208
- if (withrms)
209
- {
210
- for (int ch = 0; ch < channels; ch++)
211
- {
212
- result[channels * 2 + ch] /= read;
213
- result[channels * 2 + ch] = (float)Math.Sqrt(result[channels * 2 + ch]);
214
- }
215
- }
216
-
217
- return result;
218
-
219
186
  }
187
+ }
220
188
  ```
189
+
221
190
  ### 補足情報(FW/ツールのバージョンなど)
222
191
 
223
192
  Visual Studio 2019