private unsafe AssetBuffer ProcessAudioFileInstance(InstanceDeclaration declaration) { AssetBuffer result = new AssetBuffer(); bool isBigEndian = _platform != TargetPlatform.Win32; Node node = new Node(declaration.Node.CreateNavigator(), declaration.Document.NamespaceManager); AudioFileRuntime *audioFileRuntime; using (Tracker tracker = new Tracker((void **)&audioFileRuntime, (uint)sizeof(AudioFileRuntime), isBigEndian)) { AudioFile audioFile = new AudioFile(); audioFile.Marshal(node); string file = declaration.XmlNode.Attributes["File"].Value; bool isSound = Path.GetDirectoryName(file).ToLower().EndsWith("\\sounds"); int codec; PCAudioCompressionSetting compression = audioFile.PCCompression ?? (isSound ? PCAudioCompressionSetting.XAS : PCAudioCompressionSetting.NONE); switch (compression) { case PCAudioCompressionSetting.NONE: // would become '1' codec = 1; break; case PCAudioCompressionSetting.XAS: // would become '29', xbox XMA would be '28' codec = 29; break; case PCAudioCompressionSetting.EALAYER3: // would become '31', '30' in TW (-ealayer3_int) codec = 31; break; default: throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Internal error: xml compiler returned bad PC audio compression type of {0}", compression); } bool isStreamed = audioFile.IsStreamedOnPC ?? !isSound; if (audioFile.SubtitleStringName is null) { audioFile.SubtitleStringName = $"DIALOGEVENT:{Path.GetFileNameWithoutExtension(file)}SubTitle"; } Marshaler.Marshal(audioFile.SubtitleStringName, &audioFileRuntime->SubtitleStringName, tracker); int type = SIMEX_id(file, 0); if (type < 0) { Console.WriteLine("Warning: Unable to identify format of \"{0}\"; cannot process. (Error: {1})", file, SIMEX_getlasterr()); return(null); } if (type != 1) { Console.WriteLine("Warning: Input files must be WAVE format. Cannot process \"{0}\"", file); return(null); } SINSTANCE *instance = null; int count = SIMEX_open(file, 0, type, &instance); using (AutoSINSTANCECloser closer = new AutoSINSTANCECloser(instance)) { if (count <= 0 || instance == null) { Console.WriteLine("Warning: Could not begin audio processing of \"{0}\": {1}.", file, SIMEX_getlasterr()); return(null); } string tempFile = declaration.CustomDataPath + _tempFilenameSuffix; using (AutoCleanUpTempFiles tempFiles = new AutoCleanUpTempFiles(tempFile)) { SINSTANCE *instance2 = null; if (SIMEX_create(tempFile, 39, &instance2) == 0) { throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Internal error preparing audio output file \"{0}\" (SIMEX_create(): {1}).", tempFile, SIMEX_getlasterr()); } using (AutoSINSTANCEWCloser closer2 = new AutoSINSTANCEWCloser(instance2)) { for (int i = 0; i < count; i++) { SINFO *info = null; if (SIMEX_info(instance, &info, i) == 0) { throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Internal error reading element {0} of \"{1}\" (SIMEX_info(): {2}).", file, SIMEX_getlasterr()); } using (AutoSINFOFreer freer = new AutoSINFOFreer(info)) { if (info != null) { if (SIMEX_read(instance, info, i) == 0) { throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Internal error reading element {0} of \"{1}\" (SIMEX_read(): {2}).", file, SIMEX_getlasterr()); } if (isStreamed) { SIMEX_setplayloc(info, 4096); _tracer.TraceNote("Setting play location to streamed"); } else { SIMEX_setplayloc(info, 2048); _tracer.TraceNote("Setting play location to RAM"); } SIMEX_setcodec(info, codec); _tracer.TraceNote("Setting compression type to {0}. ", SIMEX_getsamplerepname(codec)); } if (codec == 31 || codec == 38) { int quality = audioFile.PCQuality; if (quality < 0 || quality > 100) { throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Audio file {0}: Quality parameter must be between 0 and 100", file); } SIMEX_setvbrquality(info, quality); _tracer.TraceNote("Setting compression quality to {0}", quality); } if (audioFile.PCSampleRate.HasValue) { int oldrate = SIMEX_getsamplerate(info); int newrate = audioFile.PCSampleRate.Value; if (oldrate != newrate) { if (newrate < 400 || newrate > 96000) { throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Audio file {0}: Sample rate must be between 400 and 96000", file); } _tracer.TraceNote("Resampling from {0} to {1} ", oldrate, newrate); SIMEX_resample(info, newrate); int rate = SIMEX_getsamplerate(info); if (newrate != rate) { Console.WriteLine("Warning: Downsampling of audio file {0} not completely sucessful. Wanted final sample of {1}Hz but got {2}Hz", file, newrate, rate); } } } audioFileRuntime->NumberOfChannels = SIMEX_getchannelconfig(info); audioFileRuntime->NumberOfSamples = SIMEX_getnumsamples(info); audioFileRuntime->SampleRate = SIMEX_getsamplerate(info); if (audioFileRuntime->NumberOfChannels != 1 && audioFileRuntime->NumberOfChannels != 2 && audioFileRuntime->NumberOfChannels != 4 && audioFileRuntime->NumberOfChannels != 6) { Console.WriteLine("Warning: Audio file {0} has {1} channels. The only supported channel counts are 1, 2, 4, and 6; sample will probably use only the first channel in the engine", file, audioFileRuntime->NumberOfChannels); } if (SIMEX_write(instance2, info, i) == 0) { throw new BinaryAssetBuilderException(ErrorCode.InternalError, "Internal error writing element {0} of \"{1}\" (SIMEX_write(): {2}).", i, tempFile, SIMEX_getlasterr()); } } } string snr = tempFile + ".snr"; byte[] buffer; using (Stream stream = File.Open(snr, FileMode.Open, FileAccess.Read, FileShare.Read)) { buffer = new byte[stream.Length]; stream.Read(buffer, 0, buffer.Length); } if (isStreamed) { string sns = tempFile + ".sns"; if (File.Exists(declaration.CustomDataPath)) { File.Delete(declaration.CustomDataPath); } _tracer.TraceNote("Creating output file {0}\n", declaration.CustomDataPath); File.Move(sns, declaration.CustomDataPath); audioFileRuntime->HeaderDataSize = buffer.Length; fixed(byte *pBuffer = &buffer[0]) { using (Tracker.Context context = tracker.Push(&audioFileRuntime->HeaderData, 1u, (uint)buffer.Length)) { Native.MsVcRt.MemCpy(new IntPtr(audioFileRuntime->HeaderData), new IntPtr(pBuffer), new Native.SizeT(buffer.Length)); } } } else { if (File.Exists(declaration.CustomDataPath)) { File.Delete(declaration.CustomDataPath); } _tracer.TraceNote("Creating output file {0}\n", declaration.CustomDataPath); File.Move(snr, declaration.CustomDataPath); } FinalizeTracker(tracker, result); } } } } return(result); }