Skip to content

Commit 8099ba6

Browse files
committed
AudioClip - pyfmodex fix
1 parent 2b749f9 commit 8099ba6

2 files changed

Lines changed: 208 additions & 32 deletions

File tree

UnityPy/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.5.3.2"
1+
__version__ = "1.5.3.3"
22

33
from .environment import Environment
44

UnityPy/export/AudioClipConverter.py

Lines changed: 207 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,19 @@
1-
import ctypes
1+
from ctypes import *
2+
from enum import Enum
23
import os
34
import platform
45
from UnityPy.streams import EndianBinaryWriter, EndianBinaryReader
5-
# pyfmodex init
6-
try:
7-
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
8-
LIB_PATH = os.path.join(ROOT, "lib", "FMOD", platform.system(), platform.architecture()[0])
9-
10-
if platform.system() == 'Windows':
11-
_dll = ctypes.WinDLL(os.path.join(LIB_PATH, "fmod.dll"))
12-
elif platform.system() == "Linux":
13-
_dll = ctypes.CDLL(os.path.join(LIB_PATH, "libfmod.so"))
14-
elif platform.system() == "Darwin":
15-
_dll = ctypes.CDLL(os.path.join(LIB_PATH, "libfmod.dylib"))
16-
17-
import pyfmodex
18-
pyfmodex.fmodex._dll = _dll
19-
from pyfmodex.structures import CREATESOUNDEXINFO
20-
from pyfmodex.flags import MODE, TIMEUNIT, INIT_FLAGS
21-
except Exception as e:
22-
print("Error during the pyfmodex initialisation - AudioClips.samples won't work", e)
23-
6+
# dll init
7+
ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
8+
LIB_PATH = os.path.join(ROOT, "lib", "FMOD",platform.system(), platform.architecture()[0])
9+
if platform.system() == 'Windows':
10+
_dll = WinDLL(os.path.join(LIB_PATH, "fmod.dll"))
11+
elif platform.system() == "Linux":
12+
_dll = CDLL(os.path.join(LIB_PATH, "libfmod.so"))
13+
elif platform.system() == "Darwin":
14+
_dll = CDLL(os.path.join(LIB_PATH, "libfmod.dylib"))
15+
else:
16+
print("UnityPy/export/AudioClipConverter - Warning: unknown system\nIf you want to export AudioClips, you have to set UnityPy.export.AudioClipConverter._dll yourself.")
2417

