must

Форк
0
/
backend_fmodex.py 
676 строк · 36.8 Кб
1
import sys
2
import ctypes
3
import backend_base
4
import log
5

6

7
class FmodExWrapper(backend_base.BaseWrapper):
8
    def __init__(self, fmod_lib: ctypes.CDLL) -> None:
9
        super().__init__()
10
        self.lib = fmod_lib
11
        if not self.lib:
12
            raise FileNotFoundError('Failed to load FmodEx library')
13
        # TODO: prettify
14
        # - Errors -
15
        self.FMOD_OK = 0
16
        self.FMOD_ERR_BAD_COMMAND = 1
17
        self.FMOD_ERR_CHANNEL_ALLOC = 2
18
        self.FMOD_ERR_CHANNEL_STOLEN = 3
19
        self.FMOD_ERR_DMA = 4
20
        self.FMOD_ERR_DSP_CONNECTION = 5
21
        self.FMOD_ERR_DSP_DONT_PROCESS = 6
22
        self.FMOD_ERR_DSP_FORMAT = 7
23
        self.FMOD_ERR_DSP_INUSE = 8
24
        self.FMOD_ERR_DSP_NOTFOUND = 9
25
        self.FMOD_ERR_DSP_RESERVED = 10
26
        self.FMOD_ERR_DSP_SILENCE = 11
27
        self.FMOD_ERR_DSP_TYPE = 12
28
        self.FMOD_ERR_FILE_BAD = 13
29
        self.FMOD_ERR_FILE_COULD_NOT_SEEK = 14
30
        self.FMOD_ERR_FILE_DISK_EJECTED = 15
31
        self.FMOD_ERR_FILE_EOF = 16
32
        self.FMOD_ERR_FILE_END_OF_DATA = 17
33
        self.FMOD_ERR_FILE_NOTFOUND = 18
34
        self.FMOD_ERR_FORMAT = 19
35
        self.FMOD_ERR_HEADER_MISMATCH = 20
36
        self.FMOD_ERR_HTTP = 21
37
        self.FMOD_ERR_HTTP_ACCESS = 22
38
        self.FMOD_ERR_HTTP_PROXY_AUTH = 23
39
        self.FMOD_ERR_HTTP_SERVER_ERROR = 24
40
        self.FMOD_ERR_HTTP_TIMEOUT = 25
41
        self.FMOD_ERR_INITIALIZATION = 26
42
        self.FMOD_ERR_INITIALIZED = 27
43
        self.FMOD_ERR_INTERNAL = 28
44
        self.FMOD_ERR_INVALID_FLOAT = 29
45
        self.FMOD_ERR_INVALID_HANDLE = 30
46
        self.FMOD_ERR_INVALID_PARAM = 31
47
        self.FMOD_ERR_INVALID_POSITION = 32
48
        self.FMOD_ERR_INVALID_SPEAKER = 33
49
        self.FMOD_ERR_INVALID_SYNC_POINT = 34
50
        self.FMOD_ERR_INVALID_THREAD = 35
51
        self.FMOD_ERR_INVALID_VECTOR = 36
52
        self.FMOD_ERR_MAX_AUDIBLE = 37
53
        self.FMOD_ERR_MEMORY = 38
54
        self.FMOD_ERR_MEMORY_CANT_POINT = 39
55
        self.FMOD_ERR_NEEDS3D = 40
56
        self.FMOD_ERR_NEED_SHARD_WARE = 41
57
        self.FMOD_ERR_NET_CONNECT = 42
58
        self.FMOD_ERR_NET_SOCKET_ERROR = 43
59
        self.FMOD_ERR_NET_URL = 44
60
        self.FMOD_ERR_NET_WOULD_BLOCK = 45
61
        self.FMOD_ERR_NOT_READY = 46
62
        self.FMOD_ERR_OUTPUT_ALLOCATED = 47
63
        self.FMOD_ERR_OUTPUT_CREATE_BUFFER = 48
64
        self.FMOD_ERR_OUTPUT_DRIVER_CALL = 49
65
        self.FMOD_ERR_OUTPUT_FORMAT = 50
66
        self.FMOD_ERR_OUTPUT_INIT = 51
67
        self.FMOD_ERR_OUTPUT_NO_DRIVERS = 52
68
        self.FMOD_ERR_PLUGIN = 53
69
        self.FMOD_ERR_PLUGIN_MISSING = 54
70
        self.FMOD_ERR_PLUGIN_RESOURCE = 55
71
        self.FMOD_ERR_PLUGIN_VERSION = 56
72
        self.FMOD_ERR_RECORD = 57
73
        self.FMOD_ERR_REVERB_CHANNEL_GROUP = 58
74
        self.FMOD_ERR_REVERB_INSTANCE = 59
75
        self.FMOD_ERR_SUB_SOUNDS = 60
76
        self.FMOD_ERR_SUB_SOUND_ALLOCATED = 61
77
        self.FMOD_ERR_SUB_SOUND_CANT_MOVE = 62
78
        self.FMOD_ERR_TAG_NOT_FOUND = 63
79
        self.FMOD_ERR_TOO_MANY_CHANNELS = 64
80
        self.FMOD_ERR_TRUNCATED = 65
81
        self.FMOD_ERR_UNIMPLEMENTED = 66
82
        self.FMOD_ERR_UNINITIALIZED = 67
83
        self.FMOD_ERR_UNSUPPORTED = 68
84
        self.FMOD_ERR_VERSION = 69
85
        self.FMOD_ERR_EVENT_ALREADY_LOADED = 70
86
        self.FMOD_ERR_EVENT_LIVE_UPDATE_BUSY = 71
87
        self.FMOD_ERR_EVENT_LIVE_UPDATE_MISMATCH = 72
