Odtwarzanie więcej niż jednego dźwięku jednocześnie w Visual Studio (C#)

Odpowiedz Nowy wątek
2019-04-08 22:38
0

Cześć. Napisałem sobie metodę, która z pomocą SoundPlayer odtwarza mi dźwięki:

public bool[] error_do_not_play_sound_anymore = new bool[10];

// [...]

public void PlayRequestedSoundEffect(string SelectedSoundEffect) 
        {
            // wykorzystanie: PlayRequestedSoundEffect("TorpedoExplosion");

            // Domyślnie error_do_not_play_sound_anymore[x] = false, jeśli raz nie uda się odtworzyć danego dźwięku, 
            // to zmienia się na true i więcej tego już nie próbuje
            if (error_do_not_play_sound_anymore[0] == false && SelectedSoundEffect == "MessageLog")
            {
                try
                {
                    SoundPlayer simpleSound0 = new SoundPlayer(@"sounds\messagelogsound.wav");
                    simpleSound0.Play();
                }
                catch
                {
                    MessageBox.Show("Error:\n\nfile not found: sounds\\messagelogsounds.wav", "No file found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    error_do_not_play_sound_anymore[0] = true;
                }
            }

            // TorpedoExplosion - play a sound when torpedo hits a target

            if (error_do_not_play_sound_anymore[1] == false && SelectedSoundEffect == "TorpedoExplosion")
            {
                try
                {
                    SoundPlayer simpleSound1 = new SoundPlayer(@"sounds\torpedo_explosion.wav");
                    simpleSound1.Play();
                }
                catch
                {
                    MessageBox.Show("Error:\n\nfile not found: sounds\\torpedo_explosion.wav", "No file found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    error_do_not_play_sound_anymore[1] = true;
                }
            }

            // TorpedoFire - play a sound when torpedo is launched from the submarine

            if (error_do_not_play_sound_anymore[2] == false && SelectedSoundEffect == "TorpedoFire")
            {
                try
                {
                    SoundPlayer simpleSound2 = new SoundPlayer(@"sounds\torpedo_fire.wav");
                    simpleSound2.Play();
                }
                catch
                {
                    MessageBox.Show("Error:\n\nfile not found: sounds\\torpedo_fire.wav", "No file found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    error_do_not_play_sound_anymore[2] = true;
                }
            }

            // RadarPing - play a sound when enemy contact is detected

            if (error_do_not_play_sound_anymore[3] == false && SelectedSoundEffect == "RadarPing")
            {
                try
                {
                    SoundPlayer simpleSound3 = new SoundPlayer(@"sounds\radar_ping.wav");
                    simpleSound3.Play();
                }
                catch
                {
                    MessageBox.Show("Error:\n\nfile not found: sounds\\radar_ping.wav", "No file found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    error_do_not_play_sound_anymore[3] = true;
                }
            }

            // SonarSweepQuiet

            if (error_do_not_play_sound_anymore[4] == false && SelectedSoundEffect == "SonarSweepQuiet")
            {
                try
                {
                    SoundPlayer simpleSound4 = new SoundPlayer(@"sounds\sonar_sweep_quiet.wav");
                    simpleSound4.Play();
                }
                catch
                {
                    MessageBox.Show("Error:\n\nfile not found: sounds\\sonar_sweep_quiet.wav", "No file found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    error_do_not_play_sound_anymore[4] = true;
                }
            }

            // SonarSweepQuiet

            if (error_do_not_play_sound_anymore[5] == false && SelectedSoundEffect == "SonarSweepNormal")
            {
                try
                {
                    SoundPlayer simpleSound5 = new SoundPlayer(@"sounds\sonar_sweep.wav");
                    simpleSound5.Play();
                }
                catch
                {
                    MessageBox.Show("Error:\n\nfile not found: sounds\\sonar_sweep.wav", "No file found", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    error_do_not_play_sound_anymore[5] = true;
                }
            }
        }

Problem pojawia się, gdy zorientowałem się przeglądając stackoverflow, że SoundPlayer może odtwarzać tylko jeden plik jednocześnie. Niestety, nie znalazłem kodu, który umiałbym wykorzystać, aby można było odtworzyć dwa różne dźwięki na raz (lub ten sam dźwięk więcej niż jeden raz), np poprzez szybkie wciśnięcie jednego buttona (odtwarzającego jeden dźwięk) i szybko po nim wciskając inny (odtwarzając inny dźwięk).

Czy ktoś by mógł KLAROWNIE wytłumaczyć mi, jak mogę to zrobić (lub wklepać kod)?
Próbowałem wykorzystać WindowsMediaPlayer, ale ma tyle funkcji, że i tak umiem odtworzyć dźwięk tylko raz.
Szukałem na google i na yt, ale chyba za mało, bo odpowiedzi jeszcze nie znalazłem.

Z góry dziękuję

Pozostało 580 znaków

2019-04-09 01:50
0

Z tego co ja wiem to jeżeli nie użyjesz

ObiektSoundPlayera.PlaySync();

to dźwięk jest odpalany w nowym wątku chociaż mogę się mylić (Wybacz nie mam żadnego pliku na którym mógłbym to przetestować :( )

Jeżeli używasz SoundPlayer'a to myślę że to może też ci jakoś pomóc

        // Lepiej stworzyć tablicę obiektów niż 10 różnych 
        SoundPlayer[] Spl = { new SoundPlayer("plik1.wav"), new SoundPlayer("plik2.wav") };

        // Jeżeli twoje pliki z dźwiękami trochę ważą to możesz je załadować do pamięci przed puszczeniem
        foreach (var sp in Spl)
                sp.Load();

Wiem że to niewiele ale mam nadzieje że chociaż trochę ci to pomoże ^ ^

edytowany 2x, ostatnio: Yukiteru Gromadzki, 2019-04-09 01:55

Pozostało 580 znaków

2019-04-09 18:56
0

SoundPlayer.PlaySync odtwarza na bieżąco to co załadował. Więc nie rozwiąże problemu.
Sam SoundPlayer w obrębie jednego wątku tylko zmienia nagranie przy próbie użycia nowego obiektu.

Rozwiązaniem jest wielowątkowość:

        static void PlayWav(string path)
        {
            SoundPlayer simpleSound = new SoundPlayer(path);
            simpleSound.Play();
        }

            // A potem gdzieś w kodzie:
            var tasks = new List<Task>();
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"C:\dzwiek1.wav")));
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"C:\dzwiek2.wav")));
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"C:\dzwiek3.wav")));
            Task.WaitAll(tasks.ToArray());
