9
9
10
10
from ..helpers .ResourceReader import get_resource_data
11
11
12
+ try :
13
+ import numpy as np
14
+ except ImportError :
15
+ np = None
16
+ import struct
17
+
12
18
if TYPE_CHECKING :
13
19
from ..classes import AudioClip
14
20
@@ -48,16 +54,18 @@ def import_pyfmodex():
48
54
arch = platform .architecture ()[0 ]
49
55
machine = platform .machine ()
50
56
51
- if "arm" in machine or "aarch64" in machine :
57
+ if "arm" in machine :
52
58
arch = "arm"
59
+ elif "aarch64" in machine :
60
+ if system == "Linux" :
61
+ arch = "arm64"
62
+ else :
63
+ arch = "arm"
53
64
elif arch == "32bit" :
54
65
arch = "x86"
55
66
elif arch == "64bit" :
56
67
arch = "x64"
57
68
58
- if system == "Linux" and "aarch64" in platform .machine ():
59
- arch = "arm64"
60
-
61
69
fmod_rel_path = get_fmod_path (system , arch )
62
70
fmod_path = os .path .join (
63
71
os .path .dirname (os .path .dirname (os .path .realpath (__file__ ))), fmod_rel_path
@@ -73,7 +81,9 @@ def import_pyfmodex():
73
81
import pyfmodex
74
82
75
83
76
- def extract_audioclip_samples (audio : AudioClip ) -> Dict [str , bytes ]:
84
+ def extract_audioclip_samples (
85
+ audio : AudioClip , convert_pcm_float : bool = True
86
+ ) -> Dict [str , bytes ]:
77
87
"""extracts all the samples from an AudioClip
78
88
:param audio: AudioClip
79
89
:type audio: AudioClip
@@ -98,10 +108,12 @@ def extract_audioclip_samples(audio: AudioClip) -> Dict[str, bytes]:
98
108
return {f"{ audio .m_Name } .wav" : audio_data }
99
109
elif magic [4 :8 ] == b"ftyp" :
100
110
return {f"{ audio .m_Name } .m4a" : audio_data }
101
- return dump_samples (audio , audio_data )
111
+ return dump_samples (audio , audio_data , convert_pcm_float )
102
112
103
113
104
- def dump_samples (clip : AudioClip , audio_data : bytes ) -> Dict [str , bytes ]:
114
+ def dump_samples (
115
+ clip : AudioClip , audio_data : bytes , convert_pcm_float : bool = True
116
+ ) -> Dict [str , bytes ]:
105
117
if pyfmodex is None :
106
118
import_pyfmodex ()
107
119
if not pyfmodex :
@@ -128,69 +140,79 @@ def dump_samples(clip: AudioClip, audio_data: bytes) -> Dict[str, bytes]:
128
140
else :
129
141
filename = "%s.wav" % clip .m_Name
130
142
subsound = sound .get_subsound (i )
131
- samples [filename ] = subsound_to_wav (subsound )
143
+ samples [filename ] = subsound_to_wav (subsound , convert_pcm_float )
132
144
subsound .release ()
133
145
134
146
sound .release ()
135
147
system .release ()
136
148
return samples
137
149
138
150
139
- def subsound_to_wav (subsound ) -> bytes :
151
+ def subsound_to_wav (subsound , convert_pcm_float : bool = True ) -> bytes :
140
152
# get sound settings
141
153
sound_format = subsound .format .format
142
- length = subsound .get_length (pyfmodex .enums .TIMEUNIT .PCMBYTES )
154
+ sound_data_length = subsound .get_length (pyfmodex .enums .TIMEUNIT .PCMBYTES )
143
155
channels = subsound .format .channels
144
156
bits = subsound .format .bits
145
157
sample_rate = int (subsound .default_frequency )
146
158
147
-
148
- if sound_format == pyfmodex .enums .SOUND_FORMAT .PCM16 :
149
- # write to buffer
150
- w = EndianBinaryWriter (endian = "<" )
151
- # riff chucnk
152
- w .write (b"RIFF" )
153
- w .write_int (length + 36 ) # sizeof(FmtChunk) + sizeof(RiffChunk) + length
154
- w .write (b"WAVE" )
155
- # fmt chunck
156
- w .write (b"fmt " )
157
- w .write_int (16 ) # sizeof(FmtChunk) - sizeof(RiffChunk)
158
- w .write_short (1 )
159
- w .write_short (channels )
160
- w .write_int (sample_rate )
161
- w .write_int (sample_rate * channels * bits // 8 )
162
- w .write_short (channels * bits // 8 )
163
- w .write_short (bits )
164
- # data chunck
165
- w .write (b"data" )
166
- w .write_int (length )
167
- # data
168
- lock = subsound .lock (0 , length )
169
- for ptr , length in lock :
170
- ptr_data = ctypes .string_at (ptr , length .value )
171
- w .write (ptr_data )
172
- subsound .unlock (* lock )
173
- return w .bytes
174
- elif sound_format == pyfmodex .enums .SOUND_FORMAT .PCMFLOAT :
175
- w = EndianBinaryWriter (endian = "<" )
176
- w .write (b"RIFF" )
177
- w .write_int (length + 44 )
178
- w .write (b"WAVE" )
179
- w .write (b"fmt " )
180
- w .write_int (16 )
181
- w .write_short (3 )
182
- w .write_short (subsound .format .channels )
183
- w .write_int (int (subsound .default_frequency ))
184
- w .write_int (int (subsound .default_frequency * subsound .format .channels * subsound .format .bits / 8 ))
185
- w .write_short (int (subsound .format .channels * subsound .format .bits / 8 ))
186
- w .write_short (32 )
187
- w .write (b"data" )
188
- w .write_int (length )
189
- lock = subsound .lock (0 , length )
190
- for ptr , length in lock :
191
- ptr_data = ctypes .string_at (ptr , length .value )
192
- w .write (ptr_data )
193
- subsound .unlock (* lock )
194
- return w .bytes
159
+ if sound_format in [
160
+ pyfmodex .enums .SOUND_FORMAT .PCM8 ,
161
+ pyfmodex .enums .SOUND_FORMAT .PCM16 ,
162
+ pyfmodex .enums .SOUND_FORMAT .PCM24 ,
163
+ pyfmodex .enums .SOUND_FORMAT .PCM32 ,
164
+ ]:
165
+ audio_format = 1
166
+ wav_data_length = sound_data_length
167
+ convert_pcm_float = False
168
+ elif sound_format == pyfmodex .enums .SOUND_FORMAT .PCMFLOAT :
169
+ if convert_pcm_float :
170
+ audio_format = 1
171
+ bits = 16
172
+ wav_data_length = sound_data_length // 2
173
+ else :
174
+ audio_format = 3
175
+ wav_data_length = sound_data_length
195
176
else :
196
177
raise NotImplementedError ("Sound format " + sound_format + " is not supported." )
178
+
179
+ w = EndianBinaryWriter (endian = "<" )
180
+
181
+ # RIFF header
182
+ w .write (b"RIFF" ) # chunk id
183
+ w .write_int (
184
+ wav_data_length + 36
185
+ ) # chunk size - 4 + (8 + 16 (sub chunk 1 size)) + (8 + length (sub chunk 2 size))
186
+ w .write (b"WAVE" ) # format
187
+
188
+ # fmt chunk - sub chunk 1
189
+ w .write (b"fmt " ) # sub chunk 1 id
190
+ w .write_int (16 ) # sub chunk 1 size, 16 for PCM
191
+ w .write_short (audio_format ) # audio format, 1: PCM integer, 3: IEEE 754 float
192
+ w .write_short (channels ) # number of channels
193
+ w .write_int (sample_rate ) # sample rate
194
+ w .write_int (sample_rate * channels * bits // 8 ) # byte rate
195
+ w .write_short (channels * bits // 8 ) # block align
196
+ w .write_short (bits ) # bits per sample
197
+
198
+ # data chunk - sub chunk 2
199
+ w .write (b"data" ) # sub chunk 2 id
200
+ w .write_int (wav_data_length ) # sub chunk 2 size
201
+ # sub chunk 2 data
202
+ lock = subsound .lock (0 , sound_data_length )
203
+ for ptr , sound_data_length in lock :
204
+ ptr_data = ctypes .string_at (ptr , sound_data_length .value )
205
+ if convert_pcm_float :
206
+ if np is not None :
207
+ ptr_data = np .frombuffer (ptr_data , dtype = np .float32 )
208
+ ptr_data = (ptr_data * 2 ** 15 ).astype (np .int16 ).tobytes ()
209
+ else :
210
+ ptr_data = struct .unpack ("<%df" % (len (ptr_data ) // 4 ), ptr_data )
211
+ ptr_data = struct .pack (
212
+ "<%dh" % len (ptr_data ), * [int (f * 2 ** 15 ) for f in ptr_data ]
213
+ )
214
+
215
+ w .write (ptr_data )
216
+ subsound .unlock (* lock )
217
+
218
+ return w .bytes
0 commit comments