Search Unity

Audio Correct way to use the Microphone class to record multiple AudioClip's

Discussion in 'Audio & Video' started by halilkayim, May 31, 2018.

  1. halilkayim

    halilkayim

    Joined:
    Feb 27, 2018
    Posts:
    21
    I have an iOS application and I want to let the user record audio whenever they push a button, and stop recording on a second press. I don't know if I'm using the Microphone class correctly, but there isn't any detailed information about it on the documentation page. My code is as follows:

    public static class MicRecorder 
    {
    public static AudioClip StartRecording(string deviceName = null)
    {
    var audioClip = UnityEngine.Microphone.Start(deviceName, true, 15, 44100);
    while (UnityEngine.Microphone.GetPosition(deviceName) <= 0) ;

    return audioClip;
    }

    public static void StopRecording(string deviceName = null)
    {
    UnityEngine.Microphone.End(deviceName);
    }
    }


    The first time I press the button, the audio clip turns out fine. However, after I stop recording and then press the button again, I get an error with message:
    I searched for this error code but no one seems to have gotten it, or asked about it anywhere.

    What is the correct way to use the Microphone API? Should Microphone.Start() be called just once in the lifecycle of my app? If so, how can I create multiple AudioClip's?
     
  2. halilkayim

    halilkayim

    Joined:
    Feb 27, 2018
    Posts:
    21
    So I searched around a bit more and turns out this error comes from FMOD, the audio engine Unity uses. Unfortunately, it's not an open source project, so I don't know what exactly causes it.

    I believe this should be a fairly easy thing to achieve, however I can't seem to get anywhere. I would really appreciate it if I could get some more information on this.
     
  3. halilkayim

    halilkayim

    Joined:
    Feb 27, 2018
    Posts:
    21
    The problem seems to be gone after unchecking Prepare iOS for Recording in Player Settings. Edit: This is a really bad solution, see my next comment for the correct way to handle this.

    I would still like to know more about why this happens though. It seems like a Unity bug since I'm not using any third party tool and what I'm trying to achieve should be pretty straightforward.
     
    Last edited: Jun 7, 2018
  4. halilkayim

    halilkayim

    Joined:
    Feb 27, 2018
    Posts:
    21
    Update:
    If this setting is unchecked, the error goes away, but every time you use Microphone.Start() and Microphone.Stop(), a significant overhead (0.5 ms for me on iPhone SE, iOS 11.3) is added to the drawing of the frame at that instant, because iOS needs to prepare itself for recording every time.

    To handle this error in a much better way, you do need to check Prepare iOS for Recording in Player Settings and call
    Code (Swift):
    1. AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
    from the native side of your application before using any Microphone API on the Unity side.

    It really sucks that this information is nowhere to be found in the Unity documentation, but is briefly mentioned in FMOD's (audio engine used by Unity) iOS documentation and also in this question in FMOD Questions. And even if you do find these two sources, you need to have found out the ERR_RECORD error code seen in the question corresponds to the error thrown by Unity's Microphone API.
     
  5. rtilton1

    rtilton1

    Joined:
    Jan 4, 2017
    Posts:
    62
    AVAudioSession does not exist in the current context within C#/Unity. Is it available with a 'using XXXXX'? Not sure the best way to go about this within Unity and C#.
     
  6. wintermuute

    wintermuute

    Joined:
    May 12, 2017
    Posts:
    16
    I realize this is old and may have been solved by now, but for anyone else who may run into this issue, this is how I'm solving it.

    AVAudioSession is an object native to iOS, so it does not exist in Unity's API. In order to access it you have to make a native script and then import it as a DLL to access the commands via C#. For reference, I'm using these scripts to control the audio session categories in the app I'm working on. Copy each script, name the first one AudioSessionSetter.mm, and the second one AudioSession.cs. AudioSessionSetter.mm is a native Obj-C script for setting the AVAudioSessionCategory. AudioSession.cs is the Unity interface for interacting with the native in your Unity project. Make sure to put both in a folder under Assets > Plugins > iOS and in the Unity Inspector for AudioSessionSetter.mm check iOS for the platform. Since the AudioSession class is static you can call the methods from anywhere, so when setting up your Microphone call AudioSession.SetAudioPlayAndRecord, this will prepare your device's microphone for recording and mixing audio. If you want you can also call AudioSession.SetAudioAmbient to set the devices AVAudioSession back to default.

    Code (CSharp):
    1. #import "AVFoundation/AVFoundation.h"
    2.  
    3. @implementation AudioSessionSetter
    4.  
    5. extern "C"
    6. {
    7.      void _SetAudioAmbient()
    8.     {
    9.         [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil];
    10.         [[AVAudioSession sharedInstance] setActive:YES error:nil];
    11.     }
    12.  
    13.     void _SetAudioPlayback(){
    14.             [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
    15.             [[AVAudioSession sharedInstance] setActive:YES error:nil];
    16.     }
    17.  
    18.     void _SetAudioPlayAndRecord(){
    19.             [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];
    20.             [[AVAudioSession sharedInstance] setActive:YES error:nil];
    21.     }
    22.    
    23.      bool _IsAmbientSet()
    24.      {
    25.           AVAudioSessionCategory cat = [[AVAudioSession sharedInstance] category];
    26.           if(cat == AVAudioSessionCategoryAmbient){
    27.               return true;
    28.           }
    29.           else return false;
    30.      }
    31. }
    32. @end
    33.  
    Code (CSharp):
    1. using System.Runtime.InteropServices;
    2.  
    3. public static class AudioSession
    4. {
    5. #if UNITY_IOS
    6.     [DllImport("__Internal")]
    7.     private static extern void _SetAudioAmbient();
    8.     [DllImport("__Internal")]
    9.     private static extern bool _SetAudioPlayback();
    10.     [DllImport("__Internal")]
    11.     private static extern bool _SetAudioPlayAndRecord();
    12.     [DllImport("__Internal")]
    13.     private static extern bool _IsAmbientSet();
    14.  
    15.     public static void SetAudioAmbient()
    16.     {
    17.         _SetAudioAmbient();
    18.     }
    19.  
    20.     public static void SetAudioPlayback()
    21.     {
    22.         _SetAudioPlayback();
    23.     }
    24.  
    25.     public static void SetAudioPlayAndRecord()
    26.     {
    27.         _SetAudioPlayAndRecord();
    28.     }
    29.  
    30.     public static bool IsAmbientSet()
    31.     {
    32.         return _IsAmbientSet();
    33.     }
    34. #endif
    35. }
     
    JohnTube and Colin_MacLeod like this.
  7. kevinlaguitan2121

    kevinlaguitan2121

    Joined:
    Oct 20, 2018
    Posts:
    2
    This works in android as well?
     
  8. jgmakes

    jgmakes

    Joined:
    Jan 10, 2019
    Posts:
    75
    The solution above from wintermuute solves the problem for iOS specifically. You're having the same problem with Android?
     
  9. BAaoo

    BAaoo

    Joined:
    Feb 20, 2014
    Posts:
    1
    Yes having this exact issue on Quest 2 but not on Quest 1, not sure its a Unity problem or Quests.
    Unity Version 2020.1.6f1