1

我正在用游戏中的乐器和录音设备制作游戏。我希望用户能够演奏乐器并将它们录制到音频剪辑和 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;
                        }
                    }
                }
4

2 回答 2

0

认为这是解决方案,但仍然无法在运行时加载。(它只是指剪辑的旧副本,直到我重新加载游戏。起初没有注意到这一点。)

虽然我无法让 Web 请求为我工作或让 WWW 工作,但我确实得到了我想要的效果,方法是在 .wav 保存的资产文件夹中创建一个空白剪辑,然后将其拖动到属性中窗口作为参考,将音频剪辑设置为流式传输,然后使用上面的代码编写 wav,它会自动用我记录的数据覆盖剪辑。音频有一点失真(噼啪声),我接下来会研究一下。

我知道这个解决方案可能非常明显(我想我必须给统一一些东西来参考但没有意识到)并且如果它们不是像我的那样预定义的,则不能解决在运行时加载文件的问题,但它至少解决了我的问题。

于 2019-08-24T14:32:23.320 回答
0

尝试使用以下方法加载为音频剪辑:

        private IENumerator LoadAudio(string url) {

        WWW www;           
        www = new WWW("file:///" + url);  

        yield return www;

        if (www != null && www.isDone)
        {
            AudioClip audioClip;
            audioClip = www.GetAudioClip(true, false, AudioType.WAV);
        }
        }

这至少对我有用。

于 2019-08-23T21:18:35.773 回答