88
        self.FMOD_ERR_EVENT_LIVE_UPDATE_TIMEOUT = 73
89
        self.FMOD_ERR_EVENT_NOTFOUND = 74
90
        self.FMOD_ERR_STUDIO_UNINITIALIZED = 75
91
        self.FMOD_ERR_STUDIO_NOT_LOADED = 76
92
        self.FMOD_ERR_INVALID_STRING = 77
93
        self.FMOD_ERR_ALREADY_LOCKED = 78
94
        self.FMOD_ERR_NOT_LOCKED = 79
95
        self.FMOD_ERR_RECORD_DISCONNECTED = 80
96
        self.FMOD_ERR_TOO_MANY_SAMPLES = 81
97
        self.error_map = {
98
            self.FMOD_OK: "No errors.",
99
            self.FMOD_ERR_BAD_COMMAND: "Tried to call a function on a data type that does not allow this type of"
100
                                       " functionality (ie calling Sound::lock on a streaming sound).",
101
            self.FMOD_ERR_CHANNEL_ALLOC: "Error trying to allocate a channel.",
102
            self.FMOD_ERR_CHANNEL_STOLEN: "The specified channel has been reused to play another sound.",
103
            self.FMOD_ERR_DMA: "DMA Failure.  See debug output for more information.",
104
            self.FMOD_ERR_DSP_CONNECTION: "DSP connection error.  Connection possibly caused a cyclic dependency or"
105
                                          " connected dsps with incompatible buffer counts.",
106
            self.FMOD_ERR_DSP_DONT_PROCESS: "DSP return code from a DSP process query callback.  Tells mixer not to"
107
                                            " call the process callback and therefore not consume CPU.  Use this to"
108
                                            " optimize the DSP graph.",
109
            self.FMOD_ERR_DSP_FORMAT: "DSP Format error.  A DSP unit may have attempted to connect to this network"
110
                                      " with the wrong format, or a matrix may have been set with the wrong size"
111
                                      " if the target unit has a specified channel map.",
112
            self.FMOD_ERR_DSP_INUSE: "DSP is already in the mixer's DSP network. It must be removed before being"
113
                                     " reinserted or released.",
114
            self.FMOD_ERR_DSP_NOTFOUND: "DSP connection error.  Couldn't find the DSP unit specified.",
115
            self.FMOD_ERR_DSP_RESERVED: "DSP operation error.  Cannot perform operation on this DSP as it is reserved"
116
                                        " by the system.",
117
            self.FMOD_ERR_DSP_SILENCE: "DSP return code from a DSP process query callback.  Tells mixer silence would"
118
                                       " be produced from read, so go idle and not consume CPU.  Use this to optimize"
119
                                       " the DSP graph.",
120
            self.FMOD_ERR_DSP_TYPE: "DSP operation cannot be performed on a DSP of this type.",
121
            self.FMOD_ERR_FILE_BAD: "Error loading file.",
122
            self.FMOD_ERR_FILE_COULD_NOT_SEEK: "Couldn't perform seek operation.  This is a limitation of the medium"
123
                                               " (ie netstreams) or the file format.",
124
            self.FMOD_ERR_FILE_DISK_EJECTED: "Media was ejected while reading.",
125
            self.FMOD_ERR_FILE_EOF: "End of file unexpectedly reached while trying to read essential data"
126
                                    " (truncated?).",
127
            self.FMOD_ERR_FILE_END_OF_DATA: "End of current chunk reached while trying to read data.",
128
            self.FMOD_ERR_FILE_NOTFOUND: "File not found.",
129
            self.FMOD_ERR_FORMAT: "Unsupported file or audio format.",
130
            self.FMOD_ERR_HEADER_MISMATCH: "There is a version mismatch between the FMOD header and either the FMOD"
131
                                           " Studio library or the FMOD Low Level library.",
132
            self.FMOD_ERR_HTTP: "A HTTP error occurred. This is a catch-all for HTTP errors not listed elsewhere.",
133
            self.FMOD_ERR_HTTP_ACCESS: "The specified resource requires authentication or is forbidden.",
134
            self.FMOD_ERR_HTTP_PROXY_AUTH: "Proxy authentication is required to access the specified resource.",
135
            self.FMOD_ERR_HTTP_SERVER_ERROR: "A HTTP server error occurred.",
136
            self.FMOD_ERR_HTTP_TIMEOUT: "The HTTP request timed out.",
137
            self.FMOD_ERR_INITIALIZATION: "FMOD was not initialized correctly to support this function.",
138
            self.FMOD_ERR_INITIALIZED: "Cannot call this command after System::init.",
139
            self.FMOD_ERR_INTERNAL: "An error occurred that wasn't supposed to.  Contact support.",
140
            self.FMOD_ERR_INVALID_FLOAT: "Value passed in was a NaN, Inf or denormalized float.",
141
            self.FMOD_ERR_INVALID_HANDLE: "An invalid object handle was used.",
142
            self.FMOD_ERR_INVALID_PARAM: "An invalid parameter was passed to this function.",
143
            self.FMOD_ERR_INVALID_POSITION: "An invalid seek position was passed to this function.",
144
            self.FMOD_ERR_INVALID_SPEAKER: "An invalid speaker was passed to this function based on the current"
145
                                           " speaker mode.",
146
            self.FMOD_ERR_INVALID_SYNC_POINT: "The syncpoint did not come from this sound handle.",
147
            self.FMOD_ERR_INVALID_THREAD: "Tried to call a function on a thread that is not supported.",
148
            self.FMOD_ERR_INVALID_VECTOR: "The vectors passed in are not unit length, or perpendicular.",
149
            self.FMOD_ERR_MAX_AUDIBLE: "Reached maximum audible playback count for this sound's soundgroup.",
150
            self.FMOD_ERR_MEMORY: "Not enough memory or resources.",
151
            self.FMOD_ERR_MEMORY_CANT_POINT: "Can't use FMOD_OPENMEMORY_POINT on non PCM source data, or non"
152
                                             " mp3/xma/adpcm data if FMOD_CREATECOMPRESSEDSAMPLE was used.",
153
            self.FMOD_ERR_NEEDS3D: "Tried to call a command on a 2d sound when the command was meant for 3d sound.",
154
            self.FMOD_ERR_NEED_SHARD_WARE: "Tried to use a feature that requires hardware support.",
155
            self.FMOD_ERR_NET_CONNECT: "Couldn't connect to the specified host.",
156
            self.FMOD_ERR_NET_SOCKET_ERROR: "A socket error occurred.  This is a catch-all for socket-related errors"
157
                                            " not listed elsewhere.",
158
            self.FMOD_ERR_NET_URL: "The specified URL couldn't be resolved.",
159
            self.FMOD_ERR_NET_WOULD_BLOCK: "Operation on a non-blocking socket could not complete immediately.",
160
            self.FMOD_ERR_NOT_READY: "Operation could not be performed because specified sound/DSP connection is not"
161
                                     " ready.",
162
            self.FMOD_ERR_OUTPUT_ALLOCATED: "Error initializing output device, but more specifically, the output device"
163
                                            " is already in use and cannot be reused.",
164
            self.FMOD_ERR_OUTPUT_CREATE_BUFFER: "Error creating hardware sound buffer.",
165
            self.FMOD_ERR_OUTPUT_DRIVER_CALL: "A call to a standard soundcard driver failed, which could possibly mean"
166
                                              " a bug in the driver or resources were missing or exhausted.",
167
            self.FMOD_ERR_OUTPUT_FORMAT: "Soundcard does not support the specified format.",
168
            self.FMOD_ERR_OUTPUT_INIT: "Error initializing output device.",
169
            self.FMOD_ERR_OUTPUT_NO_DRIVERS: "The output device has no drivers installed.  If pre-init,"
170
                                             " FMOD_OUTPUT_NOSOUND is selected as the output mode.  If post-init,"
171
                                             " the function just fails.",
172
            self.FMOD_ERR_PLUGIN: "An unspecified error has been returned from a plugin.",
173
            self.FMOD_ERR_PLUGIN_MISSING: "A requested output, dsp unit type or codec was not available.",
174
            self.FMOD_ERR_PLUGIN_RESOURCE: "A resource that the plugin requires cannot be allocated or found."
175
                                           " (ie the DLS file for MIDI playback)",
176
            self.FMOD_ERR_PLUGIN_VERSION: "A plugin was built with an unsupported SDK version.",
177
            self.FMOD_ERR_RECORD: "An error occurred trying to initialize the recording device.",
178
            self.FMOD_ERR_REVERB_CHANNEL_GROUP: "Reverb properties cannot be set on this channel because a parent"
179
                                                " channelgroup owns the reverb connection.",
180
            self.FMOD_ERR_REVERB_INSTANCE: "Specified instance in FMOD_REVERB_PROPERTIES couldn't be set. Most likely"
181
                                           " because it is an invalid instance number or the reverb doesn't exist.",
182
            self.FMOD_ERR_SUB_SOUNDS: "The error occurred because the sound referenced contains subsounds when it"
183
                                      " shouldn't have, or it doesn't contain subsounds when it should have."
184
                                      "  The operation may also not be able to be performed on a parent sound.",
185
            self.FMOD_ERR_SUB_SOUND_ALLOCATED: "This subsound is already being used by another sound, you cannot have"
186
                                               " more than one parent to a sound.  Null out the other parent's entry"
187
                                               " first.",
188
            self.FMOD_ERR_SUB_SOUND_CANT_MOVE: "Shared subsounds cannot be replaced or moved from their parent stream,"
189
                                               " such as when the parent stream is an FSB file.",
190
            self.FMOD_ERR_TAG_NOT_FOUND: "The specified tag could not be found or there are no tags.",
191
            self.FMOD_ERR_TOO_MANY_CHANNELS: "The sound created exceeds the allowable input channel count.  This can be"
192
                                             " increased using the 'maxinputchannels' parameter in"
193
                                             " System::setSoftwareFormat.",
194
            self.FMOD_ERR_TRUNCATED: "The retrieved string is too long to fit in the supplied buffer and has been"
195
                                     " truncated.",
196
            self.FMOD_ERR_UNIMPLEMENTED: "Something in FMOD hasn't been implemented when it should be!"
197
                                         " contact support!",
198
            self.FMOD_ERR_UNINITIALIZED: "This command failed because System::init or System::setDriver"
199
                                         " was not called.",
200
            self.FMOD_ERR_UNSUPPORTED: "A command issued was not supported by this object.  Possibly a plugin without"
201
                                       " certain callbacks specified.",
202
            self.FMOD_ERR_VERSION: "The version number of this file format is not supported.",
203
            self.FMOD_ERR_EVENT_ALREADY_LOADED: "The specified bank has already been loaded.",
204
            self.FMOD_ERR_EVENT_LIVE_UPDATE_BUSY: "The live update connection failed due to the game already being"
205
                                                  " connected.",
206
            self.FMOD_ERR_EVENT_LIVE_UPDATE_MISMATCH: "The live update connection failed due to the game data being"
207
                                                      " out of sync with the tool.",
208
            self.FMOD_ERR_EVENT_LIVE_UPDATE_TIMEOUT: "The live update connection timed out.",
209
            self.FMOD_ERR_EVENT_NOTFOUND: "The requested event, parameter, bus or vca could not be found.",
210
            self.FMOD_ERR_STUDIO_UNINITIALIZED: "The Studio::System object is not yet initialized.",
211
            self.FMOD_ERR_STUDIO_NOT_LOADED: "The specified resource is not loaded, so it can't be unloaded.",
212
            self.FMOD_ERR_INVALID_STRING: "An invalid string was passed to this function.",
213
            self.FMOD_ERR_ALREADY_LOCKED: "The specified resource is already locked.",
214
            self.FMOD_ERR_NOT_LOCKED: "The specified resource is not locked, so it can't be unlocked.",
215
            self.FMOD_ERR_RECORD_DISCONNECTED: "The specified recording driver has been disconnected.",
216
            self.FMOD_ERR_TOO_MANY_SAMPLES: "The length provided exceeds the allowable limit.",
217
        }
