Il modulo che vi riporterò di seguito serve per poter far emettere un suono al vostro programma python (in genere suoni brevi associati a particolari eventi) senza preoccuparvi se vi trovate su piattaforma windows o su linux chiamando una semplice funzione con parametro il nome del file da suonare.
Ecco un basilare esempio di utilizzo:
import sndplay
sndplay.play("percorso_del_file.wav")
Come avrete notato è molto semplice da usare. Se vi trovate su una piattaforma diversa dalle due supportate la funzione non fa nulla. Come parametro ho inserito un file wav. In realtà su linux viene accettato come parametro un qualsiasi formato supportato dalle librerie gstreamer ma se mettiamo un file diverso dal wav perdiamo la portabilità perché (almeno per ora) su windows è supportato solo il formato wav.
Passiamo ora al codice:
import threading
import os
(
GSTPLAY,
WINPLAY,
NOENGINE
) = range(3)
try:
import gst
import gobject
ENGINE = GSTPLAY
except ImportError:
try:
import winsound
ENGINE = WINPLAY
except ImportError:
ENGINE = NOENGINE
class __GstPlayThread(threading.Thread):
def __init__(self, ply):
self.ply = ply
threading.Thread.__init__(self)
def run(self):
self.ply.set_state(gst.STATE_PLAYING)
def bus_event(bus, message):
t = message.type
if t == gst.MESSAGE_EOS:
self.ply.set_state(gst.STATE_NULL)
return True
self.ply.get_bus().add_watch(bus_event)
def __gstplay(filename):
cwd = os.getcwd()
location = os.path.join(cwd, filename)
ply = gst.element_factory_make("playbin", "player")
ply.set_property("uri", "file://" + location)
pt = __GstPlayThread(ply)
pt.start()
def __winplay(filename):
cwd = os.getcwd()
location = os.path.join(cwd, filename)
import thread
def wplay():
winsound.PlaySound(location,
winsound.SND_FILENAME)
thread.start_new_thread(wplay, ())
if ENGINE == GSTPLAY:
play = __gstplay
pass
elif ENGINE == WINPLAY:
play = __winplay
else:
def play(filename):
pass
Attenzione: su linux è supportata solo la versione 0.10 delle pygst!!!
Come si può notare l'effettivo playing del file viene eseguito da un thread in modo che la play non sia bloccante. Cio vuol dire che mentre il suono viene riprodotto in background, il nostro codice può continuare ad essere eseguito ;)
Questo modulo l'ho scritto inizialmente per planimo ma ho pensato che sarebbe stato utile postarlo a parte con qualche spiegazione a contorno
Il modulo è bellissimo, e funziona bene (almeno su linux).
RispondiEliminaPurtroppo però ho un problema nel creare un programma con gui: la gui funziona bene, la thread che la separa dal codice funziona bene; non riesco ad integrarla con gstreamer, ogni volta che c'è una sequenza tipo
[...].set_state(gst.STATE_PLAYING)
time.sleep(1)
o l'equivalente, utilizzando il tuo modulo,
sndplay.play(NOTE_DIR + "/" + note)
time.sleep(1)
il programma va sistematicamente in segmentation fault.
Sai darmi una mano?
Aggiornamento, se tengo sndplay.play(...) e time.sleep(1) nella stessa thread della gui il suono si sente ma la gui va in freeze;
RispondiEliminaSe invece separo i due comandi dalla thread della gui ho il segmentation fault che dicevo prima.
Onestamente non so risponderti. A me è servito gstreamer solo per quello che fa il modulo ... :(
RispondiEliminaNooo, dai, sei la mia ultima speranza, non so a chi chiedere altrimenti... posso passarti il codice in qualche maniera? Magari ho fatto qualche cappella esagerata e non me ne accorgo.
RispondiEliminaSe usi time.sleep(1) all'interno della gui è normale che va in freeze. Perché devi fare time.sleep(1)?
RispondiEliminaIl modulo è fatto in modo che i suoni vengano riprodotti in modo asincrono e cioè la funzione "play" non è bloccante.
Per quanto riguarda il codice mi devi perdonare ma non avrei il tempo di mettermi a studiarlo.
Inoltre ricorda che il modulo è pensato per eseguire suoni associati ad eventi e quindi di breve durata e non per eseguire ad esempio un intero pezzo musicale.
Spero di esserti stato utile
Parzialmente ci hai azzeccato.
RispondiEliminaUno degli sleep distanzia leggermente il controlli per il blocco della thread.
L'altro sleep(1) non è all'interno della gui, è all'interno di un metodo che suona una serie di file lunghi due-tre secondi (fino a che qualcuno non interrompe tale metodo).
Il programma vorrebbe essere un allenatore per l'orecchio; tra la riproduzione della nota e la riproduzione del nome, e tra due esercizi diversi, c'è una pausa impostabile dall'utente tramite due slidebar;
In generale se tolgo le chiamate a sndplay.play il programma funziona perfettamente, rispetta le pause come impostate... ma ovviamente non suona :) Se le rimetto si presenta la situazione come descritta due post fa.
Purtroppo detto così non riesco a capire bene quello che intendi. Comunque come ho scritto in questo post, quando usi la gui devi fare molta attenzione a come usi i threads perché possono combinare molti casini e risulta complesso trovare dove sono i problemi.
RispondiEliminaIn ogni modo per capire meglio, se puoi posta "solo" le porzioni di codice che interessano così cerchiamo di risolvere.
Ti ringrazio. Vista l'impossibilità di inserire tabulazioni raggruppo all'interno di parentesi graffe.
RispondiEliminawhile True:{
time.sleep(1)
if self._want_abort:{
wx.PostEvent(self._notify_window, ResultEvent(None))
return}
RANDOM_INDEX = random.randint(0,len(SOUNDFILES) -1) # genero un indice casuale
note = SOUNDFILES[RANDOM_INDEX] # recupero il file audio
name = NOTES_DICT[note] # ...e il suo nome
sndplay.play(NOTE_DIR + "/" + note)
time.sleep(SLEEPTIME)
sndplay.play(NOTE_DIR + "/" + name)
time.sleep(SLEEPTIME2) # aspetto tra un esercizio e l'altro
}
SOUNDFILES è un array, NOTES_DICT è un dizionario. Se commento le due chiamate a sndplay.play() funziona perfettamente.
Grazie ancora per l'aiuto.
per iniziare in genere per fare un operazione come quella che vuoi fare tu non si usano cicli while True. Se tu usi un ciclo di questo tipo, il programma non uscirà mai dal while e la gui non verrà aggiornata (ecco perché va in freeze, non a causa del modulo sndplay)
RispondiEliminaPer fare l'operazione che serve a te dovresti utilizzare i timeouts. Ora tu stai usando i wxWidgets? Se si non so come aiutarti perché non li conosco.
Con le gtk avresti dovuto utilizzare la funzione gobject.timeout_add (guardati la documentazione se non la conosci) che richiama periodicamente (allo scadere del timeout) una funzione da te specificata senza mandare in freeze la gui. Se usi i wxWidgets probabilmente esisterà qualcosa di simile.
Inoltre ho scoperto in questi giorni un metodo più semplice per riprodurre i suoni se usi gnome:
il codice è il seguente:
import gnome
gnome.sound_init('localhost')
gnome.sound_play(percorso_al_file_da_suonare)
Spero di esserti stato utile
Ciao ;)
Il codice che ti ho postato non è quello che manda in freeze la gui, è quello che va in segmentation fault subito prima di suonare.
RispondiEliminaIl "while true" è interrompibile quando si verifica la condizione self._want_abort, altrimenti deve continuare a ciclare, potenzialmente all'infinito.
Sono riuscito forse a circoscrivere un po' il problema: arriva in questa porzione del tuo modulo sndplay
def run(self):{
self.ply.set_state(gst.STATE_PLAYING)
def bus_event(bus, message):{
t = message.type
if t == gst.MESSAGE_EOS:{
self.ply.set_state(gst.STATE_NULL)
}
return True
}
self.ply.get_bus().add_watch(bus_event)
Se metto un "print" subito prima dell'if viene stampato una trentina di volte e poi va in segfault. E' normale? Se invece faccio suonare il modulo dalla python-shell non scrive nulla.
Io sono su gnome, ma il programma serve anche ad un amico su windows, per quello il tuo modulo mi è così utile.
Sto usando le wxwidgets, con wxglade sono riuscito a generare il codice mentre ora glade non fa altrettanto per le gtk.
Ti ringrazio per il tempo che stai impiegando per me.
Prova a modificare il modulo e piuttosto che usare gstreamer usa la gnome play in questo modo:
RispondiEliminatry:{
import gnome
gnome.sound_init("localhost")
ENGINE = GNOMEPLAY
}
except ImportError:
...
...
if ENGINE == GNOMEPLAY:{
def play(filename):{
gnome.sound_play(filename)
}
}
In ogni caso è strano.
Comunque o i wxWidgets per qualche motivo non vanno d'accordo con gstreamer o c'è qualcos'altro che non va nel tuo programma.
Con le modifiche il programma non va in segmentation fault ma non suona nulla.
RispondiEliminaNon è che posso mandarti il sorgente? Sono meno di 300 righe, ma la parte importante sarà di meno di 50.
Sto provando anche a capire come fa a suonare il player "listen", che usa le pygtk, ma senza sleep() i suoni si sovrappongono, altrimenti va in freeze la UI (come dicevi tu).
Che casino!
Non avrei il tempo di studiarmi il tuo codice. Sono impegnato con l'università. Comunque se proprio non riesci a risolvere in alcun modo ti suggerisco di provare a chiedere sul forum di ubuntu http://forum.ubuntu-it.org/
RispondiEliminaMi spiace non poterti essere maggiormente d'aiuto
L'applicazione è partita.
RispondiEliminaSai che ho fatto? Niente. Niente, o quasi...
Gentoo per l'importanza di portage non è ancora passata a python 2.5 (siamo ancora con python 2.4.3), così ho riavviato su $altro_os, ho installato l'ultimissimo python e ho ammirato il programma funzionare splendidamente.
Grazie per il tempo concessomi, e complimenti per tutte le belle idee che escono dal tuo blog.
sono contento che tu abbia risolto :)
RispondiElimina