Sound Menu

音楽CDの Wave データを編集するメニューを設定します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. 音楽CD仕様(ステレオ, 16bit, Sampling:44.1k) にしか対応しません。
    Wave 入力と出力のメニューを設定します。
    ボリュームのアップとダウンのメニューを設定します。
    曲の無音部分の削除と挿入のメニューを設定します。
    これで音楽CDの Wave Data を修正することが出来ます。
    プログラムをシンプルにするために、画面の見栄えなど余分なことは省略しています。
    このプログラムは実用的で、私はこれを使って歌謡曲の音量や無音部分の長さを設定しています。
  2. 音楽CDの Wave データを編集するプログラムを、空のプロジェクトで作成したソースコードです。
    歌謡曲のサイズに合わせて byt を 70000000 にしています。
    これを超えるサイズを編集するときはプログラムを修正して下さい。
    //★ WAV FILE ステレオ 16bit のメニューを設定する    前田 稔
    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.IO;
    
    public class MyForm : Form
    {
        static string file_name;
        static byte[] byt = new byte[70000000];
        static int    m_leng, m_idx, m_siz;
        public MyForm()
        {
            Width = 1200;
            Height = 700;
            Text = "Wave S16 Graph";
            m_leng = -1;
            MainMenu menu = new MainMenu();
            MenuItem item = menu.MenuItems.Add("ファイル(&F)");
            item.MenuItems.Add(new MenuItem("WAV 入力(&R)", new EventHandler(this.Read)));
            item.MenuItems.Add(new MenuItem("WAV 出力(&W)", new EventHandler(this.Write)));
            item.MenuItems.Add(new MenuItem("終了(&X)", new EventHandler(this.FileExit), Shortcut.CtrlQ));
            item = menu.MenuItems.Add("Volume(&H)");
            item.MenuItems.Add(new MenuItem("5% Up(&I)", new EventHandler(this.Up5)));
            item.MenuItems.Add(new MenuItem("10% Up(&J)", new EventHandler(this.Up10)));
            item.MenuItems.Add(new MenuItem("20% Up(&K)", new EventHandler(this.Up20)));
            item.MenuItems.Add(new MenuItem("50% Up(&L)", new EventHandler(this.Up50)));
            item.MenuItems.Add(new MenuItem("100% Up(&L)", new EventHandler(this.Up100)));
            item.MenuItems.Add(new MenuItem("5% Down(&M)", new EventHandler(this.Down5)));
            item.MenuItems.Add(new MenuItem("10% Down(&N)", new EventHandler(this.Down10)));
            item.MenuItems.Add(new MenuItem("20% Down(&O)", new EventHandler(this.Down20)));
            item.MenuItems.Add(new MenuItem("50% Down(&P)", new EventHandler(this.Down50)));
            item.MenuItems.Add(new MenuItem("Auto Vol(&X)", new EventHandler(this.Auto)));
            item = menu.MenuItems.Add("無音(&N)");
            item.MenuItems.Add(new MenuItem("前部削除(&M)", new EventHandler(this.Del)));
            item.MenuItems.Add(new MenuItem("無音削除(&M)", new EventHandler(this.AutoDel)));
            item.MenuItems.Add(new MenuItem("無音挿入(&O)", new EventHandler(this.Ins)));
            item.MenuItems.Add(new MenuItem("後部削除(&P)", new EventHandler(this.BDel)));
            item.MenuItems.Add(new MenuItem("後部挿入(&Q)", new EventHandler(this.BIns)));
            item = menu.MenuItems.Add("ヘルプ(&H)");
            item.MenuItems.Add(new MenuItem("バージョン情報(&A)...", new EventHandler(this.HelpAbout)));
            this.Menu = menu;
            Paint += new PaintEventHandler(MyHandler);
        }
    
        // 入出力メニュー
        private void Read(object sender, EventArgs e)
        {
            OpenFileDialog opendlg = new OpenFileDialog();
            opendlg.Filter = "WAVE ファイル|*.wav|すべてのファイル (*.*)|*.*" ;
            if (opendlg.ShowDialog() == DialogResult.OK)
            {   file_name = opendlg.FileName;  }
            m_siz = WavRead();
            Text = file_name;
            Invalidate();
        }
        private void Write(object sender, EventArgs e)
        {
            if (m_leng==-1) return;
            SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "WAVE ファイル|*.wav;|すべてのファイル|*.*";
            if (saveFileDialog1.ShowDialog(this) == DialogResult.OK)
            {
                FileStream writer = File.Create(saveFileDialog1.FileName);
                writer.Write(byt,0,m_leng);
                writer.Close();
                Console.Write("complete");
            }
            saveFileDialog1.Dispose();
        }
    
        // ボリューム設定メニュー
        private void Up5(object sender, EventArgs e)
        {   Volume((float)1.05);  }
        private void Up10(object sender, EventArgs e)
        {   Volume((float)1.1);  }
        private void Up20(object sender, EventArgs e)
        {   Volume((float)1.2);  }
        private void Up50(object sender, EventArgs e)
        {   Volume((float)1.5);  }
        private void Up100(object sender, EventArgs e)
        {   Volume((float)2.0); }
        private void Down5(object sender, EventArgs e)
        {   Volume((float)0.95);  }
        private void Down10(object sender, EventArgs e)
        {   Volume((float)0.9);  }
        private void Down20(object sender, EventArgs e)
        {   Volume((float)0.8);  }
        private void Down50(object sender, EventArgs e)
        {   Volume((float)0.5);  }
        private void Auto(object sender, EventArgs e)
        {   AutoVol();  }
        
        // 前部の削除(300000 byte)
        private void Del(object sender, EventArgs e)
        {   int i,idx;
            m_siz -= 300000;
            m_leng -= 300000;
            for(i=0; i<m_siz; i++)
            {   idx = m_idx+i;
                byt[idx] = byt[idx+300000];
            }
            set_int(4, m_leng);         // File Length
            set_int(m_siz, m_idx-4);    // Data Size
            Invalidate();
        }
    
        // 無音の自動削除
        private void AutoDel(object sender, EventArgs e)
        {   int i,idx,pt;
            short v;
            for(pt=i=0; i<m_siz; i+=2)
            {   idx = m_idx+i;
                v = get_short(idx);
                if (v>5)    break;
                pt = i;
            }
            m_siz -= pt;
            m_leng -= pt;
            for(i=0; i<m_siz; i++)
            {   idx = m_idx+i;
                byt[idx] = byt[idx+pt];
            }
            set_int(4, m_leng);
            set_int(m_siz, m_idx-4);
            MessageBox.Show(pt.ToString());
            Invalidate();
        }
    
        // 無音の挿入(300000 byte)
        private void Ins(object sender, EventArgs e)
        {   int i,idx;
            for(i=m_siz-1; i>=0; i--)
            {   idx = m_idx+i;
                byt[idx+300000] = byt[idx];
            }
            for(i=0; i<300000; i++)
            {   idx = m_idx+i;
                byt[idx] = 0;
            }
            m_siz += 300000;
            m_leng += 300000;
            set_int(4, m_leng);
            set_int(m_siz, m_idx-4);
            Invalidate();
        }
    
        // 後部の削除(300000 byte)
        private void BDel(object sender, EventArgs e)
        {
            m_siz -= 300000;
            m_leng -= 300000;
            set_int(4, m_leng);
            set_int(m_siz, m_idx-4);
            Invalidate();
        }
    
        // 後部無音の挿入(300000 byte)
        private void BIns(object sender, EventArgs e)
        {   int i,idx;
            for(i=0; i<300000; i++)
            {   idx = m_idx+m_siz+i;
                byt[idx] = 0;
            }
            m_siz += 300000;
            m_leng += 300000;
            set_int(4, m_leng);
            set_int(m_siz, m_idx-4);
            Invalidate();
        }
    
        // ファイル-終了メニューのイベントハンドラ
        private void FileExit(object sender, EventArgs e)
        {   this.Close();  }
    
        // ヘルプ-バージョン情報メニューのイベントハンドラ
        private void HelpAbout(object sender, EventArgs e)
        {   MessageBox.Show("Wave Graph S16    by Maeda Minoru");  }
    
        // ウインドウの描画
        private void MyHandler(object sender, PaintEventArgs e)
        {
            int     i, idx, dt, wk;
            short   v;
            Graphics g = e.Graphics;
            g.DrawLine(new Pen(Color.Black),100,100,1100,100);
            g.DrawLine(new Pen(Color.Black,3),100,200,1100,200);
            g.DrawLine(new Pen(Color.Black), 100, 300, 1100, 300);
            g.DrawLine(new Pen(Color.Black, 3), 100, 400, 1100, 400);
            g.DrawLine(new Pen(Color.Black), 100, 500, 1100, 500);
            if (m_leng==-1) return;
            dt = m_siz/1000;
            if (dt%2==1)    dt++;
            for (i=0; i<1000; i++)
            {
                if (i*dt>=m_siz)    break;
                idx = m_idx + i*dt;
                v = (short)get_short(idx);
                wk = v/327;
                if (wk<0) wk = 0-wk;
                if (v < 0) g.DrawLine(new Pen(Color.Blue), 100+i, 200-wk, 100+i, 200);
                else g.DrawLine(new Pen(Color.Blue), 100+i, 200, 100+i, 200+wk);
                idx+=2;
                v = (short)get_short(idx);
                wk = v/327;
                if (wk<0) wk = 0-wk;
                if (v < 0) g.DrawLine(new Pen(Color.Red), 100+i, 400-wk, 100+i, 400);
                else g.DrawLine(new Pen(Color.Red), 100+i, 400, 100+i, 400+wk);
            }
        }
    
        // WAVE FILE の入力
        private int WavRead()
        {   int siz;    //Data Size
            int ix;     //byt Index
    
            if (!File.Exists(file_name)) return -1;
            FileStream reader = File.Open(file_name,FileMode.Open);
            m_leng= reader.Read(byt,0,byt.Length);
            reader.Close();
    
            if (get_str(0)!="RIFF")
            {   MessageBox.Show("Header RIFF Error");
                return -1;
            }
            if (get_str(8)!="WAVE")
            {   MessageBox.Show("Header WAVE Error");
                return -1;
            }
            ix = 12;
            if (get_str(ix) != "fmt ")
            {   ix += get_int(ix+4)+8;
                if (get_str(ix) != "fmt ")
                {   MessageBox.Show("Header fmt Error");
                    return -1;
                }
            }
            if (get_short(ix+8) != 1)
            {   MessageBox.Show("Format Error");
                return -1;
            }
            if (get_short(ix+10) != 2)
            {   MessageBox.Show("Format Error");
                return -1;
            }
            ix += get_int(ix+4)+8;
            if (get_str(ix) != "data")
            {   ix += get_int(ix+4)+8;
                if (get_str(ix) != "data")
                {   MessageBox.Show("Header data Error");
                    return -1;
                }
            }
            siz = get_int(ix+4);        //byt[ix]=="data"
            m_idx = ix + 8;
            return siz;
        }
    
        // ボリュームの調整
        private void Volume(float rate)
        {   int i,idx;
            float fv;
            short v;
            for (i=0; i<m_siz; i+=2)
            {   idx = m_idx+i;
                fv = (float)get_short(idx);
                fv = fv * rate;
                if (fv>32767)   fv = 32767;
                if (fv<-32767)  fv = -32767;
                v = (short)fv;
                set_short(v, idx);
            }
            Invalidate();
        }
    
        // ボリュームの自動設定
        private void AutoVol()
        {   int i,idx;
            short mx,v;
            float r;
            mx = (short)0;
            for (i=0; i<m_siz; i+=2)
            {   idx = m_idx+i;
                v = get_short(idx);
                if (mx<v)   mx = v;
            }
            r = (float)12000 / (float)mx;   // MP3Gain に合わす
            //r = (float)30000 / (float)mx;
            Volume(r);
            MessageBox.Show(mx.ToString() + " : " + r.ToString());
        }
    
        //☆ get_str() 関数, get_short() 関数, get_int() 関数, set_short() 関数, set_int() 関数の定義
    }
    
    class Draw
    {
        [STAThread]
        public static void Main()
        {
            MyForm mf = new MyForm();
            Application.Run(mf);
        }
    }
    
  3. メニューから[WAV 入力]を選択して読み込むとグラフが表示されます。
    Volime メニューで音量を調整します。
    [WAV 出力]で Wave ファイルに保存します。
    [Auto Vol]は波形の最大値から計算して、歪のないレベルに調整します。
    このときグラフに表示されなくても、ノイズなど髭状のパルスが含まれると最適レベルが狂います。
    音量を上げてノイズをオーバーフローさせて、影響を無くしてから[Auto Vol]を実行して下さい。
    MP3Gain(音量調整のフリーソフト)の規定値に合わすと 12000 ぐらいですが、音量が小さすぎるようです。
    [無音削除]で曲の開始の無音部分を削除します。
    [無音挿入]で曲の開始の無音部分を挿入します。(繰り返すと無音部分が長くなります)
    [後部削除, 後部挿入] は後部から一定の長さを「削除/挿入」します。
  4. WavRead() 関数で WAVE FILE を入力して形式のチェックを行います。
    ファイルの先頭は "RIFF" で始まり、"WAVE" に続いて "fmt " のチャンクが格納されています。
    所がファイルによって "fmt " チャンクの前後に不要なチャンク?が挿入されているケースがあります。
    "fmt " チャンクには WAVE FILE のフォーマット(記録形式)が定義されています。
    "fmt " チャンクに続く "data" チャンクには音のデータが格納されていて、その位置を m_idx に設定します。
        static int	m_leng;     //File Length
        static int  m_idx;      //Data が格納されている先頭 Index
        static int  m_siz;      //Data の Size(Byte)
    
    WAVE FILE の説明は WAV ファイルフォーマット を参照して下さい。
  5. 次の関数は Volume M8 を参照して下さい。
    get_str() 関数で byt[idx] から4文字を取り出します。
    get_short() 関数で byt[idx] から short データを取り出します。
    get_int() 関数で byt[idx] から int データを取り出します。
    set_short() 関数で short データを設定します。
    set_int() 関数で short データを設定します。

[Previous Chapter ↑] Graph S16

超初心者のプログラム入門(C# Frame Work)