218
        # - Music Formats -
219
        self.FMOD_SOUND_TYPE_UNKNOWN = 0
220
        self.FMOD_SOUND_TYPE_AIFF = 1
221
        self.FMOD_SOUND_TYPE_ASF = 2
222
        self.FMOD_SOUND_TYPE_DLS = 3
223
        self.FMOD_SOUND_TYPE_FLAC = 4
224
        self.FMOD_SOUND_TYPE_FSB = 5
225
        self.FMOD_SOUND_TYPE_IT = 6
226
        self.FMOD_SOUND_TYPE_MIDI = 7
227
        self.FMOD_SOUND_TYPE_MOD = 8
228
        self.FMOD_SOUND_TYPE_MPEG = 9
229
        self.FMOD_SOUND_TYPE_OGG_VORBIS = 10
230
        self.FMOD_SOUND_TYPE_PLAYLIST = 11
231
        self.FMOD_SOUND_TYPE_RAW = 12
232
        self.FMOD_SOUND_TYPE_S3M = 13
233
        self.FMOD_SOUND_TYPE_USER = 14
234
        self.FMOD_SOUND_TYPE_WAV = 15
235
        self.FMOD_SOUND_TYPE_XM = 16
236
        self.FMOD_SOUND_TYPE_XMA = 17
237
        self.FMOD_SOUND_TYPE_AUDIO_QUEUE = 18
238
        self.FMOD_SOUND_TYPE_AT9 = 19
239
        self.FMOD_SOUND_TYPE_VORBIS = 20
240
        self.FMOD_SOUND_TYPE_MEDIA_FOUNDATION = 21
241
        self.FMOD_SOUND_TYPE_MEDIA_CODEC = 22
242
        self.FMOD_SOUND_TYPE_FAD_PCM = 23
243
        self.FMOD_SOUND_TYPE_OPUS = 24
244
        self.format_map = {
245
            self.FMOD_SOUND_TYPE_UNKNOWN: 'none',
246
            self.FMOD_SOUND_TYPE_AIFF: 'aiff',
247
            self.FMOD_SOUND_TYPE_ASF: 'asf',
248
            self.FMOD_SOUND_TYPE_DLS: 'dls',
249
            self.FMOD_SOUND_TYPE_FLAC: 'flac',
250
            self.FMOD_SOUND_TYPE_FSB: 'fsb',
251
            self.FMOD_SOUND_TYPE_IT: 'it',
252
            self.FMOD_SOUND_TYPE_MIDI: 'mid',
253
            self.FMOD_SOUND_TYPE_MOD: 'mod',
254
            self.FMOD_SOUND_TYPE_MPEG: 'mp3',
255
            self.FMOD_SOUND_TYPE_OGG_VORBIS: 'ogg',
256
            self.FMOD_SOUND_TYPE_PLAYLIST: 'playlist',
257
            self.FMOD_SOUND_TYPE_RAW: 'raw',
258
            self.FMOD_SOUND_TYPE_S3M: 's3m',
259
            self.FMOD_SOUND_TYPE_USER: 'user',
260
            self.FMOD_SOUND_TYPE_WAV: 'wav',
261
            self.FMOD_SOUND_TYPE_XM: 'xm',
262
            self.FMOD_SOUND_TYPE_XMA: 'xma',
263
            self.FMOD_SOUND_TYPE_AUDIO_QUEUE: 'audio_queue',
264
            self.FMOD_SOUND_TYPE_AT9: 'at9',
265
            self.FMOD_SOUND_TYPE_VORBIS: 'vorbis',
266
            self.FMOD_SOUND_TYPE_MEDIA_FOUNDATION: 'media_foundation',
267
            self.FMOD_SOUND_TYPE_MEDIA_CODEC: 'media_codec',
268
            self.FMOD_SOUND_TYPE_FAD_PCM: 'pcm',
269
            self.FMOD_SOUND_TYPE_OPUS: 'opus'
270
        }
271
        # - Init Flags -
272
        self.FMOD_INIT_NORMAL = 0x00000000
273
        self.FMOD_INIT_STREAM_FROM_UPDATE = 0x00000001
274
        self.FMOD_INIT_MIX_FROM_UPDATE = 0x00000002
275
        self.FMOD_INIT_3D_RIGHT_HANDED = 0x00000004
276
        self.FMOD_INIT_CLIP_OUTPUT = 0x00000008
277
        self.FMOD_INIT_CHANNEL_LOWPASS = 0x00000100
278
        self.FMOD_INIT_CHANNEL_DISTANCE_FILTER = 0x00000200
279
        self.FMOD_INIT_PROFILE_ENABLE = 0x00010000
280
        self.FMOD_INIT_VOL0_BECOMES_VIRTUAL = 0x00020000
281
        self.FMOD_INIT_GEOMETRY_USE_CLOSEST = 0x00040000
282
        self.FMOD_INIT_PREFER_DOLBY_DOWN_MIX = 0x00080000
283
        self.FMOD_INIT_THREAD_UNSAFE = 0x00100000
284
        self.FMOD_INIT_PROFILE_METER_ALL = 0x00200000
285
        self.FMOD_INIT_MEMORY_TRACKING = 0x00400000
286
        # - Music Modes -
287
        self.FMOD_DEFAULT = 0x00000000
288
        self.FMOD_LOOP_OFF = 0x00000001
289
        self.FMOD_LOOP_NORMAL = 0x00000002
290
        self.FMOD_LOOP_BIDI = 0x00000004
291
        self.FMOD_2D = 0x00000008
292
        self.FMOD_3D = 0x00000010
293
        self.FMOD_CREATE_STREAM = 0x00000080
294
        self.FMOD_CREATE_SAMPLE = 0x00000100
295
        self.FMOD_CREATE_COMPRESSED_SAMPLE = 0x00000200
296
        self.FMOD_OPEN_USER = 0x00000400
297
        self.FMOD_OPEN_MEMORY = 0x00000800
298
        self.FMOD_OPEN_MEMORY_POINT = 0x10000000
299
        self.FMOD_OPEN_RAW = 0x00001000
300
        self.FMOD_OPEN_ONLY = 0x00002000
301
        self.FMOD_ACCURATE_TIME = 0x00004000
302
        self.FMOD_MPEG_SEARCH = 0x00008000
303
        self.FMOD_NONBLOCKING = 0x00010000
304
        self.FMOD_UNIQUE = 0x00020000
305
        self.FMOD_3D_HEAD_RELATIVE = 0x00040000
306
        self.FMOD_3D_WORLD_RELATIVE = 0x00080000
307
        self.FMOD_3D_INVERSE_ROLL_OFF = 0x00100000
308
        self.FMOD_3D_LINEAR_ROLL_OFF = 0x00200000
309
        self.FMOD_3D_LINEAR_SQUARE_ROLL_OFF = 0x00400000
310
        self.FMOD_3D_INVERSE_TAPERED_ROLL_OFF = 0x00800000
311
        self.FMOD_3D_CUSTOM_ROLL_OFF = 0x04000000
312
        self.FMOD_3D_IGNORE_GEOMETRY = 0x40000000
313
        self.FMOD_IGNORE_TAGS = 0x02000000
314
        self.FMOD_LOW_MEM = 0x08000000
315
        self.FMOD_VIRTUAL_PLAY_FROM_START = 0x80000000
316
        # - Time Units -
317
        self.FMOD_TIMEUNIT_MS = 0x00000001
318
        self.FMOD_TIMEUNIT_PCM = 0x00000002
319
        self.FMOD_TIMEUNIT_PCM_BYTES = 0x00000004
320
        self.FMOD_TIMEUNIT_RAW_BYTES = 0x00000008
321
        self.FMOD_TIMEUNIT_PCM_FRACTION = 0x00000010
322
        self.FMOD_TIMEUNIT_MOD_ORDER = 0x00000100
323
        self.FMOD_TIMEUNIT_MOD_ROW = 0x00000200
324
        self.FMOD_TIMEUNIT_MOD_PATTERN = 0x00000400
325
        # - Output Types -
326
        self.FMOD_OUTPUT_TYPE_AUTODETECT = 0
327
        self.FMOD_OUTPUT_TYPE_UNKNOWN = 1
328
        self.FMOD_OUTPUT_TYPE_NO_SOUND = 2
329
        self.FMOD_OUTPUT_TYPE_WAV_WRITER = 3
330
        self.FMOD_OUTPUT_TYPE_NO_SOUND_NRT = 4
331
        self.FMOD_OUTPUT_TYPE_WAV_WRITER_NRT = 5
332
        self.FMOD_OUTPUT_TYPE_WASAPI = 6
333
        self.FMOD_OUTPUT_TYPE_ASIO = 7
334
        self.FMOD_OUTPUT_TYPE_PULSEAUDIO = 8
335
        self.FMOD_OUTPUT_TYPE_ALSA = 9
336
        self.FMOD_OUTPUT_TYPE_CORE_AUDIO = 10
337
        self.FMOD_OUTPUT_TYPE_AUDIOTRACK = 11
338
        self.FMOD_OUTPUT_TYPE_OPENSL = 12
339
        self.FMOD_OUTPUT_TYPE_AUDIO_OUT = 13
340
        self.FMOD_OUTPUT_TYPE_AUDIO3D = 14
341
        self.FMOD_OUTPUT_TYPE_WEB_AUDIO = 15
342
        self.FMOD_OUTPUT_TYPE_NNAUDIO = 16
343
        self.FMOD_OUTPUT_TYPE_WIN_SONIC = 17
344
        self.FMOD_OUTPUT_TYPE_A_AUDIO = 18
345
        self.FMOD_OUTPUT_TYPE_AUDIO_WORK_LET = 19
346
        self.FMOD_OUTPUT_TYPE_PHASE = 20
347
        self.output_map = {
348
            '': self.FMOD_OUTPUT_TYPE_AUTODETECT,
349
            'auto': self.FMOD_OUTPUT_TYPE_AUTODETECT,
350
            'dummy': self.FMOD_OUTPUT_TYPE_NO_SOUND,
351
            'disk': self.FMOD_OUTPUT_TYPE_WAV_WRITER,
352
            'wasapi': self.FMOD_OUTPUT_TYPE_WASAPI,
353
            'asio': self.FMOD_OUTPUT_TYPE_ASIO,
354
            'pulseaudio': self.FMOD_OUTPUT_TYPE_PULSEAUDIO,
355
            'alsa': self.FMOD_OUTPUT_TYPE_ALSA,
356
            'coreaudio': self.FMOD_OUTPUT_TYPE_CORE_AUDIO,
357
            'opensl': self.FMOD_OUTPUT_TYPE_OPENSL,
358
            'webaudio': self.FMOD_OUTPUT_TYPE_WEB_AUDIO,
359
            'winsonic': self.FMOD_OUTPUT_TYPE_WIN_SONIC
360
        }
