Hope is a Dream. Dream is a Hope.

非公開ブログは再開しました。

NAudioで信号処理 (その5)

NAudioで信号処理 (目次) - Hope is a Dream. Dream is a Hope.

NAudioで信号処理 (その5)

f:id:hope_is_dream:20170506124450p:plain

エフェクト処理の準備

C# Audio Tutorial 5 - EffectStream Part 1

Tutorial 5ではエフェクト処理の準備をします。WaveFIleReaderを使ってロードしたデータに対して、自作関数EffectStream(WaveStream継承)を間に挟みます。 これにより、任意の信号処理の後にBLockAlignReductionStreamを通して、再生が可能になります。

※ 本チュートリアルでは、エフェクトはかけてません。

 // Audio Chain
WaveChannel32 wave = new WaveChannel32(new WaveFileReader(open.FileName));
EffectStream effect = new EffectStream(wave); // エフェクトをかけるためのパイプライン
stream = new BlockAlignReductionStream(effect);

output = new DirectSoundOut(200);
output.Init(stream);
output.Play();
using System;
using System.Windows.Forms;
using NAudio.Wave;

namespace Tutorial
{
    public partial class Form1 : Form
    {
        #region form
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// フォームの初期設定。
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {
            // ボタンの初期状態
            this.Text = @"Tutorial 6 - Audio Loopback using";
            linkLabel1.Text = "https://www.youtube.com/watch?v=BjnTgIdTXwI";
        }

        #endregion

        #region Member
        private DirectSoundOut output = null;
        private BlockAlignReductionStream stream = null;
        #endregion


        /// <summary>
        /// フォームのクロージング処理
        /// Wave関連オブジェクトのDispose処理を担当
        /// </summary>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            output?.Dispose();
            output = null;

            stream?.Dispose();
            stream = null;
        }

        private void button_OpenWave_Click(object sender, EventArgs e)
        {
            // WAV File Open
            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "WAV File (*.wav)|*.wav;";
            if (open.ShowDialog() != DialogResult.OK) return;

            // Audio Chain
            WaveChannel32 wave = new WaveChannel32(new WaveFileReader(open.FileName));
            EffectStream effect = new EffectStream(wave); // エフェクトをかけるためのパイプライン
            stream = new BlockAlignReductionStream(effect);

            output = new DirectSoundOut(200);
            output.Init(stream);
            output.Play();

        }
    }

}

EffectStream.cs

using System;

using NAudio.Wave;

namespace Tutorial
{
    public class EffectStream : WaveStream
    {
        public WaveStream SourceStream { get; set; }

        public EffectStream(WaveStream stream)
        {
            this.SourceStream = stream;
        }

        public override WaveFormat WaveFormat
        {
            get { return this.SourceStream.WaveFormat; }
        }

        public override long Length
        {
            get { return SourceStream.Length; }
        }

        public override long Position
        {
            get { return this.SourceStream.Position; }
            set { this.SourceStream.Position = value; }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            //*****************************************************************//
            // ここで信号処理をする
            //*****************************************************************//
            Console.WriteLine("DirectSoundOut request {0} bytes", count);
            return this.SourceStream.Read(buffer, offset, count);
        }
    }
}

NAudioで信号処理 (その6)

NAudioで信号処理 (目次) - Hope is a Dream. Dream is a Hope.

NAudioで信号処理 (その6)

f:id:hope_is_dream:20170506124002p:plain

録音デバイスの選択と、再生

C# Audio Tutorial 6 - Audio Loopback using

やっとデバイスの選択方法がでてきました。まずは録音デバイスということで入力側のデバイスの一覧を取得し、ListViewに表示します。

// SetUp WaveIn Devices
List<WaveInCapabilities> sources = new List<WaveInCapabilities>();

for (int i = 0; i < WaveIn.DeviceCount; i++)
{
    sources.Add(WaveIn.GetCapabilities(i));
}

listView_Sources.Items.Clear();

foreach (var source in sources)
{
    ListViewItem item = new ListViewItem(source.ProductName);
    item.SubItems.Add(new ListViewItem.ListViewSubItem(item, source.Channels.ToString()));

    listView_Sources.Items.Add(item);
}

オーディオチェーンは以下のようになります。

#region Member
private NAudio.Wave.WaveIn sourceStream = null; // 録音なのでWaveInを使用
private NAudio.Wave.DirectSoundOut waveOut = null;
#endregion


private void button_Start_Click(object sender, EventArgs e)
{
    if (listView_Sources.SelectedItems.Count == 0) return;

    int deviceNumber = listView_Sources.SelectedItems[0].Index;

    // waveIn Select Recording Device
    sourceStream = new NAudio.Wave.WaveIn();
    sourceStream.DeviceNumber = deviceNumber; // 使用するデバイスを選択
    sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(44100, WaveIn.GetCapabilities(deviceNumber).Channels); 
    
    // WaveInProviderで包み込む
    WaveInProvider waveIn = new NAudio.Wave.WaveInProvider(sourceStream); // ?

    // waveOut
    waveOut = new DirectSoundOut();
    waveOut.Init(waveIn);

    sourceStream.StartRecording();
    waveOut.Play();

}

using System;
using System.Windows.Forms;
using NAudio.Wave;
using System.Collections.Generic;

namespace Tutorial
{
    public partial class Form1 : Form
    {
        #region form
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// フォームの初期設定。
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {
            // ボタンの初期状態
            this.Text = @"Tutorial 5";
            linkLabel1.Text = "https://www.youtube.com/watch?v=CFUBse8Rjko";
        }

