Unstable app work when playing fast on a MIDI keyboard (NAudio)

47 Views Asked by At

When I press the keys on the MIDI keyboard very quickly, my application is unstable, silence may occur and all WAV-files will be played at once in a few seconds. It works well if you don't play too fast.

What's wrong with my C# code? What I need to change to make the program stable with fast playing on a MIDI keyboard?


using NAudio.Wave;
using NAudio.Midi;

namespace MidiReader
{
  public class MidiPlayer
  {
    private string folder;
    private Dictionary<int, string> noteMappings;
    private MidiIn midiIn;
    private Dictionary<int, WaveOutEvent> players;

    public void SetMessageText(string text)
    {
      folder = text; 
    }

    public MidiPlayer(Dictionary<int, string> noteMappings, string folder)
    {
      this.noteMappings = noteMappings;
      this.midiIn = new MidiIn(3);
      this.midiIn.MessageReceived += MidiIn_MessageReceived;
      this.folder = folder;
      this.players = new Dictionary<int, WaveOutEvent>();
    }


    public void Start()
    {
      this.midiIn.Start();
      Console.WriteLine("Press any key to stop.");
      Console.ReadKey();
    }

    private void MidiIn_MessageReceived(object sender, MidiInMessageEventArgs e)
    {
      var midiMessage = e.MidiEvent;
      if (midiMessage.CommandCode == MidiCommandCode.NoteOn)
      {
        var noteOnEvent = (NoteOnEvent)midiMessage;
        int noteNumber = noteOnEvent.NoteNumber;
        if (noteMappings.ContainsKey(noteNumber))
        {

          folder = TelegramBot.SetFolder();

          string fileName = Path.Combine(folder, noteMappings[noteNumber]);
          Console.WriteLine(fileName);
          PlayWavFileAsync(fileName);
        }
      }
    }

    public async Task PlayWavFileAsync(string fileName, int noteNumber)
    {
      Console.WriteLine("Logged into PlayWavFile");
      await Task.Run(async () =>
      {
        using (var audioFile = new AudioFileReader(fileName))
        {
          var player = new WaveOutEvent();
          player.Init(audioFile);
          try
          {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Logged into try");
            player.Play();
            Console.ResetColor();
          }
          catch
          {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(new InvalidOperationException("Must call Init first"));
            Console.ResetColor();
          }
          Task.Delay(1000).Wait();         
        }
      });
    }
  }
}

I will be glad to see your tips.

1

There are 1 best solutions below

1
Mark Heath On

You are opening a separate WaveOut device for every sound you play. It is much better to have a single audio output device and mix in sounds as you need to play them. You could use an approach like I describe here, which mixes the audio with MixingSampleProvider: https://www.markheath.net/post/fire-and-forget-audio-playback-with