Get data from 4 microphones (audio interface with 4 channels)

Hi,

I am trying to get data of 4 microphones simultaneous through an audio interface having 4 channels.
I want to retrieve float data like the Unity AudioCLip.GetData () method.

I have made the code below and tried to save data in a csv but the results seem strange.

private const int maxChannels = 4;
private FMOD.System fmodSystem;
private FMOD.RESULT fmodResult;
private DriverData driverData = new DriverData();
private List<FMOD.Channel> fmodChannels = new List<FMOD.Channel>();

public class DriverData
{
    public string driverName;
    public int driverNameLentgh = 256;
    public System.Guid guid;
    public int systemRate;
    public FMOD.SPEAKERMODE speakerMode;
    public int speakerModeChannels;
    public FMOD.DRIVER_STATE driverState;
}

private void Start()
{
    InitInput();
}

private void InitInput()
{
    fmodResult = FMOD.Factory.System_Create(out fmodSystem);
    ResultCheck(fmodResult);

    fmodResult = fmodSystem.init(maxChannels, FMOD.INITFLAGS.NORMAL, System.IntPtr.Zero);
    ResultCheck(fmodResult);
}

    public List<string> GetInputs()
    {
        List<string> devices = new List<string>();
        int numSoundSources;
        int numSoundSourcesConnected;

        fmodResult = fmodSystem.getRecordNumDrivers(out numSoundSources, out numSoundSourcesConnected);
        ResultCheck(fmodResult);

        for (int i = 0; i < numSoundSources; i++)
        {
            fmodResult = fmodSystem.getRecordDriverInfo(
                i,
                out driverData.driverName,
                driverData.driverNameLentgh,
                out driverData.guid,
                out driverData.systemRate,
                out driverData.speakerMode,
                out driverData.speakerModeChannels,
                out driverData.driverState
            );
            ResultCheck(fmodResult);
            devices.Add(driverData.driverName);
        }

        return devices;
    }

    public void SelectInput(string driverName)
    {
        int numSoundSources;
        int numSoundSourcesConnected;

        fmodResult = fmodSystem.getRecordNumDrivers(out numSoundSources, out numSoundSourcesConnected);
        ResultCheck(fmodResult);

        for (int i = 0; i < numSoundSources; i++)
        {
            fmodResult = fmodSystem.getRecordDriverInfo(
                i,
                out driverData.driverName,
                driverData.driverNameLentgh,
                out driverData.guid,
                out driverData.systemRate,
                out driverData.speakerMode,
                out driverData.speakerModeChannels,
                out driverData.driverState
            );
            ResultCheck(fmodResult);

            if (driverData.driverName == driverName)
            {
                Debug.Log(
                    "NAME: <color=blue>" + driverData.driverName + "</color>" +
                    " | RATE: <color=blue>" + driverData.systemRate + "</color>" +
                    " | MODE: <color=blue>" + driverData.speakerMode + "</color>" +
                    " | CHANNELS: <color=blue>" + driverData.speakerModeChannels + "</color>" +
                    " | STATE: <color=blue>" + driverData.driverState + "</color>"
                );

                fmodSystem.setDriver(i);
                break;
            }
        }

        MicrophonesListener();
    }

    private void ResultCheck(FMOD.RESULT result)
    {
        if (result != FMOD.RESULT.OK)
            Debug.LogError(result);
    }

    private void MicrophonesListener()
    {
        FMOD.ChannelGroup fmodChannelGroup = new FMOD.ChannelGroup();

        for (int i = 0; i < driverData.speakerModeChannels; i++)
        {
            FMOD.Sound fmodSound;
            FMOD.Channel fmodChannel;
            FMOD.CREATESOUNDEXINFO exinfo = new FMOD.CREATESOUNDEXINFO();

            exinfo.cbsize = Marshal.SizeOf(typeof(FMOD.CREATESOUNDEXINFO));
            exinfo.numchannels = driverData.speakerModeChannels;
            exinfo.format = FMOD.SOUND_FORMAT.PCM16;
            exinfo.defaultfrequency = driverData.systemRate;
            exinfo.length = (uint)(exinfo.defaultfrequency * exinfo.numchannels * sizeof(ushort) * 0.5);
            exinfo.channelorder = FMOD.CHANNELORDER.ALLMONO;

            fmodResult = fmodSystem.createSound("Microphone_"+i, FMOD.MODE.LOOP_NORMAL | FMOD.MODE.OPENUSER, ref exinfo, out fmodSound);
            ResultCheck(fmodResult);

            fmodResult = fmodSystem.recordStart(i, fmodSound, true);
            ResultCheck(fmodResult);

            // Is it necessary to wait before playing sound?

            fmodResult = fmodSystem.playSound(fmodSound, fmodChannelGroup, false, out fmodChannel);
            fmodChannel.setVolume(0);
            ResultCheck(fmodResult);

            fmodChannels.Add(fmodChannel);
        }
    }
  • Are microphones properly initialized?
  • How can I correctly save the data of microphones in a csv file? (I did not put the code of the recording because it is very messy. I do not think to recover all the samples and sometimes I get NaN values.)

