From 28099110b9e120051ab14d674e2bfaf983bfbfa5 Mon Sep 17 00:00:00 2001 From: skomoroh Date: Tue, 5 Sep 2006 21:28:19 +0000 Subject: [PATCH] * added support pygame (sound) git-svn-id: file:///home/shlomif/Backup/svn-dumps/PySolFC/svnsync-repos/pysolfc/PySolFC/trunk@63 efabe8c0-fbe8-4139-b769-b5e6d273206e --- MANIFEST.in | 27 +-- pysollib/app.py | 2 +- pysollib/main.py | 48 +++-- pysollib/pysolaudio.py | 299 ++++++++++++++++++++++++------ pysollib/pysolgtk/menubar.py | 2 +- pysollib/settings.py | 2 +- pysollib/tk/menubar.py | 9 +- pysollib/tk/soundoptionsdialog.py | 2 +- 8 files changed, 292 insertions(+), 99 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 2298846d..4b3ed1a1 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,13 +7,26 @@ include pysol setup.py setup.cfg MANIFEST.in Makefile COPYING README include pysollib/*.py pysollib/tk/*.py pysollib/pysolgtk/*.py include pysollib/games/*.py pysollib/games/special/*.py include pysollib/games/ultra/*.py pysollib/games/mahjongg/*.py -include docs/* -include po/* include scripts/build.bat scripts/create_iss.py scripts/mahjongg_utils.py include scripts/all_games.py scripts/cardset_viewer.py ## ## data ## +include docs/* +graft data/html +graft data/html-src +#graft data/images +recursive-include data/images *.gif *.png +#graft data/music +#graft data/plugins +graft data/sound +graft data/tiles +include data/pysol.xbm data/pysol.xpm data/pysol.ico +include po/* +graft locale +## +## cardsets +## graft data/cardset-2000 graft data/cardset-crystal-mahjongg graft data/cardset-dashavatara-ganjifa @@ -26,13 +39,3 @@ graft data/cardset-oxymoron graft data/cardset-standard graft data/cardset-tuxedo graft data/cardset-vienna-2k -graft data/html -graft data/html-src -#graft data/images -recursive-include data/images *.gif *.png -#graft data/music -#graft data/plugins -graft data/sound -graft data/tiles -include data/pysol.xbm data/pysol.xpm data/pysol.ico -graft locale diff --git a/pysollib/app.py b/pysollib/app.py index 3af70717..03427760 100644 --- a/pysollib/app.py +++ b/pysollib/app.py @@ -1675,7 +1675,7 @@ Please select a %s type %s. except: pass ##print dirs - ext_re = re.compile(r"\.((it)|(mod)|(mp3)|(pym)|(s3m)|(xm))$", re.I) + ext_re = re.compile(self.audio.EXTENTIONS) self.initResource(manager, dirs, ext_re, Music) diff --git a/pysollib/main.py b/pysollib/main.py index 6b133fa5..9fa6ceac 100644 --- a/pysollib/main.py +++ b/pysollib/main.py @@ -48,7 +48,8 @@ from resource import Tile from gamedb import GI from app import Application from pysolaudio import thread, pysolsoundserver -from pysolaudio import AbstractAudioClient, PysolSoundServerModuleClient, Win32AudioClient, OSSAudioClient +from pysolaudio import AbstractAudioClient, PysolSoundServerModuleClient +from pysolaudio import Win32AudioClient, OSSAudioClient, PyGameAudioClient # Toolkit imports from pysoltk import tkversion, wm_withdraw, wm_set_icon, loadImage @@ -91,6 +92,7 @@ def parse_option(argv): "noplugins", "nosound", "debug=", + "sound-mod=", "help"]) except getopt.GetoptError, err: print _("%s: %s\ntry %s --help for more information") \ @@ -105,6 +107,7 @@ def parse_option(argv): "french-only": False, "noplugins": False, "nosound": False, + "sound-mod": None, "debug": 0, } for i in optlist: @@ -126,6 +129,9 @@ def parse_option(argv): opts["noplugins"] = True elif i[0] == "--nosound": opts["nosound"] = True + elif i[0] == "--sound-mod": + assert i[1] in ('pss', 'pygame', 'oss', 'win') + opts["sound-mod"] = i[1] elif i[0] in ("-D", "--debug"): opts["debug"] = i[1] @@ -237,23 +243,27 @@ def pysol_init(app, args): warn_pysolsoundserver = 0 app.audio = None if not opts["nosound"]: - if os.name == "nt" and app.opt.sound_mode == 0: - app.audio = Win32AudioClient() - elif pysolsoundserver: - app.audio = PysolSoundServerModuleClient() - elif os.name == "nt": - app.audio = Win32AudioClient() - elif os.name == 'posix': - app.audio = OSSAudioClient() - if app.audio: - app.audio.startServer() - if app.audio.server is None: - if os.name == "nt" and not isinstance(app.audio, Win32AudioClient): - app.audio.destroy() - app.audio = Win32AudioClient() - app.audio.startServer() - else: - app.audio = AbstractAudioClient() + if opts['sound-mod']: + d = {'pss': PysolSoundServerModuleClient, + 'pygame': PyGameAudioClient, + 'oss': OSSAudioClient, + 'win': Win32AudioClient} + c = d[opts['sound-mod']] + app.audio = c() + else: + for c in (PysolSoundServerModuleClient, + PyGameAudioClient, + OSSAudioClient, + Win32AudioClient, + AbstractAudioClient): + try: + app.audio = c() + except: + pass + else: + # success + break + app.audio.startServer() # update sound_mode if isinstance(app.audio, PysolSoundServerModuleClient): app.opt.sound_mode = 1 @@ -394,7 +404,7 @@ Please check your %s installation. app.audio.connectServer(app) if app.audio.audiodev is None: app.opt.sound = 0 - if not opts["nosound"] and pysolsoundserver and not app.audio.connected: + if not opts["nosound"] and not opts['sound-mod'] and pysolsoundserver and not app.audio.connected: print PACKAGE + ": could not connect to pysolsoundserver, sound disabled." warn_pysolsoundserver = 1 app.audio.updateSettings() diff --git a/pysollib/pysolaudio.py b/pysollib/pysolaudio.py index 2867784b..b18953f8 100644 --- a/pysollib/pysolaudio.py +++ b/pysollib/pysolaudio.py @@ -35,7 +35,7 @@ # imports -import os, sys +import os, sys, time import traceback try: @@ -55,6 +55,12 @@ except ImportError: # ************************************************************************/ class AbstractAudioClient: + + EXTENTIONS = r"\.((it)|(mod)|(mp3)|(pym)|(s3m)|(xm))$" + + CAN_PLAY_SOUND = False + CAN_PLAY_MUSIC = False + def __init__(self): self.server = None self.audiodev = None @@ -100,10 +106,6 @@ class AbstractAudioClient: # high-level interface # - def stopAll(self): - self.stopSamples() - self.stopMusic() - def playSample(self, name, priority=0, loop=0, volume=-1): ##print 'AbstractAudioClient.playSample', name if self.audiodev is None or not self.app or not self.app.opt.sound: @@ -142,33 +144,6 @@ class AbstractAudioClient: self.sample_priority = -1 self.sample_loop = 0 - def playMusic(self, basename, priority=0, loop=0, volume=-1): - if self.audiodev is None or not self.app or not self.app.opt.sound: - return 0 - if priority <= self.music_priority and self.music_loop: - return 0 - obj = self.app.music_manager.getByBasename(basename) - if not obj or not obj.absname: - return 0 - try: - if self._playMusic(obj.absname, priority, loop, volume): - self.music_priority = priority - self.music_loop = loop - return 1 - except: - if traceback: traceback.print_exc() - return 0 - - def stopMusic(self): - if self.audiodev is None: - return - try: - self._stopMusic() - except: - if traceback: traceback.print_exc() - self.music_priority = -1 - self.music_loop = 0 - # # subclass - core implementation # @@ -188,12 +163,6 @@ class AbstractAudioClient: def _stopSamplesLoop(self): self._stopSamples() - def _playMusic(self, name, priority, loop, volume): - return 0 - - def _stopMusic(self): - pass - # # subclass - extensions # @@ -216,6 +185,14 @@ class AbstractAudioClient: # ************************************************************************/ class PysolSoundServerModuleClient(AbstractAudioClient): + + CAN_PLAY_SOUND = True + CAN_PLAY_MUSIC = True + + def __init__(self): + import pysolsoundserver + AbstractAudioClient.__init__(self) + def startServer(self): # use the module try: @@ -257,13 +234,6 @@ class PysolSoundServerModuleClient(AbstractAudioClient): def _stopSamplesLoop(self): self.cmd("stopwavloop") - def _playMusic(self, filename, priority, loop, volume): - self.cmd("playmus '%s' %d %d %d %d" % (filename, -1, priority, loop, volume)) - return 1 - - def _stopMusic(self): - self.cmd("stopmus") - def getMusicInfo(self): if self.audiodev: return self.audiodev.getMusicInfo() @@ -303,12 +273,19 @@ class PysolSoundServerModuleClient(AbstractAudioClient): # ************************************************************************/ class Win32AudioClient(AbstractAudioClient): + + CAN_PLAY_SOUND = True + CAN_PLAY_MUSIC = False + + def __init__(self): + import winsound + AbstractAudioClient.__init__(self) + def startServer(self): # use the built-in winsound module try: import winsound self.audiodev = winsound - del winsound self.server = 0 # success - see also tk/menubar.py except: self.server = None @@ -338,31 +315,32 @@ class Win32AudioClient(AbstractAudioClient): # // OSS audio # ************************************************************************/ -#import wave, ossaudiodev - class OSSAudioServer: def __init__(self, pipe): - import ossaudiodev self.pipe = pipe + #import ossaudiodev #self.audiodev = ossaudiodev.open('w') + self.sound_priority = -1 + self._busy = False def mainLoop(self): while True: s = os.read(self.pipe, 256) - ss = s.split('\0', 2) + ss = s.split('\0') if not ss[0]: os._exit(0) if ss[0] == 'break': self._play_loop = False continue - filename, loop = ss[0], int(ss[1]) + filename, priority, loop = ss[0], int(ss[1]), int(ss[2]) if loop: self._play_loop = True th = Thread(target=self.playLoop, args=(filename,)) th.start() else: - self.play(filename) + if not self._busy: + self.play(filename, priority) def _getParameters(self, filename): import ossaudiodev, wave @@ -392,10 +370,11 @@ class OSSAudioServer: if traceback: traceback.print_exc() return 0 - def play(self, filename, priority=None): + def play(self, filename, priority): ##print '_play:', filename import ossaudiodev, wave try: + self._busy = True #audiodev = self.audiodev audiodev = ossaudiodev.open('w') #audiodev.nonblock() @@ -404,15 +383,22 @@ class OSSAudioServer: audiodev.write(frames) #audiodev.close() #self.audiodev = ossaudiodev.open('w') + self.sound_priority = priority + self._busy = False return 1 except: if traceback: traceback.print_exc() + self._busy = False return 0 class OSSAudioClient(AbstractAudioClient): + CAN_PLAY_SOUND = True + CAN_PLAY_MUSIC = False + def __init__(self): + import ossaudiodev, wave AbstractAudioClient.__init__(self) def startServer(self): @@ -434,12 +420,213 @@ class OSSAudioClient(AbstractAudioClient): def _playSample(self, filename, priority, loop, volume): ##print '_playSample:', filename, loop - os.write(self.pout, '%s\0%s\0' % (filename, loop)) + os.write(self.pout, '%s\0%s\0%s\0' % (filename, priority, loop)) return 1 def _stopSamples(self): - os.write(self.pout, 'break\0\0') + os.write(self.pout, 'break\0\0\0') def _destroy(self): - os.write(self.pout, '\0\0') + os.write(self.pout, '\0\0\0') + +# /*********************************************************************** +# // PyMedia (http://pymedia.org/) +# ************************************************************************/ + +class PyMediaAudioClient(AbstractAudioClient): + + CAN_PLAY_SOUND = True + CAN_PLAY_MUSIC = True + + def __init__(self): + import pymedia, wave + AbstractAudioClient.__init__(self) + + def startServer(self): + try: + import pymedia, wave + self.server = 1 # success - see also tk/menubar.py + self.audiodev = pymedia + self.splayer = pymedia.Player() + self.splayer.start() + self.mplayer = pymedia.Player() + self.mplayer.start() + except: + if traceback: traceback.print_exc() + self.server = None + self.audiodev = None + + + def _playSample(self, filename, priority, loop, volume): + print '_playSample:', filename, loop + self.splayer.setLoops(loop) + self.splayer.startPlayback(filename) + return 1 + + def _stopSamples(self): + self.splayer.stopPlayback() + + + def _playMusicLoop(self, music_list): + while True: + if not self.play_music: + break + for m in music_list: + if not self.play_music: + break + if m.absname.endswith('.mp3'): + print m.absname, m.volume + self.mplayer.startPlayback(m.absname) + while self.mplayer and self.mplayer.isPlaying() and self.play_music: + time.sleep(0.1) + if self.mplayer: + self.mplayer.stopPlayback() + + + def playContinuousMusic(self, music_list): + print 'playContinuousMusic' + if self.audiodev is None or not self.app: + return + self.play_music = True + th = Thread(target=self._playMusicLoop, args=(music_list,)) + th.start() + + def updateSettings(self): + if self.audiodev is None or not self.app: + return + s, m = 0, 0 + if self.app.opt.sound: + s = self.app.opt.sound_sample_volume*512 + s = min(65535, s) + s = max(0, s) + m = self.app.opt.sound_music_volume*512 + m = min(65535, m) + m = max(0, m) + print 'updateSettings:', s, m + try: + pass + self.splayer.setVolume(s) + self.mplayer.setVolume(m) + except: + if traceback: traceback.print_exc() + + +# /*********************************************************************** +# // PyGame +# ************************************************************************/ + +class PyGameAudioClient(AbstractAudioClient): + + EXTENTIONS = r'\.((ogg)|(mp3)|(it)|(mod)|(s3m)|(xm)|(mid))$' + if os.name == 'nt': # without mp3 + EXTENTIONS = r'\.((ogg)|(it)|(mod)|(s3m)|(xm)|(mid))$' + + CAN_PLAY_SOUND = True + CAN_PLAY_MUSIC = True + + def __init__(self): + import pygame.mixer, pygame.time + AbstractAudioClient.__init__(self) + + def startServer(self): + try: + import pygame.mixer, pygame.time + self.server = 0 # success - see also tk/menubar.py + self.audiodev = pygame.mixer + self.mixer = pygame.mixer + self.mixer.init() + self.music = self.mixer.music + self.time = pygame.time + self.sound = None + self.sound_channel = None + self.sound_priority = -1 + except: + if traceback: traceback.print_exc() + self.server = None + self.audiodev = None + + def _playSample(self, filename, priority, loop, volume): + ##print '_playSample:', filename, priority, loop, volume + if self.sound_channel and self.sound_channel.get_busy(): + if self.sound_priority >= priority: + return 0 + else: + self.sound.stop() + vol = self.app.opt.sound_sample_volume/128.0 + try: + self.sound = self.mixer.Sound(filename) + self.sound.set_volume(vol) + self.sound_channel = self.sound.play(loop) + self.sound_priority = priority + except: + if traceback: traceback.print_exc() + pass + return 1 + + def _stopSamples(self): + if self.sound: + self.sound.stop() + self.sound = None + self.sound_channel = None + + def _playMusicLoop(self): + ##print '_playMusicLoop' + music_list = self.music_list + if not music_list: + return + while True: + if not self.music: + break + for m in music_list: + if not self.music: + break + vol = self.app.opt.sound_music_volume/128.0 + try: + self.music.load(m.absname) + self.music.set_volume(vol) + self.music.play() + while self.music and self.music.get_busy(): + self.time.wait(200) + if self.time: + self.time.wait(300) + except: + if traceback: traceback.print_exc() + self.time.wait(1000) + + def playContinuousMusic(self, music_list): + ##print 'playContinuousMusic' + self.music_list = music_list + #if self.audiodev is None or not self.app: + # return + if not music_list: + return + th = Thread(target=self._playMusicLoop) + th.start() + + def _destroy(self): + try: + self.mixer.stop() + self.mixer.quit() + self.music = None + except: + if traceback: traceback.print_exc() + pass + + def updateSettings(self): + if not self.app.opt.sound or self.app.opt.sound_music_volume == 0: + if self.music: + self.music.stop() + self.music = None + else: + if not self.music: + self.music = self.mixer.music + th = Thread(target=self._playMusicLoop) + th.start() + else: + vol = self.app.opt.sound_music_volume/128.0 + self.music.set_volume(vol) + + def playNextMusic(self): + if self.music: + self.music.stop() diff --git a/pysollib/pysolgtk/menubar.py b/pysollib/pysolgtk/menubar.py index 13739572..79b859d1 100644 --- a/pysollib/pysolgtk/menubar.py +++ b/pysollib/pysolgtk/menubar.py @@ -449,7 +449,7 @@ class PysolMenubar(PysolMenubarActions): menu = ui_manager.get_widget('/menubar/select').get_submenu() self._createSelectMenu(games, menu) - if self.app.audio.audiodev is None: + if not self.app.audio.CAN_PLAY_SOUND: item = ui_manager.get_widget('/menubar/options/sound') item.set_sensitive(False) diff --git a/pysollib/settings.py b/pysollib/settings.py index 9841f870..99f148a3 100644 --- a/pysollib/settings.py +++ b/pysollib/settings.py @@ -29,7 +29,7 @@ PACKAGE = 'PySol' PACKAGE_URL = 'http://sourceforge.net/projects/pysolfc/' VERSION = '4.82' -FC_VERSION = '0.9.3' +FC_VERSION = '0.9.4' VERSION_TUPLE = (4, 82) TOOLKIT = 'gtk' diff --git a/pysollib/tk/menubar.py b/pysollib/tk/menubar.py index d3e7d520..b7303f45 100644 --- a/pysollib/tk/menubar.py +++ b/pysollib/tk/menubar.py @@ -47,7 +47,6 @@ from pysollib.settings import PACKAGE from pysollib.settings import TOP_TITLE from pysollib.gamedb import GI from pysollib.actions import PysolMenubarActions -from pysollib.pysolaudio import pysolsoundserver # toolkit imports from tkconst import EVENT_HANDLED, EVENT_PROPAGATE, CURSOR_WATCH, COMPOUNDS @@ -463,7 +462,7 @@ class PysolMenubar(PysolMenubarActions): submenu.add_checkbutton(label=n_("Show hint &arrow (in Shisen-Sho games)"), variable=self.tkopt.shisen_show_hint, command=self.mOptShisenShowHint) menu.add_separator() label = n_("&Sound...") - if self.app.audio.audiodev is None: + if not self.app.audio.CAN_PLAY_SOUND: menu.add_checkbutton(label=label, variable=self.tkopt.sound, command=self.mOptSoundDialog, state=Tkinter.DISABLED) else: menu.add_checkbutton(label=label, variable=self.tkopt.sound, command=self.mOptSoundDialog) @@ -1113,12 +1112,6 @@ class PysolMenubar(PysolMenubarActions): self.app.opt.shisen_show_hint = self.tkopt.shisen_show_hint.get() ##self.game.updateMenus() -## def mOptSound(self, *args): -## if self._cancelDrag(break_pause=False): return -## self.app.opt.sound = self.tkopt.sound.get() -## if not self.app.opt.sound: -## self.app.audio.stopAll() - def mSelectCardsetDialog(self, *event): if self._cancelDrag(break_pause=False): return ##strings, default = ("&OK", "&Load", "&Cancel"), 0 diff --git a/pysollib/tk/soundoptionsdialog.py b/pysollib/tk/soundoptionsdialog.py index bc3d2f67..ea2ea49a 100644 --- a/pysollib/tk/soundoptionsdialog.py +++ b/pysollib/tk/soundoptionsdialog.py @@ -118,7 +118,7 @@ class SoundOptionsDialog(MfxDialog): command=self.mOptSoundDirectX, anchor='w') w.grid(row=row, column=0, columnspan=2, sticky='ew') # - if pysolsoundserver and app.startup_opt.sound_mode > 0: + if app.audio.CAN_PLAY_MUSIC: # and app.startup_opt.sound_mode > 0: row += 1 w = Tkinter.Label(frame, text=_('Sample volume:')) w.grid(row=row, column=0, sticky='w')