unity3D学习之音频播放 、实时变声简化版-audio菜鸟笔记5

本文介绍了一个基于SoundTouch库的C#实时变声脚本实现,通过滑动条调节变声参数,并演示了如何在Unity中进行录音、变声及保存。
部署运行你感兴趣的模型镜像

这是基于soundtouch写的一个实时变声的C#脚本,因为是调试版,所以可能会有BUG


核心代码:

private void OnAudioFilterRead(float[] data, int channels)
    {
        if (isRecording)
        {
            //根据data,一段段处理变声
            soundTouch.PutSamples(data, data.Length);
            float[] tempsample_after = new float[data.Length];
            soundTouch.ReceiveSamples(tempsample_after, data.Length);
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = tempsample_after[i];
            }
        }
    }

关于OnAudioFilterRead的问题,有兴趣的同学可以去看看它的API,我在这里就不多说了


下面直接代码:

using SoundTouch;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;

#if SOUNDTOUCH_INTEGER_SAMPLES
using TSampleType = System.Int16;
using TLongSampleType = System.Int64;
#else
using TSampleType = System.Single;
using TLongSampleType = System.Double;
#endif

public class ClipChangeTest : MonoBehaviour {

    public AudioSource audioSource;
    //滑动条
    public Slider slider1;
    public Slider slider2;
    public Slider slider3;
    //输入框
    public InputField inputField1;
    public InputField inputField2;
    public InputField inputField3;
    public InputField inputField_modelName;

    private float tmpTempo;
    private float tmpPitch;
    private float tmpRate;
    private float temp_ischange1;
    private float temp_ischange2;
    private bool isRecording = false;
    private bool isChange_pitch = false;
    SoundTouch soundTouch = new SoundTouch();

    // Use this for initialization
    void Start ()
    {
        slider1.minValue = -12f;
        slider1.maxValue = 12f;
        slider2.minValue = 0f;
        slider2.maxValue = 6f;
        slider3.minValue = 0f;
        slider3.maxValue = 6f;
        slider1.value = 0f;
        slider2.value = 1f;
        slider3.value = 1f;
    }
	
	// Update is called once per frame
	void Update () {
        if (!isChange_pitch)
        {
            if (slider3.value != temp_ischange2)
            {
                slider2.value = 1f / slider3.value;
                temp_ischange1 = slider2.value;
                temp_ischange2 = slider3.value;
            }
            else if (slider2.value != temp_ischange1)
            {
                slider3.value = 1f / slider2.value;
                temp_ischange1 = slider2.value;
                temp_ischange2 = slider3.value;
            }
            if (inputField1.isFocused)
            {
                slider1.value = float.Parse(inputField1.text);
            }
            else if(inputField2.isFocused)
            {
                slider2.value = float.Parse(inputField2.text);
            }
            else if (inputField3.isFocused)
            {
                slider3.value = float.Parse(inputField3.text);
            }
            else
            {
                inputField1.text = slider1.value.ToString();
                inputField2.text = slider2.value.ToString();
                inputField3.text = slider3.value.ToString();
            }
            tmpPitch = slider1.value;
            tmpTempo = slider2.value;
            tmpRate = slider3.value;
        }
    }

