changeset 325:cddfd3cb4797

Add music support for >PCB.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 21 Jun 2012 15:01:01 +0200
parents c412df42aa15
children efcdf2ce747c
files pytouhou/formats/fmt.py pytouhou/resource/loader.py pytouhou/ui/music.py
diffstat 3 files changed, 105 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/pytouhou/formats/fmt.py
@@ -0,0 +1,69 @@
+# -*- encoding: utf-8 -*-
+##
+## Copyright (C) 2012 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published
+## by the Free Software Foundation; version 3 only.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+
+
+from struct import unpack
+
+
+class Track(object):
+    def __init__(self):
+        self.name = ''
+
+        # loop info
+        self.intro = 0
+        #self.unknown
+        self.start = 0
+        self.duration = 0
+
+        # WAVE header
+        self.wFormatTag = 1
+        self.wChannels = 2
+        self.dwSamplesPerSec = 44100
+        self.dwAvgBytesPerSec = 176400
+        self.wBlockAlign = 4
+        self.wBitsPerSample = 16
+
+
+class FMT(list):
+    @classmethod
+    def read(cls, file):
+        self = cls()
+
+        file.seek(0)
+        while True:
+            track = Track()
+            track.name = unpack('<16s', file.read(16))[0]
+            if not ord(track.name[0]):
+                break
+
+            # loop info
+            track.intro, unknown, track.start, track.duration = unpack('<IIII', file.read(16))
+
+            # WAVE header
+            (track.wFormatTag,
+             track.wChannels,
+             track.dwSamplesPerSec,
+             track.dwAvgBytesPerSec,
+             track.wBlockAlign,
+             track.wBitsPerSample) = unpack('<HHLLHH', file.read(16))
+
+            assert track.wFormatTag == 1 # We don’t support non-PCM formats
+            assert track.dwAvgBytesPerSec == track.dwSamplesPerSec * track.wBlockAlign
+            assert track.wBlockAlign == track.wChannels * track.wBitsPerSample // 8
+            assert b'\00\00\00\00' == file.read(4)
+
+            self.append(track)
+
+        return self
+
--- a/pytouhou/resource/loader.py
+++ b/pytouhou/resource/loader.py
@@ -25,6 +25,7 @@ from pytouhou.formats.msg import MSG
 from pytouhou.formats.sht import SHT
 from pytouhou.formats.exe import SHT as EoSDSHT, InvalidExeException
 from pytouhou.formats.music import Track
+from pytouhou.formats.fmt import FMT
 
 
 from pytouhou.resource.anmwrapper import AnmWrapper
@@ -108,6 +109,7 @@ class Loader(object):
         self.instanced_msgs = {}
         self.instanced_shts = {}
         self.instanced_tracks = {}
+        self.instanced_fmts = {}
 
 
     def scan_archives(self, paths_lists):
@@ -194,6 +196,13 @@ class Loader(object):
         return self.instanced_tracks[name]
 
 
+    def get_fmt(self, name):
+        if name not in self.instanced_fmts:
+            file = self.get_file(name)
+            self.instanced_fmts[name] = FMT.read(file) #TODO: modular
+        return self.instanced_fmts[name]
+
+
     def get_anm_wrapper(self, names, offsets=None):
         """Create an AnmWrapper for ANM files “names”.
 
--- a/pytouhou/ui/music.py
+++ b/pytouhou/ui/music.py
@@ -13,7 +13,7 @@
 ##
 
 
-from pyglet.media import AudioData
+from pyglet.media import AudioData, AudioFormat
 from pyglet.media.riff import WaveSource
 
 
@@ -51,3 +51,29 @@ class InfiniteWaveSource(WaveSource):
         duration = float(bytes) / self.audio_format.bytes_per_second
 
         return AudioData(data, bytes, timestamp, duration)
+
+
+    def seek(self, timestamp):
+        raise NotImplementedError('irrelevant')
+
+
+class ZwavSource(InfiniteWaveSource):
+    def __init__(self, filename, format, file=None):
+        if file is None:
+            file = open(filename, 'rb')
+
+        self._file = file
+
+        assert b'ZWAV' == self._file.read(4)
+
+        self.audio_format = AudioFormat(
+            channels=format.wChannels,
+            sample_size=format.wBitsPerSample,
+            sample_rate=format.dwSamplesPerSec)
+
+        self._start_offset = 0
+        self._offset = format.intro
+
+        self._file.seek(self._offset)
+        self._start = format.intro + format.start
+        self._end = format.intro + format.duration