/* =============================================================================== GenSineWave.mc : 正弦波合成音の発生(倍音ごとに立上り/立下りを個別設定可) =============================================================================== Copyright (C) 2008-2011 Masahiko Watanabe Edition History: 2008.11.04 初版( GK Library: Rev.2008.02.29 使用 ) 2011.09.01 改版( Library: GK, MK とも Rev.2011.09.01 を使用 ) -----------------------------------------------------------------------------*/ #include "GkLib\GkDefs.h" #include "GkLib\GkSysDefs.h" #include "GkLib\GkKey.h" #include "MkLib\MkAudio.h" if( ::Module.Include( "GkLibrary" ) > 0 && ::Module.Include( "MkLibrary" ) > 0 ) Main( argc, argv ); return; //----------------------------------------------------------------------------- /*****/ // デバッグプリント無効化(リリース時) #set pc #comment #set pv #set pi #comment /*****/ /***** // デバッグプリント有効化(デバッグ時) #set pc print #set pv print #set pi print /*****/ #set p print //============================================================================= function Main( argc, argv ) { #pc "開始"; class MyWindow : ::GK.Window { function Construct( Title, Xo, Yo, Xd, Yd ) { .[ ::GK.Window.Construct ]( Title, Xo, Yo, Xd, Yd ); } function OnPaint() { #pc "OnPaint"; .GetHotRect( R'STRUCT ); //.gc.ClearRect( R, #RGB(255,255,255) ); .gc.DrawEdge( R, 0, #BF_MIDDLE ); } } #pv ^wnd = MyWindow( "正弦波合成音の発生", 550, 0, 450, 610 ); // 全体音量 [%] R0 = ::GK.Rect( 16, 22, 80, 20 ); R1 = ::GK.Rect( R0.Xo + R0.Xd + 16, R0.Yo - 2, 300, 20 ); #pv ^Vol = ^wnd.AddHScrollBar( R1 ); ^Vol.OnScroll = HScroll; ^Vol.SetRange( 10, 1000 ); ^Vol.SetPos( 100 ); ^Vol.label = ^wnd.AddLabel( "Vol=100", R0 ); // 基本周波数 [Hz] R0.Yo += 30; R1.Yo += 30; #pv ^Freq = ^wnd.AddHScrollBar( R1 ); ^Freq.OnScroll = HScroll; ^Freq.SetRange( 30, 5000 ); ^Freq.SetPos( 440 ); ^Freq.label = ^wnd.AddLabel( "Freq=440", R0 ); // 第2倍音の周波数/基本周波数 [単位:1/1000倍] R0.Yo += 30; R1.Yo += 30; #pv ^B2 = ^wnd.AddHScrollBar( R1 ); ^B2.OnScroll = HScroll; ^B2.SetRange( 30, 5000 ); ^B2.SetPos( 2000 ); ^B2.label = ^wnd.AddLabel( "B2=2000", R0 ); // 第3倍音の周波数/基本周波数 [単位:1/1000倍] R0.Yo += 30; R1.Yo += 30; #pv ^B3 = ^wnd.AddHScrollBar( R1 ); ^B3.OnScroll = HScroll; ^B3.SetRange( 30, 5000 ); ^B3.SetPos( 3000 ); ^B3.label = ^wnd.AddLabel( "B3=3000", R0 ); // 第4倍音の周波数/基本周波数 [単位:1/1000倍] R0.Yo += 30; R1.Yo += 30; #pv ^B4 = ^wnd.AddHScrollBar( R1 ); ^B4.OnScroll = HScroll; ^B4.SetRange( 30, 5000 ); ^B4.SetPos( 4000 ); ^B4.label = ^wnd.AddLabel( "B4=4000", R0 ); // 振幅(1) R0.Yo += 30; R1.Yo += 30; #pv ^A1 = ^wnd.AddHScrollBar( R1 ); ^A1.OnScroll = HScroll; ^A1.SetRange( 0, 5000 ); ^A1.SetPos( 5000 ); ^A1.label = ^wnd.AddLabel( "A1=5000", R0 ); // 振幅(2) R0.Yo += 30; R1.Yo += 30; #pv ^A2 = ^wnd.AddHScrollBar( R1 ); ^A2.OnScroll = HScroll; ^A2.SetRange( 0, 5000 ); ^A2.SetPos( 2000 ); ^A2.label = ^wnd.AddLabel( "A2=2000", R0 ); // 振幅(3) R0.Yo += 30; R1.Yo += 30; #pv ^A3 = ^wnd.AddHScrollBar( R1 ); ^A3.OnScroll = HScroll; ^A3.SetRange( 0, 5000 ); ^A3.SetPos( 1000 ); ^A3.label = ^wnd.AddLabel( "A3=1000", R0 ); // 振幅(4) R0.Yo += 30; R1.Yo += 30; #pv ^A4 = ^wnd.AddHScrollBar( R1 ); ^A4.OnScroll = HScroll; ^A4.SetRange( 0, 5000 ); ^A4.SetPos( 500 ); ^A4.label = ^wnd.AddLabel( "A4=500", R0 ); // 立ち上がり時間(1)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Ta1 = ^wnd.AddHScrollBar( R1 ); ^Ta1.OnScroll = HScroll; ^Ta1.SetRange( 0, 1000 ); ^Ta1.SetPos( 20 ); ^Ta1.label = ^wnd.AddLabel( "Ta1=20", R0 ); // 立ち上がり時間(2)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Ta2 = ^wnd.AddHScrollBar( R1 ); ^Ta2.OnScroll = HScroll; ^Ta2.SetRange( 0, 1000 ); ^Ta2.SetPos( 40 ); ^Ta2.label = ^wnd.AddLabel( "Ta2=40", R0 ); // 立ち上がり時間(3)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Ta3 = ^wnd.AddHScrollBar( R1 ); ^Ta3.OnScroll = HScroll; ^Ta3.SetRange( 0, 1000 ); ^Ta3.SetPos( 60 ); ^Ta3.label = ^wnd.AddLabel( "Ta3=60", R0 ); // 立ち上がり時間(4)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Ta4 = ^wnd.AddHScrollBar( R1 ); ^Ta4.OnScroll = HScroll; ^Ta4.SetRange( 0, 1000 ); ^Ta4.SetPos( 80 ); ^Ta4.label = ^wnd.AddLabel( "Ta4=80", R0 ); // 減衰時間(1)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Td1 = ^wnd.AddHScrollBar( R1 ); ^Td1.OnScroll = HScroll; ^Td1.SetRange( 0, 5000 ); ^Td1.SetPos( 2000 ); ^Td1.label = ^wnd.AddLabel( "Td1=2000", R0 ); // 減衰時間(2)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Td2 = ^wnd.AddHScrollBar( R1 ); ^Td2.OnScroll = HScroll; ^Td2.SetRange( 0, 5000 ); ^Td2.SetPos( 2000 ); ^Td2.label = ^wnd.AddLabel( "Td2=2000", R0 ); // 減衰時間(3)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Td3 = ^wnd.AddHScrollBar( R1 ); ^Td3.OnScroll = HScroll; ^Td3.SetRange( 0, 5000 ); ^Td3.SetPos( 2000 ); ^Td3.label = ^wnd.AddLabel( "Td3=2000", R0 ); // 減衰時間(4)[ms] R0.Yo += 30; R1.Yo += 30; #pv ^Td4 = ^wnd.AddHScrollBar( R1 ); ^Td4.OnScroll = HScroll; ^Td4.SetRange( 0, 5000 ); ^Td4.SetPos( 2000 ); ^Td4.label = ^wnd.AddLabel( "Td4=2000", R0 ); // 再生/停止ボタン R0.Yo += 35; R0.Xd = 80; R0.Yd = 25; #pv ^btn = ^wnd.AddButton( "push", "再生", R0 ); ^btn.OnCommand = Btn_Cmd; ^btn.Playing? = #FALSE; ^wnd.Open(); #pv ^WaveOut = ::MK.WaveOut(); ^WaveOut.OnDataReq = ^OnDataReq; ^WaveOut.OnDataEnd = ^OnDataEnd; ::GK.WindowMsgLoop(); delete ^WaveOut; delete ^wnd; #pc "終了"; } //----------------------------------------------------------------------------- function HScroll( act, position ) { switch( act ) { case #SB_TOP: v = .min; break; case #SB_LINEUP: v = .pos - 1; break; case #SB_PAGEUP: v = .pos - 10; break; case #SB_ENDSLIDE: v = position; break; case #SB_SLIDING: v = position; break; case #SB_PAGEDOWN: v = .pos + 10; break; case #SB_LINEDOWN: v = .pos + 1; break; case #SB_BOTTOM: v = .max; break; case #SB_ENDSCROLL: goto END; } if( v < .min ) v = .min; else if( v > .max ) v = .max; .SetPos( v ); .label.SetTitle( this'name + "=" + v'd ); END: #pc "HScroll :" : this'name, act, v; ; } //----------------------------------------------------------------------------- function Btn_Cmd( cmd ) { #pc "ButtonCommand: " : .GetTitle(), .id, cmd; if( ! .Playing? ) { #pc "再生コマンド: "; ^PI2 = ::Math.arctan(-1,0) * 2; ^T = 0.0; ^H1 = 0.0; ^H2 = 0.0; ^H3 = 0.0; ^H4 = 0.0; if( ^WaveOut.Open( ^wnd, -1, 1, 11025, 16, 11025 ) < 0 ) return; .SetTitle( "停止" ); .Playing? = #TRUE; } else { #pc "停止コマンド: "; ^WaveOut.Close(); } } //----------------------------------------------------------------------------- function OnDataReq( data ) { if( ^T > 4 ) { #pc "OnDataReq: off"; //.Reset(); return 0; } #pc "OnDataReq: on"; V = ^Vol.pos'float / 100; F1 = ^Freq.pos'float; F2 = F1 * ^B2.pos / 1000; F3 = F1 * ^B2.pos / 1000; F4 = F1 * ^B2.pos / 1000; max_f = .SampleRate / 2; A1 = ( F1 <= max_f ) ? ^A1.pos : 0 ; A2 = ( F2 <= max_f ) ? ^A2.pos : 0 ; A3 = ( F3 <= max_f ) ? ^A3.pos : 0 ; A4 = ( F4 <= max_f ) ? ^A4.pos : 0 ; dt = 1.0 / .SampleRate; dh1 = ^PI2 * F1 / .SampleRate; dh2 = ^PI2 * F2 / .SampleRate; dh3 = ^PI2 * F3 / .SampleRate; dh4 = ^PI2 * F4 / .SampleRate; Ta1 = ^Ta1.pos'float / 1000; Td1 = ^Td1.pos'float / 1000; Ta2 = ^Ta2.pos'float / 1000; Td2 = ^Td2.pos'float / 1000; Ta3 = ^Ta3.pos'float / 1000; Td3 = ^Td3.pos'float / 1000; Ta4 = ^Ta4.pos'float / 1000; Td4 = ^Td4.pos'float / 1000; for( i = 0 ; i < .nSamples ; i++ ) { ^T += dt; x = 0.0; if( ^T <= Ta1 ) k = ^T / Ta1; else k = ::Math.exp( ( Ta1 - ^T ) * Td1 ); x += A1 * k * ::Math.sin( ^H1 += dh1 ); if( ^T <= Ta2 ) k = ^T / Ta2; else k = ::Math.exp( ( Ta2 - ^T ) * Td2 ); x += A2 * k * ::Math.sin( ^H2 += dh2 ); if( ^T <= Ta3 ) k = ^T / Ta3; else k = ::Math.exp( ( Ta3 - ^T ) * Td3 ); x += A3 * k * ::Math.sin( ^H3 += dh3 ); if( ^T <= Ta4 ) k = ^T / Ta4; else k = ::Math.exp( ( Ta4 - ^T ) * Td4 ); x += A4 * k * ::Math.sin( ^H4 += dh4 ); data(i) = V * x; } //.Write( data ); return .nSamples * .SampleSize ; } //----------------------------------------------------------------------------- function ^OnDataEnd() { #pc "OnDataEnd"; ^btn.SetTitle( "再生" ); ^btn.Playing? = #FALSE; } //-----------------------------------------------------------------------------