# Capturing Unity game’s FMOD output (5.1 / Busses)

**URL:** <https://qa.fmod.com/t/capturing-unity-game-s-fmod-output-5-1-busses/20485>\
**Category:** Unity\
**Tags:** unity\
**Created:** [July 31, 2023, 7:45am UTC](https://qa.fmod.com/t/capturing-unity-game-s-fmod-output-5-1-busses/20485 "2023-07-31T07:45:41Z")\
**Posts on this page:** 1\
**Showing post:** 7

<div class="post-metadata">

**Author:** ![Leah\_FMOD](https://yyz2.discourse-cdn.com/flex036/user_avatar/qa.fmod.com/leah_fmod/32/3685_2.png) [@Leah\_FMOD](https://qa.fmod.com/u/Leah_FMOD)\
**Post date:** [May 14, 2024, 4:51am UTC](https://qa.fmod.com/t/capturing-unity-game-s-fmod-output-5-1-busses/20485/7 "2024-05-14T04:51:17Z")

</div>

Thanks for your patience @xantomen and @dada_da.

The following `DSPCaptureAndWrite` script should serve as a straightforward example of how to accomplish what you want. It adds a capture DSP to a specified Bus’s underlying ChannelGroup (similar to our Unity DSP Capture example), and captures the audio data in memory. Then, on “R” being input, it will write the captured audio data to WAV file a specified file path and clear the audio data stored in memory.

> **DSPCaptureAndWrite.cs**
>
> ```auto
> using System;
> using UnityEngine;
> using System.Runtime.InteropServices;
> using System.Collections.Generic;
> using System.IO;
> 
> class DSPCaptureAndWrite : MonoBehaviour
> {
> [SerializeField]
> private string mFilePath;
> [SerializeField]
> private string mBusPath;
> 
> private FMOD.DSP_READ_CALLBACK mReadCallback;
> private GCHandle mObjHandle;
> private FMOD.DSP_DESCRIPTION mDSPDescription;
> 
> private FMOD.DSP mDSP;
> private FMOD.ChannelGroup mCG;
> private List<float> mAudioData;
> 
> private int mSampleRate;
> private int mNumChannels;
> private int mBitDepth = 32; // Assumes data will be 32 bit PCM Float, which it will automatically be in the FMOD Mixer graph
> 
> private bool mDSPsCreated;
> private bool mRecording;
> private FileStream fs;
> private BinaryWriter bw;
> 
> [AOT.MonoPInvokeCallback(typeof(FMOD.DSP_READ_CALLBACK))]
> static FMOD.RESULT CaptureDSPReadCallback(ref FMOD.DSP_STATE dsp_state, IntPtr inbuffer, IntPtr outbuffer, uint length, int inchannels, ref int outchannels)
> {
> // Copy the input buffer to an intermediate buffer
> int lengthElements = (int)length * inchannels;
> float[] data = new float[lengthElements];
> Marshal.Copy(inbuffer, data, 0, lengthElements);
> 
> // Get instance of DSPCaptureAndWrite from user data assigned to DSP
> FMOD.DSP_STATE_FUNCTIONS functions = (FMOD.DSP_STATE_FUNCTIONS)Marshal.PtrToStructure(dsp_state.functions, typeof(FMOD.DSP_STATE_FUNCTIONS));
> IntPtr userData;
> functions.getuserdata(ref dsp_state, out userData);
> if (userData != IntPtr.Zero)
> {
> GCHandle objHandle = GCHandle.FromIntPtr(userData);
> DSPCaptureAndWrite obj = objHandle.Target as DSPCaptureAndWrite;
> 
> // If currently recording, add buffer contents to recorded audio data
> if (obj.mRecording)
> {
> obj.mNumChannels = inchannels;
> obj.mAudioData.AddRange(data);
> }
> }
> 
> // Copy the input buffer to the output buffer so we can still hear it
> Marshal.Copy(data, 0, outbuffer, lengthElements);
> 
> return FMOD.RESULT.OK;
> }
> 
> void Start()
> {
> // Assign the DSP capture callback to a member variable to avoid garbage collection
> mReadCallback = CaptureDSPReadCallback;
> 
> // Get DSP/Audio info and initialize list used to to store capture audio
> uint bufferLength;
> FMODUnity.RuntimeManager.CoreSystem.getDSPBufferSize(out _, out _);
> FMODUnity.RuntimeManager.CoreSystem.getSoftwareFormat(out mSampleRate, out _, out _);
> mAudioData = new();
> 
> // Get a handle to this object to pass into the callback
> mObjHandle = GCHandle.Alloc(this, GCHandleType.Pinned);
> if (mObjHandle != null)
> {
> // Define a basic DSP that receives a callback each mix to capture audio
> mDSPDescription = new FMOD.DSP_DESCRIPTION();
> mDSPDescription.numinputbuffers = 1;
> mDSPDescription.numoutputbuffers = 1;
> mDSPDescription.read = mReadCallback;
> mDSPDescription.userdata = GCHandle.ToIntPtr(mObjHandle);
> }
> else
> {
> Debug.LogWarningFormat("FMOD: Unable to create a GCHandle: mObjHandle");
> }
> 
> // Don't start recording until DSPs have been created
> mRecording = false;
> mDSPsCreated = false;
> 
> }
> 
> void Update()
> {
> // Bus' underlying ChannelGroup needs to be active before DSPs can be added
> if (!mDSPsCreated)
> {
> FMOD.Studio.Bus bus = FMODUnity.RuntimeManager.GetBus(mBusPath);
> mRecording = AddDSP(bus);
> mDSPsCreated = mRecording;
> if (mRecording) Debug.Log("FMOD: Started capturing audio data");
> }
> 
> if (mDSPsCreated && mRecording)
> {
> // When recording, press R to stop writing to file
> if (Input.GetKey(KeyCode.R))
> {
> Debug.Log("FMOD: Stopped capturing audio data, writing audio to file at " + mFilePath);
> mRecording = false;
> fs = File.Create(mFilePath);
> bw = new BinaryWriter(fs);
> WriteWavHeader(mAudioData.Count);
> byte[] bytes = new byte[mAudioData.Count * 4];
> Buffer.BlockCopy(mAudioData.ToArray(), 0, bytes, 0, bytes.Length);
> fs.Write(bytes);
> fs.Close();
> bw.Close();
> mAudioData.Clear();
> }
> 
>             
> }
> }
> 
> bool AddDSP(FMOD.Studio.Bus bus)
> {
> FMOD.DSP captureDSP = new FMOD.DSP();
> FMOD.ChannelGroup cg;
> if (bus.getChannelGroup(out cg) == FMOD.RESULT.OK)
> {
> if (FMODUnity.RuntimeManager.CoreSystem.createDSP(ref mDSPDescription, out captureDSP) == FMOD.RESULT.OK)
> {
> if (cg.addDSP(0, captureDSP) != FMOD.RESULT.OK)
> {
> Debug.LogWarningFormat("FMOD: Unable to add DSP to the bus' channel group");
> }
> else
> {
> mDSP = captureDSP;
> mCG = cg;
> return true;
> }
> }
> else
> {
> Debug.LogWarningFormat("FMOD: Unable to create a DSP");
> }
> }
> else
> {
> Debug.LogWarningFormat("FMOD: Unable to get bus' channel group");
> }
> captureDSP.release();
> return false;
> }
> 
> void RemoveDSP()
> {
> // If the DSP is valid, remove it from the ChannelGroup and release
> if (mDSP.hasHandle())
> {
> mCG.removeDSP(mDSP);
> mDSP.release();
> }
> }
> 
> void OnDestroy()
> {
> if (mObjHandle != null)
> {
> RemoveDSP();
> mObjHandle.Free();
> }
> }
> 
> void WriteWavHeader(int length)
> {
> bw.Seek(0, SeekOrigin.Begin);
> 
> bw.Write(System.Text.Encoding.ASCII.GetBytes("RIFF")); //RIFF 4 bytes chars
> bw.Write(32 + length * 4 - 8); //File Size (after this chunk) 4 bytes int (32 for rest of header + wave data)
> 
> bw.Write(System.Text.Encoding.ASCII.GetBytes("WAVEfmt ")); //WAVEfmt 8 bytes chars
> bw.Write(16); //Length of above fmt data 4 bytes int
> bw.Write((short)3); //Format 1 is PCM 2 bytes short
> bw.Write((short)mNumChannels); //Number of Channels 2 bytes short
> bw.Write(mSampleRate); //Sample Rate 4 bytes int
> bw.Write(mSampleRate * mBitDepth / 8 * mNumChannels); // 4 bytes int
> bw.Write((short)(mBitDepth / 8 * mNumChannels)); // 2 bytes short
> bw.Write((short)mBitDepth); //Bits per sample 2 bytes short
> 
> bw.Write(System.Text.Encoding.ASCII.GetBytes("data")); //data 4 bytes chars
> bw.Write(length * 4); //Size of data section 4 bytes int
> }
> }
> 
> ```

Note that an instance of this class will only capture audio from a single bus. If you want to capture each of your three buses individually, you can just use three instances of this class and write the audio data to seperate WAV files.

If you want to capture and **combine** the audio from each of your buses, the script will require a little modification, but it should be as simple as placing an instance of the capture DSP on each bus, storing the captured audio data in three separate lists, and then summing each element of the three lists together before writing to file.

If you run into any issues using the script, please feel free to let me know.

---

_[View the full topic](https://qa.fmod.com/t/capturing-unity-game-s-fmod-output-5-1-busses/20485)._
