音视频开发必看:“秒变萝莉音”,游戏中的变声是如何实现的

image

  • 时域(Time domain)是描述数学函数或物理信号对时间的关系。例如一个信号的时域波形可以表达信号随着时间的变化。是真实世界,是惟一实际存在的域。很多物理变量的定义都是跟时间相关的,比如速度(位移与时间之比)、电流 、功率(单位时间做的功)。
  • 频域(frequency domain)是指在对函数或信号进行分析时,分析其和频率有关部分,而不是和时间有关的部分,和时域一词相对。例如,许多物理元件的特性会随着输入讯号的频率而改变,例如电容在低频时阻抗变大,高频时阻抗变小,而电感恰好相反,高频时阻抗变大,低频时阻抗变小。
2.6 变声原理

我们在上文中理解到音色取决于哪些条件,意味着我们改变其中的一些参数都会对我们最终听到的声音有所差异。

常用的变声,如女生、男生、小黄人都是对音调(即频率)进行的处理。当音调高时就是女声,低时即男声,常常听到的女声比男声高八度还是有点道理的。

声音的高级处理,如:混响(Reverb)、回声(Echo)、EQ、锯齿(Flange)

3、Fmod使用

Fmod

3.1 相关配置

先下载源码

  • 下载源码
    image

将lib的so库和头文件拷进来

image

  • cmake相关配置
    前几篇文章已经讲了很多相关知识点,就不做赘述了
3.2 Fmod变声效果实现

Fmod相关的API可以看这篇文章
fmod核心API

以下举个例子,通过修改音调来实现萝莉音的实现方式,让大家熟悉下调用流程。

3.2.1 fmod DSP(数字信号处理)

jni部分

extern “C” JNIEXPORT void JNICALL Java_com_hugh_audiofun_FmodSound_playSound
(JNIEnv *env, jclass jcls, jstring path_jstr, jint type) {
LOGI(“%s”, “–> start”);

System *system;
Sound *sound;
DSP *dsp;
// Channel *channel;
float frequency;
bool isPlaying = true;

System_Create(&system);
system->init(32, FMOD_INIT_NORMAL, NULL);
const char *path_cstr = env->GetStringUTFChars(path_jstr, NULL);

try {
system->createSound(path_cstr, FMOD_DEFAULT, NULL, &sound);
switch (type) {
case TYPE_NORMAL: // 普通
LOGI(“%s”, path_cstr)
system->playSound(sound, 0, false, &channel);
LOGI(“%s”, “fix normal”);
break;
case TYPE_LOLITA: // 萝莉
system->createDSPByType(FMOD_DSP_TYPE_PITCHSHIFT, &dsp); // 可改变音调
dsp->setParameterFloat(FMOD_DSP_PITCHSHIFT_PITCH, 8.0); // 8.0 为一个八度
system->playSound(sound, 0, false, &channel);
channel->addDSP(0, dsp);
break;
………………
}
} catch (…) {
LOGE(“%s”, “catch exception…”)
goto end;
}

// system->update();

// 每隔一秒检测是否播放结束
while (isPlaying) {
channel->isPlaying(&isPlaying);
usleep(1000 * 1000);
}

goto end;

//释放资源
end:
env->ReleaseStringUTFChars(path_jstr, path_cstr);
sound->release();
system->close();
system->release();
}

调用流程

  • 首先创建System ,再做System初始化

  • createDSPByType(创建数字信号模拟器) 来看下都能实现什么音响效果
    有兴趣的小伙伴可以通过传入不同的type和参数来尝试

