2009-07-21 9 views
11

Ecco una domanda apparentemente semplice:Come utilizzare System.Media.SoundPlayer per riprodurre in modo asincrono un file audio?

Qual è il modo corretto di giocare in modo asincrono un file di risorse wav incorporato in Windows Form?

Tentativo # 1:

var player = new SoundPlayer(); 
player.Stream = Resources.ResourceManager.GetStream("mySound"); 
player.Play(); // Note that Play is asynchronous 
  • Buono: non blocca il thread UI
  • Bad: non SoundPlayer e il flusso risorsa incorporata sono immediatamente smaltiti.

Tentativo # 2:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound")) 
{ 
    using (var player = new SoundPlayer(audioMemory)) 
    { 
     player.Play(); 
    } 
} 
  • Buono: thread UI non è bloccato, SoundPlayer e flusso di memoria audio vengono immediatamente smaltiti.
  • Scorretto: condizioni di gara! Play() è asincrono e se la memoria audio viene eliminata prima che il gioco sia fatto ... boom! Viene generata l'eccezione di runtime.

Tentativo # 3:

using (var audioMemory = Resources.ResourceManager.GetStream("mySound")) 
{ 
    using (var player = new SoundPlayer(audioMemory)) 
    { 
     player.PlaySync(); 
    } 
} 
  • Buono: Player e flusso audio vengono immediatamente smaltiti.
  • Bad: PlaySync blocca il thread UI

Tentativo # 4:

ThreadPool.QueueUserWorkItem(ignoredState => 
    { 
    using (var audioMemory = Resources.ResourceManager.GetStream("mySound")) 
    { 
     using (var player = new SoundPlayer(audioMemory)) 
     { 
      player.PlaySync(); 
     } 
    } 
    }); 
  • Buono: interfaccia utente non congelare, giocatore e flusso di memoria vengono immediatamente smaltiti.
  • Scorretto: poiché questo si attiva spesso, è possibile che si esauriscano i thread del pool di thread! Vedi Larry Osterman's what's wrong with this code part 26.

Sembra che SoundPlayer abbia un evento PlayAsyncCompleted. Sfortunatamente, nessun evento del genere esiste. Mi sto perdendo qualcosa? Qual è il modo corretto per riprodurre in modo asincrono una risorsa incorporata .wav in Windows Form?

risposta

6

Non ho abbastanza reputazione per commentare, quindi risponderò semplicemente.

Se i requisiti per riprodurre il suono sono "ingannevolmente semplici" (si desidera riprodurre il suono occasionale quando un singolo utente di winform fa qualcosa), utilizzare il Tentativo n.

"che cosa c'è di sbagliato in questa parte di codice 26" ha il suo "sistema" spin off un nuovo thread di thread (per riprodurre l'audio) di Larry Osterman con ogni tasto. Indica che martellare via su di esso ha saturato la dimensione predefinita del lotto di 500 thread in circa 15 secondi di digitazione, ma questo era anche con un'app client/server che utilizzava RPC asincrono che utilizzava anche il threadpool. Davvero non un'applicazione "apparentemente semplice".

Se si sta tentando di accodare i byte audio ogni secondo (o più veloce) per 10 secondi o 100 secondi di secondi, in realtà non è una "semplice applicazione" e un sottosistema di threading/priorità accodato sarebbe probabilmente in ordine.

+0

Grazie per il feedback. La mia app è un'app client/server con RPC asincrono, con un suono riprodotto quando un utente remoto invia un messaggio. È esattamente il tipo di problema che Osterman descrive, sfortunatamente. La mia app non è semplice, tuttavia, penso che "riprodurre in modo asincrono un file audio da una risorsa incorporata" sia uno scenario semplice. –

+0

Sono d'accordo che "in modo asincrono la riproduzione di un file audio da una risorsa incorporata" è uno scenario semplice, ma il vostro requisito per riprodurre un suono quando un utente remoto invia un messaggio (dato che è possibile ottenere tonnellate di messaggi) non è così ... Se l'audio è già in riproduzione e arriva un nuovo messaggio. Vuoi (a) ignorare il messaggio perché il suono è già in riproduzione? Se è così, non usare il threadpool, basta usare un thread con nome e se il thread è già in esecuzione, non fare nulla. se il thread non è in esecuzione, avvialo. – BlueShepherd

+0

(b) mettere in coda il suono "SAME" per riprodurlo? in tal caso, ogni volta che ricevi un messaggio, incrementa una variabile condivisa (o, se zero, avvia il thread). nel thread del threadpool, ogni volta che si riproduce il messaggio, decrementa la variabile in loop. (c) mettere in coda un suono diverso? Dovrai creare una coda per "trattenere" i suoni da riprodurre. Metti i suoni in coda quando ricevi un messaggio, toglili quando li suoni – BlueShepherd

2

Uso ancora le funzioni ol 'waveOut____ dell'API win32. Ecco un esempio di codice buono:

http://www.codeproject.com/KB/audio-video/cswavplay.aspx

Edit: una soluzione molto più semplice per il vostro problema è quello di estrarre il risorsa incorporata, salvarlo come un vero e proprio file da qualche parte, e quindi utilizzare SoundPlayer per riprodurre il file. Un po 'goffo, ma semplice e non avrai il problema dello smaltimento delle risorse.

+0

Con waveOut, si può riprodurre un suono in modo asincrono e smaltire il flusso audio quando hai finito? –

+0

Sicuro. Il codice nel link fa esattamente questo (a meno che non lo faccia - potrei averti dato il link sbagliato). waveOut____ è l'API dei vecchi tempi che puoi davvero fare quello che vuoi. Relativamente troppo complicato, ma potente. – MusiGenesis

Problemi correlati