我正在用游戏中的乐器和录音设备制作游戏。我希望用户能够演奏乐器并将它们录制到音频剪辑和 wav 文件中,然后他们可以将其作为自己演奏另一种乐器的伴奏进行播放,再次记录输出,允许他们制作音轨组合仪器。到目前为止,我一切正常,除了我无法弄清楚如何获取音频剪辑 - 我将 .wav 文件保存到资产文件夹中,但我对代码的理解不足以找出将音频剪辑的数据设置到的位置正在写入的数据。
我在这里使用 Olkor 的代码将录音保存为 wav,效果很好: https ://unitylist.com/p/za/Output-Audio-Recorder
我从中保存音频剪辑的两个选项似乎是a)在保存到磁盘的同时保存到音频剪辑(我无法弄清楚)或将其保存到磁盘然后将其作为音频剪辑加载到游戏中- 使用我尝试过的 Unity Web 请求,但我得到一个错误,要么无法访问已中止的 DownloadHandlerAudioClip 的 .audioclip 属性,要么如果我延迟调用该函数来加载文件,则解码音频时出错。无论哪种方式,我的资产文件夹中都有一个保存的 .wav 音频文件,但没有音频剪辑。
InvalidOperationException: Cannot access the .audioClip property of an aborted DownloadHandlerAudioClip
UnityEngine.Networking.DownloadHandlerAudioClip.GetContent
(UnityEngine.Networking.UnityWebRequest www) (at
C:/buildslave/unity/build/Modules/UnityWebRequestAudio/Public/DownloadHandler
Audio.bindings.cs:49)
OutputAudioRecorder+<GetAudioClip>d__27.MoveNext () (at
Assets/Scripts/OutputAudioRecorder.cs:202)
UnityEngine.SetupCoroutine.InvokeMoveNext (System.Collections.IEnumerator
enumerator, System.IntPtr returnValueAddress) (at C:/buildslave/unity/build/Runtime/Export/Scripting/Coroutines.cs:17)
我正在使用的下面的脚本附加到 audiolistener 上,因此它会从游戏中获取所有音频输出。我已经研究过使用 audioListener GetOutputData 做一些事情并将其传递给 audioClip SetData 但我无法弄清楚,我承认这超出了我目前的能力 - 我真的很感激对如何解决这个问题的一些见解以任何可能的方式。
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class OutputAudioRecorder : MonoBehaviour
{
public const string DEFAULT_FILENAME = "record";
public const string FILE_EXTENSION = ".wav";
public bool IsRecording { get { return recOutput; } }
private int bufferSize;
private int numBuffers;
private int outputRate;
private int headerSize = 44; //default for uncompressed wav
private String fileName;
private bool recOutput = false;
private AudioClip newClip;
private FileStream fileStream;
private AudioClip[] audioClips;
private AudioSource[] audioSources;
public int currentSlot;
float[] tempDataSource;
void Awake()
{
outputRate = AudioSettings.outputSampleRate;
}
void Start()
{
AudioSettings.GetDSPBufferSize(out bufferSize, out numBuffers);
audioSources = new AudioSource[3];
audioSources[0] = GameObject.FindWithTag("RecSlot1").GetComponent<AudioSource>();
audioSources[1] = GameObject.FindWithTag("RecSlot2").GetComponent<AudioSource>();
audioSources[2] = GameObject.FindWithTag("RecSlot3").GetComponent<AudioSource>();
}
public void StartRecording(string recordFileName)
{
fileName = Path.GetFileNameWithoutExtension(recordFileName) + FILE_EXTENSION;
if (!recOutput)
{
StartWriting(fileName);
recOutput = true;
}
else
{
Debug.LogError("Recording is in progress already");
}
}
public void StopRecording()
{
recOutput = false;
WriteHeader();
UpdateClip();
}
private void StartWriting(String name)
{
fileStream = new FileStream(Application.dataPath + "/" + name, FileMode.Create);
var emptyByte = new byte();
for (int i = 0; i < headerSize; i++) //preparing the header
{
fileStream.WriteByte(emptyByte);
}
}
private void OnAudioFilterRead(float[] data, int channels)
{
if (recOutput)
{
ConvertAndWrite(data); //audio data is interlaced
}
}
private void ConvertAndWrite(float[] dataSource)
{
var intData = new Int16[dataSource.Length];
//converting in 2 steps : float[] to Int16[], //then Int16[] to Byte[]
var bytesData = new Byte[dataSource.Length * 2];
//bytesData array is twice the size of
//dataSource array because a float converted in Int16 is 2 bytes.
var rescaleFactor = 32767; //to convert float to Int16
for (var i = 0; i < dataSource.Length; i++)
{
intData[i] = (Int16)(dataSource[i] * rescaleFactor);
var byteArr = new Byte[2];
byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
tempDataSource = new float[dataSource.Length];
tempDataSource = dataSource;
}
private void WriteHeader()
{
fileStream.Seek(0, SeekOrigin.Begin);
var riff = System.Text.Encoding.UTF8.GetBytes("RIFF");
fileStream.Write(riff, 0, 4);
var chunkSize = BitConverter.GetBytes(fileStream.Length - 8);
fileStream.Write(chunkSize, 0, 4);
var wave = System.Text.Encoding.UTF8.GetBytes("WAVE");
fileStream.Write(wave, 0, 4);
var fmt = System.Text.Encoding.UTF8.GetBytes("fmt ");
fileStream.Write(fmt, 0, 4);
var subChunk1 = BitConverter.GetBytes(16);
fileStream.Write(subChunk1, 0, 4);
UInt16 two = 2;
UInt16 one = 1;
var audioFormat = BitConverter.GetBytes(one);
fileStream.Write(audioFormat, 0, 2);
var numChannels = BitConverter.GetBytes(two);
fileStream.Write(numChannels, 0, 2);
var sampleRate = BitConverter.GetBytes(outputRate);
fileStream.Write(sampleRate, 0, 4);
var byteRate = BitConverter.GetBytes(outputRate * 4);
fileStream.Write(byteRate, 0, 4);
UInt16 four = 4;
var blockAlign = BitConverter.GetBytes(four);
fileStream.Write(blockAlign, 0, 2);
UInt16 sixteen = 16;
var bitsPerSample = BitConverter.GetBytes(sixteen);
fileStream.Write(bitsPerSample, 0, 2);
var dataString = System.Text.Encoding.UTF8.GetBytes("data");
fileStream.Write(dataString, 0, 4);
var subChunk2 = BitConverter.GetBytes(fileStream.Length - headerSize);
fileStream.Write(subChunk2, 0, 4);
fileStream.Close();
}
void UpdateClip()
{
StartCoroutine(GetAudioClip());
}
IEnumerator GetAudioClip()
{
using (UnityWebRequest www = UnityWebRequestMultimedia.GetAudioClip("file://" + Application.dataPath + "myRecord1.wav", AudioType.WAV))
{
yield return www.Send();
if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
AudioClip newClip = DownloadHandlerAudioClip.GetContent(www);
Debug.Log(newClip.name + "name " + newClip.length);
keyboardScript.audioClip = newClip;
}
}
}