typedef enum
{
FMOD_DSP_TYPE_UNKNOWN,
FMOD_DSP_TYPE_MIXER, //混合输入。
FMOD_DSP_TYPE_OSCILLATOR, //生成正弦/正方形/锯齿/三角形或噪声音调。
FMOD_DSP_TYPE_LOWPASS,
FMOD_DSP_TYPE_ITLOWPASS,
FMOD_DSP_TYPE_HIGHPASS,
FMOD_DSP_TYPE_ECHO, //在声音上产生回声,并以所需的速率衰减。
FMOD_DSP_TYPE_FADER,
FMOD_DSP_TYPE_FLANGE, //对声音产生法兰效应。
FMOD_DSP_TYPE_DISTORTION,
FMOD_DSP_TYPE_NORMALIZE,
FMOD_DSP_TYPE_LIMITER,
FMOD_DSP_TYPE_PARAMEQ,
FMOD_DSP_TYPE_PITCHSHIFT,
FMOD_DSP_TYPE_CHORUS, //在声音上产生一种合唱效果。
FMOD_DSP_TYPE_VSTPLUGIN,
FMOD_DSP_TYPE_WINAMPPLUGIN,
FMOD_DSP_TYPE_ITECHO,
FMOD_DSP_TYPE_COMPRESSOR,
FMOD_DSP_TYPE_SFXREVERB, //自解压混响
FMOD_DSP_TYPE_LOWPASS_SIMPLE,
FMOD_DSP_TYPE_DELAY,
FMOD_DSP_TYPE_TREMOLO, //在声音上产生一种颤音/斩波的效果。
FMOD_DSP_TYPE_LADSPAPLUGIN,
FMOD_DSP_TYPE_SEND,
FMOD_DSP_TYPE_RETURN,
FMOD_DSP_TYPE_HIGHPASS_SIMPLE,
FMOD_DSP_TYPE_PAN,
FMOD_DSP_TYPE_THREE_EQ, //三波段均衡器。
FMOD_DSP_TYPE_FFT,
FMOD_DSP_TYPE_LOUDNESS_METER,
FMOD_DSP_TYPE_ENVELOPEFOLLOWER,
FMOD_DSP_TYPE_CONVOLUTIONREVERB, //卷积混响。
FMOD_DSP_TYPE_CHANNELMIX,
FMOD_DSP_TYPE_TRANSCEIVER,
FMOD_DSP_TYPE_OBJECTPAN,
FMOD_DSP_TYPE_MULTIBAND_EQ,

FMOD_DSP_TYPE_MAX,
FMOD_DSP_TYPE_FORCEINT = 65536 /* Makes sure this enum is signed 32bit. */
} FMOD_DSP_TYPE;

  • system->playSound(sound, 0, false, &channel); channel->addDSP(0, dsp);

播放声音

3.2.2 fmod Reverb3D(混响3D) 3D声音和空间化

Reverb3D 官方文档

有玩过csgo的小伙伴,或者一些射击类游戏,一些技术厉害的玩家都比较喜欢带耳机,通过声音来辨别对手的位置。

image
FMOD Core API支持多种功能,这些功能允许将声音放置在3D空间中,从而通过平移,多普勒音高移位以及通过音量缩放甚至是特殊滤波进行衰减,使声音作为环境的一部分在听众周围移动。

FMOD 3D空间化功能:

  • 多个衰减衰减模型
    滚降是声音在靠近听众或远离听众时的音量行为。在线性,反向,线性平方,反向锥形和自定义滚降模式之间进行选择。自定义滚降允许设置FMOD_3D_ROLLOFF_CALLBACK以允许用户计算音量滚降的方式。如果回调不方便,则FMOD还允许使用ChannelControl :: set3DCustomRolloff在其间线性内插的点数组表示“曲线” 。

  • 多普勒音调移位
    由收听者和通道或通道组的用户速度设置控制的准确音高偏移是由FMOD 3D空间化系统实时计算和设置的。

  • 基于矢量的振幅平移(VBAP)
    该系统实时平移用户扬声器中的声音,支持单声道,立体声,高达5.1和7.1环绕声扬声器设置。

  • 咬合
    通道或通道组可以应用低通滤波,以模拟声音穿过墙壁或被大型物体遮挡。

  • 3D混响区用于混响平移
    混响也可以被遮挡,以免穿过墙壁或物体。

  • 基于多边形的几何遮挡
    将多边形数据添加到FMOD的几何引擎中,FMOD将使用光线投射实时自动遮挡声音。有关更多信息,请参见基于3D多边形的几何遮挡部分。

  • 多个听众
    在分屏模式的游戏中,FMOD可以为每个玩家支持一个听众,以便3D声音正确衰减。

  • 在多声道声音的2D和3D之间变形
    声音可以是点源,也可以由用户变形为2D声音,这对于基于距离的包络非常有用。声音越近,它越能传播到其他扬声器中,而不是随着声音从一侧平移到另一侧而从一侧翻转到另一侧。有关允许用户更改此混合的功能,请参见ChannelControl :: set3DLevel。

  • 立体声和多声道声音可以是3D
    通常,单声道声音用于3D音频。多声道声音可以用来产生额外的影响。默认情况下,多声道声音会折叠为单点源。要“传播”多声道声音的声道,请使用ChannelControl :: set3DSpread。对于从某个方向发出的声音,这可以带来更大的空间效果。声音在远处的细微散布可能会给人留下更有效的空间感觉,就好像它是从附近的表面反射出来的一样,或者是“大”的声音并在不同方向上发出不同的声音。

  • 空间化插件支持
    第三方VR音频插件可用于在耳机上提供更逼真的平移。

image

3.2.2.1 3D实现

在创建的声音时需要改成3D,以及距离的相关参数

System_Create(&system);
system->init(32, FMOD_INIT_NORMAL, NULL);
const char *path_cstr = env->GetStringUTFChars(path_jstr, NULL);
FMOD_VECTOR pos = { -10.0f, 0.0f, 0.0f };
Reverb3D *reverb;
FMOD_RESULT result = system->createReverb3D(&reverb);

LOGI(“createReverb3D %c”, result);
FMOD_REVERB_PROPERTIES prop2 = FMOD_PRESET_CONCERTHALL;
reverb->setProperties(&prop2);
float mindist = 10.0f;
float maxdist = 20.0f;

FMOD_VECTOR listenerpos = { 10.0f, 5.0f, -1.0f };
system->set3DListenerAttributes(0, &listenerpos, 0, 0, 0);

system->createSound(path_cstr, FMOD_3D, NULL, &sound);
system->playSound(sound, 0, false, &channel);

4、SoundTouch使用

SoundTouch

SoundTouch用于更改音频流或音频文件的速度,音调和播放速率。

  • 速度 (时间拉伸):更改声音以比原始速度更快或更慢的速度播放,而不影响音高。
  • 音高(音调):在保持原始速度(速度)的同时改变音高或音调。
  • 播放速率:一起改变速度和音高,就好像以不同的RPM速率播放黑胶唱片一样。
4.1相关配置
  • 下载源码
    sound源码下载

  • 分配拷贝C++ 和 jni代码 包括 soundtouch-jni.cpp 、SoundTouch.java

image
这边拷贝android相关

image
这边需要修改jni相关的包名和类名 以及引入so库的名字

  • cmake脚本

cmake_minimum_required(VERSION 3.4.1)

include_directories (“src/main/cpp/include”)
add_library( # Sets the name of the library.
soundtouch-lib

Sets the library as a shared library.

SHARED

Provides a relative path to your source file(s).

src/main/cpp/soundtouch-jni.cpp
src/main/cpp/SoundTouch/AAFilter.cpp
src/main/cpp/SoundTouch/BPMDetect.cpp
src/main/cpp/SoundTouch/cpu_detect_x86.cpp
src/main/cpp/SoundTouch/FIFOSampleBuffer.cpp
src/main/cpp/SoundTouch/FIRFilter.cpp
src/main/cpp/SoundTouch/InterpolateCubic.cpp
src/main/cpp/SoundTouch/InterpolateLinear.cpp
src/main/cpp/SoundTouch/InterpolateShannon.cpp
src/main/cpp/SoundTouch/mmx_optimized.cpp
src/main/cpp/SoundTouch/PeakFinder.cpp
src/main/cpp/SoundTouch/RateTransposer.cpp
src/main/cpp/SoundTouch/SoundTouch.cpp
src/main/cpp/SoundTouch/sse_optimized.cpp
src/main/cpp/SoundTouch/TDStretch.cpp
src/main/cpp/SoundStretch/WavFile.cpp
)

find_library( # Sets the name of the path variable.
log-lib

Specifies the name of the NDK library that

you want CMake to locate.

log )

target_link_libraries( # Specifies the target library.
soundtouch-lib

Links the target library to the log library

included in the NDK.

${log-lib} )

4.2 调用soundTouch 相关功能

这边举个例子,看下如何调用SoundTouch的函数

  • jni部分

extern “C” DLL_PUBLIC jstring Java_com_hugh_sound_SoundTouch_getVersionString(JNIEnv *env, jobject thiz)
{
const char *verStr;

LOGV(“JNI call SoundTouch.getVersionString”);

// Call example SoundTouch routine
verStr = SoundTouch::getVersionString();

/// gomp_tls storage bug workaround - see comments in _init_threading() function!
_init_threading(false);

int threads = 0;
#pragma omp parallel
{
#pragma omp atomic
threads ++;
}
LOGV(“JNI thread count %d”, threads);

// return version as string
return env->NewStringUTF(verStr);
}

  • java 部分

public native final static String getVersionString();

面试复习笔记:

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的Android开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

《960页Android开发笔记》

《1307页Android开发面试宝典》

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

[外链图片转存中…(img-u9pf58pJ-1715432305582)]

《507页Android开发相关源码解析》

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 15
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Record sounds / noises around you and turn them into music. It’s a work in progress, at the moment it enables you to record live audio straight from your browser, edit it and save these sounds as a WAV file. There's also a sequencer part where you can create small loops using these sounds with a drone synth overlaid on them. See it working: http://daaain.github.com/JSSoundRecorder Technology ---------- No servers involved, only Web Audio API with binary sound Blobs passed around! ### Web Audio API #### GetUserMedia audio for live recording Experimental API to record any system audio input (including USB soundcards, musical instruments, etc). ```javascript // shim and create AudioContext window.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext; var audio_context = new AudioContext(); // shim and start GetUserMedia audio stream navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; navigator.getUserMedia({audio: true}, startUserMedia, function(e) { console.log('No live audio input: ' + e); }); ``` #### Audio nodes for routing You can route audio stream around, with input nodes (microphone, synths, etc), filters (volume / gain, equaliser, low pass, etc) and outputs (speakers, binary streams, etc). ```javascript function startUserMedia(stream) { // create MediaStreamSource and GainNode var input = audio_context.createMediaStreamSource(stream); var volume = audio_context.createGain(); volume.gain.value = 0.7; // connect them and pipe output input.connect(volume); volume.connect(audio_context.destination); // connect recorder as well - see below var recorder = new Recorder(input); } ``` ### WebWorker Processing (interleaving) record buffer is done in the background to not block the main thread and the UI. Also WAV conversion for export is also quite heavy for longer recordings, so best left to run in the background. ```javascript this.context = input.context; this.node = this.context.createScriptProcessor(4096, 2, 2); this.node.onaudioprocess = function(e){ worker.postMessage({ command: 'record', buffer: [ e.inputBuffer.getChannelData(0), e.inputBuffer.getChannelData(1) ] }); } ``` ```javascript function record(inputBuffer){ var bufferL = inputBuffer[0]; var bufferR = inputBuffer[1]; var interleaved = interleave(bufferL, bufferR); recBuffers.push(interleaved); recLength += interleaved.length; } function interleave(inputL, inputR){ var length = inputL.length + inputR.length; var result = new Float32Array(length); var index = 0, inputIndex = 0; while (index < length){ result[index++] = inputL[inputIndex]; result[index++] = inputR[inputIndex]; inputIndex++; } return result; } ``` ```javascript function encodeWAV(samples){ var buffer = new ArrayBuffer(44 + samples.length * 2); var view = new DataView(buffer); /* RIFF identifier */ writeString(view, 0, 'RIFF'); /* file length */ view.setUint32(4, 32 + samples.length * 2, true); /* RIFF type */ writeString(view, 8, 'WAVE'); /* format chunk identifier */ writeString(view, 12, 'fmt '); /* format chunk length */ view.setUint32(16, 16, true); /* sample format (raw) */ view.setUint16(20, 1, true); /* channel count */ view.setUint16(22, 2, true); /* sample rate */ view.setUint32(24, sampleRate, true); /* byte rate (sample rate * block align) */ view.setUint32(28, sampleRate * 4, true); /* block align (channel count * bytes per sample) */ view.setUint16(32, 4, true); /* bits per sample */ view.setUint16(34, 16, true); /* data chunk identifier */ writeString(view, 36, 'data'); /* data chunk length */ view.setUint32(40, samples.length * 2, true); floatTo16BitPCM(view, 44, samples); return view; } ``` ### Binary Blob Instead of file drag and drop interface this binary blob is passed to editor. Note: BlobBuilder deprecated (but a lot of examples use it), you should use Blob constructor instead! ```javascript var f = new FileReader(); f. { audio_context.decodeAudioData(e.target.result, function(buffer) { $('#audioLayerControl')[0].handleAudio(buffer); }, function(e) { console.warn(e); }); }; f.readAsArrayBuffer(blob); ``` ```javascript function exportWAV(type){ var buffer = mergeBuffers(recBuffers, recLength); var dataview = encodeWAV(buffer); var audioBlob = new Blob([dataview], { type: type }); this.postMessage(audioBlob); } ``` ### Virtual File – URL.createObjectURL You can create file download link pointing to WAV blob, but also set it as the source of an Audio element. ```javascript var url = URL.createObjectURL(blob); var audioElement = document.createElement('audio'); var downloadAnchor = document.createElement('a'); audioElement.controls = true; audioElement.src = url; downloadAnchor.href = url; ``` TODO ---- * Sequencer top / status row should be radio buttons :) * Code cleanup / restructuring * Enable open / drag and drop files for editing * Visual feedback (levels) for live recording * Sequencer UI (and separation to a different module) Credits / license ----------------- Live recording code adapted from: http://www.phpied.com/files/webaudio/record.html Editor code adapted from: https://github.com/plucked/html5-audio-editor Copyright (c) 2012 Daniel Demmel MIT License

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值