Lyntin sound plugin with PyMedia
Guido Gloor <[email protected]> Mon, 09 Aug 2004 00:39:47 +0200
| Newsgroups | gmane.comp.games.mud.client.lyntin |
|---|---|
| Message-ID | <[email protected]> |
Hi all
======
First off, I have to apologize for posting this to two newsgroups. But
the problem, as I see it, is in the way those two things work together
(or don't). I guess the problem is more Lyntin's than PyMedia's though,
and it may be a stupid error on my part as well (actually, I think it
is), I just started learning Python.
***
I really like Lyntin [1], it's my MUD client of choice for quite some
time now. A friend of mine created a plugin, I extended it (see [2]),
and this made me think I could create a plugin of my own.
I thought adding sound support to Lyntin would be a good idea. Something
to learn Python with as well, since I don't know that language yet. So
my question is also not primarily a Lyntin or a PyMedia question, the
problem may just as well lie in my limited Python knowledge.
I looked if I'd find something for playing back sound in the Python API,
didn't find anything, but then saw the PyMedia framework [3] and guessed
that would work.
***
The attached file is said sound plugin for Lyntin, with textual
triggers. The triggering works (Lyntin side: I made it a mud_filter_hook
[4]), the whole thing is multithreaded, inter-thread communication works
as well.
The sound playback code works in the interactive Python window. I've
copied it from the PyMedia documenation at [5].
***
The problem stems from this code segment:
f = open(self.file, 'rb')
f.seek(0)
s = ' '
while len(s) > 0:
# exported.write_message(len(s))
s = f.read(10000)
snd.play(s)
Somehow the thread is cut off as soon as ... I don't know what happens.
The effect of it is, that the sound is not played until its end. The
lower the bit count for the buffer, the less of the sound is played,
down to plain nothing.
I tried the same code in the interactive window. Some notes:
* you'll have to adapt the path to the sound file
* the number 10000 is plain arbitrary, changing it has no effect
(apart from more or less loops)
* the 8 is actually the constant pymedia.AFMT_U8, but it is easier for
the plugin configuration if I write it out
import pymedia.audio.sound as pymedia
snd = pymedia.Output(22050, 1, 8)
f = open('C:\Python23\Scripts\test.wav', 'rb')
f.seek(0)
s = ' '
while len(s) > 0:
len(s)
s = f.read(10000)
snd.play(s)
***
The following line in the lyntinrc loads the plugin correctly and
initializes it with all needed parameters:
#soundset test test.wav 22050 1 8
Afterwards, if there's 'test' somewhere in the MUD's or telnet's or
whatever output, the sound will play. Well, it won't play completely,
with a small buffer size of 10000 it will probably not play at all.
***
Anybody has any idea what I'm doing wrong?
Cheers,
Guido
***
[1] http://lyntin.sourceforge.net/
[2] http://www.haslo.ch/permalink_300~en
[3] http://pymedia.sourceforge.net/
[4] http://lyntin.sourceforge.net/4.0/tutorials/tutorial2.php and
http://lyntin.sourceforge.net/phpwiki/index.php?Hooks
[5] http://pymedia.sourceforge.net/docs/pymedia.audio.sound.html
test.wav
(audio/wav, 16.8 KB) - not displayed
soundtriggers.py
(text/plain, 2.8 KB)
# # Copyright (c) 2004 by Guido Gloor # Published under the GNU General Public License # by [email protected] # # Loosely based on the dosomething plugin by # Stefan Aeschbacher # __author__ = "Guido Gloor" __version__ = "alpha" __date__ = "August 9, 2004" from lyntin import exported from lyntin.modules import modutils import string, os, threading import pymedia.audio.sound as pymedia # The dictionary of the commands that are added to lyntin commands_dict = {} # all the sounds sounds = {} def sound_set(session, args, input): """ Add a sound with trigger and parameters to the list (NOTE: if you want spaces in the trigger escape them with \\) """ global sounds trigger = args['trigger'] file = args['file'] bitrate = args['bitrate'] channels = args['channels'] bitdepth = args['bitdepth'] if sounds.has_key(trigger): exported.write_message('duplicate trigger ' + trigger) return sound = [] sound.append(file) sound.append(bitrate) sound.append(channels) sound.append(bitdepth) sound.append(False) sounds[trigger] = sound exported.write_message('OK, sound trigger for ' + trigger + ' added with file ' + file) def handle_mudtext(args): """ This function parses all the text from the MUD and checks if a sound should be played back """ line = args['data'] # check if the line triggers something for trigger in sounds: if line.find(trigger) != -1: # yes, it did trigger. check the block sound = sounds[trigger] if not sound[4]: # block the sound (inter-thread communication) sound[4] = True SoundPlayer(sound[0], int(sound[1]), int(sound[2]), sound[3], trigger).start() # the line remains unchanged return line class SoundPlayer(threading.Thread): """ A thread for the sound playback """ def __init__(self, file, bitrate, channels, bitdepth, trigger): threading.Thread.__init__(self) # get and save parameters self.file = file self.bitrate = bitrate self.channels = channels self.bitdepth = bitdepth self.trigger = trigger def run(self): # init PyMedia and play back the sound snd = pymedia.Output(self.bitrate, self.channels, self.bitdepth) f = open(self.file, 'rb') f.seek(0) s = ' ' while len(s) > 0: exported.write_message(len(s)) s = f.read(10000) snd.play(s) f.close() sound = sounds[self.trigger] sound[4] = False commands_dict['soundset'] = (sound_set, 'trigger file= bitrate= channels= bitdepth=') def load(): """ Initializes the module by binding all the commands.""" modutils.load_commands(commands_dict) exported.hook_register("mud_filter_hook", handle_mudtext) def unload(): """ Unbinds the commands (for when we reimport the module).""" modutils.unload_commands(commands_dict) exported.hook_unregister("mud_filter_hook", handle_mudtext)