質問編集履歴

1

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

2021/01/26 11:33

投稿

hibin0bin0
hibin0bin0

スコア2

test CHANGED
File without changes
test CHANGED
@@ -1,14 +1,18 @@
1
1
  ### 前提・実現したいこと
2
2
 
3
+ ※スレッドに関して追記しました。
4
+
3
5
 
4
6
 
5
7
  自作ツールでNaudioを使って音声データを扱っています。
6
8
 
9
+ メインスレッドで音声を読み込んでから、
10
+
7
- A・音声データの読み込み直後
11
+ A・音声データの読み込み直後(別スレッド)
8
-
12
+
9
- B・Aの後にFFTの計算時
13
+ B・Aの後にFFTの計算時(別スレッド)
10
-
14
+
11
- C・波形データの描画
15
+ C・波形データの描画(メインスレッド)
12
16
 
13
17
  主にこの3つのタイミングでAudioFileReader.Readを行っています。
14
18
 
@@ -66,19 +70,59 @@
66
70
 
67
71
 
68
72
 
69
- Aソーコード
73
+ ※追記 Winforms Form1クラスにコピペできる簡易版のスクリプトはこちらです:
70
74
 
71
75
  ```C#
72
76
 
73
- //AudioFileReaderリスト:元データ、波形描画用ピークファイルtasks個分、再生用ストリームを追加していく
77
+ public partial class Form1 : Form
78
+
74
-
79
+ {
80
+
75
- public List<AudioFileReader> streams = new List<AudioFileReader>();
81
+ List<AudioFileReader> streams = new List<AudioFileReader>();
82
+
76
-
83
+ int ch;
84
+
77
-
85
+ long fullsamples;
86
+
78
-
87
+ WaveFormat waveformat;
88
+
89
+ long fullms;
90
+
91
+ int block = 128;
92
+
93
+ int fftLength = 128;
94
+
95
+
96
+
97
+ public Form1()
98
+
99
+ {
100
+
101
+ InitializeComponent();
102
+
103
+
104
+
105
+ streams.Add(new AudioFileReader(/*任意のメディアファイル*/));
106
+
107
+ ch = streams[0].WaveFormat.Channels;
108
+
109
+ fullsamples = (int)(streams[0].Length / streams[0].BlockAlign);
110
+
111
+ waveformat = streams[0].WaveFormat;
112
+
113
+ fullms = (int)(fullsamples * 1000 / waveformat.SampleRate);
114
+
115
+
116
+
79
- //backgroundworkerのDoWork
117
+ backgroundWorker1.RunWorkerAsync();
118
+
80
-
119
+ }
120
+
121
+
122
+
123
+ //Aの処理
124
+
81
- public void CreatePeakFiles(object sender, DoWorkEventArgs e)
125
+ private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
82
126
 
83
127
  {
84
128
 
@@ -96,23 +140,23 @@
96
140
 
97
141
 
98
142
 
99
- for (int x = 0; x < tasks; x++)
143
+ for (int x = 0; x < 3; x++)
100
144
 
101
145
  {
102
146
 
103
- var writer = new WaveFileWriter("tempshrink" + x + ".wav", streams[x].WaveFormat);
147
+ var writer = new WaveFileWriter("tempshrink" + x + ".wav", streams[x].WaveFormat);
104
148
 
105
149
  streams[x].Position = 0;
106
150
 
107
151
 
108
152
 
109
- int read = streams[x].Read(sample, 0, block * ch); //ここのReadは問題なし
153
+ int read = streams[x].Read(sample, 0, block * ch);
110
154
 
111
155
  while (read != 0)
112
156
 
113
157
  {
114
158
 
115
- for(int b =0; b < block; b++)
159
+ for (int b = 0; b < block; b++)
116
160
 
117
161
  {
118
162
 
@@ -152,291 +196,185 @@
152
196
 
153
197
 
154
198
 
199
+ read = streams[x].Read(sample, 0, block * ch);
200
+
201
+ }
202
+
203
+ writer.Dispose();
204
+
205
+ streams.Add(new AudioFileReader("tempshrink" + x + ".wav"));
206
+
207
+ }
208
+
209
+ }
210
+
211
+
212
+
213
+ private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
214
+
215
+ {
216
+
217
+ backgroundWorker2.RunWorkerAsync();
218
+
219
+ }
220
+
221
+
222
+
155
- //キャンセル確認
223
+ //Bの処理
224
+
156
-
225
+ private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
226
+
227
+ {
228
+
229
+ BackgroundWorker control = (BackgroundWorker)sender;
230
+
231
+
232
+
233
+ AudioFileReader reader = streams[0];
234
+
235
+ reader.Position = 0;
236
+
237
+
238
+
239
+ int yoko = (int)(fullsamples / fftLength);
240
+
241
+ int tate = fftLength / 2;
242
+
243
+ int stride = (yoko % 4 == 0) ? yoko : (yoko / 4 + 1) * 4;
244
+
245
+ byte[] result = new byte[stride * tate];
246
+
247
+ byte[][] spec = new byte[yoko][];
248
+
249
+ int m = (int)Math.Log(fftLength, 2);
250
+
251
+ int bundle = 10;
252
+
253
+ Complex[] buffer = new Complex[fftLength];
254
+
255
+ int count = 0;
256
+
257
+
258
+
259
+ int readoncelength = fftLength * ch * bundle;
260
+
261
+ float[] sample = new float[readoncelength];
262
+
263
+
264
+
265
+ while (reader.Read(sample, 0, readoncelength) != 0)
266
+
267
+ {
268
+
157
- if (control.CancellationPending)
269
+ for (int i = 0; i < bundle; i++)
270
+
271
+ {
272
+
273
+ int timex = count * bundle + i;
274
+
275
+ if (timex >= yoko) break;
276
+
277
+ for (int r = 0; r < fftLength; r++)
158
278
 
159
279
  {
160
280
 
281
+ int index = (i * fftLength + r) * ch;
282
+
283
+ if (ch == 2) sample[index] = (sample[index] + sample[index + 1]) / 2;
284
+
285
+
286
+
287
+ buffer[r].X = sample[index] * (float)FastFourierTransform.HammingWindow(r, fftLength);
288
+
161
- e.Cancel = true;
289
+ buffer[r].Y = 0.0f;
162
-
163
- return;
164
290
 
165
291
  }
166
292
 
293
+
294
+
295
+ FastFourierTransform.FFT(true, m, buffer);
296
+
297
+
298
+
299
+ spec[timex] = new byte[tate];
300
+
301
+
302
+
303
+ for (int k = 0; k < tate; k++)
304
+
305
+ {
306
+
167
- control.ReportProgress((int)(streams[x].Position * 100 / streams[x].Length), $"ピークを取得しています...");
307
+ double diagonal = Math.Sqrt(buffer[k].X * buffer[k].X + buffer[k].Y * buffer[k].Y);
308
+
168
-
309
+ double intensityDB = 10.0 * Math.Log10(diagonal);
310
+
311
+
312
+
169
-
313
+ const double minDB = -50.0;
170
-
314
+
171
- read = streams[x].Read(sample, 0, block * ch); //ここも問題なし
315
+ double percent = (intensityDB < minDB) ? 1.0 : intensityDB / minDB;
316
+
317
+
318
+
319
+ result[timex + (tate - 1 - k) * stride] = (byte)(255f * percent / 16);
320
+
321
+ spec[timex][k] = (byte)(255f * percent);
322
+
323
+ }
172
324
 
173
325
  }
174
326
 
175
- writer.Dispose();
327
+
176
-
177
- streams.Add(new AudioFileReader("tempshrink" + x + ".wav"));
328
+
178
-
179
- Console.WriteLine($"tempshrink{x}ピーク算出終了:" + watch.ElapsedMilliseconds.ToString());
180
-
181
- watch.Restart();
329
+ count++;
182
330
 
183
331
  }
184
332
 
333
+
334
+
185
- streams.Add(new AudioFileReader(soundpath)); //再生用
335
+ e.Result = result;
186
-
187
-
188
-
336
+
189
- }
337
+ }
338
+
339
+
340
+
341
+ private void backgroundWorker2_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
342
+
343
+ {
344
+
345
+ timer1.Enabled = true;
346
+
347
+ }
348
+
349
+
350
+
351
+ //Tickで描画させているという体
352
+
353
+ private void timer1_Tick(object sender, EventArgs e)
354
+
355
+ {
356
+
357
+ float[] samples = new float[2];
358
+
359
+
360
+
361
+ //ここで例外発生
362
+
363
+ while (streams[0].Read(samples, 0, 2)!=0)
364
+
365
+ {
366
+
367
+
368
+
369
+ }
370
+
371
+ }
372
+
373
+ }
190
374
 
191
375
  ```