    public void Mic_test()
    {
        if (Microphone.devices.Length == 0)
        {
            Debug.LogWarning("找不到麦克风!");
        }
        Microphone.End(null);
        audioSource.clip = Microphone.Start(null, false, 100, 44100);

        //实时变声调整(初始化)
        isChange_pitch = true;
        soundTouch.SetTempo(tmpTempo);
        soundTouch.SetPitchSemiTones(tmpPitch);//噪音相对大一点
        soundTouch.SetRate(tmpRate);
        soundTouch.SetSampleRate(44100);
        soundTouch.SetChannels(1);
        soundTouch.SetSetting(SettingId.UseQuickseek, 0);
        soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 0);
        soundTouch.SetSetting(SettingId.SequenceDurationMs, 40);
        soundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15);
        soundTouch.SetSetting(SettingId.OverlapDurationMs, 8);

        isRecording = true;
        while (Microphone.GetPosition(null) <= 0) { }
        audioSource.Play();
    }
    
    //保存声音
    public void Save_test()
    {
        soundTouch.Flush();
        float[] saveClip_befor = new float[audioSource.timeSamples];
        audioSource.Stop();
        audioSource.clip.GetData(saveClip_befor, 0);

        //变声处理
        soundTouch.SetTempo(tmpTempo);
        soundTouch.SetPitchSemiTones(tmpPitch);
        soundTouch.SetRate(tmpRate);
        soundTouch.SetSampleRate(44100);
        soundTouch.SetChannels(1);
        soundTouch.SetSetting(SettingId.UseQuickseek, 0);
        soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 0);
        soundTouch.SetSetting(SettingId.SequenceDurationMs, 40);
        soundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15);
        soundTouch.SetSetting(SettingId.OverlapDurationMs, 8);
        soundTouch.PutSamples(saveClip_befor, saveClip_befor.Length);

        //获取变声后的数据并保存
        float[] saveClip_after = new float[saveClip_befor.Length];
        soundTouch.ReceiveSamples(saveClip_after, saveClip_befor.Length);
        soundTouch.Flush();
        audioSource.clip = AudioClip.Create("changedClip", saveClip_befor.Length, 1, 44100,false);
        audioSource.clip.SetData(saveClip_after, 0);
        SavWav.Save("test1", audioSource.clip);
        isChange_pitch = false;
    }

    //实时变声
    private void OnAudioFilterRead(float[] data, int channels)
    {
        if (isRecording)
        {
            //根据data,一段段处理变声
            soundTouch.PutSamples(data, data.Length);
            float[] tempsample_after = new float[data.Length];
            soundTouch.ReceiveSamples(tempsample_after, data.Length);
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = tempsample_after[i];
            }
        }
    }

    //控制录音时是否有声音
    public void ChangeController()
    {
        if (audioSource.volume == 0)
        {
            audioSource.volume = 1;
        }
        else
        {
            audioSource.volume = 0;
        }
        
    }

    //测试所用
    //回放
    public void replay()
    {
        isChange_pitch = false;
        isRecording = true;
        //实时变声调整(初始化)
        soundTouch.SetTempo(tmpTempo);
        soundTouch.SetPitchSemiTones(tmpPitch);//噪音相对大一点
        soundTouch.SetRate(tmpRate);
        soundTouch.SetSampleRate(44100);
        soundTouch.SetChannels(1);
        soundTouch.SetSetting(SettingId.UseQuickseek, 0);
        soundTouch.SetSetting(SettingId.UseAntiAliasFilter, 0);
        soundTouch.SetSetting(SettingId.SequenceDurationMs, 40);
        soundTouch.SetSetting(SettingId.SeekwindowDurationMs, 15);
        soundTouch.SetSetting(SettingId.OverlapDurationMs, 8);
        audioSource.Play();
    }

    //保存不同的音色
    public void Save_changedata()
    {
        if (!File.Exists(Application.dataPath + "/Assets/Resources/speech_model.txt"))
        {
            File.Create(Application.dataPath + "/Assets/Resources/speech_model.txt");
            Debug.LogWarning("创建文件");
        }
        FileStream fileStream = new FileStream(Application.dataPath + "/Assets/Resources/speech_model.txt",FileMode.Append);
        StreamWriter streamWriter = new StreamWriter(fileStream);
        streamWriter.Write(inputField_modelName.text + ": ");
        streamWriter.Write(" pitch:" + tmpPitch);
        streamWriter.Write(" tempo:" + tmpTempo);
        streamWriter.Write(" rate:" + tmpRate);
        streamWriter.WriteLine();
        streamWriter.Close();
        Debug.Log("保存音色数据成功!");
    }

}


您可能感兴趣的与本文相关的镜像

Qwen-Image

Qwen-Image

图片生成
Qwen

Qwen-Image是阿里云通义千问团队于2025年8月发布的亿参数图像生成基础模型,其最大亮点是强大的复杂文本渲染和精确图像编辑能力,能够生成包含多行、段落级中英文文本的高保真图像

本资源是基于SoundTouch算法实现的Unity音频速处理插件,能够在改音频播放速度的同时保持原始音调不。该插件专为Unity游戏引擎设计,提供了完整的音频倍速播放解决方案,适用于需要播放音频的各类应用场景。 插件核心功能包括: 音频播放(0.5x-3.0x速度调节) 保持原始音调不的高质量算法 内置缓存机制,避免重复处理相同音频 功能特点 1. 高质量音频速 基于SoundTouch开源音频处理库,能够在改播放速度的同时保持音频的原始音调,避免出现"Chipmunk effect"(调)问题。 2. 多平台支持 支持Windows、Mac、Android、iOS等主流平台 不适配WebGL平台 3. 灵活的API接口 提供多种播放接口,满足不同使用场景: 基础音频播放 参数化控制(速度、音调、频率等) 4. 性能优化 内置音频缓存机制,避免重复处理 对象池管理,减少GC压力 按需处理,只在需要时生成音频 5. 易于集成 提供完整的示例场景和测试面板 详细的API文档和使用示例 支持Unity编辑器扩展 使用场景: 教育培训应用中的音频播放控制 游戏中的音效速处理 语音学习应用的播放 需要精确控制音频播放速度的各类应用 文件结构 Assets/Plugins/SoundTouch/ ├── Scripts/ │ ├── Core/ # 核心算法实现 │ ├── Mgr/ # 管理器类 │ ├── UI/ # 测试界面 └── README.md # 使用说明
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值