質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Q&A

解決済

3回答

2861閲覧

タイピングゲームを作るにあたってタイピング音をリアルタイムで再生させたい。

kjkjmk

総合スコア7

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

0グッド

1クリップ

投稿2020/07/14 06:34

編集2020/07/15 00:34

前提・実現したいこと

下記環境下でタイピングゲームを作るにあたって、キーボードを押下するたびに
リアルタイムでタイピング音を鳴らしたいのですが、再生の挙動がとても遅く、
ゆっくり押せば毎回再生されますが、
少し素早く(秒間8回以上くらい)キーボードを押下すると最初の一回のみしか音が鳴らずに理想通りになりません。

あと関係ないですが僕の初期アイコン酷くないですか?完璧にアレですよね。
嫌がらせかと思いました。

※下記ソースコードは音を鳴らすためだけにテストとして書いたコードです。(ほぼコピペ)
初投稿と開発経験も半年ほどですのでなるべく優しく教えていただけると助かります。

使用タイピング音:https://soundeffect-lab.info/sound/search.php?searchtext=%E3%82%AD%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89&x=0&y=0

上記サイトの「キーボード1」

音を再生する際に参照したサイト:https://dobon.net/vb/dotnet/programing/playwavfile.html

環境:visualstudio(コミュニティ)
言語:C#

発生している問題・エラーメッセージ

キー入力のリアルタイムで音を鳴らしたい。

該当のソースコード

C#

