删除AS3中生成的声音中的未发送的点击

时间:2013-08-26 09:26:43

标签: actionscript-3 flash audio

在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 和我自己的探索。

2 个答案:

答案 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
    }
}