I’m working on a custom ScriptedImporter
– let’s call it LmmsImporter
– that loads project files from the LMMS sequencer as AudioClip
s. I don’t parse the project file; instead, I expect users to install LMMS, configure its location, then store their project files inside their Assets
directory. After the asset settings are configured, the import process looks like this:
- Create command-line arguments based on the properties offered by
LmmsImporter
- Get a temporary path inside the project’s
Temp
directory (not the OS’s equivalent) - Spawn an LMMS process that renders the imported project file to the path given by (2), with the arguments given by (1).
- Wait for the process to complete.
- Load the
AudioClip
at “runtime” (i.e. in an editor script, not as an imported asset) withUnityWebRequestMultimedia.GetAudioClip
- Clone the
AudioClip
. - Set the cloned
AudioClip
to be the LMMS project asset’s main object, as described here. - Save the imported asset, then delete the original audio file generated by (3).
Everything up to but not including step 7 works as expected. In fact, even step 7 appears to work as expected. But when I open up the imported AudioClip
, it has no samples. When I try to play the preview in the editor, I get this error in the editor console:
Error: Cannot create FMOD::Sound instance for resource , (Error loading file. ) UnityEngine.GUIUtility:ProcessEvent(Int32, IntPtr)
What makes this weird is that:
- The sound is properly rendered by LMMS and saved to the filesystem. Nothing unusual happens here.
- Playing the
AudioClip
inside the importer script immediately after it’s loaded (i.e. while still inOnImportAsset
) withAudioSource.PlayAtPoint
works as expected. - The data is properly cloned; I checked, the arrays are equal.
- I do not receive any errors or exceptions except those that I’ve already described, not even from LMMS.
This is an excerpt of my code with the parts I think are most relevant:
using System; using System.Diagnostics; using System.IO; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; using UnityEditor; using UnityEditor.Experimental.AssetImporters; [ScriptedImporter(1, new[] { "mmp", "mmpz" })] public class LmmsImporter : ScriptedImporter { // Properties omitted for brevity public override void OnImportAsset(AssetImportContext ctx) { var relativeTempPath = FileUtil.GetUniqueTempPathInProject(); var projectDir = Path.GetDirectoryName(Application.dataPath); var absoluteAssetPath = Path.Combine(projectDir, assetPath); var tempPath = Path.Combine(projectDir, relativeTempPath); try { using (Process lmms = new Process()) { // Process invocation details and error handling omitted for brevity // ...one successfully rendered WAV, OGG, or MP3 file later... using (var request = UnityWebRequestMultimedia.GetAudioClip(new Uri(tempPath), audioFormat)) { var handler = request.downloadHandler as DownloadHandlerAudioClip; handler.compressed = false; handler.streamAudio = false; request.timeout = LOAD_MEDIA_TIMEOUT; var operation = request.SendWebRequest(); while (!(request.isHttpError || request.isNetworkError || handler.isDone)) ; // Let the request finish, unless we get an error (possibly including a timeout) var clip = DownloadHandlerAudioClip.GetContent(request); clip.LoadAudioData(); var assetName = Path.GetFileNameWithoutExtension(assetPath); var clone = AudioClip.Create("Clip", clip.samples, clip.channels, clip.frequency, false); using (var so = new SerializedObject(clone)) { var samples = new float[clip.samples * clip.channels]; clip.GetData(samples, 0); clone.SetData(samples, 0); // Everything above this line works fine ctx.AddObjectToAsset("AudioClip", clone); ctx.SetMainObject(clone); so.ApplyModifiedProperties(); EditorUtility.SetDirty(clone); } } } } catch (Exception e) { ctx.LogImportError($"Failed import with {e.GetType()}: {e.Message}"); } } private const int LOAD_MEDIA_TIMEOUT = 5; // seconds }
I’m using Unity 2019.1.7f1 Personal, on Ubuntu 19.04. Is this a bug, or am I doing something wrong?