361
        self.FMOD_System_Create = self.wrap('FMOD_System_Create', args=(ctypes.POINTER(ctypes.c_void_p), ctypes.c_uint))
362
        self.FMOD_System_Release = self.wrap('FMOD_System_Release', args=(ctypes.c_void_p,))
363
        self.FMOD_System_Init = self.wrap('FMOD_System_Init', args=(
364
            ctypes.c_void_p, ctypes.c_int, ctypes.c_uint, ctypes.c_void_p
365
        ))
366
        self.FMOD_System_GetVersion = self.wrap(
367
            'FMOD_System_GetVersion', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint))
368
        )
369
        self.FMOD_System_Close = self.wrap('FMOD_System_Close', args=(ctypes.c_void_p,))
370
        self.FMOD_System_CreateStream = self.wrap('FMOD_System_CreateStream', args=(
371
            ctypes.c_void_p, ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p, ctypes.POINTER(ctypes.c_void_p)
372
        ))
373
        self.FMOD_Sound_Release = self.wrap('FMOD_Sound_Release', args=(ctypes.c_void_p,))
374
        self.FMOD_System_PlaySound = self.wrap('FMOD_System_PlaySound', args=(
375
            ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, ctypes.POINTER(ctypes.c_void_p)
376
        ))
377
        self.FMOD_System_Update = self.wrap('FMOD_System_Update', args=(ctypes.c_void_p,))
378
        self.FMOD_Channel_Stop = self.wrap('FMOD_Channel_Stop', args=(ctypes.c_void_p,))
379
        self.FMOD_Channel_SetPaused = self.wrap('FMOD_Channel_SetPaused', args=(ctypes.c_void_p, ctypes.c_int))
380
        self.FMOD_Channel_SetVolume = self.wrap('FMOD_Channel_SetVolume', args=(ctypes.c_void_p, ctypes.c_float))
381
        self.FMOD_Channel_SetPitch = self.wrap('FMOD_Channel_SetPitch', args=(ctypes.c_void_p, ctypes.c_float))
382
        self.FMOD_Channel_GetPitch = self.wrap(
383
            'FMOD_Channel_GetPitch', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_float))
384
        )
385
        self.FMOD_Channel_SetFrequency = self.wrap('FMOD_Channel_SetFrequency', args=(ctypes.c_void_p, ctypes.c_float))
386
        self.FMOD_Channel_GetFrequency = self.wrap(
387
            'FMOD_Channel_GetFrequency', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_float))
388
        )
389
        self.FMOD_Channel_IsPlaying = self.wrap(
390
            'FMOD_Channel_IsPlaying', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))
391
        )
392
        self.FMOD_Sound_GetDefaults = self.wrap('FMOD_Sound_GetDefaults', args=(
393
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_int)
394
        ))
395
        self.FMOD_Sound_GetLength = self.wrap('FMOD_Sound_GetLength', args=(
396
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint), ctypes.c_uint
397
        ))
398
        self.FMOD_Channel_GetPosition = self.wrap('FMOD_Channel_GetPosition', args=(
399
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_uint), ctypes.c_uint
400
        ))
401
        self.FMOD_Channel_SetPosition = self.wrap('FMOD_Channel_SetPosition', args=(
402
            ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint
403
        ))
404
        self.FMOD_System_GetOutput = self.wrap('FMOD_System_GetOutput', args=(
405
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)
406
        ))
407
        self.FMOD_System_SetOutput = self.wrap('FMOD_System_SetOutput', args=(ctypes.c_void_p, ctypes.c_int))
408
        self.FMOD_Sound_GetFormat = self.wrap('FMOD_Sound_GetFormat', args=(
409
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int),
410
            ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)
411
        ))
412
        self.FMOD_System_GetNumDrivers = self.wrap(
413
            'FMOD_System_GetNumDrivers', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))
414
        )
415
        self.FMOD_System_GetDriverInfo = self.wrap('FMOD_System_GetDriverInfo', args=(
416
            ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p, ctypes.POINTER(ctypes.c_int),
417
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_int)
418
        ))
419
        self.FMOD_System_SetDriver = self.wrap('FMOD_System_SetDriver', args=(ctypes.c_void_p, ctypes.c_int))
420
        self.FMOD_System_GetDriver = self.wrap(
421
            'FMOD_System_GetDriver', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))
422
        )
423
        self.FMOD_System_SetSoftwareChannels = self.wrap(
424
            'FMOD_System_SetSoftwareChannels', args=(ctypes.c_void_p, ctypes.c_int)
425
        )
426
        self.FMOD_System_GetSoftwareChannels = self.wrap(
427
            'FMOD_System_GetSoftwareChannels', args=(ctypes.c_void_p, ctypes.POINTER(ctypes.c_int))
428
        )
429
        self.FMOD_System_SetSoftwareFormat = self.wrap('FMOD_System_SetSoftwareFormat', args=(
430
            ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_int
431
        ))
432
        self.FMOD_System_GetSoftwareFormat = self.wrap('FMOD_System_GetSoftwareFormat', args=(
433
            ctypes.c_void_p, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)
434
        ))
435

436
    def wrap(self, func_name: str, args: tuple = (), res: any = ctypes.c_int) -> any:
437
        return super().wrap(func_name=func_name, args=args, res=res)
438

439

440
class FmodExMusic(backend_base.BaseMusic):
441
    def __init__(self, bk: any, fmod: FmodExWrapper, fp: str, mus: ctypes.c_void_p) -> None:
