2020年07月31日

GearHead SDL 版を Linux Mint 18.3 32bit でコンパイルしました

5 年ぶり二度目. あのとき, 手順をメモしておかなかったおかげで, 5 年後の自分が苦労している. もう苦労したくないので, 今度は書いておきます.

参考ページ http://www.fenix.ne.jp/~G-HAL/scribble/gearhead/gearhead1.scribble#build

1. libiconv をインストール
https://www.kkaneko.jp/tools/ubuntu/gcc10.html の "libiconv のインストール" に書いてある通りにします.
以下はコピー.
$ cd /tmp
$ wget https://ftp.gnu.org/gnu/libiconv/libiconv-1.16.tar.gz
$ rm -rf libiconv-1.16
$ tar -xvzof libiconv-1.16.tar.gz
$ cd libiconv-1.16
$ mkdir build
$ cd build
$ ../configure --with-mpfr --with-gmp --prefix=/usr/local --enable-static --enable-shared
$ make -j2
$ sudo make install

2. /usr/local/lib/ 以下のライブラリが使えるようにする
http://weble.org/2011/12/20/ubuntu-ld-so-conf
を参考に作業します. 以下はほぼコピー.
私は vim を使いましたが, 他のエディタでも構いません.
$ sudo vim /etc/ld.so.conf
次の一行を追加.
include /usr/local/lib
設定を反映させる.
$ sudo ldconfig

3. SDL-ttf をインストール
$ sudo apt install libsdl-ttf2.0-dev

4. smpeg をインストール
$ sudo apt install libsmpeg-dev
他にもインストールする必要があるライブラリがあるはずですが,
私の環境ではすでにインストールされていたようです.

5. Free Pascal をインストール.
$ sudo apt install fpc-3.0.0

6. コンパイル
$ cd ~
$ wget https://sourceforge.net/projects/gearhead/files/gearhead/1.100/gh-1100-src.zip/download -O gh-1100-src.zip
$ wget http://www.fenix.ne.jp/~G-HAL/scribble/gearhead/GearHead1-I18N.data.jp.zip
$ wget http://www.fenix.ne.jp/~G-HAL/scribble/gearhead/GearHead1-I18N.image.zip
$ wget http://www.fenix.ne.jp/~G-HAL/scribble/gearhead/GearHead1-I18N.sound.zip
$ wget http://www.fenix.ne.jp/~G-HAL/scribble/gearhead/GearHead1-I18N.src.zip
$ unzip gh-1100-src.zip
$ mv GearHead GH1
$ unzip -d GH1 GearHead1-I18N.src.zip
$ unzip -o GearHead1-I18N.image.zip
$ unzip -o GearHead1-I18N.sound.zip
$ unzip -o GearHead1-I18N.data.jp.zip
$ cd GH1/build/xterm-boxdrawing
$ mv * ../../xterm-boxdrawing
$ cd ..
$ rm -r xterm-boxdrawing
$ mv * ../
$ rm -r build
$ /bin/sh ./build.sh sdl clean build nolibiconv_plug nolibc_iconv
$ mv build.sdl.Linux_athlon/gharena gharena
$ rm -r build.sdl.Linux_athlon

7. 環境変数を設定する
$HOME/.profile に環境変数 GEARHEAD_LANG を追加します.
下の一行は vim を使う場合.
$ vim ~/.profile
以下三行を追加します.

if [ -d "$HOME/GH1" ] ; then
    export GEARHEAD_LANG=ja_JP.UTF-8
fi

GEARHEAD_LANG の値については,
環境によって適切なものを選ぶ必要があるはずです.
以下のコマンドで設定を反映.
$ source ~/.profile

8. gharena.cfg
$ ./gharena
起動すると, ほとんどの文字が四角になっています.
一番下の項目で Enter を押して終了します.
起動したことで $HOME/.config/gharena/ に
gharena.cfg が作製されます.
GH1/gharena.cfg.jp_debianlinux を参考にフォントを変更します.
もう一度
$ ./gharena
と入力し起動すると, 文字化けは解消しているはずです.
linuxgh1title.png

これで完成です.

9. 次回以降の起動方法

$ cd ~
$ cd GH1
$ ./gharena
で起動できます.

posted by Takenoko at 20:59| Comment(0) | game | このブログの読者になる | 更新情報をチェックする

2020年07月26日

Python 3 で, Ogg Vorbis と Wave を再生する Player を書きました 続き

pysoundsample.png

前回 の続きです. 上の画像は, sample の gui です.

Ogg Vorbis ファイルと, Wave ファイルを複数同時に再生できるプレイヤーです. MIT License です. 使用方法は, zip 内の readme.txt と, sample.py に書いてあります. 下にあるコードも, zip 内に全て入っています.

マルチスレッドによるエラーを対策したり, wave の再生終了まで待機できるようにしたりしました. 他の変更点はコード冒頭 update.txt に書いてあります.


'''
thread2.py  v1.1

参考ページ: Python スレッドの停止と再開の簡易サンプル
https://qiita.com/BlueSilverCat/items/44a0a2a3c45fc3e88b19
'''

from threading import Thread, Event

class Thread2(Thread):

    def __init__(self, daemon = True, callback = 0):
        super().__init__(daemon = daemon)
        self.started = Event()
        self.alive = 1
        self.active = 1
        self.callback = callback
        self.start()

    def __del__(self):
        self.kill()

    def begin(self):
        self.started.set()
        self.active = 1

    def end(self):
        self.active = 0
        self.started.clear()
        if callable(self.callback):
            self.callback()

    def kill(self):
        self.started.set()
        self.active = 0
        self.alive = 0
        self.join()




'''
soundthread.py  v1.2
'''

from threading import Lock

from pyaudio import PyAudio

from .thread2 import Thread2

class SoundThread(Thread2):

    __lock = Lock()

    def __init__(self, callback):
        super().__init__(callback = callback)

    def close(self):
        self.stream.stop_stream()
        self.stream.close()
        self.p.terminate()

    def kill(self):
        self.close()
        self.end()
        super().kill()

    def open(self, format, channels, rate):
        SoundThread.__lock.acquire()
        self.p = PyAudio()  # 使い回すとエラーが出ることがある
        self.stream = self.p.open(format = format,
                                  channels = channels,
                                  rate = rate,
                                  output = True)
        SoundThread.__lock.release()

    def run(self):
        while self.active:
            self.play()  # Sub Class で実装すること
            self.started.wait()




'''
player.py  v1.0
'''

class Player:

    def __init__(self, enabled):
        self.__enabled = enabled  # False で関数呼び出しを無視する

    def is_enabled(self):
        return self.__enabled

    def on(self):
        if self.__enabled:
            return
        self.__enabled = 1

    def off(self):
        if not self.__enabled:
            return
        self.__enabled = 0




'''
vorbisplayer.py  v1.3

Copyright (c) 2020 Takenoko (http://nekokiss.seesaa.net/)
Released under the MIT license.
see https://opensource.org/licenses/MIT

PyAudio, PyOgg, audio-metadata が install されている必要があります.
'''

import time
from threading import Event

import pyogg as po
import pyaudio as pa

from .soundthread import SoundThread
from .player import Player

class Vorbis:

    def __init__(self, ogg, chunk, loop, tagloop):
        self.ogg = ogg
        self.loop = loop
        self.file = po.VorbisFile(ogg)
        '''
        bit rate size
        1, 3, 4 等の場合もあるかもしれないが,
        取得方法がわからないので 2 で固定
        '''
        self.sampwidth = 2
        # block size
        self.block = (self.sampwidth
                      * self.file.channels)
        self.format = pa.get_format_from_width(self.sampwidth)
        self.cache = {}
        '''
        Loop Tag
        '''
        self.loopstart = 0
        self.loopend = (self.file.buffer_length
                        * self.block)
        if loop and tagloop:
            import audio_metadata as am
            md = am.load(ogg)
            if 'tags' in md and 'loopstart' in md.tags:
                self.loopstart = int(md.tags.loopstart[0]) * self.block
                if 'looplength' in md.tags:
                    self.loopend = (int(md.tags.looplength[0])
                                    * self.block + self.loopstart)
        self.chunk = chunk
        if self.loopend - self.loopstart < self.chunk:
            self.chunk = self.loopend - self.loopstart

    def getdata(self, start):
        if start not in self.cache:
            end = start + self.chunk
            if end > self.loopend:
                data = self.file.buffer[start:self.loopend]
                if self.loop:
                    end = self.loopstart + end - self.loopend
                    data += self.file.buffer[self.loopstart:end]
                else:
                    end = -1
                self.cache[start] = data, end
            else:
                self.cache[start] = self.file.buffer[start:end], end
        return self.cache[start]


class VorbisThread(SoundThread):

    def __init__(self, vorbis, callback):
        self.vorbis = vorbis
        self.paused = Event()
        super().__init__(callback)

    def rebegin(self):
        self.paused.set()
        if self.active:
            self.end()
            # sleep しないと曲の先頭に戻らないことがある
            time.sleep(0.1)
        self.begin()

    def play(self):
        self.paused.set()
        self.open(self.vorbis.format, self.vorbis.file.channels,
                  self.vorbis.file.frequency)
        start = 0
        while self.active:
            data, start = self.vorbis.getdata(start) 
            self.stream.write(data)
            self.paused.wait()
            if start < 0:
                self.end()
        self.close()


class VorbisPlayer(Player):

    def __init__(self, chunk = 1024, loop = 1, tagloop = 1,
                 enabled = 1):
        super().__init__(enabled)
        self.__chunk = chunk
        self.__vorbises = {}
        self.__thread = 0
        self.__loop = loop        # True で loop する
        self.__tagloop = tagloop  # True で LOOPSTART & LOOPLENGTH を使う

    def off(self):
        super().off()
        self.stop()

    def play(self, ogg, callback = 0):
        if not self.is_enabled():
            return
        if ogg in self.__vorbises:
            v = self.__vorbises[ogg]
        else:
            v = Vorbis(ogg, self.__chunk, self.__loop, self.__tagloop)
            self.__vorbises[ogg] = v
        if self.__thread:
            self.__thread.vorbis = v
            self.__thread.callback = 0
            self.__thread.rebegin()
            self.__thread.callback = callback
        else:
            self.__thread = VorbisThread(v, callback)

    def stop(self):
        if self.__thread and self.__thread.active:
            self.__thread.end()

    def pause(self):
        if not self.is_enabled():
            return
        if self.__thread:
            self.__thread.paused.clear()

    def resume(self):
        if not self.is_enabled():
            return
        if self.__thread:
            self.__thread.paused.set()




'''
waveplayer.py  v1.4

Copyright (c) 2020 Takenoko (http://nekokiss.seesaa.net/)
Released under the MIT license.
see https://opensource.org/licenses/MIT

PyAudio が install されている必要があります.
'''

from threading import Event

import pyaudio as pa
import wave

from .soundthread import SoundThread
from .player import Player

class Wave:

    def __init__(self, wav, chunk):
        self.file = wav
        with wave.open(wav, 'rb') as wf:
            self.format = pa.get_format_from_width(wf.getsampwidth())
            self.channels = wf.getnchannels()
            self.rate = wf.getframerate()
            self.buffer = []
            while 1:
                data = wf.readframes(chunk)
                if len(data) <= 0:
                    break
                self.buffer.append(data)


class WavThread(SoundThread):

    def __init__(self, wp, wav, callback):
        self.wp = wp
        self.wav = wav
        super().__init__(callback)

    def close(self):
        super().close()
        self.wp._WavePlayer__blocked.set()

    def play(self):
        self.open(self.wav.format, self.wav.channels,
                  self.wav.rate)
        for data in self.wav.buffer:
            if self.active:
                self.stream.write(data)
        if self.active:
            self.end()
        self.close()


class WavePlayer(Player):

    def __init__(self, chunk = 1024, maxthreads = 8, enabled = 1):
        super().__init__(enabled)
        self.__chunk = chunk
        self.__waves = {}
        self.__threads = []
        self.__blocked = Event()
        if maxthreads < 1:
            maxthreads = 1
        self.__maxthreads = maxthreads  # 最大同時再生 Thread 数

    def __getwav(self, wav):
        if wav not in self.__waves:
            self.__waves[wav] = Wave(wav, self.__chunk)
        return self.__waves[wav]

    def off(self):
        super().off()
        self.stopall()

    def getmaxthreads(self):
        return self.__maxthreads

    def setmaxthreads(self, v):
        if v < 1:
            return
        over = min(self.__maxthreads, len(self.__threads)) - v
        if over > 0:
            for i in range(over):
                t = self.__threads.pop(0)
                if t.active:
                    t.end()
                self.__threads.append(t)
        self.__maxthreads = v

    def play(self, wav, block = 0, callback = 0):
        if not self.is_enabled():
            return
        if block:
            self.__blocked.clear()
        for i, t in enumerate(self.__threads):
            if i < self.__maxthreads:
                if not t.active:
                    t.wav = self.__getwav(wav)
                    t.callback = callback
                    t.begin()
                    break
        else:
            if len(self.__threads) >= self.__maxthreads:
                t = self.__threads.pop(0)
                t.end()
                t.wav = self.__getwav(wav)
                t.callback = callback
                t.begin()
            else:
                t = WavThread(self, self.__getwav(wav), callback)
            self.__threads.append(t)
        if block:
            self.__blocked.wait()

    def stop(wav):
        for t in self.__threads:
            if t.wav.file == wav and t.active:
                t.end()
                return 1
        return 0

    def stopall(self):
        for t in self.__threads:
            if t.active:
                t.end()



vwp200727.zip

スレッドについて, 少しだけわかった気がします.

20/07/27 追記

ファイルを差し替えました. 更新履歴を, ソースコードではなく update.txt にまとめただけですが. あと, この記事にコードを貼り付けました. コードに問題があった場合, 指摘して頂けるとありがたいです.

20/07/28 追記

説明文を追加.

ラベル:Python download
posted by Takenoko at 09:42| Comment(0) | programming | このブログの読者になる | 更新情報をチェックする

2020年07月09日

Python 3 で, Ogg Vorbis と Wave を再生する Player を書きました

タイトルの通りです. ゲーム制作で必要だったので書きました. ゲームでよく使われるのは .ogg (Ogg Vorbis) と .wav だと思うので, それらを再生できます. ogg は, LOOPSTART, LOOPLENGTH に対応しています.

PyAudio, PyOgg, audio-metadata を使用しているので, それらの install が必要です.

vorbisplayer.py で import している audio-metadata が, Python 3.6 以降を要求するので, それ以降の Python で動きます. waveplayer.py だけを使うのであれば, それ以下でも動くはずです.

ソースファイル (vwp1.1.zip) のダウンロード

zip 内の readme.txt, sample.py を読むと, 使い方がわかると思います.

Linux Mint 18.3 & Python 3.6.11, Windows 8.1 & Python 3.6.8 で動作しました. Mac は持っていないのでわかりません.

何か間違い等があった場合, 指摘していただけるとありがたいです.

20/07/12 追記

ファイルを Version 1.1 に差し替えました.
vorbisplayer に, pause と resume を実装する等の変更を行いました.

20/07/26 追記

続き を書きました. ファイルも次の記事にあります.

ラベル:Python download
posted by Takenoko at 20:52| Comment(0) | programming | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は180日以上新しい記事の投稿がないブログに表示されております。