edytowany 1x, ostatnio: Dyzma, 2019-04-09 18:57

Pozostało 580 znaków

2019-04-09 21:22
0

@Dyzma: A czy sam SoundPlayer.Play() nie tworzy nowego wątku do odtwarzania?

Przyznam natomiast że metoda PlayWav jest lepsza od mojej propozycji Tablicy :D

screenshot-20190409212103.png

edytowany 1x, ostatnio: Yukiteru Gromadzki, 2019-04-09 21:35

Pozostało 580 znaków

2019-04-09 21:35
0

Nie wnikałem. Zauważyłem efekt: dwa "play" po sobie (z różnych instancji obiektu) nie powodują odtwarzania jednocześnie, a po prostu odpala się od nowa ostatnio odtwarzany.
Zatem zgaduje, że chodzi to na tym samym wątku, a to, że to może być wątek inny niż interfejsu to inna bajka. ;)
Nie mam zbytnio czasu analizować działania klasy do odtwarzania .wav. :D Skupiłem się na rozwiązaniu problemu.

Pozostało 580 znaków

2019-04-11 07:44
0
Dyzma napisał(a):

SoundPlayer.PlaySync odtwarza na bieżąco to co załadował. Więc nie rozwiąże problemu.
Sam SoundPlayer w obrębie jednego wątku tylko zmienia nagranie przy próbie użycia nowego obiektu.

Rozwiązaniem jest wielowątkowość:

        static void PlayWav(string path)
        {
            SoundPlayer simpleSound = new SoundPlayer(path);
            simpleSound.Play();
        }

            // A potem gdzieś w kodzie:
            var tasks = new List<Task>();
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"C:\dzwiek1.wav")));
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"C:\dzwiek2.wav")));
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"C:\dzwiek3.wav")));
            Task.WaitAll(tasks.ToArray());

Nie wiem, czy kod dobrze wykorzystałem, ale sposób, w jaki to zrobiłem nie pomogło:


          private void button1_Click(object sender, EventArgs e)
        {
            var tasks = new List<Task>();
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"sounds\messagelogsound.wav")));
            Task.WaitAll(tasks.ToArray());
        }

        private void button2_Click(object sender, EventArgs e)
        {
            var tasks = new List<Task>();
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"sounds\torpedo_explosion.wav")));
            Task.WaitAll(tasks.ToArray());
        }

        private void button3_Click(object sender, EventArgs e)
        {
            var tasks = new List<Task>();
            tasks.Add(Task.Factory.StartNew(() => PlayWav(@"sounds\torpedo_fire.wav")));
            Task.WaitAll(tasks.ToArray());
        }

Cały napisany przez Ciebie kod (oprócz metody PlayWav) wklepałem również do

private void Form1_Load(object sender, EventArgs e)

i też się nie udało. Generalnie dźwięki mają się odtwarzać w

private void timer1_Tick(object sender, EventArgs e)

po spełnieniu określonego warunku, jak to w grach: czasem jednocześnie, czasem tylko jeden dźwięki:

private void timer1_Tick(object sender, EventArgs e)
{
  // if (a == 1) { odtwarzaj_dzwiek("dzwiek1"); }
  // if (a == 2) { odtwarzaj_dzwiek("dzwiek2"); }
  // itp itd etc
}

Czy kod źle zaimplementowałem?

EDIT:

Ten sposób też nie pomógł:

public int TEST_int = 0;