442
        super().__init__(fp)
443
        self.bk = bk
444
        self.fmod = fmod
445
        self.mus = mus
446
        self.ch = ctypes.c_void_p()
447
        type_buf = ctypes.c_int(0)
448
        bits_buf = ctypes.c_int(0)
449
        self.bk.check_result_warn(
450
            self.fmod.FMOD_Sound_GetFormat(self.mus, type_buf, None, None, bits_buf), 'Failed to get sound info'
451
        )
452
        self.type = self.fmod.format_map.get(type_buf.value) or 'none'
453
        self.bits = bits_buf.value
454
        length_buf = ctypes.c_uint(0)
455
        self.bk.check_result_warn(
456
            self.fmod.FMOD_Sound_GetLength(self.mus, length_buf, self.fmod.FMOD_TIMEUNIT_MS),
457
            'Failed to get sound length'
458
        )
459
        self.length = length_buf.value / 1000
460
        # We don't need this in the current context
461
        '''freq_buf = ctypes.c_float(0.0)
462
        self.bk.check_result_warn(self.fmod.FMOD_Sound_GetDefaults(self.mus, freq_buf, None), 'Failed to get def info')
463
        self.freq = freq_buf.value'''
464

465
    def play(self) -> None:
466
        self.bk.check_result_warn(self.fmod.FMOD_System_PlaySound(
467
            self.bk.sys, self.mus, None, 0, self.ch
468
        ), 'Failed to play music')
469
        freq_buf = ctypes.c_float(0.0)
470
        res = self.fmod.FMOD_Channel_GetFrequency(self.ch, freq_buf)
471
        if res == self.fmod.FMOD_OK:
472
            self.freq = freq_buf.value
473
        else:
474
            self.bk.check_result_warn(res, 'Failed to play music')
475
        pitch_buf = ctypes.c_float(0.0)
476
        res = self.fmod.FMOD_Channel_GetPitch(self.ch, pitch_buf)
477
        if res == self.fmod.FMOD_OK:
478
            self.pitch = pitch_buf.value
479
        else:
480
            self.bk.check_result_warn(res, 'Failed to play music')
481

482
    def stop(self) -> None:
483
        res = self.fmod.FMOD_Channel_Stop(self.ch)
484
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
485
            return
486
        self.bk.check_result_warn(res, 'Failed to stop channel')
487

488
    def is_playing(self) -> bool:
489
        buf = ctypes.c_int(0)
490
        res = self.fmod.FMOD_Channel_IsPlaying(self.ch, buf)
491
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
492
            return False
493
        self.bk.check_result_warn(res, 'Failed to get is channel playing')
494
        return bool(buf.value)
495

496
    def set_paused(self, paused: bool) -> None:
497
        res = self.fmod.FMOD_Channel_SetPaused(self.ch, paused)
498
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
499
            return
500
        self.paused = paused
501
        self.bk.check_result_warn(res, 'Failed to set channel paused')
502

503
    def rewind(self) -> None:
504
        self.set_pos(0.0)
505

506
    def set_volume(self, volume: float = 1.0) -> None:
507
        res = self.fmod.FMOD_Channel_SetVolume(self.ch, volume)
508
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
509
            return
510
        self.bk.check_result_warn(res, 'Failed to set channel volume')
511

512
    def set_speed(self, speed: float = 1.0) -> None:
513
        # if not self.freq:
514
        #     return
515
        # res = self.fmod.FMOD_Channel_SetFrequency(self.ch, self.freq * speed)
516
        res = self.fmod.FMOD_Channel_SetPitch(self.ch, self.pitch * speed)
517
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
518
            return
519
        self.bk.check_result_warn(res, 'Failed to set channel speed')
520

521
    def set_pos(self, pos: float) -> None:
522
        res = self.fmod.FMOD_Channel_SetPosition(self.ch, int(pos * 1000), self.fmod.FMOD_TIMEUNIT_MS)
523
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
524
            return
525
        self.bk.check_result_warn(res, 'Failed to set channel position')
526

527
    def get_pos(self) -> float:
528
        pos_buf = ctypes.c_uint(0)
529
        res = self.fmod.FMOD_Channel_GetPosition(self.ch, pos_buf, self.fmod.FMOD_TIMEUNIT_MS)
530
        if res == self.fmod.FMOD_ERR_INVALID_HANDLE:
531
            return 0.0
532
        self.bk.check_result_warn(res, 'Failed to get channel position')
533
        return pos_buf.value / 1000
534

535
    def destroy(self) -> None:
536
        if not self.fmod:
537
            return
538
        if self.mus:
539
            result = self.fmod.FMOD_Sound_Release(self.mus)
540
            if self.bk.check_result_warn:
541
                self.bk.check_result_warn(result, 'Failed to close music')
542
            self.mus = None
543
        self.bk = None
544
        self.fmod = None
545

546

547
class FmodExBackend(backend_base.BaseBackend):
548
    def __init__(self, app: any, libs: dict) -> None:
549
        super().__init__()
550
        self.title = 'FmodEx'
551
        self.app = app
552
        self.header_version = eval(app.config['fmod_version'])
553
        self.sys = ctypes.c_void_p()
554
        self.fmod = FmodExWrapper(libs.get('fmod'))
555
        self.device_names = []
556
        self.current_device_name = ''
557

558
    def init(self) -> None:
559
        res = self.fmod.FMOD_System_Create(self.sys, self.header_version)
560
        if res == self.fmod.FMOD_ERR_HEADER_MISMATCH:
561
            for i in range(100000000):  # Brute force
562
                res = self.fmod.FMOD_System_Create(self.sys, i)
563
                if res == self.fmod.FMOD_OK:
564
                    break
565
        self.check_result_err(res, 'Failed to create system')