        /// <summary>
        /// フォームのクロージング処理
        /// Wave関連オブジェクトのDispose処理を担当
        /// </summary>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
        }

        #endregion


        private void button_Refresh_Click(object sender, EventArgs e)
        {

            // SetUp WaveIn Devices
            List<WaveInCapabilities> sources = new List<WaveInCapabilities>();

            for (int i = 0; i < WaveIn.DeviceCount; i++)
            {
                sources.Add(WaveIn.GetCapabilities(i));
            }

            listView_Sources.Items.Clear();

            foreach (var source in sources)
            {
                ListViewItem item = new ListViewItem(source.ProductName);
                item.SubItems.Add(new ListViewItem.ListViewSubItem(item, source.Channels.ToString()));

                listView_Sources.Items.Add(item);
            }

        }

        #region Member
        private NAudio.Wave.WaveIn sourceStream = null;
        private NAudio.Wave.DirectSoundOut waveOut = null;
        #endregion


        private void button_Start_Click(object sender, EventArgs e)
        {
            if (listView_Sources.SelectedItems.Count == 0) return;

            int deviceNumber = listView_Sources.SelectedItems[0].Index;

            // waveIn Select Recording Device
            sourceStream = new NAudio.Wave.WaveIn();
            sourceStream.DeviceNumber = deviceNumber;
            sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(44100, WaveIn.GetCapabilities(deviceNumber).Channels);

            WaveInProvider waveIn = new NAudio.Wave.WaveInProvider(sourceStream); // ?

            // waveOut
            waveOut = new DirectSoundOut();
            waveOut.Init(waveIn);

            sourceStream.StartRecording();
            waveOut.Play();

        }

        private void button_Stop_Click(object sender, EventArgs e)
        {
            waveOut?.Stop();
            waveOut?.Dispose();
            waveOut = null;

            sourceStream?.StopRecording();
            sourceStream?.Dispose();
            sourceStream = null;
        }

        private void button_Exit_Click(object sender, EventArgs e)
        {
            button_Stop_Click(sender, e);
            this.Close();
        }
    }

}



NAudioで信号処理 (その4)

NAudioで信号処理 (目次) - Hope is a Dream. Dream is a Hope.

チュートリアル その4

f:id:hope_is_dream:20170506085429p:plain

NAudioをつかってSin音を生成

C# Audio Tutorial 2 - MP3/WAV File with NAudio

// 再生デバイス
private DirectSoundOut output = null;
private BlockAlignReductionStream stream = null;

// オーディオチェイン
WaveTone tone = new WaveTone(1000, 0.1);
stream = new BlockAlignReductionStream(tone);

output = new DirectSoundOut();
output.Init(stream);
output.Play();

// SinWaveを生成
for (int i = 0; i < samples; i++)
{
    double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
    time += 1.0 / 44100;

    short truncated = (short)Math.Round(sine * (Math.Pow(2, 15) - 1)); //16bit

    buffer[i * 2] = (byte)(truncated & 0x00ff); // 下半分を取り出し 8bit
    buffer[i * 2 + 1] = (byte)((truncated & 0xff00) >> 8); // 上半分を取り出してシフト 8bit
}
using System;
using System.Windows.Forms;
using NAudio.Wave;

namespace Tutorial
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// フォームの初期設定。
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {
            // ボタンの初期状態
            this.Text = @"Tutorial 4";
            linkLabel1.Text = "https://www.youtube.com/watch?v=Tumpkl-xJuA";
        }

        #region Member
        private DirectSoundOut output = null;
        private BlockAlignReductionStream stream = null;
        #endregion


        /// <summary>
        /// フォームのクロージング処理
        /// Wave関連オブジェクトのDispose処理を担当
        /// </summary>
        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            output?.Dispose();
            output = null;

            stream?.Dispose();
            stream = null;
        }

        private void button_StartTone_Click(object sender, EventArgs e)
        {
            WaveTone tone = new WaveTone(1000, 0.1);
            stream = new BlockAlignReductionStream(tone);

            output = new DirectSoundOut();
            output.Init(stream);
            output.Play();

        }

        private void button_StopTone_Click(object sender, EventArgs e)
        {
            output?.Stop();
        }
    }

    /// <summary>
    /// 
    /// WaveStreamは抽象クラス
    /// </summary>
    public class WaveTone : WaveStream
    {
        private double frequency;
        private double amplitude;
        private double time;

        public WaveTone(double f, double a)
        {
            this.time = 0;
            this.frequency = f;
            this.amplitude = a;
        }


        public override long Position { get; set; }

        public override long Length
        {
            get
            {
                return long.MaxValue;
            }
        }

        public override WaveFormat WaveFormat
        {
            get { return new WaveFormat(44100, 32, 1); }
        }

        /// <summary>
        /// </summary>
        public override int Read(byte[] buffer, int offset, int count)
        {
            // byte(8bit)
            // short(16bit)

            int samples = count / 2; // ?
            for (int i = 0; i < samples; i++)
            {
                double sine = amplitude * Math.Sin(Math.PI * 2 * frequency * time);
                time += 1.0 / 44100;

                short truncated = (short)Math.Round(sine * (Math.Pow(2, 15) - 1)); //16bit

                buffer[i * 2] = (byte)(truncated & 0x00ff); // 下半分を取り出し 8bit
                buffer[i * 2 + 1] = (byte)((truncated & 0xff00) >> 8); // 上半分を取り出してシフト 8bit
            }

            return count;
        }
    }
}