I have been reading documentation and trying to understand the system, but I am still confused. I want to record the output of FMOD on Android and iOS devices so that, later on, the user can share the recording output.
We are working on a music app, and the process for the recording should be as follows:
Stop playback
Set FMOD to record
Record a file based on the length of the song
Stop recording
Resume original playback behaviour
It would be really helpful if anyone has any examples, sample code, or links to other forums that handle something similar to this. Thank you!
There’s a couple of ways to accomplish what you’re trying to do.
The simplest would be to essentially implement a “manual” WAVWRITER by placing a Capture DSP somewhere in your system to retrieve audio data and store it in memory, and then writing the captured data to a WAV file as required at a later point. A simple example script that does this can be found here: Capturing Unity game’s FMOD output (5.1 / Busses) - #7 by Leah_FMOD
A step up in complexity would be to use actually the WAVWRITER output, which will, if you’re using the FMOD for Unity integration, require you to make some changes to the integration code. A simple example of using the WAVWRITER output with the FMOD integration can be found here: Capturing Unity game’s FMOD output (5.1 / Busses) - #2 by Leah_FMOD
For your purposes, however, this will likely require you to reinitialize the existing system, or initialize a separate system, in order to both pass the intended WAV filepath as extradriverdata for Studio::System::initialize, and to set the output to WAVWRITER (the output can be changed after init on Android, but not on iOS). This may be more or less difficult depending on the complexity of your music app - i.e. if the “state” of events and parameters in your music app are essentially “saved” outside of the FMOD system, and you definitely don’t require simultaneous playback AND writing, it should be on the simpler side of thing to initialize a new system and swap to it to write to WAV. If this isn’t the case, it may be more complex.
The previous two methods both work in real-time. If you’re able to get WAVWRITER working for you, the next step up would be to use WAVWRITER_NRT instead, which will allow you drive audio mixing from Studio::System::update and record/write as fast as possible, without needing to wait for events to play out in real-time.
Hello Leah, Thank you for your answer! Yes, I had previously checked this post and attempted to follow the implementation and changes to the CoreSystem script; however, once I started going through the code, the changes and lines of code you mentioned were not present, or the changes caused major compilation issues. We are currently using FMOD 2.02 and Unity integration 2.02.20.
Preferably, the NRTWAVEWRITER is the method I want to use from the get-go. I don’t need the user to hear back their audio, as they have already been modifying it. All I need is to grab those changes they made and export them as quickly as possible; there is no need for playback at the moment of recording. Once the recording is done, all that is needed is to reset the original system back to the original playback mode.
Thank you for your help in this matter and I look forward to your reply!
To use the WAVWRITER_NRT output type you will need to make a minor change to the RuntimeManager initialization code to allow it to mix from update:
//Assets\Plugins\FMOD\src\RuntimeManager.cs
private FMOD.RESULT Initialize()
{
... // Line ~376
+ result = studioSystem.initialize(virtualChannels, studioInitFlags, FMOD.INITFLAGS.MIX_FROM_UPDATE, IntPtr.Zero);
if (result != FMOD.RESULT.OK && initResult == FMOD.RESULT.OK)
...
}
With that you can set you can now switch between the WAVWRITER_NRT and other output types, depending on whether you want to export or playback your scene in Unity. Here is a basic script demonstrating how to export the audio from a Unity scene using WAVWRITER_NRT:
Bounce.cs
using UnityEngine;
public class Bounce : MonoBehaviour
{
[SerializeField]
bool bounce = false;
[SerializeField]
int durationSeconds = 25;
void Start()
{
if (bounce)
{
FMODUnity.RuntimeManager.CoreSystem.getOutput(out FMOD.OUTPUTTYPE output);
FMODUnity.RuntimeManager.CoreSystem.getMasterChannelGroup(out FMOD.ChannelGroup cg);
cg.getDSPClock(out ulong child, out _);
FMODUnity.RuntimeManager.CoreSystem.getSoftwareFormat(out int sampleRate, out _, out _);
ulong future = child + (ulong)sampleRate * (uint)durationSeconds;
FMODUnity.RuntimeManager.CoreSystem.setOutput(FMOD.OUTPUTTYPE.WAVWRITER_NRT);
while (child < future)
{
FMODUnity.RuntimeManager.StudioSystem.update();
cg.getDSPClock(out child, out _);
}
FMODUnity.RuntimeManager.CoreSystem.setOutput(output);
}
}
}
That will then produce a file called “fmodoutput.wav”, which I believe should be the application’s persistent data path in Unity. Otherwise you could potentially override the FMOD file system with System::setFileSystem to save it to a different location without reinitializing your system object just to change the path.
Following this implementation and based on some of the other posts I’ve seen from Leah and Mathew, how would I rename the file to something more meaningful? I have seen in their answers that to do that, the DSP system gets initialized, and the name is passed as part of the .wav header; however, in this case, a new system is not being initialized.
Is there a specific callback that has to be used, or would it be better to directly grab the file and use the C# native file stream library to rename the file?
It appears that the WAVWRITER output types bypass the user-defined filesystem (System::setFileSystem / System::attachFileSystem), so it looks like renaming the file immediately after exporting using regular C# IO is the only option other than reinitializing the system.
Thank you so much for the follow-up, Jeff! That makes things easier in that sense. I was getting scared looking at the callback implementation I have seen on other posts. Thank you for all your help in this matter; I really appreciate it!