.mmlãã¡ã¤ã«ãç£è¦ãã¦ä¸æ¸ãä¿åæã«Wineä¸ã®ppmckã§.mmlãã¡ã¤ã«ãã.nsfãã¡ã¤ã«ã¸èªåã§å¤æããããã®GUIãã¼ã«ã使(2009/12/17ç)
(2010/2/17)ãGNU/Linux上における ppmckについてのその後(2010/2/2現在)ãã«ã¦GNU/Linuxåãã«ãã«ãããppmckã使ããããã«ãªã£ã¦ãããã¨ãåããã夿´ç£è¦/èªå夿ãã¼ã«ãããã«åããã¦ãMMLファイルの変更を監視して上書き保存時にppmck(ppmckc/nesasm)でNSF変換を自動で行うツールを更新(2010/2/17版)ãã«ã¦æ°ãããã¼ã¸ã§ã³ãå
¬éããããããã®è¨äºã®ã¹ã¯ãªããã¯å¤ããã®ã¨ãªã£ã¦ããã
以ä¸ã以åã®å
容ã¨ãªãã
ãPythonでファイルの変更を監視する(OS非依存・PyGObject使用)ãã®ãã¡ã¤ã«å¤æ´ç£è¦ã®ä»çµã¿ãç¨ãã¦ã.mmlãã¡ã¤ã«ã®æ´æ°(䏿¸ãä¿å)æã«ãWine上のppmckで.mmlファイルを.nsfファイルに変換する処理を自動化するPythonスクリプト(2009/12/7版)ãã§ä½æããã¹ã¯ãªãããå®è¡ããæ´ã«ãåºåããã.nsfãã¡ã¤ã«ãèªåçã«ãã¬ã¼ã¤ã§åçãããããªã¹ã¯ãªããã使ãããã¾ã ä½ãã®éãªé¨åãæ®ã£ã¦ããæ°ãããããã¨ãããããã®ç¶æ
ã§è²¼ãä»ããã
ãã®ã¹ã¯ãªããããWine上のppmckで.mmlファイルを.nsfファイルに変換する処理を自動化するPythonスクリプト(2009/12/7版)ãã®ã¹ã¯ãªããã¨åããã£ã¬ã¯ããªã«é
ç½®ãã¦å®è¡ãã対象ã®.mmlãã¡ã¤ã«ãæå®ãã¦ãã¿ã³ãæ¼ã(GUIãã¡ã¤ã«ããã¼ã¸ã£ããã®ãã©ãã°ã»ã¢ã³ãã»ããããã§ãå¯)ã¨ç£è¦ãå§ã¾ãããã®ãã¡ã¤ã«ãæ´æ°ãããã¨èªåã§åºåãã¡ã¤ã«ã®å¤æã¨åçãè¡ãã
åçã«é¢ãã¦ã¯ã2009å¹´12æç¾å¨ãGNU/Linuxåãã®.nsfå½¢å¼å¯¾å¿ã®ãªã¼ãã£ãªãã¬ã¼ã¤ã§ppmckã対å¿ããå
¨ã¦ã®(nesã®)æ¡å¼µé³æºãæ±ãããã®ã¯ãªãããã(æ«å®ã§)Mednafenã¨ããã½ããã¦ã§ã¢ã®.nsfãã¡ã¤ã«åçæ©è½ãç¨ããããã«ãã¦ããããããç¡ããã°åçã¯è¡ãããªã(å¥éã¤ã³ã¹ãã¼ã«ããå¿
è¦ããã)ã
(2009/12/21)GNU/Linuxã§åä½ããnsfãã¬ã¼ã¤ã§æ¡å¼µé³æºå¯¾å¿ãååãªãã®ããã®å¾èª¿ã¹ã¦ã¿ãã¨ãããFestalon(http://projects.raphnet.net/#festalon)ãè¦ã¤ããx86_64åãããããå½ã¦ã¦consoleçããã«ããã*1ããVRC7鳿ºã®ãã¼ã¿ã§ãã¤ãºãåºããWineä¸ã®foobar2000ã§foo_input_nsfãç¨ããã¨åä½ã¯è¯å¥½ã
[ä»»æ]ãã¡ã¤ã«å: mmlwatchergtk.py ã©ã¤ã»ã³ã¹: GPL-3 (or lator)
#! /usr/bin/python # -*- encoding: utf-8 -*- # mmlwatchergtk.py for ppmck 20091217 (C) 2009 kakurasan # compile MML file automatically when updated # Licensed under GPLv3+ import urllib import ctypes import time import sys import os try: import pygtk pygtk.require('2.0') except: pass try: import gtk except: print >> sys.stderr, 'Error: PyGTK is not installed' sys.exit(1) try: from glib import timeout_add_seconds as glib_timeout_add_seconds from glib import source_remove as glib_source_remove from glib import spawn_async as glib_spawn_async from glib import child_watch_add as glib_child_watch_add from glib import SPAWN_DO_NOT_REAP_CHILD as glib_SPAWN_DO_NOT_REAP_CHILD except: try: from gobject import timeout_add_seconds as glib_timeout_add_seconds from gobject import source_remove as glib_source_remove from gobject import spawn_async as glib_spawn_async from gobject import child_watch_add as glib_child_watch_add from gobject import SPAWN_DO_NOT_REAP_CHILD as glib_SPAWN_DO_NOT_REAP_CHILD except: print >> sys.stderr, 'Error: cannot import GLib functions' sys.exit(1) class MainWindow(gtk.Window): """ application main window """ def __init__(self, path, *args, **kwargs): gtk.Window.__init__(self, *args, **kwargs) # accelgroup self.__accelgroup = gtk.AccelGroup() self.add_accel_group(self.__accelgroup) # menuitems self.__item_quit = gtk.ImageMenuItem(gtk.STOCK_QUIT, self.__accelgroup) self.__menu_file = gtk.Menu() self.__menu_file.add(self.__item_quit) self.__item_file = gtk.MenuItem('_File') self.__item_file.set_submenu(self.__menu_file) self.__menubar = gtk.MenuBar() self.__menubar.append(self.__item_file) # entry for file path self.__entry = gtk.Entry() # browse self.__btn_browse = gtk.Button('...') # start/stop watching self.__btn_watch = gtk.ToggleButton('Watch') self.__btn_watch.set_sensitive(False) # message self.__textview = gtk.TextView() self.__textview.set_sensitive(False) self.__textbuf = self.__textview.get_buffer() self.__sw = gtk.ScrolledWindow() self.__sw.add(self.__textview) self.__sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) # containers self.__hbox = gtk.HBox() self.__hbox.pack_start(self.__entry) self.__hbox.pack_start(self.__btn_browse, expand=False, fill=False) self.__hbox.pack_start(self.__btn_watch, expand=False, fill=False) self.__vbox = gtk.VBox() self.__vbox.pack_start(self.__menubar, expand=False, fill=False) self.__vbox.pack_start(self.__hbox, expand=False, fill=False) self.__vbox.pack_start(self.__sw) # signals self.connect('delete_event', gtk.main_quit) self.__item_quit.connect('activate', gtk.main_quit) self.__entry.connect('changed', self.__on_entry_changed) self.__btn_browse.connect('clicked', self.__on_button_browse_clicked) self.__id_btnsig = self.__btn_watch.connect('toggled', self.__on_button_watch_toggled) self.connect('drag_data_received', self.__on_drag_data_received) self.__id_watch = None # window self.set_title('MMLWatcherGTK') self.add(self.__vbox) self.set_size_request(400, 300) # dnd self.__TARGET_TYPE_TEXT_URI_LIST = 12345 self.__dnd_list = [('text/uri-list', 0, self.__TARGET_TYPE_TEXT_URI_LIST),] self.__drag_set_acceptable(True) # path from argv if path: self.__entry.set_text(path) if os.access(path, os.R_OK): self.__btn_watch.set_active(True) # GLib self.glib = ctypes.cdll.LoadLibrary('libglib-2.0.so.0') def __on_entry_changed(self, widget): """ file path changed """ self.__btn_watch.set_sensitive(os.access(widget.get_text(), os.R_OK)) def __on_button_browse_clicked(self, widget): """ button clicked """ opendlg = gtk.FileChooserDialog(title='Select file', parent=self, buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_ACCEPT)) opendlg.set_local_only(True) if opendlg.run() == gtk.RESPONSE_ACCEPT: self.__entry.set_text(opendlg.get_filename()) opendlg.destroy() def __on_button_watch_toggled(self, widget): """ toggled 'watch' button """ if widget.get_active(): # start watching try: self.__mtime = os.stat(self.__entry.get_text()).st_mtime except OSError, (errno, msg): self.__append_log_and_scroll('cannot stat "%s"\n errno: %d\n message: %s\n' % (self.__entry.get_text(), errno, msg)) # block handler and set 'inactive' self.__btn_watch.handler_block(self.__id_btnsig) self.__btn_watch.set_active(False) self.__btn_watch.handler_unblock(self.__id_btnsig) return self.__entry.set_sensitive(False) self.__btn_browse.set_sensitive(False) self.__id_watch = glib_timeout_add_seconds(1, self.__to_watch_file) self.__append_log_and_scroll('start watching "%s" (%s)\n' % (self.__entry.get_text(), time.ctime(None))) else: # stop watching glib_source_remove(self.__id_watch) self.__id_watch = None self.__append_log_and_scroll('stop watching "%s"\n' % self.__entry.get_text()) self.__entry.set_sensitive(True) self.__btn_browse.set_sensitive(True) def __append_log_and_scroll(self, text): """ append log to TextBuffer and scroll TextView """ self.__textbuf.place_cursor(self.__textbuf.get_end_iter()) self.__textbuf.insert_at_cursor(text) self.__textview.scroll_to_mark(self.__textbuf.get_insert(), 0) def __to_watch_file(self): """ watch timestamp / autocompile / launch player """ try: new_mtime = os.stat(self.__entry.get_text()).st_mtime except OSError, (errno, msg): self.__append_log_and_scroll('cannot stat "%s"\n errno: %d\n message: %s\n' % (self.__entry.get_text(), errno, msg)) self.__btn_watch.set_active(False) return False # check timestamp if self.__mtime != new_mtime: self.__mtime = new_mtime # store new timestamp # file is updated self.__append_log_and_scroll('file "%s" updated (%s)\n' % (self.__entry.get_text(), time.ctime(self.__mtime))) self.__drag_set_acceptable(False) # autocompile script pid_autocompile = glib_spawn_async([os.path.join(os.path.dirname(__file__), 'mml2nsf.py'), self.__entry.get_text()], flags=glib_SPAWN_DO_NOT_REAP_CHILD)[0] self.__id_childwatch = glib_child_watch_add(pid_autocompile, self.__cb_close_child) return True; def __cb_close_child(self, pid, status): """ child(autocompile script) exited """ self.__append_log_and_scroll('compiled "%s"\n' % self.__entry.get_text()) self.__drag_set_acceptable(True) # remove event source for childwatch glib_source_remove(self.__id_childwatch) # close pid self.glib.g_spawn_close_pid(pid) # launch nsf player glib_spawn_async(['/usr/bin/mednafen', os.path.join(os.path.abspath(os.path.dirname(__file__)), 'out', '%s.nsf' % os.path.splitext(os.path.basename(self.__entry.get_text()))[0])]) def __drag_set_acceptable(self, acceptable): """ set acceptable status """ if acceptable == True: self.drag_dest_set(gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP, self.__dnd_list, gtk.gdk.ACTION_COPY) else: self.drag_dest_unset() def __on_drag_data_received(self, widget, context, x, y, selection, info, time): """ received drag data """ item = selection.data.rstrip('\x00').splitlines()[-1] # use only last one if item.startswith('file:'): path = os.path.normpath(urllib.url2pathname(item[5:])) # 5=len('file:') if self.__id_watch: # now watching self.__btn_watch.set_active(False) # stop watching self.__entry.set_text(path) # update entry self.__btn_watch.set_active(True) # start watching class MMLWatcherGTK: """ MML watcher for ppmck """ def main(self): """ main """ path = None if len(sys.argv) > 1: path = sys.argv[1] win = MainWindow(path) win.show_all() gtk.main() if __name__ == '__main__': app = MMLWatcherGTK() app.main()
Mednafenã«é¢ããã¡ã¢
å·»ãæ»ã
.nsfãã¡ã¤ã«ã¯F10ã§æåããåçãç´ãããåºåãµã¦ã³ãã·ã¹ãã ã¨åºåããã¤ã¹ã®æå®
ALSAãµã¦ã³ãã·ã¹ãã ã®æ¢å®ã®PCMããã¤ã¹(ãã©ã°ã¤ã³)ã«åºåããå ´åãä¸ã®ãããªè¨å®ãè¡ãã[ä¸é¨]ãã¡ã¤ã«å: ~/.mednafen/mednafen.cfg
;Select sound driver. sounddriver alsa ;Select sound output device. sounddevice sexyal-literal-default
ãsounddeviceãã«ã¯ALSAã®PCMããã¤ã¹(ãã©ã°ã¤ã³)åãæå®ã§ããå¥éãtype pulseããªPCMãå®ç¾©(関連記事)ãããããæå®ãããã¨ã§ãPulseAudioã¸åºåãããã¨ãã§ããã
JACK Audio Connection Kitã¸åºåããã«ã¯ä¸ã®ããã«è¨å®ããã
[ä¸é¨]ãã¡ã¤ã«å: ~/.mednafen/mednafen.cfg
;Select sound driver. sounddriver jack ;Select sound output device. sounddevice default
使ç¨ãããã¼ã¸ã§ã³:
- Wine 1.1.34
- ppmck 09
- Mednafen 0.8.C (0.8.12)
*1:./configure --enable-interface=console; make