窗口刷新音频

Posted

技术标签:

【中文标题】窗口刷新音频【英文标题】:Window Refresh Audio 【发布时间】:2014-02-13 03:14:51 【问题描述】:

所以我有点问题,我正在编写一个拍手传感器,当有人拍手并执行某个命令时它会听到。

        //CLAP
    private float bigValue;
    WaveIn waveIn;
    private double MaxValue;
    private void button1_Loaded(object sender, RoutedEventArgs e)
    
                    if (Convert.ToInt16(textBox1.Text) > 100)
        
            MessageBox.Show("Invalid Value");
            return;
        
        else
            MaxValue = Convert.ToDouble(textBox1.Text) / 100;
        bigValue = 0;
        waveIn = new WaveIn();
        int waveInDevices = waveIn.DeviceNumber;

        //Get Device Count
        for ( int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
        
            WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
        
        waveIn.DeviceNumber = 0;
        waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
        int sampleRate = 8000;
        int channels = 1;
        waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
        waveIn.StartRecording();
    

    //CLAP
    void waveIn_DataAvailable(object sender, WaveInEventArgs e)
    
        for (int index = 0; index < e.BytesRecorded; index += 2)
        
            short sample = (short)((e.Buffer[index + 1] << 8) | e.Buffer[index + 0]);

            float sample32 = sample / 32768f;
            label1.Content = sample32.ToString();
            if (bigValue < sample32)
            
                bigValue = sample32;
                label2.Content = bigValue.ToString();
                if (bigValue > MaxValue)
                
                    waveIn.StopRecording();
                    SendMessage(MONITOR_ON, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
                    MessageBox.Show("Did you Clap?");
                
            
        
    

代码本身按原样工作,但我需要它能够根据需要多次重置。这个程序基本上是听一个拍手声,然后唤醒显示器并启动它。每当我添加另一个“waveIn.StartRecording();”时,程序就会中断

关于如何刷新页面或让它永远收听的任何想法?

【问题讨论】:

您可能需要将WaveIn 代码移动到后端线程(BackgroundWorker),它只会在检测到拍手时在主线程上触发一个事件,但始终保持监听。 你能给我一个例子来说明如何做到这一点 我会使用 WasapiCapture 而不是 WaveIn。 WaveIn 已经过时了。 Wasapi 是一个相当新的 api,自 windows vista 以来所有 windows 系统都支持它。 【参考方案1】:

基本上您的代码所做的是打开waveIn 以接收音频数据,然后检查数据中的大声 样本。当它接收到超过阈值的样本时,它会停止侦听并发出命令。

正如所写,代码在检测到第一个大样本后停止。不再接收音频数据等。可能不是您想要的。相反,您需要改进您的拍手检测,以便它在检测到第一个大样本后停止处理传入的数据一段时间 - 比如说几秒钟。不要停止接收音频数据,只是停止对其做出反应。

向您的类添加一个DataTime 字段,用于记录最后一次拍手检测的时间戳。在您的 waveIn_DataAvailable 方法开始时,检查自上次检测以来经过的时间是否小于您的静音时间,如果是,则直接返回而不处理音频块。当您检测到足够大的样本时,触发事件并更新最后拍手检测字段。

类似这样的:

DateTime LastDetection = DateTime.Now.AddMinutes(-1);

void waveIn_DataAvailable(object sender, WaveInEventArgs e)

    if (LastDetection.AddSeconds(3) >= DateTime.Now)
        return;
    if (DetectClap(e.Buffer))
    
        LastDetection = DateTime.Now;
        SendMessage(MONITOR_ON, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
        MessageBox.Show("Clap detected.");
    


bool DetectClap(byte[] audiobytes)

    for (int i = 0; i < audiobytes.Length; i += 2)
    
        float sample32 = (float)((short)((audiobytes[0] << 8) | audiobytes[1]))/32768f;
        if (sample32 > MaxValue)
            return true;
    
    return false;

【讨论】:

【参考方案2】:

这是一个将WaveIn 逻辑移动到后台线程的示例。它应该给你足够的开始。请查看documentation 以获取包含后台线程取消的完整示例。

    //CLAP
private float bigValue;
WaveIn waveIn;
private double MaxValue;

private BackgroundWorker worker;

private void button1_Loaded(object sender, RoutedEventArgs e)

    if (Convert.ToInt16(textBox1.Text) > 100)
    
        MessageBox.Show("Invalid Value");
        return;
    
    else
        MaxValue = Convert.ToDouble(textBox1.Text) / 100;

    bigValue = 0;

        // You'll need to handle the thread cancellation
        // when the user clicks the button again

    worker = new BackgroundWorker();

    worker.WorkerReportsProgress = true;
    worker.WorkerSupportsCancellation = true;

    worker.DoWork += (s, e) =>
    
        waveIn = new WaveIn();
        int waveInDevices = waveIn.DeviceNumber;

        //Get Device Count
        for ( int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
        
            WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
        
        waveIn.DeviceNumber = 0;
        waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
        int sampleRate = 8000;
        int channels = 1;
        waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
        waveIn.StartRecording();
    ;

    worker.ProgressChanged += (s, e) =>
    
        SendMessage(MONITOR_ON, WM_SYSCOMMAND, SC_MONITORPOWER, MONITOR_ON);
        MessageBox.Show("Did you Clap?");
    ;

     worker.RunWorkerAsync();


//CLAP
void waveIn_DataAvailable(object sender, WaveInEventArgs e)

    for (int index = 0; index < e.BytesRecorded; index += 2)
    
        short sample = (short)((e.Buffer[index + 1] << 8) | e.Buffer[index + 0]);

        float sample32 = sample / 32768f;
        label1.Content = sample32.ToString();
        if (bigValue < sample32)
        
            bigValue = sample32;
            label2.Content = bigValue.ToString();
            if (bigValue > MaxValue)
            
                worker.ReportProgress(0);
                break;
            
        
    

【讨论】:

按钮是否需要它,因为我在加载事件而不是点击事件上执行此操作 好的。不需要取消代码,除非您需要在表单关闭时释放非托管资源。【参考方案3】:

所以最后我采用了与两个建议的答案不同的方式。

        private float bigValue;
    WaveIn waveIn;
    private double MaxValue;
    private void button1_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
    
        if (Convert.ToInt16(textBox1.Text) > 100)
        
            MessageBox.Show("Invalid Value");
            return;
        
        else
            MaxValue = Convert.ToDouble(textBox1.Text) / 100;
        bigValue = 0;
        waveIn = new WaveIn();
        int waveInDevices = waveIn.DeviceNumber;

        //Get Device Count
        for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
        
            WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
        
        waveIn.DeviceNumber = 0;
        waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
        int sampleRate = 8000;
        int channels = 1;
        waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
        waveIn.StartRecording();
    

    private void button1_Loaded(object sender, RoutedEventArgs e)
    
        if (Convert.ToInt16(textBox1.Text) > 100)
        
            MessageBox.Show("Invalid Value");
            return;
        
        else
            MaxValue = Convert.ToDouble(textBox1.Text) / 100;
        bigValue = 0;
        waveIn = new WaveIn();
        int waveInDevices = waveIn.DeviceNumber;
        for (int i = 0; i <= 100; i++)
        
        

        //Get Device Count
        for (int waveInDevice = 0; waveInDevice < waveInDevices; waveInDevice++)
        
            WaveInCapabilities deviceInfo = WaveIn.GetCapabilities(waveInDevice);
        
        waveIn.DeviceNumber = 0;
        waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);
        int sampleRate = 8000;
        int channels = 1;
        waveIn.WaveFormat = new WaveFormat(sampleRate, channels);
        waveIn.StartRecording();
    

    int i = 0;

    void waveIn_DataAvailable(object sender, WaveInEventArgs e)
    
        for (int index = 0; index < e.BytesRecorded; index += 2)
        
            short sample = (short)((e.Buffer[index + 1] << 8) | e.Buffer[index + 0]);

            float sample32 = sample / 32768f;
            label1.Content = sample32.ToString();
            if (bigValue < sample32)
            
                bigValue = sample32;
                label2.Content = bigValue.ToString();
                if (bigValue > MaxValue)
                
                    waveIn.StopRecording();
                    if (IsOdd(i))
                    
                        button1.IsEnabled = false;
                    
                    else
                    
                        button1.IsEnabled = true;
                    
                    MessageBox.Show("Did you Clap?");
                    i++;
                
            
        
    
    public static bool IsOdd(int value)
    
        return value % 2 != 0;
    

第一个加载事件将其关闭。第二个使用 IsEnabled 事件在按钮打开和按钮关闭之间来回切换。 on 和 off 由 and if 语句在奇数和偶数之间进行选择来实现。

这就是我实现这个无限循环的方法。

注意:这种方式可能不是最有效的方式,但它完成了工作。 我也将(打开窗口)代码从这个答案中删除了。

【讨论】:

以上是关于窗口刷新音频的主要内容,如果未能解决你的问题,请参考以下文章

QT5怎样设置父窗口刷新时,不刷新子窗口?

父窗口刷新的问题!

firefox下如何关闭子窗口刷新父窗口? - 技术问答

子窗口关闭,父窗口如何刷新

Meteor - 脚本在刷新/仅在某些路由上无法正确加载 Web 音频缓冲区

C#里面,子窗口关闭以后,刷新一个父窗口的控件的属性,怎么办