diff --git a/Source/Editor/Content/Import/AudioImportSettings.cs b/Source/Editor/Content/Import/AudioImportSettings.cs index 2fc0a1795..e0bfb6e20 100644 --- a/Source/Editor/Content/Import/AudioImportSettings.cs +++ b/Source/Editor/Content/Import/AudioImportSettings.cs @@ -1,144 +1,52 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. -using System.ComponentModel; -using System.Reflection; -using System.Runtime.InteropServices; +using System.Collections.Generic; +using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.Scripting; using FlaxEngine; -using FlaxEngine.Interop; +using FlaxEngine.Tools; + +namespace FlaxEngine.Tools +{ + partial class AudioTool + { + partial struct Options + { + private bool ShowBtiDepth => Format != AudioFormat.Vorbis; + } + } +} + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Custom editor for . + /// + [CustomEditor(typeof(FlaxEngine.Tools.AudioTool.Options)), DefaultEditor] + public class AudioToolOptionsEditor : GenericEditor + { + /// + protected override List GetItemsForType(ScriptType type) + { + // Show both fields and properties + return GetItemsForType(type, true, true); + } + } +} namespace FlaxEditor.Content.Import { /// /// Proxy object to present audio import settings in . /// + [HideInEditor] public class AudioImportSettings { /// - /// A custom set of bit depth audio import sizes. + /// The settings data. /// - public enum CustomBitDepth - { - /// - /// The 8. - /// - _8 = 8, - - /// - /// The 16. - /// - _16 = 16, - - /// - /// The 24. - /// - _24 = 24, - - /// - /// The 32. - /// - _32 = 32, - } - - /// - /// Converts the bit depth to enum. - /// - /// The bit depth. - /// The converted enum. - public static CustomBitDepth ConvertBitDepth(int f) - { - FieldInfo[] fields = typeof(CustomBitDepth).GetFields(); - for (int i = 0; i < fields.Length; i++) - { - var field = fields[i]; - if (field.Name.Equals("value__")) - continue; - - if (f == (int)field.GetRawConstantValue()) - return (CustomBitDepth)f; - } - - return CustomBitDepth._16; - } - - /// - /// The audio data format to import the audio clip as. - /// - [EditorOrder(10), DefaultValue(AudioFormat.Vorbis), Tooltip("The audio data format to import the audio clip as.")] - public AudioFormat Format { get; set; } = AudioFormat.Vorbis; - - /// - /// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality. - /// - [EditorOrder(15), DefaultValue(0.4f), Limit(0, 1, 0.01f), Tooltip("The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality.")] - public float CompressionQuality { get; set; } = 0.4f; - - /// - /// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds). - /// - [EditorOrder(20), DefaultValue(false), Tooltip("Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds).")] - public bool DisableStreaming { get; set; } = false; - - /// - /// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format. - /// - [EditorOrder(30), DefaultValue(false), EditorDisplay(null, "Is 3D"), Tooltip("Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format.")] - public bool Is3D { get; set; } = false; - - /// - /// The size of a single sample in bits. The clip will be converted to this bit depth on import. - /// - [EditorOrder(40), DefaultValue(CustomBitDepth._16), Tooltip("The size of a single sample in bits. The clip will be converted to this bit depth on import.")] - public CustomBitDepth BitDepth { get; set; } = CustomBitDepth._16; - - [StructLayout(LayoutKind.Sequential)] - internal struct InternalOptions - { - [MarshalAs(UnmanagedType.I1)] - public AudioFormat Format; - public byte DisableStreaming; - public byte Is3D; - public int BitDepth; - public float Quality; - } - - internal void ToInternal(out InternalOptions options) - { - options = new InternalOptions - { - Format = Format, - DisableStreaming = (byte)(DisableStreaming ? 1 : 0), - Is3D = (byte)(Is3D ? 1 : 0), - Quality = CompressionQuality, - BitDepth = (int)BitDepth, - }; - } - - internal void FromInternal(ref InternalOptions options) - { - Format = options.Format; - DisableStreaming = options.DisableStreaming != 0; - Is3D = options.Is3D != 0; - CompressionQuality = options.Quality; - BitDepth = ConvertBitDepth(options.BitDepth); - } - - /// - /// Tries the restore the asset import options from the target resource file. - /// - /// The options. - /// The asset path. - /// True settings has been restored, otherwise false. - public static bool TryRestore(ref AudioImportSettings options, string assetPath) - { - if (AudioImportEntry.Internal_GetAudioImportOptions(assetPath, out var internalOptions)) - { - // Restore settings - options.FromInternal(ref internalOptions); - return true; - } - - return false; - } + [EditorDisplay(null, EditorDisplayAttribute.InlineStyle)] + public AudioTool.Options Settings = AudioTool.Options.Default; } /// @@ -147,7 +55,7 @@ namespace FlaxEditor.Content.Import /// public partial class AudioImportEntry : AssetImportEntry { - private AudioImportSettings _settings = new AudioImportSettings(); + private AudioImportSettings _settings = new(); /// /// Initializes a new instance of the class. @@ -157,7 +65,7 @@ namespace FlaxEditor.Content.Import : base(ref request) { // Try to restore target asset Audio import options (useful for fast reimport) - AudioImportSettings.TryRestore(ref _settings, ResultUrl); + Editor.TryRestoreImportOptions(ref _settings.Settings, ResultUrl); } /// @@ -166,27 +74,23 @@ namespace FlaxEditor.Content.Import /// public override bool TryOverrideSettings(object settings) { - if (settings is AudioImportSettings o) + if (settings is AudioImportSettings s) { - _settings = o; + _settings.Settings = s.Settings; + return true; + } + if (settings is AudioTool.Options o) + { + _settings.Settings = o; return true; } - return false; } /// public override bool Import() { - return Editor.Import(SourceUrl, ResultUrl, _settings); + return Editor.Import(SourceUrl, ResultUrl, _settings.Settings); } - - #region Internal Calls - - [LibraryImport("FlaxEngine", EntryPoint = "AudioImportEntryInternal_GetAudioImportOptions", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] - [return: MarshalAs(UnmanagedType.U1)] - internal static partial bool Internal_GetAudioImportOptions(string path, out AudioImportSettings.InternalOptions result); - - #endregion } } diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 06a6f0979..e0c460d0c 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -935,21 +935,6 @@ namespace FlaxEditor Animation = 11, } - /// - /// Imports the audio asset file to the target location. - /// - /// The source file path. - /// The result asset file path. - /// The settings. - /// True if importing failed, otherwise false. - public static bool Import(string inputPath, string outputPath, AudioImportSettings settings) - { - if (settings == null) - throw new ArgumentNullException(); - settings.ToInternal(out var internalOptions); - return Internal_ImportAudio(inputPath, outputPath, ref internalOptions); - } - /// /// Serializes the given object to json asset. /// @@ -1667,10 +1652,6 @@ namespace FlaxEditor [return: MarshalAs(UnmanagedType.U1)] internal static partial bool Internal_CloneAssetFile(string dstPath, string srcPath, ref Guid dstId); - [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_ImportAudio", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] - [return: MarshalAs(UnmanagedType.U1)] - internal static partial bool Internal_ImportAudio(string inputPath, string outputPath, ref AudioImportSettings.InternalOptions options); - [LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetAudioClipMetadata", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))] internal static partial void Internal_GetAudioClipMetadata(IntPtr obj, out int originalSize, out int importedSize); diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index afcbb740a..d2d2f216a 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -50,43 +50,6 @@ Guid ManagedEditor::ObjectID(0x91970b4e, 0x99634f61, 0x84723632, 0x54c776af); -// Disable warning C4800: 'const byte': forcing value to bool 'true' or 'false' (performance warning) -#if defined(_MSC_VER) -#pragma warning( push ) -#pragma warning( disable : 4800) -#endif - -struct InternalAudioOptions -{ - AudioFormat Format; - byte DisableStreaming; - byte Is3D; - int32 BitDepth; - float Quality; - - static void Convert(InternalAudioOptions* from, ImportAudio::Options* to) - { - to->Format = from->Format; - to->DisableStreaming = from->DisableStreaming; - to->Is3D = from->Is3D; - to->BitDepth = from->BitDepth; - to->Quality = from->Quality; - } - - static void Convert(ImportAudio::Options* from, InternalAudioOptions* to) - { - to->Format = from->Format; - to->DisableStreaming = from->DisableStreaming; - to->Is3D = from->Is3D; - to->BitDepth = from->BitDepth; - to->Quality = from->Quality; - } -}; - -#if defined(_MSC_VER) -#pragma warning( pop ) -#endif - // Pack log messages into a single scratch buffer to reduce dynamic memory allocations CriticalSection CachedLogDataLocker; Array CachedLogData; @@ -295,16 +258,6 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_CanImport(MString* extensionObj) return importer ? MUtils::ToString(importer->ResultExtension) : nullptr; } -DEFINE_INTERNAL_CALL(bool) EditorInternal_ImportAudio(MString* inputPathObj, MString* outputPathObj, InternalAudioOptions* optionsObj) -{ - ImportAudio::Options options; - InternalAudioOptions::Convert(optionsObj, &options); - String inputPath, outputPath; - MUtils::ToString(inputPathObj, inputPath); - MUtils::ToString(outputPathObj, outputPath); - return ManagedEditor::Import(inputPath, outputPath, &options); -} - DEFINE_INTERNAL_CALL(void) EditorInternal_GetAudioClipMetadata(AudioClip* clip, int32* originalSize, int32* importedSize) { INTERNAL_CALL_CHECK(clip); @@ -765,24 +718,6 @@ DEFINE_INTERNAL_CALL(MTypeObject*) CustomEditorsUtilInternal_GetCustomEditor(MTy return CustomEditorsUtil::GetCustomEditor(targetType); } -DEFINE_INTERNAL_CALL(bool) AudioImportEntryInternal_GetAudioImportOptions(MString* pathObj, InternalAudioOptions* result) -{ - String path; - MUtils::ToString(pathObj, path); - FileSystem::NormalizePath(path); - - ImportAudio::Options options; - if (ImportAudio::TryGetImportOptions(path, options)) - { - // Convert into managed storage - InternalAudioOptions::Convert(&options, result); - - return true; - } - - return false; -} - DEFINE_INTERNAL_CALL(MArray*) LayersAndTagsSettingsInternal_GetCurrentLayers(int* layersCount) { *layersCount = Math::Max(1, Level::GetNonEmptyLayerNamesCount()); @@ -830,3 +765,15 @@ bool ManagedEditor::TryRestoreImportOptions(ModelTool::Options& options, String FileSystem::NormalizePath(assetPath); return ImportModelFile::TryGetImportOptions(assetPath, options); } + +bool ManagedEditor::Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options) +{ + return Import(inputPath, outputPath, (void*)&options); +} + +bool ManagedEditor::TryRestoreImportOptions(AudioTool::Options& options, String assetPath) +{ + // Get options from model + FileSystem::NormalizePath(assetPath); + return ImportAudio::TryGetImportOptions(assetPath, options); +} diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index ddd94b47e..93d1723a3 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -7,6 +7,7 @@ #include "Engine/ShadowsOfMordor/Types.h" #include "Engine/Tools/TextureTool/TextureTool.h" #include "Engine/Tools/ModelTool/ModelTool.h" +#include "Engine/Tools/AudioTool/AudioTool.h" namespace CSG { @@ -190,6 +191,25 @@ public: API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) ModelTool::Options& options, String assetPath); #endif +#if COMPILE_WITH_AUDIO_TOOL + /// + /// Imports the audio asset file to the target location. + /// + /// The source file path. + /// The result asset file path. + /// The import settings. + /// True if importing failed, otherwise false. + API_FUNCTION() static bool Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options); + + /// + /// Tries the restore the asset import options from the target resource file. + /// + /// The options. + /// The asset path. + /// True settings has been restored, otherwise false. + API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) AudioTool::Options& options, String assetPath); +#endif + private: void OnEditorAssemblyLoaded(MAssembly* assembly); diff --git a/Source/Editor/Windows/Assets/AudioClipWindow.cs b/Source/Editor/Windows/Assets/AudioClipWindow.cs index f5f244c58..e6fabc165 100644 --- a/Source/Editor/Windows/Assets/AudioClipWindow.cs +++ b/Source/Editor/Windows/Assets/AudioClipWindow.cs @@ -116,7 +116,7 @@ namespace FlaxEditor.Windows.Assets _window = window; // Try to restore target asset AudioClip import options (useful for fast reimport) - AudioImportSettings.TryRestore(ref ImportSettings, window.Item.Path); + Editor.TryRestoreImportOptions(ref ImportSettings.Settings, window.Item.Path); // Prepare restore data PeekState(); diff --git a/Source/Engine/ContentImporters/ImportAudio.cpp b/Source/Engine/ContentImporters/ImportAudio.cpp index 10915b2d1..7a8c952e4 100644 --- a/Source/Engine/ContentImporters/ImportAudio.cpp +++ b/Source/Engine/ContentImporters/ImportAudio.cpp @@ -18,32 +18,6 @@ #include "Engine/Tools/AudioTool/OggVorbisDecoder.h" #include "Engine/Tools/AudioTool/OggVorbisEncoder.h" #include "Engine/Serialization/JsonWriters.h" -#include "Engine/Scripting/Enums.h" - -String ImportAudio::Options::ToString() const -{ - return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, BitDepth); -} - -void ImportAudio::Options::Serialize(SerializeStream& stream, const void* otherObj) -{ - SERIALIZE_GET_OTHER_OBJ(ImportAudio::Options); - - SERIALIZE(Format); - SERIALIZE(DisableStreaming); - SERIALIZE(Is3D); - SERIALIZE(Quality); - SERIALIZE(BitDepth); -} - -void ImportAudio::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) -{ - DESERIALIZE(Format); - DESERIALIZE(DisableStreaming); - DESERIALIZE(Is3D); - DESERIALIZE(Quality); - DESERIALIZE(BitDepth); -} bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options) { @@ -112,16 +86,14 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder& sampleBuffer.Link(audioData.Get()); // Convert bit depth if need to - if (options.BitDepth != static_cast(info.BitDepth)) + uint32 outputBitDepth = (uint32)options.BitDepth; + if (outputBitDepth != info.BitDepth) { - const uint32 outBufferSize = info.NumSamples * (options.BitDepth / 8); + const uint32 outBufferSize = info.NumSamples * (outputBitDepth / 8); sampleBuffer.Allocate(outBufferSize); - - AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), options.BitDepth, info.NumSamples); - - info.BitDepth = options.BitDepth; + AudioTool::ConvertBitDepth(audioData.Get(), info.BitDepth, sampleBuffer.Get(), outputBitDepth, info.NumSamples); + info.BitDepth = outputBitDepth; bytesPerSample = info.BitDepth / 8; - bufferSize = outBufferSize; } @@ -149,7 +121,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder& context.Data.Header.Chunks[chunkIndex]->Data.Copy(dataPtr, dataSize); #define WRITE_DATA(chunkIndex, dataPtr, dataSize) \ - samplesPerChunk[chunkIndex] = (dataSize) / (options.BitDepth / 8); \ + samplesPerChunk[chunkIndex] = (dataSize) / (outputBitDepth / 8); \ switch (options.Format) \ { \ case AudioFormat::Raw: \ diff --git a/Source/Engine/ContentImporters/ImportAudio.h b/Source/Engine/ContentImporters/ImportAudio.h index fb4ee64e3..3f3eda5ed 100644 --- a/Source/Engine/ContentImporters/ImportAudio.h +++ b/Source/Engine/ContentImporters/ImportAudio.h @@ -3,12 +3,12 @@ #pragma once #include "Types.h" -#include "Engine/Tools/AudioTool/AudioDecoder.h" -#include "Engine/Core/ISerializable.h" -#include "Engine/Audio/Config.h" #if COMPILE_WITH_ASSETS_IMPORTER +#include "Engine/Tools/AudioTool/AudioTool.h" +#include "Engine/Tools/AudioTool/AudioDecoder.h" + /// /// Enable/disable caching audio import options /// @@ -20,23 +20,7 @@ class ImportAudio { public: - /// - /// Importing audio options - /// - struct Options : public ISerializable - { - AudioFormat Format = AudioFormat::Vorbis; - bool DisableStreaming = false; - bool Is3D = false; - int32 BitDepth = 16; - float Quality = 0.4f; - - String ToString() const; - - // [ISerializable] - void Serialize(SerializeStream& stream, const void* otherObj) override; - void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; - }; + typedef AudioTool::Options Options; public: /// diff --git a/Source/Engine/Tools/AudioTool/AudioTool.cpp b/Source/Engine/Tools/AudioTool/AudioTool.cpp index 89d613116..3136a5a0d 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.cpp +++ b/Source/Engine/Tools/AudioTool/AudioTool.cpp @@ -1,14 +1,49 @@ // Copyright (c) 2012-2023 Wojciech Figat. All rights reserved. +#if COMPILE_WITH_AUDIO_TOOL + #include "AudioTool.h" #include "Engine/Core/Core.h" #include "Engine/Core/Memory/Allocation.h" +#if USE_EDITOR +#include "Engine/Serialization/Serialization.h" +#include "Engine/Scripting/Enums.h" +#endif #define CONVERT_TO_MONO_AVG 1 #if !CONVERT_TO_MONO_AVG #include "Engine/Core/Math/Math.h" #endif +#if USE_EDITOR + +String AudioTool::Options::ToString() const +{ + return String::Format(TEXT("Format:{}, DisableStreaming:{}, Is3D:{}, Quality:{}, BitDepth:{}"), ScriptingEnum::ToString(Format), DisableStreaming, Is3D, Quality, (int32)BitDepth); +} + +void AudioTool::Options::Serialize(SerializeStream& stream, const void* otherObj) +{ + SERIALIZE_GET_OTHER_OBJ(AudioTool::Options); + + SERIALIZE(Format); + SERIALIZE(DisableStreaming); + SERIALIZE(Is3D); + SERIALIZE(Quality); + SERIALIZE(BitDepth); +} + +void AudioTool::Options::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + DESERIALIZE(Format); + DESERIALIZE(DisableStreaming); + DESERIALIZE(Is3D); + DESERIALIZE(Quality); + DESERIALIZE(BitDepth); +} + +#endif + void ConvertToMono8(const int8* input, uint8* output, uint32 numSamples, uint32 numChannels) { for (uint32 i = 0; i < numSamples; i++) @@ -277,3 +312,5 @@ void AudioTool::ConvertFromFloat(const float* input, int32* output, uint32 numSa input++; } } + +#endif diff --git a/Source/Engine/Tools/AudioTool/AudioTool.h b/Source/Engine/Tools/AudioTool/AudioTool.h index b9525ffa7..cd61ee37a 100644 --- a/Source/Engine/Tools/AudioTool/AudioTool.h +++ b/Source/Engine/Tools/AudioTool/AudioTool.h @@ -2,16 +2,85 @@ #pragma once +#if COMPILE_WITH_AUDIO_TOOL + #include "Engine/Core/Config.h" #include "Engine/Core/Types/BaseTypes.h" +#if USE_EDITOR +#include "Engine/Core/ISerializable.h" +#endif +#include "Engine/Audio/Types.h" /// /// Audio data importing and processing utilities. /// -class FLAXENGINE_API AudioTool +API_CLASS(Namespace="FlaxEngine.Tools", Static) class FLAXENGINE_API AudioTool { -public: + DECLARE_SCRIPTING_TYPE_MINIMAL(AudioTool); +#if USE_EDITOR + +public: + /// + /// Declares the imported audio clip bit depth. + /// + API_ENUM(Attributes="HideInEditor") enum class BitDepth : int32 + { + // 8-bits per sample. + _8 = 8, + // 16-bits per sample. + _16 = 16, + // 24-bits per sample. + _24 = 24, + // 32-bits per sample. + _32 = 32, + }; + + /// + /// Audio import options. + /// + API_STRUCT(Attributes="HideInEditor") struct FLAXENGINE_API Options : public ISerializable + { + DECLARE_SCRIPTING_TYPE_MINIMAL(Options); + + /// + /// The audio data format to import the audio clip as. + /// + API_FIELD(Attributes="EditorOrder(10)") + AudioFormat Format = AudioFormat::Vorbis; + + /// + /// The audio data compression quality. Used only if target format is using compression. Value 0 means the smallest size, value 1 means the best quality. + /// + API_FIELD(Attributes="EditorOrder(20), EditorDisplay(null, \"Compression Quality\"), Limit(0, 1, 0.01f)") + float Quality = 0.4f; + + /// + /// Disables dynamic audio streaming. The whole clip will be loaded into the memory. Useful for small clips (eg. gunfire sounds). + /// + API_FIELD(Attributes="EditorOrder(30)") + bool DisableStreaming = false; + + /// + /// Checks should the clip be played as spatial (3D) audio or as normal audio. 3D audio is stored in Mono format. + /// + API_FIELD(Attributes="EditorOrder(40), EditorDisplay(null, \"Is 3D\")") + bool Is3D = false; + + /// + /// The size of a single sample in bits. The clip will be converted to this bit depth on import. + /// + API_FIELD(Attributes="EditorOrder(50), VisibleIf(nameof(ShowBtiDepth))") + BitDepth BitDepth = BitDepth::_16; + + String ToString() const; + + // [ISerializable] + void Serialize(SerializeStream& stream, const void* otherObj) override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) override; + }; + +public: /// /// Converts a set of audio samples using multiple channels into a set of mono samples. /// @@ -58,4 +127,7 @@ public: { return (input[2] << 24) | (input[1] << 16) | (input[0] << 8); } +#endif }; + +#endif