private void timer1_Tick(object sender, EventArgs e)
        {
            var tasks = new List<Task>();

            if (TEST_int == 1)
            {
                tasks.Add(Task.Factory.StartNew(() => PlayWav(@"sounds\torpedo_fire.wav")));
            }

            if (TEST_int == 2)
            {
                tasks.Add(Task.Factory.StartNew(() => PlayWav(@"sounds\torpedo_explosion.wav")));
            }

            if (TEST_int == 3)
            {
                tasks.Add(Task.Factory.StartNew(() => PlayWav(@"sounds\sonar_sweep.wav")));
            }

            Task.WaitAll(tasks.ToArray());
}

private void button1_Click(object sender, EventArgs e)
        {
            TEST_int = 1;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            TEST_int = 2;
        }

        private void button3_Click(object sender, EventArgs e)
        {
            TEST_int = 3;
        }
edytowany 1x, ostatnio: MariuszPoz, 2019-04-11 13:31

Pozostało 580 znaków

2019-04-11 20:47
1

Sorry, pobrałem trzy przykładowe dźwięki z neta i się okazało, że trzeci jest mixem dwóch pierwszych.
Dlatego myślałem, że działa. :D

Jakby co - moje wypowiedzi we wcześniejszych postach na temat SoundPlayer są miejscami niepoprawne.
Nie jestem pewien w jaki sposób działa odtwarzanie w nim dźwięków i nie mam czasu tego teraz weryfikować.
Zatem jakby co, trzeba to sprawdzić.

W ramach rekompensaty trochę się dokształciłem w tym temacie i się okazuje, że to nie taki banał.
Dość prostą i szybką drogą jest użycie natywnego API windows'a.

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApp5
{
    public partial class Form1 : Form
    {
        [DllImport("winmm.dll")]
        static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
                mciSendString(@"open E:\DesiJourney.wav type waveaudio alias dzwiek1", null, 0, IntPtr.Zero);
                mciSendString(@"play dzwiek1", null, 0, IntPtr.Zero);
        }

        private void button2_Click(object sender, EventArgs e)
        {
                mciSendString(@"open E:\KissesinParadise.wav type waveaudio alias dzwiek2", null, 0, IntPtr.Zero);
                mciSendString(@"play dzwiek2", null, 0, IntPtr.Zero);
        }
    }
}
edytowany 4x, ostatnio: Dyzma, 2019-04-11 20:51

Pozostało 580 znaków

2019-04-11 21:35
0

PRAWIE idealnie, ponieważ dźwięki za pomocą buttona mogę co prawda odtworzyć jednocześnie, jednakże po jednorazowym ich odtworzeniu więcej już nie da rady. Czegoś zabrakło?

Pozostało 580 znaków

2019-04-11 21:51

Z jakiegoś powodu jak jest ten sam alias, to nie chce drugi raz go odtwarzać.
Więc tak na szybko:

        private void Play(string path)
        {
            string nazwa = Guid.NewGuid().ToString();
            mciSendString(@"open "+ path + " type waveaudio alias " + nazwa, null, 0, IntPtr.Zero);
            mciSendString(@"play " + nazwa, null, 0, IntPtr.Zero);
        }

Pewnie da się ładniej, bardziej zgodnie ze sztuką. Ale takiego rozwiązania już musisz poszukać. ;)

Pozostało 580 znaków

2019-04-11 21:56
0

EUREKA!

int a = 1; int b = 1;

// [...]

private void button3_Click(object sender, EventArgs e)
        {
            mciSendString(@"open 1.wav type waveaudio alias dzwiek1" +a, null, 0, IntPtr.Zero);
            mciSendString(@"play dzwiek1" + a, null, 0, IntPtr.Zero);
            a+=1;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            mciSendString(@"open 2.wav type waveaudio alias dzwiek2" +b, null, 0, IntPtr.Zero);
            mciSendString(@"play dzwiek2" + b, null, 0, IntPtr.Zero);
            b+=1;
        }

Czy jeśli ten sam dźwięk odtworzy się około powiedzmy 10 000 razy to nie "zasyfi" to pamięci? Czy po odtworzeniu dźwięku trzeba jakoś obiekt (?) skasować?
Dzięki za pomoc. Cały dzień szukania odpowiedzi za mną :)

edytowany 1x, ostatnio: MariuszPoz, 2019-04-11 21:56

Pozostało 580 znaków

2019-04-11 22:47
0

Dobra, skoro już zacząłem ten temat, a Ty zadajesz w sumie sensowne pytania...
To rozwiązanie wyżej jest trochę słabe. Więc poszukałem za Ciebie, bo aż mi źle z nim było. :D
Tutaj masz dokumentację: link
Tutaj szczególnie przydatną - polecenia: link
I po użyciu trzeba zamknąć "obiekt" wykorzystując polecenie close: link
W praktyce:

mciSendString("close dzwiek1", null, 0, IntPtr.Zero);

Zależy jak to zaprojektujesz, możesz to np. wołać na Dispose() obiektu w C#.

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0