2518
def extract_audioclip_samples(audio) -> dict:
2619
"""extracts all the samples from an AudioClip
@@ -44,16 +37,19 @@ def extract_audioclip_samples(audio) -> dict:
4437

4538
def dump_samples(clip):
4639
# init system
47-
system = pyfmodex.System()
48-
system.init(1, INIT_FLAGS.NORMAL, None)
49-
exinfo = CREATESOUNDEXINFO(length=clip.m_Size,)
40+
#system = pyfmodex.System()
41+
sys_ptr = c_void_p()
42+
ckresult(_dll.FMOD_System_Create(byref(sys_ptr)))
43+
#system.init(1, INIT_FLAGS.NORMAL, None)
44+
ckresult(_dll.FMOD_System_Init(sys_ptr, clip.m_Channels, None, None))
5045

5146
# get sound
52-
sound = system.create_sound(
53-
bytes(clip.m_AudioData),
54-
mode=MODE.OPENMEMORY,
55-
exinfo=exinfo
56-
)
47+
exinfo = byref(CREATESOUNDEXINFO(length=clip.m_Size))
48+
#sound = system.create_sound(bytes(clip.m_AudioData),mode=MODE.OPENMEMORY,exinfo=exinfo)
49+
snd_ptr = c_void_p()
50+
ckresult(_dll.FMOD_System_CreateSound(sys_ptr, bytes(
51+
clip.m_AudioData), 0x00000800, exinfo, byref(snd_ptr)))
52+
sound = Sound(snd_ptr)
5753
# iterate over subsounds
5854
samples = {}
5955
for i in range(sound.num_subsounds):
@@ -66,13 +62,14 @@ def dump_samples(clip):
6662
subsound.release()
6763

6864
sound.release()
69-
system.release()
65+
# system.release()
66+
ckresult(_dll.FMOD_System_Release(sys_ptr))
7067
return samples
7168

7269

7370
def subsound_to_wav(subsound):
7471
# get sound settings
75-
length = subsound.get_length(TIMEUNIT.PCMBYTES)
72+
length = subsound.get_length(0x00000004) # TIMEUNIT.PCMBYTES
7673
channels = subsound.format.channels
7774
bits = subsound.format.bits
7875
sample_rate = int(subsound.default_frequency)
@@ -98,7 +95,186 @@ def subsound_to_wav(subsound):
9895
# data
9996
lock = subsound.lock(0, length)
10097
for ptr, length in lock:
101-
ptr_data = ctypes.string_at(ptr, length.value)
98+
ptr_data = string_at(ptr, length.value)
10299
w.write(ptr_data)
103100
subsound.unlock(*lock)
104101
return w.bytes
102+
103+
104+
# following code is copied from
105+
# https://github.com/tyrylu/pyfmodex
106+
# pyfmodex can't be used by itself
107+
# because it would require adding the libs to the path first
108+
109+
class CREATESOUNDEXINFO(Structure):
110+
_fields_ = [("cbsize", c_int), ("length", c_uint)]
111+
112+
def __init__(self, *args, **kwargs):
113+
Structure.__init__(self, *args, **kwargs)
114+
self.cbsize = sizeof(self)
115+
116+
117+
class so:
118+
def __init__(self, **kwargs):
119+
self.__dict__.update(**kwargs)
120+
121+
122+
class Sound(object):
123+
def __init__(self, ptr):
124+
"""Constructor.
125+
:param ptr: The pointer representing this object.
126+
"""
127+
self._ptr = ptr
128+
129+
def _call_fmod(self, funcname, *args):
130+
result = getattr(_dll, funcname)(self._ptr, *args)
131+
ckresult(result)
132+
133+
@property
134+
def num_subsounds(self):
135+
num = c_int()
136+
self._call_fmod("FMOD_Sound_GetNumSubSounds", byref(num))
137+
return num.value
138+
139+
def get_subsound(self, index):
140+
sh_ptr = c_void_p()
141+
self._call_fmod("FMOD_Sound_GetSubSound", index, byref(sh_ptr))
142+
return Sound(sh_ptr)
143+
144+
def get_length(self, ltype):
145+
len = c_uint()
146+
self._call_fmod("FMOD_Sound_GetLength", byref(len), int(ltype))
147+
return len.value
148+
149+
@property
150+
def format(self):
151+
type = c_int()
152+
format = c_int()
153+
channels = c_int()
154+
bits = c_int()
155+
self._call_fmod("FMOD_Sound_GetFormat", byref(type),
156+
byref(format), byref(channels), byref(bits))
157+
return so(type=type.value, format=format.value, channels=channels.value, bits=bits.value)
158+
159+
@property
160+
def default_frequency(self):
161+
freq = c_float()
162+
pri = c_int()
163+
self._call_fmod("FMOD_Sound_GetDefaults", byref(freq), byref(pri))
164+
return freq.value
165+
166+
def lock(self, offset, length):
167+
ptr1 = c_void_p()
168+
len1 = c_uint()
169+
ptr2 = c_void_p()
170+
len2 = c_uint()
171+
ckresult(_dll.FMOD_Sound_Lock(self._ptr, offset, length,
172+
byref(ptr1), byref(ptr2), byref(len1), byref(len2)))
173+
return ((ptr1, len1), (ptr2, len2))
174+
175+
def release(self):
176+
self._call_fmod("FMOD_Sound_Release")
177+
178+
def unlock(self, i1, i2):
179+
"""I1 and I2 are tuples of form (ptr, len)."""
180+
ckresult(_dll.FMOD_Sound_Unlock(self._ptr, i1[0], i2[0], i1[1], i2[1]))
181+
182+
183+
def ckresult(result):
184+
result = RESULT(result)
185+
if result is not RESULT.OK:
186+
raise FmodError(result)
187+
188+
189+
class FmodError(Exception):
190+
def __init__(self, result):
191+
self.result = result
192+
self.message = result.name.replace("_", " ")
193+
194+
def __str__(self):
195+
return self.message
196+
197+
198+
class RESULT(Enum):
199+
OK = 0
200+
BADCOMMAND = 1
201+
CHANNEL_ALLOC = 2
202+
CHANNEL_STOLEN = 3
203+
DMA = 4
204+
DSP_CONNECTION = 5
205+
DSP_DONTPROCESS = 6
206+
DSP_FORMAT = 7
207+
DSP_INUSE = 8
208+
DSP_NOTFOUND = 9
209+
DSP_RESERVED = 10
210+
DSP_SILENCE = 11
211+
DSP_TYPE = 12
212+
FILE_BAD = 13
213+
FILE_COULDNOTSEEK = 14
214+
FILE_DISKEJECTED = 15
215+
FILE_EOF = 16
216+
FILE_ENDOFDATA = 17
217+
FILE_NOTFOUND = 18
218+
FORMAT = 19
219+
HEADER_MISMATCH = 20
220+
HTTP = 21
221+
HTTP_ACCESS = 22
222+
HTTP_PROXY_AUTH = 23
223+
HTTP_SERVER_ERROR = 24
224+
HTTP_TIMEOUT = 25
225+
INITIALIZATION = 26
226+
INITIALIZED = 27
227+
INTERNAL = 28
228+
INVALID_FLOAT = 29
229+
INVALID_HANDLE = 30
230+
INVALID_PARAM = 31
231+
INVALID_POSITION = 32
232+
INVALID_SPEAKER = 33
233+
INVALID_SYNCPOINT = 34
234+
INVALID_THREAD = 35
235+
INVALID_VECTOR = 36
236+
MAXAUDIBLE = 37
237+
MEMORY = 38
238+
MEMORY_CANTPOINT = 39
239+
NEEDS3D = 40
240+
NEEDSHARDWARE = 41
241+
NET_CONNECT = 42
242+
NET_SOCKET_ERROR = 43
243+
NET_URL = 44
244+
NET_WOULD_BLOCK = 45
245+
NOTREADY = 46
246+
OUTPUT_ALLOCATED = 47
247+
OUTPUT_CREATEBUFFER = 48
248+
OUTPUT_DRIVERCALL = 49
249+
OUTPUT_FORMAT = 50
250+
OUTPUT_INIT = 51
251+
OUTPUT_NODRIVERS = 52
252+
PLUGIN = 53
253+
PLUGIN_MISSING = 54
254+
PLUGIN_RESOURCE = 55
255+
PLUGIN_VERSION = 56
256+
RECORD = 57
257+
REVERB_CHANNELGROUP = 58
258+
REVERB_INSTANCE = 59
259+
SUBSOUNDS = 60
260+
SUBSOUND_ALLOCATED = 61
261+
SUBSOUND_CANTMOVE = 62
262+
TAGNOTFOUND = 63
263+
TOOMANYCHANNELS = 64
264+
TRUNCATED = 65
265+
UNIMPLEMENTED = 66
266+
UNINITIALIZED = 67
267+
UNSUPPORTED = 68
268+
VERSION = 69
269+
EVENT_ALREADY_LOADED = 70
270+
EVENT_LIVEUPDATE_BUSY = 71
271+
EVENT_LIVEUPDATE_MISMATCH = 72
272+
EVENT_LIVEUPDATE_TIMEOUT = 73
273+
EVENT_NOTFOUND = 74
274+
STUDIO_UNINITIALIZED = 75
275+
STUDIO_NOT_LOADED = 76
276+
INVALID_STRING = 77
277+
ALREADY_LOCKED = 78
278+
NOT_LOCKED = 79
279+
RECORD_DISCONNECTED = 80
280+
TOOMANYSAMPLES = 81

0 commit comments

Comments
 (0)