566
        ver_buf = ctypes.c_uint()
567
        if self.fmod.FMOD_System_GetVersion(self.sys, ver_buf) == self.fmod.FMOD_OK \
568
                and not ver_buf.value == self.header_version:
569
            log.warn(f'Incorrect FmodEx version configured. Please change it to {hex(ver_buf.value)} in config')
570
        freq_buf = ctypes.c_int(41000)
571
        mode_buf = ctypes.c_int(0)
572
        channels_buf = ctypes.c_int(1)
573
        self.check_result_warn(self.fmod.FMOD_System_GetSoftwareFormat(
574
            self.sys, freq_buf, mode_buf, channels_buf
575
        ), 'Failed to get current audio device specs')
576
        if not self.app.config['freq']:
577
            self.app.config['freq'] = freq_buf.value
578
            log.warn('Please set frequency in config to', freq_buf.value)
579
        if not self.app.config['channels'] and channels_buf.value:
580
            self.app.config['channels'] = channels_buf.value
581
            log.warn('Please set channels in config to', channels_buf.value)
582
        self.check_result_warn(self.fmod.FMOD_System_SetSoftwareFormat(
583
            self.sys, self.app.config['freq'], mode_buf.value, self.app.config['channels']
584
        ), 'Failed to set audio specs')
585
        self.check_result_err(self.fmod.FMOD_System_Init(
586
            self.sys, 1, self.fmod.FMOD_INIT_THREAD_UNSAFE, None
587
        ), 'Failed to init system')
588
        if self.app.config['audio_driver']:
589
            self.check_result_err(self.fmod.FMOD_System_SetOutput(
590
                self.sys, self.fmod.output_map.get(self.app.config['audio_driver'])
591
            ), 'Failed to set audio driver')
592
        num_buf = ctypes.c_int(10)
593
        self.check_result_warn(
594
            self.fmod.FMOD_System_GetNumDrivers(self.sys, num_buf), 'Failed to get audio devices number'
595
        )
596
        for i in range(num_buf.value):
597
            name_buf = ctypes.c_char_p(b' ' * 1024)
598
            self.check_result_warn(self.fmod.FMOD_System_GetDriverInfo(
599
                self.sys, i, name_buf, 1024, None, None, None, None
600
            ), 'Failed to get device info')
601
            if name_buf.value and name_buf.value.strip():
602
                self.device_names.append(self.app.bts(name_buf.value.strip()))
603
            else:
604
                self.device_names.append('')
605
        if self.app.config['device_name'].strip():
606
            if self.app.config['device_name'] in self.device_names:
607
                self.check_result_warn(self.fmod.FMOD_System_SetDriver(
608
                    self.sys, self.device_names.index(self.app.config['device_name'])
609
                ), 'Failed to set audio device')
610
            else:
611
                log.warn(f'Device "{self.app.config["device_name"]}" is not in devices list!')
612
        driver_buf = ctypes.c_int(0)
613
        self.check_result_warn(self.fmod.FMOD_System_GetDriver(
614
            self.sys, driver_buf
615
        ), 'Failed to get current audio device')
616
        try:
617
            self.current_device_name = self.device_names[driver_buf.value]
618
        except IndexError:
619
            self.current_device_name = ''
620

621
    def get_audio_devices_names(self) -> list:
622
        return self.device_names
623

624
    def get_current_audio_device_name(self) -> str:
625
        return self.current_device_name
626

627
    def open_music(self, fp: str) -> FmodExMusic:
628
        mus = ctypes.c_void_p()
629
        self.check_result_err(self.fmod.FMOD_System_CreateStream(
630
            self.sys,
631
            self.app.stb(fp),
632
            self.fmod.FMOD_LOOP_OFF | self.fmod.FMOD_2D | self.fmod.FMOD_CREATE_STREAM | self.fmod.FMOD_LOW_MEM,
633
            None,
634
            mus
635
        ), 'Failed to open music')
636
        return FmodExMusic(self, self.fmod, fp, mus)
637

638
    def quit(self) -> None:
639
        self.check_result_warn(self.fmod.FMOD_System_Close(self.sys), 'Failed to close system')
640
        self.check_result_warn(self.fmod.FMOD_System_Release(self.sys), 'Failed to release system')
641

642
    def destroy(self) -> None:
643
        self.sys = None
644
        self.fmod = None
645
        self.app = None
646

647
    def check_result_warn(self, result: int, error_msg: str = 'Error') -> None:
648
        if result == self.fmod.FMOD_OK:
649
            return
650
        log.warn(f'{error_msg} ({(self.fmod.error_map.get(result) or "Unknown error.")[:-1]})')
651

652
    def check_result_err(self, result: int, error_msg: str = 'Error') -> None:
653
        if result == self.fmod.FMOD_OK:
654
            return
655
        raise RuntimeError(f'{error_msg} ({(self.fmod.error_map.get(result) or "Unknown error.")[:-1]})')
656

657
    def get_audio_drivers(self) -> list:
658
        result = []
659
        if sys.platform == 'win32':
660
            result.append('wasapi')
661
            result.append('asio')
662
        else:
663
            result.append('pulseaudio')
664
            result.append('alsa')
665
        result.append('disk')
666
        result.append('dummy')
667
        return result
668

669
    def get_current_audio_driver(self) -> str:
670
        output_buf = ctypes.c_int(0)
671
        self.check_result_warn(self.fmod.FMOD_System_GetOutput(self.sys, output_buf))
672
        r_map = {x: k for k, x in self.fmod.output_map.items()}
673
        return r_map.get(output_buf.value) or 'none'
674

675
    def update(self) -> None:
676
        self.check_result_warn(self.fmod.FMOD_System_Update(self.sys), 'Failed to update system')
677

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.