1 private void PlaySound(string waveFile) 2 { 3 4 //読み込む 5 this.player = new System.Media.SoundPlayer(waveFile); 6 //非同期再生する 7 player.Play(); 8 9 10 } 11 12 private void Form1_KeyPress(object sender, KeyPressEventArgs e) 13 { 14     PlaySound("C:\Users\Username\Desktop\save\keyboard1.wav"); 15 } 16

試したこと

何を試せばよいのかわからなかったですがplayer.PlaySync();にしてみたり
イベントをkeyDownにしてみたりしましたがうまくいきませんでした。
上記記載の「音を再生する際に参照したサイト」では一番上の「SoundPlayerクラス」しか試していません。
理由は他のはコード書く前にいろいろしなきゃダメだったのと、音自体は再生されているので、他に解決策があると思ったからです。

補足情報(FW/ツールのバージョンなど)

Windowsフォームアプリケーション(.NET Framework)
.NET Framework4.7.2

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TN8001

2020/07/14 11:50

試してみたんですけど、どのくらいの速さなんでしょうか? 1本指で連打(秒間8キー位) 問題なさそうでした。 でたらめにタイピング(秒間16キー位) いくらか音が抜けている気がしました(回数が合わない) 押しっぱなし(秒間30キー以上) ブツブツ音になってダメ。 いずれにしても鳴らないということはなかったです。 毎回newするのをやめたら変わるでしょうか?
kjkjmk

2020/07/14 19:14

自分の場合1秒間に6回程度が限界でした。また押しっぱなしは初回のみ音が鳴りそれ以降は鳴りません。 なるほど、newのタイミングを変えて試してみます!ありがとうございます。
kjkjmk

2020/07/15 00:34 編集

こういうことでしょうか? 挙動は変わりませんでした。 public partial class Form1 : Form { private System.Media.SoundPlayer player = null; DataTable dt = new DataTable(); string waveFile; private void Form1_Load(object sender, EventArgs e) { waveFile = "C:\\Users\\Username\\Desktop\\save\\keyboard1.wav"; this.player = new System.Media.SoundPlayer(waveFile); } private void Form1_KeyPress(object sender, KeyPressEventArgs e) { player.Play(); }
guest

回答3

0

Hey_CHさんの記述の説明のような形で回答させていただきます。
Hey_CHさんの回答は(再生していないことの判断方法や再生位置のリセット方法等若干異なりますが)最後の記述に近いものとなります。

下記のような段階を踏んだ結果最終的な形になったと考えていただければ分かりやすいでしょうか?

1.単純にNAudioを使用してタイピング音を鳴らす場合

問題点reader及びwaveOutが1つしかない場合同時再生できるタイピング音は1つだけのため、連続して入力した場合は押しっぱなしにした場合再生が追い付かない。

c#

1public partial class Form1 : Form 2 { 3 private AudioFileReader reader; 4 private WaveOut waveOut; 5 6 public Form1() 7 { 8 InitializeComponent(); 9 reader = new AudioFileReader("keyboard1.wav"); 10 waveOut = new WaveOut(); 11 waveOut.Init(reader); 12 } 13 14 private void Form1_KeyPress(object sender, KeyPressEventArgs e) 15 { 16 // 再生位置を0秒にリセット 17 reader.Position = 0; 18 19 // 再生する 20 waveOut.Play(); 21 } 22 23 private void Form1_FormClosing(object sender, FormClosingEventArgs e) 24 { 25 waveOut.Dispose(); 26 reader.Dispose(); 27 } 28 }

2.音源を複数準備してタイピング音を鳴らす場合

問題点: (今回のコードの例だと)4つまで同時再生できるようになったが、力技(変数をたくさん定義してひたすら初期化してif文書いて。。)なのでたくさん増やすのがつらい

C#

1public partial class Form1 : Form 2 { 3 private AudioFileReader reader; 4 private AudioFileReader reader2; 5 private AudioFileReader reader3; 6 private AudioFileReader reader4; 7 private WaveOut waveOut; 8 private WaveOut waveOut2; 9 private WaveOut waveOut3; 10 private WaveOut waveOut4; 11 12 public Form1() 13 { 14 InitializeComponent(); 15 reader = new AudioFileReader("keyboard1.wav"); 16 reader2 = new AudioFileReader("keyboard1.wav"); 17 reader3 = new AudioFileReader("keyboard1.wav"); 18 reader4 = new AudioFileReader("keyboard1.wav"); 19 waveOut = new WaveOut(); 20 waveOut2 = new WaveOut(); 21 waveOut3 = new WaveOut(); 22 waveOut4 = new WaveOut(); 23 waveOut.Init(reader); 24 waveOut2.Init(reader2); 25 waveOut3.Init(reader3); 26 waveOut4.Init(reader4); 27 } 28 29 private void Form1_KeyPress(object sender, KeyPressEventArgs e) 30 { 31 Console.WriteLine("called"); 32 33 // 再生されてないやつを再生する 34 if (waveOut.PlaybackState == PlaybackState.Stopped) 35 { 36 // 再生位置を0秒にリセット 37 reader.Position = 0; 38 waveOut.Play(); 39 Console.WriteLine("play1"); 40 } 41 else if (waveOut2.PlaybackState == PlaybackState.Stopped) 42 { 43 // 再生位置を0秒にリセット 44 reader2.Position = 0; 45 waveOut2.Play(); 46 Console.WriteLine("play2"); 47 } 48 else if (waveOut3.PlaybackState == PlaybackState.Stopped) 49 { 50 // 再生位置を0秒にリセット 51 reader3.Position = 0; 52 waveOut3.Play(); 53 Console.WriteLine("play3"); 54 } 55 else if (waveOut4.PlaybackState == PlaybackState.Stopped) 56 { 57 // 再生位置を0秒にリセット 58 reader4.Position = 0; 59 waveOut4.Play(); 60 Console.WriteLine("play4"); 61 } 62 } 63 64 private void Form1_FormClosing(object sender, FormClosingEventArgs e) 65 { 66 waveOut.Dispose(); 67 waveOut2.Dispose(); 68 waveOut3.Dispose(); 69 waveOut4.Dispose(); 70 reader.Dispose(); 71 reader2.Dispose(); 72 reader3.Dispose(); 73 reader4.Dispose(); 74 } 75 }

3.音源を配列で管理してタイピング音を鳴らす場合

たくさん同時再生・連続再生できる。ソースもシンプルになった。

C#

1public partial class Form1 : Form 2 { 3 private SoundPool soundPool; 4 5 public Form1() 6 { 7 InitializeComponent(); 8 9 soundPool = new SoundPool("keyboard1.wav", 10); 10 } 11 12 private void Form1_KeyPress(object sender, KeyPressEventArgs e) 13 { 14 soundPool.Play(); 15 } 16 17 private void Form1_FormClosing(object sender, FormClosingEventArgs e) 18 { 19 soundPool.Dispose(); 20 } 21 } 22 23 /// <summary> 24 /// 同時再生のために複数の音源を管理するクラス 25 /// </summary> 26 internal class SoundPool : IDisposable 27 { 28 private AudioFileReader[] readers; 29 private WaveOut[] waveOuts; 30 31 public SoundPool(string soundFilePath, int poolSize) 32 { 33 readers = new AudioFileReader[poolSize]; 34 waveOuts = new WaveOut[poolSize]; 35 36 for (var i = 0; i < poolSize; i++) 37 { 38 readers[i] = new AudioFileReader(soundFilePath); 39 waveOuts[i] = new WaveOut(); 40 waveOuts[i].Init(readers[i]); 41 } 42 } 43 44 /// <summary> 45 /// 再生されてない音源を再生する 46 /// </summary> 47 public void Play() 48 { 49 for (var i = 0; i < waveOuts.Length; i++) 50 { 51 if (waveOuts[i].PlaybackState == PlaybackState.Stopped) 52 { 53 // 再生位置を0秒にリセット 54 readers[i].Position = 0; 55 waveOuts[i].Play(); 56 break; 57 } 58 } 59 } 60 61 public void Dispose() 62 { 63 for (var i = 0; i < readers.Length; i++) 64 { 65 waveOuts[i].Dispose(); 66 readers[i].Dispose(); 67 } 68 } 69 }

投稿2020/07/15 03:42

PgMidori

総合スコア184

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kjkjmk

2020/07/17 15:42

ご回答ありがとうございます! 3パターンも用意して説明して頂けたのでとてもわかりやすかったです! なるほど、一度に再生される音が一つしかないのでリアルタイムで再生することが出来なかったんですね???? 助かりました!ありがとうございます!
guest

0

SoundPlayerを使うにしても、「あらかじめファイルを読み込んで生成したplayerオブジェクトを保存しておき、キーを押したときにplayer.Play()する」ように書けばいいんじゃないですかね。

投稿2020/07/14 17:04

Daregada

総合スコア11990

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kjkjmk

2020/07/14 19:16

回答ありがとうございます。 newのタイミングを変えて最初に作っておくって事ですよね、明日やってみます!
kjkjmk

2020/07/15 00:35 編集

他の方の回答のコピペですいません、こういうことでしょうか? 挙動は変わりませんでした! public partial class Form1 : Form { private System.Media.SoundPlayer player = null; DataTable dt = new DataTable(); string waveFile; private void Form1_Load(object sender, EventArgs e) { waveFile = "C:\Users\Username\Desktop\save\keyboard1.wav"; this.player = new System.Media.SoundPlayer(waveFile); } private void Form1_KeyPress(object sender, KeyPressEventArgs e) { player.Play(); }
guest

0

ベストアンサー

System.Media.SoundPlayerでは細かいことができないためNuGetで「NAudio」をインストールして以下のようにしてみました。

C#

1using System; 2using System.Windows.Forms; 3using NAudio.Wave; 4 5namespace TeraKeyTypeWAVE { 6 public partial class Form1 : Form { 7 WaveOut[] waveOuts = new WaveOut[5]; 8 AudioFileReader[] readers=new AudioFileReader[5]; 9 public Form1() { 10 InitializeComponent(); 11 12 for (int i = 0; i < readers.Length; i++) { 13 readers[i] = new AudioFileReader("keyboard1.wav"); 14 waveOuts[i] = new WaveOut(); 15 waveOuts[i].Init(readers[i]); 16 waveOuts[i].PlaybackStopped += (s,e) => { 17 var tmp = 0; 18 for (int j = 0; j < waveOuts.Length; j++) { 19 if (waveOuts[j] == (WaveOut)s) { 20 tmp = j; 21 break; 22 } 23 } 24 readers[tmp].CurrentTime = new TimeSpan(0); 25 }; 26 } 27 } 28 29 private void Form1_KeyPress(object sender, KeyPressEventArgs e) { 30 for (int i = 0; i < readers.Length; i++) { 31 if (readers[i].CurrentTime > new TimeSpan(0)) 32 continue; 33 else if (readers[i].CurrentTime == new TimeSpan(0)) { 34 waveOuts[i].Play(); 35 break; 36 } 37 } 38 } 39 } 40}

投稿2020/07/14 12:17

Hey_CH

総合スコア437

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kjkjmk

2020/07/14 19:20

おお、、、ありがとうございます! System.Media.SoundPlayerでは細かい事が出来ないんですね… 今後のためにもそちらも試してみます!
YAmaGNZ

2020/07/14 22:49

PlaybackStoppedイベントのsが再生の停止したWaveOutオブジェクトなのではないですか?
kjkjmk

2020/07/15 00:41

「NAudio」をインストールして、回答いただいたコードを実行してみると 理想通りの挙動になりました!ありがとうございます! ただ、ソースを見ても何がどうなって今の挙動が実現できているのかが、いまいちわかりませんでした… もう少し自分でも調べてみますが、少し解説をしていただけるとありがたいです! お手すきの際でよいのでお願いします!
Hey_CH

2020/07/15 07:10

YAmaGNZさん > PlaybackStoppedイベントのsが再生の停止したWaveOutオブジェクトなのではないですか? その通りですね。 AudioFileReaderでしかCurrentTimeをいじれないためこのようにしています。 WaveOutオブジェクトからAudioFileReaderを取得する方法があればそうしたいのですけどね。 kjkjmkさん 考え方としては、「keyboard1.wav」をあらかじめ5個(この数は適当なので増やしてもよいと思います)用意しておき、 KeyPressイベントで「再生中でないWaveOut」を再生します。 再生中の判定に「readers[i].CurrentTime > new TimeSpan(0)」を用いています。 また、再生が終わった時(waveOuts[i].PlaybackStopped)WaveOutに設定した「AudioFileReader」を求め、その「CurrentTime」を「TimeSpan(0)」に設定(巻き戻し)しています。 このようにする事で、最大5連続で「keyboard1.wav」を同時に再生する事ができるようになります。
YAmaGNZ

2020/07/15 07:27

すみません。勘違いしておりました。 WaveOutでの制御ではなくAudioFileReaderでの制御ですね。 NAudioのソースも確認しましたがWaveOutからのストリーム制御はできませんね。
Hey_CH

2020/07/15 07:41

YAmaGNZさん わざわざソースまで確認していただきありがとうございます。
kjkjmk

2020/07/17 15:45

解説ありがとうございます! ベストアンサー迷ったんですが、やはり最初に回答頂いたHey_CHさんにしました!皆さん回答ありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問