Skip to content

Instantly share code, notes, and snippets.

@umiyuki
Forked from darktable/SavWav.cs
Last active December 9, 2022 04:48
Show Gist options
  • Save umiyuki/fe1807230532ea2678fa6d9751ea2d9a to your computer and use it in GitHub Desktop.
Save umiyuki/fe1807230532ea2678fa6d9751ea2d9a to your computer and use it in GitHub Desktop.
Record Microphone in EditMode. Need Voice Changer Filter(https://www.assetstore.unity3d.com/jp/#!/content/54963)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.IO;
public class RecorderManager : MonoBehaviour
{
[DllImport("world_dll")]
private static extern int filter(double[] datas, int data_size, int fs, float pitch, float formant);
[SerializeField]string fileName = "";
[SerializeField] int micDeviceID = 0;
[HeaderAttribute("VoiceChanger")]
[SerializeField]bool enableVoiceChanger = false;
[SerializeField,Range(0, 5)] float pitch = 1f;
[SerializeField,Range(0, 5)] float formant = 1f;
[HeaderAttribute("CutAudio")]
[SerializeField]bool enableCutAudio = false;
[SerializeField,Range(0,10)] float cutForward = 0f;
[SerializeField,Range(0,10)] float cutLast = 0f;
[SerializeField] List<string> micDevices = new List<string>();
[SerializeField] AudioClip recordClip;
[SerializeField] AudioClip trimmedClip;
int freq = 44100;
[SerializeField] string export_path = "ExportVoices";
public void UpdateMicDevices()
{
micDevices.Clear();
foreach (string str in Microphone.devices)
{
micDevices.Add(str);
}
}
void SetupClip()
{
if (recordClip == null)
{ return; }
if (!enableCutAudio && !enableVoiceChanger)
{
GetComponent<AudioSource>().clip = recordClip;
return;
}
AudioSource audioSource = GetComponent<AudioSource>();
if (enableCutAudio)
{
int cutLength = Mathf.CeilToInt(freq * (cutForward + cutLast));
if (cutLength > recordClip.samples)
{
Debug.Log("Cut length too long! recordClipSamples:" + recordClip.samples + " cutLength:" + cutLength);
return;
}
int cutStart = Mathf.FloorToInt(freq * cutForward);
int newLength = recordClip.samples - cutLength;
float[] samples = new float[recordClip.samples];
recordClip.GetData(samples, cutStart);
float[] clipSamples = new float[newLength];
System.Array.Copy(samples, clipSamples, clipSamples.Length);
trimmedClip = AudioClip.Create("trimRecordClip", clipSamples.Length, 1, freq, false);
trimmedClip.SetData(clipSamples, 0);
}
else
{
float[] samples = new float[recordClip.samples];
recordClip.GetData(samples, 0);
trimmedClip = AudioClip.Create("trimRecordClip", samples.Length, 1, freq, false);
trimmedClip.SetData(samples, 0);
}
if (enableVoiceChanger)
{
float[] samples = new float[trimmedClip.samples];
trimmedClip.GetData(samples, 0);
double[] d_samples = new double[samples.Length];
for (int i = 0; i < d_samples.Length; i++)
{
d_samples[i] = samples[i];
}
filter(d_samples, d_samples.Length, freq, pitch, formant);
for (int i = 0; i < d_samples.Length; i++)
{
samples[i] = (float)d_samples[i];
}
trimmedClip.SetData(samples, 0);
}
audioSource.clip = trimmedClip;
}
public void SaveToFile()
{
SetupClip();
string filePath = Application.dataPath + "/" + export_path + "/" + fileName + ".wav";
if (fileName == null || File.Exists(filePath))
{
SavWav.Save(filePath, GetComponent<AudioSource>().clip);
}
else
{
bool exist_flag = false;
int index = 0;
while (!exist_flag)
{
filePath = Application.dataPath + "/" + export_path + "/" + index.ToString("0000") + ".wav";
if (!File.Exists(filePath))
{
exist_flag = true;
}
else
{
index++;
}
}
SavWav.Save(filePath, GetComponent<AudioSource>().clip);
}
}
public void PlayClip()
{
SetupClip();
GetComponent<AudioSource>().Play();
}
public void PauseClip()
{
GetComponent<AudioSource>().Pause();
}
public void StopClip()
{
GetComponent<AudioSource>().Stop();
}
public void Record()
{
GetComponent<AudioSource>().clip = Microphone.Start(Microphone.devices[micDeviceID], false, 300, freq);
}
public void RecodeStop()
{
int lastTime = Microphone.GetPosition(null);
if (lastTime == 0)
{ return; }
Debug.Log("lastTime =" + lastTime);
Microphone.End(null);
AudioSource audioSource = GetComponent<AudioSource>();
float[] samples = new float[audioSource.clip.samples];
audioSource.clip.GetData(samples, 0);
float[] clipSamples = new float[lastTime];
System.Array.Copy(samples, clipSamples, clipSamples.Length - 1);
recordClip = AudioClip.Create("playRecordClip", clipSamples.Length, 1, freq, false);
recordClip.SetData(clipSamples, 0);
audioSource.clip = recordClip;
trimmedClip = null;
}
}
using UnityEngine;
using System.Collections;
using UnityEditor;
[CustomEditor(typeof(RecorderManager))]
public class RecoderManagerEditor : Editor {
public override void OnInspectorGUI()
{
RecorderManager recordManager = (RecorderManager)target;
if (GUILayout.Button("Play Clip"))
{
recordManager.PlayClip();
}
if (GUILayout.Button("Pause Clip"))
{
recordManager.PauseClip();
}
if (GUILayout.Button("Stop Clip"))
{
recordManager.StopClip();
}
if (GUILayout.Button("Record"))
{
recordManager.Record();
}
if (GUILayout.Button("Record Stop"))
{
recordManager.RecodeStop();
}
if (GUILayout.Button("Save to File"))
{
recordManager.SaveToFile();
}
DrawDefaultInspector();
if (GUILayout.Button("Update Mic Devices"))
{
recordManager.UpdateMicDevices();
}
}
}
// Copyright (c) 2012 Calvin Rien
// http://the.darktable.com
//
// This software is provided 'as-is', without any express or implied warranty. In
// no event will the authors be held liable for any damages arising from the use
// of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim
// that you wrote the original software. If you use this software in a product,
// an acknowledgment in the product documentation would be appreciated but is not
// required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
// =============================================================================
//
// derived from Gregorio Zanon's script
// http://forum.unity3d.com/threads/119295-Writing-AudioListener.GetOutputData-to-wav-problem?p=806734&viewfull=1#post806734
using System;
using System.IO;
using UnityEngine;
using System.Collections.Generic;
public static class SavWav
{
const int HEADER_SIZE = 44;
public static bool Save(string filepath, AudioClip clip)
{
/*
if (!filename.ToLower().EndsWith(".wav"))
{
filename += ".wav";
}*/
//var filepath = Path.Combine(Application.persistentDataPath, filename);
//var filepath = filename;
Debug.Log(filepath);
// Make sure directory exists if user is saving to sub dir.
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
using (var fileStream = CreateEmpty(filepath))
{
ConvertAndWrite(fileStream, clip);
WriteHeader(fileStream, clip);
}
return true; // TODO: return false if there's a failure saving the file
}
public static AudioClip TrimSilence(AudioClip clip, float min)
{
var samples = new float[clip.samples];
clip.GetData(samples, 0);
return TrimSilence(new List<float>(samples), min, clip.channels, clip.frequency);
}
public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz)
{
return TrimSilence(samples, min, channels, hz, false, false);
}
public static AudioClip TrimSilence(List<float> samples, float min, int channels, int hz, bool _3D, bool stream)
{
int i;
for (i = 0; i < samples.Count; i++)
{
if (Mathf.Abs(samples[i]) > min)
{
break;
}
}
samples.RemoveRange(0, i);
for (i = samples.Count - 1; i > 0; i--)
{
if (Mathf.Abs(samples[i]) > min)
{
break;
}
}
samples.RemoveRange(i, samples.Count - i);
var clip = AudioClip.Create("TempClip", samples.Count, channels, hz, _3D, stream);
clip.SetData(samples.ToArray(), 0);
return clip;
}
static FileStream CreateEmpty(string filepath)
{
var fileStream = new FileStream(filepath, FileMode.Create);
byte emptyByte = new byte();
for (int i = 0; i < HEADER_SIZE; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
return fileStream;
}
static void ConvertAndWrite(FileStream fileStream, AudioClip clip)
{
var samples = new float[clip.samples];
clip.GetData(samples, 0);
Int16[] intData = new Int16[samples.Length];
//converting in 2 float[] steps to Int16[], //then Int16[] to Byte[]
Byte[] bytesData = new Byte[samples.Length * 2];
//bytesData array is twice the size of
//dataSource array because a float converted in Int16 is 2 bytes.
int rescaleFactor = 32767; //to convert float to Int16
for (int i = 0; i < samples.Length; i++)
{
intData[i] = (short)(samples[i] * rescaleFactor);
Byte[] byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
}
static void WriteHeader(FileStream fileStream, AudioClip clip)
{
var hz = clip.frequency;
var channels = clip.channels;
var samples = clip.samples;
fileStream.Seek(0, SeekOrigin.Begin);
Byte[] riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff, 0, 4);
Byte[] chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(chunkSize, 0, 4);
Byte[] wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave, 0, 4);
Byte[] fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(fmt, 0, 4);
Byte[] subChunk1 = BitConverter.GetBytes(16);
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
Byte[] audioFormat = BitConverter.GetBytes(one);
fileStream.Write(audioFormat, 0, 2);
Byte[] numChannels = BitConverter.GetBytes(channels);
fileStream.Write(numChannels, 0, 2);
Byte[] sampleRate = BitConverter.GetBytes(hz);
fileStream.Write(sampleRate, 0, 4);
Byte[] byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
fileStream.Write(byteRate, 0, 4);
UInt16 blockAlign = (ushort)(channels * 2);
fileStream.Write(BitConverter.GetBytes(blockAlign), 0, 2);
UInt16 bps = 16;
Byte[] bitsPerSample = BitConverter.GetBytes(bps);
fileStream.Write(bitsPerSample, 0, 2);
Byte[] datastring = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(datastring, 0, 4);
Byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
fileStream.Write(subChunk2, 0, 4);
// fileStream.Close();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment