在Flash中生成声波时遇到以下问题。 这是生成器部分:
const SAMPLING_RATE:int = 44100;
const TWO_PI:Number = 2 * Math.PI;
const TWO_PI_OVER_SR:Number = TWO_PI / SAMPLING_RATE;
const SAMPLE_SIZE:int = 8192 / 4;
function generateSine(fq:Number):ByteArray
{
var bytes:ByteArray = new ByteArray();
var sample:Number;
var amp:Number = 1;
for (var i:int=0; i<SAMPLE_SIZE; i++)
{
sample = amp* Math.sin(i * TWO_PI_OVER_SR * fq );
bytes.writeFloat(sample);
}
bytes.position = 0;
return bytes;
}
这是播放部分:
function playbackSampleHandler(event:SampleDataEvent):void
{
var sample:Number;
for (var i:int = 0; i < SAMPLE_SIZE && soundData.bytesAvailable; i++)
{
sample = soundData.readFloat();
event.data.writeFloat(sample);
event.data.writeFloat(sample);
}
}
function onClickPlay(e:MouseEvent)
{
soundData= new ByteArray();
var sound:ByteArray= new ByteArray();
sound=generateSine(440);
soundData.writeBytes(sound,0,sound.length);
soundData.position = 0;
morphedSound.addEventListener(SampleDataEvent.SAMPLE_DATA, playbackSampleHandler);
soundChannel = morphedSound.play();
}
快速解释: 我基本上将正弦函数的值写入ByteArray,然后播放处理程序读取该数组并播放声音。我只写了一个8192/4样本缓冲区。
问题: 我的问题是,当你这样做时,声音结束时会发出恼人的咔嗒声,这是正弦波被削减为0以外的值的假象。所以我的问题是如何避免这种情况?
加成: 我还想知道如果Flash中的缓冲区如此严格,即从2048到8192,我怎么能产生正好100毫秒的正弦波?
链接: 如果它有助于我的代码基于本教程 http://www.bit-101.com/blog/?p=2669 和我自己的探索。
答案 0 :(得分:1)
您需要保留样本数据处理结束时发生的正弦波相位。否则你的正弦波不能顺利进行,你得到你所说的点击。
var totalSamples:int=0;
function generateSine(fq:Number):ByteArray
{
var bytes:ByteArray = new ByteArray();
var sample:Number;
var amp:Number = 1;
for (var i:int=0; i<SAMPLE_SIZE; i++)
{
sample = amp* Math.sin((i+totalSamples) * TWO_PI_OVER_SR * fq );
// this uses stored phase ^^^
bytes.writeFloat(sample);
}
totalSamples+=SAMPLE_SIZE; // this preserves phase between subsequent samples
bytes.position = 0;
return bytes;
}
更新:我注意到你连续两次调用发电机 - 这太可怕了。如果只需要一个工作结果,就不能再调用一次函数。
函数onClickPlay(e:MouseEvent)
{
soundData = generateSine(440); //如果我们可以使用赋值,为什么要使用writeBytes?
soundData.position = 0;
morphedSound.addEventListener(SampleDataEvent.SAMPLE_DATA,playbackSampleHandler);
soundChannel = morphedSound.play();
}
此外,要使声音以完整(半)声音结束并删除您需要的最后一次点击,请使用此技巧:
function generateSine(fq:Number,cleanFinish:Boolean=false):ByteArray
{
var bytes:ByteArray = new ByteArray();
var sample:Number;
var amp:Number = 1;
for (var i:int=0; i<SAMPLE_SIZE; i++)
{
sample = amp* Math.sin((i+totalSamples) * TWO_PI_OVER_SR * fq );
// this uses stored phase ^^^
bytes.writeFloat(sample);
if (cleanFinish)
if ((Math.abs(sample)<(SAMPLING_RATE/fq/3))&&(i>SAMPLE_SIZE-SAMPLING_RATE/fq)) break;
// this triggers a drop if we are sampling the last wave already
}
totalSamples+=i; // this preserves phase between subsequent samples
bytes.position = 0;
return bytes;
}
使用设置为true的可选布尔参数调用此函数以接收正确终止的wave。
答案 1 :(得分:0)
&#34;样本&#34;的所有SAMPLE_SIZE值必须分为三部分: 第一件和第三件必须分别具有250个值和中间值 其余的:SAMPLE_SIZE - 500.按如下方式进行: 使用步骤.004生成介于0和.996之间的250个值的数组: 我的意思是:.000,.004,.008,...,.996,并用它们填充数组,比如mod [i] 我从0到249.(mod用于调制...) 如下所示修改初始循环,因此将导致从0到(+/-)amp(前250个值)的淡入淡出,然后到最后250个值的其余值保持(+/-)amp,最后是最后250个值从(+/-)amp到0的淡入淡出。令人讨厌的点击仍然是......历史!我希望这有帮助。
import Foundation
private let configManagerSharedInstance = ConfigManager()
class Config {
class var sharedInstance: ConfigManager {
return configManagerSharedInstance
}
}
// You can put as much Environment as you need but you make sure you also put these environment in the config.plist file.
enum Environment: String {
case Debug = "Debug"
case Production = "Release"
}
class ConfigManager: NSObject {
private var environment: Environment?
var config: NSDictionary?
override init() {
super.init()
// Retrieve the current evironment from the main bundle
if let currentEnvironment = Bundle.main.infoDictionary?["Configuration"] as? String {
// Store the current environment for later use
environment = Environment(rawValue: currentEnvironment)
if let projectConfigPath = Bundle.main.path(forResource: "Config", ofType: "plist") {
if let projectConfigContents = NSDictionary(contentsOfFile: projectConfigPath) as? Dictionary<String, AnyObject> {
config = projectConfigContents[currentEnvironment] as? Dictionary<String, AnyObject> as NSDictionary?
}
} else {
print("config file not found")
}
}
}
func getCurrentEnvironment() -> Environment? {
return self.environment
}
func configForKey(key: String) -> String {
return config?[key] as! String
}
//It will use to get sub dictionary and their values.
func configForCategory(category: String, andKey: String) -> String {
let configuration = config?.value(forKeyPath: category) as! NSDictionary
return configuration.value(forKeyPath: andKey) as! String
}
}