Many thanks for your reply.

It all appears fine, most of it lines up with our examples.

No, you don’t need to wait.

You can use Sound::lock to get access to the raw data inside the sound, which you can then write out to a file.

Thanks for your reply.
I think I’m going to the right direction.

I can record a .wav and .csv file. However, both don’t seem to match. I haven’t 44100 data per second in my .csv file. (It’s currently more than my audio file)

Here is my code:

public void SaveFile(FMOD.System system, FMOD.Sound sound)
{
    if (recording)
    {
        FMOD.RESULT result;

        result = system.getDriver(out int selectedDriver);
        ERRCHECK(result);
        result = system.getRecordPosition(selectedDriver, out uint recordpos);
        ERRCHECK(result);

        if (recordpos != lastrecordpos)
        {
            int blocklength;

            blocklength = (int)recordpos - (int)lastrecordpos;
            if (blocklength < 0)
            {
                blocklength += (int)soundlength;
            }

            sound.@lock(lastrecordpos * (uint)channelsCount * 2, (uint)blocklength * (uint)channelsCount * 2, out IntPtr ptr1, out IntPtr ptr2, out uint len1, out uint len2);

            if (ptr1 != IntPtr.Zero && len1 > 0)
            {
                byte[] buf = new byte[len1];
                Marshal.Copy(ptr1, buf, 0, (int)len1);
                datalength += (int)len1;
                fileStream.Write(buf, 0, (int)len1);

                float[] buf2 = new float[len1];
                Marshal.Copy(ptr1, buf2, 0, (int)len1);
                csvWriter.SaveData(channelsCount, buf2); // Custom method that add delimiter and writes a line every channelsCount values (4 in my case)
            }
            if (ptr2 != IntPtr.Zero && len2 > 0)
            {
                byte[] buf = new byte[len2];
                Marshal.Copy(ptr2, buf, 0, (int)len2);
                datalength += (int)len2;
                fileStream.Write(buf, 0, (int)len2);
            }

            sound.unlock(ptr1, ptr2, len1, len2);
        }

        lastrecordpos = recordpos;
    }

    system.update();
} 

This method is executed in Unity Update.
I have a doubt about my offset and length initialization of Sound::lock.

Do you have an idea of my problem?

Thanks

The first two parameters of Sound::lock are the offset and length of bytes in the sound.
You shouldn’t need to multiply it by the ‘channelsCount’.

sound.@lock(lastrecordpos, (uint)blocklength, out IntPtr ptr1, out IntPtr ptr2, out uint len1, out uint len2);

Also it looks like you only handle ptr2 for the fileStream and not the csvWriter.