192
376
 
193
- Bのソースコード
377
+
194
-
195
- ```C#
196
-
197
- public void CalculateFFT(object sender, DoWorkEventArgs e)
198
-
199
- {
200
-
201
- BackgroundWorker control = (BackgroundWorker)sender;
202
-
203
-
204
-
205
- AudioFileReader reader = streams[0];
206
-
207
- reader.Position = 0;
208
-
209
-
210
-
211
- int yoko = (int)(fullsamples / fftLength);
212
-
213
- int tate = fftLength / 2;
214
-
215
- int stride = (yoko % 4 == 0) ? yoko : (yoko / 4 + 1) * 4;
216
-
217
- byte[] result = new byte[stride * tate];
218
-
219
- byte[][] spec = new byte[yoko][];
220
-
221
- int m = (int)Math.Log(fftLength, 2);
222
-
223
- int bundle = 10;
224
-
225
- Complex[] buffer = new Complex[fftLength];
226
-
227
- int count = 0;
228
-
229
-
230
-
231
- int readoncelength = fftLength * ch * bundle;
232
-
233
- float[] sample = new float[readoncelength];
234
-
235
-
236
-
237
- while (reader.Read(sample, 0, readoncelength)!=0) //問題なし
238
-
239
- {
240
-
241
-
242
-
243
-
244
-
245
- for (int i = 0; i < bundle; i++)
246
-
247
- {
248
-
249
- int timex = count * bundle + i;
250
-
251
- if (timex >= yoko) break;
252
-
253
- for (int r = 0; r < fftLength; r++)
254
-
255
- {
256
-
257
- int index =(i * fftLength + r) * ch;
258
-
259
- if (ch == 2) sample[index] = (sample[index] + sample[index + 1]) / 2;
260
-
261
-
262
-
263
- buffer[r].X = sample[index] * (float)FastFourierTransform.HammingWindow(r, fftLength);
264
-
265
- buffer[r].Y = 0.0f;
266
-
267
- }
268
-
269
-
270
-
271
- FastFourierTransform.FFT(true, m, buffer);
272
-
273
-
274
-
275
- spec[timex] = new byte[tate];
276
-
277
-
278
-
279
- for (int k = 0; k < tate; k++)
280
-
281
- {
282
-
283
- double diagonal = Math.Sqrt(buffer[k].X * buffer[k].X + buffer[k].Y * buffer[k].Y);
284
-
285
- double intensityDB = 10.0 * Math.Log10(diagonal);
286
-
287
-
288
-
289
- const double minDB = -50.0;
290
-
291
- double percent = (intensityDB < minDB) ? 1.0 : intensityDB / minDB;
292
-
293
-
294
-
295
- result[timex + (tate - 1 - k) * stride] = (byte)(255f * percent / 16);
296
-
297
- spec[timex][k] = (byte)(255f * percent);
298
-
299
- }
300
-
301
- }
302
-
303
-
304
-
305
-
306
-
307
- //キャンセル確認
308
-
309
- if (control.CancellationPending)
310
-
311
- {
312
-
313
- e.Cancel = true;
314
-
315
- return;
316
-
317
- }
318
-
319
- control.ReportProgress((int)(reader.Position * 100 / reader.Length), $"スペクトルを計算しています...");
320
-
321
- count++;
322
-
323
- }
324
-
325
- ```
326
-
327
- Cの中で呼び出している関数
328
-
329
- ```C#
330
-
331
- public float[] GetNextPeak(int id, long samplesperpeak, bool withrms = false)
332
-
333
- {
334
-
335
- int channels = streams[id].WaveFormat.Channels;
336
-
337
- float[] samples = new float[samplesperpeak * channels];
338
-
339
-
340
-
341
- //ここでなぜか例外発生
342
-
343
- //id は波形の拡縮によってストリームを切り替え、0の時は元データのストリーム
344
-
345
- //0以外はwavファイルなのでMediaFoundationReaderは使用せず
346
-
347
- //mp4を読み込んだあとのid=0の時のみ、この例外が発生
348
-
349
- int read = streams[id].Read(samples, 0, samples.Length);
350
-
351
-
352
-
353
-
354
-
355
- if (read == 0) return null;
356
-
357
-
358
-
359
- //結果を入れる配列...LMax/RMax/Lmin/Rmin
360
-
361
- float[] result = new float[channels * 2];
362
-
363
- if (withrms) result = new float[channels * 2 + channels];
364
-
365
- //初期化;
366
-
367
- for(int i = 0; i < result.Length; i++)
368
-
369
- {
370
-
371
- result[i] = 0f;
372
-
373
- }
374
-
375
-
376
-
377
- for(int i=0;i<read / channels; i++)
378
-
379
- {
380
-
381
- for(int ch = 0; ch < channels; ch++)
382
-
383
- {
384
-
385
- if (i == 0)
386
-
387
- {
388
-
389
- result[ch] = samples[channels * i + ch];
390
-
391
- result[ch + channels] = samples[channels * i + ch];
392
-
393
- }
394
-
395
- else
396
-
397
- {
398
-
399
- result[ch] = Math.Max(result[ch], samples[channels * i + ch]);
400
-
401
- result[ch + channels] = Math.Min(result[ch + channels], samples[channels * i + ch]);
402
-
403
- }
404
-
405
- if (withrms) result[channels * 2 + ch] += (float)Math.Pow(samples[channels * i + ch], 2);
406
-
407
-
408
-
409
- }
410
-
411
- }
412
-
413
-
414
-
415
- if (withrms)
416
-
417
- {
418
-
419
- for (int ch = 0; ch < channels; ch++)
420
-
421
- {
422
-
423
- result[channels * 2 + ch] /= read;
424
-
425
- result[channels * 2 + ch] = (float)Math.Sqrt(result[channels * 2 + ch]);
426
-
427
- }
428
-
429
- }
430
-
431
-
432
-
433
- return result;
434
-
435
-
436
-
437
- }
438
-
439
- ```
440
378
 
441
379
  ### 補足情報(FW/ツールのバージョンなど)
442
380