/* =============================================================================== VoiceMemo.mc : 音声の記録/再生(自動録音、再生速度変更、逆再生等も可) =============================================================================== Copyright (C) 2011-2013 Masahiko Watanabe Edition History: 2011.09.05 初版( GK, MK Library: Rev.2011.09.05 以降を使用 ) 2013.02.15 MikoScript3(ユニコード版)用に変更 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 説明: ・本スクリプトは、マイクから入力された音声を記録/再生します。 音声でメモを取る時などに便利です。 本ウィンドウの画面構成は、次のようになっています。 ・「音量」には、次のような音量を示すバーが所定の色で表示されます。 入力音量 青色: モニター中(録音停止中) 黄色: 自動録音の待機中 橙色: 入力された音声を記録中(録音中) 出力音量 緑色: 記録された音声を再生中 ・「録音開始」ボタンを押すと、録音が始まります。 ・「録音停止」ボタンを押すと、録音が止まります。 ・「自動録音」を ON にして録音を開始すると、指定の音量以上の音声だけが 記録されます。これによって、無音区間や小音区間を録音から除外できます。 この自動録音では、録音時刻も記録されます。この時刻は、自動録音された 音声を再生した時に表示されます。 自動録音の音量の境界は、「音量」バー上に赤の逆三角形で表示されます。 それをマウスでドラッグすれば、そのレベルを変更できます。 ・「再生位置」のスライダーで、再生開始位置を任意に選べます。 ・「再生速度」は、50 〜 200 % の範囲内で任意に設定できます。 ・「逆再生」を ON にすれば、音声は逆方向に再生されます。 ・録音時間は、最大で 90 分程度までです。 ・録音精度は、11.025KHz 16-bit モノラルです。 ・音声の記録/再生は、メインメモリー上で行なわれます。 ・「保存」で、メインメモリー上に記録された音声をファイルに保存できます。 ・「読込」で、ファイルに保存された音声をメインメモリー上に展開できます。 但し、11.025KHz, 22.05KHz, or 44.1KHz の 16-bit or 8-bit にのみ対応。 ・「録音消去」は、メインメモリー上に記録された音声を全て消去します。 「録音開始」では、今までの録音の続きから今回の録音が追加されるので、 新規に録音を開始する時は、「録音消去」しておく必要があります。 補説: ・本スクリプトは、Apsaly でなくても、コンソール版の miko.exe から起動可能。 ・マイク入力の音量の調整は、コントロールパネルで行なってください。 -----------------------------------------------------------------------------*/ /*****/ // ライブラリーのモジュールをロードする場合 #include #include #include if( ::Module.Include( "GkLibrary" ) > 0 && ::Module.Include( "MkLibrary" ) > 0 ) Main( argc, argv ); return; /*****/ /***** // MK Library のソースをインクルードする場合 #include #include #include #include if( ::Module.Include( "GkLibrary" ) > 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 //----------------------------------------------------------------------------- // マクロ定義 #set MAIN_TITLE "音声の記録/再生" // ウィンドウバーのタイトル #set WinXd 400 // ウィンドウの幅 #set WinYd 320 // ウィンドウの高さ // 音声記録/再生関連コマンド #set CMD_START_RECORD 1 #set CMD_STOP_RECORD 2 #set CMD_CLEAR_RECORD 3 #set CMD_AUTO_RECORD 4 #set CMD_START_PLAY 5 #set CMD_STOP_PLAY 6 #set CMD_REV_PLAY 7 #set CMD_SAVE_FILE 8 #set CMD_LOAD_FILE 9 // 音声入力の記録状態 #set REC_OFF 0 // 音声入力なし(停止中) #set REC_READY 1 // 開始待ち(モニターのみ) #set REC_WAITING 2 // 自動録音開始を待機中 #set REC_BUSY 3 // 記録中 #set REC_STOPPING 4 // 記録終了へ移行中 // 音量レベルバーの各状態の色 #set COLOR_MONITORING #RGB(0x00,0x60,0xFF) // 青 -- 監視中(録音再生の実行待ち) #set COLOR_WAITING #RGB(0xFF,0xE0,0x00) // 黄 -- 待機中(自動録音の開始待ち) #set COLOR_RECORDING #RGB(0xFF,0x80,0x00) // 橙 -- 録音中 #set COLOR_PLAYING #RGB(0x00,0xC0,0x00) // 緑 -- 再生中 // 音声入力のサンプリング特性 #set WIS_RATE 11025 // サンプル数/秒( 8000, 11025, 22050, 44100 ) #set WIS_BITS 16 // ビット数/サンプル( 8 or 16 ) #set WIS_BUFLEN 2000 // サンプル数/バッファ #set WIS_BIAS 0 // サンプル値の無音レベルのバイアス値( 8-bit: 128, 16-bit: 0 ) #set WIS_IODF 'SHORT // サンプル値のデータ型式 #set WIS_BN 2 // サンプル値のバイト数 #set WI_MAX_SAMPLES 60000000 // サンプル値の最大個数( 11.025KHz で約 90 分 ) #set WI_MAX_LEVEL 30000 // サンプル値の最大レベル表示値 // 音声出力のサンプリング特性 #set WOS_RATE 11025 // サンプル数/秒( 8000, 11025, 22050, 44100 ) #set WOS_BITS 16 // ビット数/サンプル( 8 or 16 ) #set WOS_BUFLEN 4000 // サンプル数/バッファ #set WOS_BIAS 0 // サンプル値の無音レベルのバイアス値( 8-bit: 128, 16-bit: 0 ) #set WOS_IODF 'SHORT // サンプル値のデータ型式 #set WOS_BN 2 // サンプル値のバイト数 #set WO_MAX_LEVEL 30000 // サンプル値の最大レベル表示値 // 自動録音 #set AUTO_REC_LEVEL 1000 // 音声入力のレベルがこの値を超えた時に自動録音のトリガーがかかる #set AUTO_REC_BFR_BYTES (#WIS_RATE / 2 * #WIS_BN) // トリガー前の録音時間(現行 0.5秒)のバイト数 #set AUTO_REC_AFT_BYTES (#WIS_RATE *3/2* #WIS_BN) // トリガー後の録音時間(現行 1.5秒)のバイト数 #set AUTO_REC_GAP_BYTES (#WIS_RATE / 2 * #WIS_BN) // 自動録音間の無音時間(現行 0.5秒)のバイト数 // その他 #set RIOS ( 100 * #WOS_RATE / #WIS_RATE ) //============================================================================= function Main( argc, argv ) { #pc "開始"; ^DefValue = null; ^BtnFont = ::GK.Font( "MS ゴシック", 14, #SHIFTJIS_CHARSET, #FIXED_PITCH ); ^NumFont = ::GK.Font( "MS ゴシック", 16, #SHIFTJIS_CHARSET, #FIXED_PITCH ); ^RedPen = ::GK.Pen( #RGB(192,0,0) ); ^RedBrush = ::GK.Brush( #RGB(255,0,0) ); class ^MainWindow : ::GK.Window {} ^MainWindow.Construct( #MAIN_TITLE, , , #WinXd, #WinYd ); ^MainWindow.Open(); ^MainWindow.Setup(); ::GK.WindowMsgLoop(); FIN: delete ^MainWindow; delete ^RedPen, ^RedBrush; delete ^BtnFont, ^NumFont; #pc "終了"; return; OnError: print ::ErrorMessage( $err_code, $err_info ); goto FIN; } //============================================================================= function ^MainWindow.OnCreate() { #pc "OnCreate"; #pv .WaveIn = ::MK.WaveIn(); .WaveIn.OnDataInput = function( sp, sn ) { .owner.OnDataInput( sp, sn ); }; .TotalInBytes = 0; #pc "waveInGetNumDevs=": ::MK.waveInGetNumDevs(); #pc .WaveIn.GetCaps( 0 ); #pc .WaveIn.GetCaps( 1 ); #pv .WaveOut = ::MK.WaveOut(); .WaveOut.OnDataReq = function( dp ) { return .owner.OnDataReq( dp ); }; .WaveOut.OnDataEnd = function() { .owner.OnDataEnd(); }; .CurOutBytes = 0; .TotalOutBytes = 0; #pc "waveOutGetNumDevs=": ::MK.waveOutGetNumDevs(); #pc .WaveOut.GetCaps( 0 ); #pc .WaveOut.GetCaps( 1 ); .DefWidgetFont := ^BtnFont; // 音量レベル関連 .LevelLabel = .AddLabel( "音量", ::GK.Rect( 28, 25, 40, 20 ) ); .LevelBarR0 = ::GK.Rect( 70, 20, 280, 22 ); .LevelBarR1 = .LevelBarR0; .LevelBarR1.Shift( +4, +3, -8, -6 ); .MaxLevelXd = .LevelBarR0.Xd - 4; // 自動録音の境界を示すマーク関連 .MarkPoly = ::GK.Polygon( , 3, #FALSE ); .MarkR1 = .LevelBarR1; .MarkR1.Shift( 0, - .LevelBarR1.Yd - 3, 0, -2 ); .MarkR0 = .MarkR1; .MarkR0.Shift( -10, 0, +20, +1 ); .MarkXc = .MarkR1.Xo + .MaxLevelXd * #AUTO_REC_LEVEL / #WI_MAX_LEVEL; .SetMarkLevel(); .MarkDrag? = #FALSE; // 録音関連ボタン等 BtnFunc = function() { .owner.SetFocus(); .owner.ExecCmd( .Cmd ); }; .StartRecBtn = .AddButton( #BS_PUSHBUTTON, "録音開始", ::GK.Rect( 30, 60, 80, 28 ) ); .StartRecBtn.OnCommand = BtnFunc; .StartRecBtn.Cmd = #CMD_START_RECORD; .StopRecBtn = .AddButton( #BS_PUSHBUTTON, "録音停止", ::GK.Rect( 30, 98, 80, 28 ) ); .StopRecBtn.OnCommand = BtnFunc; .StopRecBtn.Cmd = #CMD_STOP_RECORD; .ClearRecBtn = .AddButton( #BS_PUSHBUTTON, "録音消去", ::GK.Rect( 30, 136, 80, 28 ) ); .ClearRecBtn.OnCommand = BtnFunc; .ClearRecBtn.Cmd = #CMD_CLEAR_RECORD; .RecTimeLabel = .AddLabel( "録音時間", ::GK.Rect( 30, 180, 64, 20 ) ); .RecTimeRect = ::GK.Rect( 30, 200, 80, 20 ); .AutoRecBtn = .AddButton( #BS_AUTOCHECKBOX, "自動録音", ::GK.Rect( 30, 230, 80, 28 ) ); .AutoRecBtn.OnCommand = BtnFunc; .AutoRecBtn.Cmd = #CMD_AUTO_RECORD; .AutoRec? = #FALSE; // 再生関連ボタン等 .StartPlayBtn = .AddButton( #BS_PUSHBUTTON, "再生開始", ::GK.Rect( 150, 60, 80, 28 ) ); .StartPlayBtn.OnCommand = BtnFunc; .StartPlayBtn.Cmd = #CMD_START_PLAY; .StopPlayBtn = .AddButton( #BS_PUSHBUTTON, "再生停止", ::GK.Rect( 150, 98, 80, 28 ) ); .StopPlayBtn.OnCommand = BtnFunc; .StopPlayBtn.Cmd = #CMD_STOP_PLAY; .PlayPosLabel = .AddLabel( "再生位置", ::GK.Rect( 150, 146, 64, 20 ) ); .PlayPosBar = .AddTrackBar( #TBS_NOTICKS | #TBS_BOTTOM, ::GK.Rect( 214, 144, 160, 24 ) ); .PlayPosBar.OnScroll = function( act, pos ) { .owner::DrawPlayPos(); }; .PlayPosBar.SetRange( 0, 100 ); .PlayPosBar.SetPos( 0 ); .PlayPosBar.PageSize( 10 ); .PlayPosBar.LineSize( 1 ); .PlayTimeLabel = .AddLabel( "再生時間", ::GK.Rect( 150, 180, 64, 20 ) ); .PlayTimeRect = ::GK.Rect( 150, 200, 80, 20 ); .SpeedLabel = .AddLabel( "再生速度", ::GK.Rect( 150, 230, 64, 20 ) ); .SpeedValRect = ::GK.Rect( 160, 250, 50, 20 ); .SpeedBar = .AddTrackBar( #TBS_NOTICKS | #TBS_BOTTOM, ::GK.Rect( 214, 234, 160, 24 ) ); .SpeedBar.OnScroll = function( act, pos ) { .owner::DrawSpeedVal(); }; .SpeedBar.SetRange( 50, 200 ); .SpeedBar.SetPos( 100 ); .SpeedBar.PageSize( 10 ); .SpeedBar.LineSize( 1 ); .RevPlayBtn = .AddButton( #BS_AUTOCHECKBOX, "逆再生", ::GK.Rect( 274, 180, 80, 28 ) ); .RevPlayBtn.OnCommand = BtnFunc; .RevPlayBtn.Cmd = #CMD_REV_PLAY; // ファイル関連ボタン等 .SaveFileBtn = .AddButton( #BS_PUSHBUTTON, "保存", ::GK.Rect( 270, 60, 80, 28 ) ); .SaveFileBtn.OnCommand = BtnFunc; .SaveFileBtn.Cmd = #CMD_SAVE_FILE; .LoadFileBtn = .AddButton( #BS_PUSHBUTTON, "読込", ::GK.Rect( 270, 100, 80, 28 ) ); .LoadFileBtn.OnCommand = BtnFunc; .LoadFileBtn.Cmd = #CMD_LOAD_FILE; .RecFileName = "memo.wav"; // 現音声ファイル名の初期値 .RecFilePath = ::Module.FilePath(); // 現音声ファイルパスの初期値 // 各種状態 .RecState = #REC_OFF; .Playing? = #FALSE; .Rev? = .PrevRev? = #FALSE; // 録音データバッファ関連 .InpBuf := ::Buffer( #WIS_RATE * #WIS_BN * 10 ); // 10 秒分の容量確保(メモリーが足りない時はエラーワープが発生) .InpBufFul? = #FALSE; .InpBuf.Find( // 自動録音開始時刻を示す文字列の検索用正規表現の事前設定 $"\*\0\*\0\*\0 \0" //「*** 」 $"\d\0\d\0\d\0\d\0\.\0\d\0\d\0\.\0\d\0\d\0 \0" //「YYYY.MM.DD 」 年月日 $"\d\0\d\0:\0\d\0\d\0:\0\d\0\d\0 \0" //「hh:mm:ss 」 時分秒 $"\*\0\*\0\*\0", 0 ); //「***」 .Year = .Month = .Day = .Hour = .Minute = .Second = 0; #set REC_DATE_CN 27 // 自動録音開始時刻を示す文字列(ユニコード)の字数 .Date'USHORT( #REC_DATE_CN ); .B = .E = 0; // 描画関連の初期設定 .gc.SetFont( ^NumFont ); .gc.SetBackMode( #TRANSPARENT ); .gc.SetPen( ^RedPen ); .gc.SetBrush( ^RedBrush ); } function ^MainWindow.Setup() { if( .WaveIn.Open( this, -1, 1, #WIS_RATE, #WIS_BITS, #WIS_BUFLEN ) > 0 ) .RecState = #REC_READY; .SetButtonState(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnClose() { #pc "OnClose"; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnSize() { #pc "OnSize", .gc.Xd, .gc.Yd; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnPaint() { #pc "OnPaint"; .GetHotRect( R'STRUCT ); .gc.DrawEdge( R, 0, #BF_MIDDLE ); .gc.DrawEdge( .LevelBarR0, #BDR_SUNKENOUTER, #BF_RECT | #BF_MIDDLE ); .DrawRecTime(); .DrawPlayTime(); .DrawSpeedVal(); } //----------------------------------------------------------------------------- function ^MainWindow.OnMouseButton( x, y, bs ) { //#pc "OnMouseButton: ", x, y, bs'x; if(( bs & #MK_LBUTTON ) && .MarkR1.InPnt?( x, y ) && .AutoRec? && .AutoRecBtn.Enabled?() ) { .MarkXc = .MarkXb = x; .DrawLevelBar(); .MarkDrag? = #TRUE; .SetMarkLevel(); } else .MarkDrag? = #FALSE; } function ^MainWindow.OnMouseMove( x, y, ks ) { if(( ks & #MK_LBUTTON ) == 0 ) .MarkDrag? = #FALSE; else if( .MarkDrag? ) { //#pc "OnMouseDrag: ", x, y, ks'x; X = x'cut( .MarkR1.Xo, .MarkR1.Xo + .MarkR1.Xd - 1 ); Y = y'cut( .MarkR1.Yo, .MarkR1.Yo + .MarkR1.Yd - 1 ); if( X != x || Y != y ) .SetMousePos( X, Y ); if( X != .MarkXc ) { .MarkXc = X; .DrawLevelBar(); .SetMarkLevel(); } } } function ^MainWindow.SetMarkLevel() { .MarkLv = #WI_MAX_LEVEL * ( .MarkXc - .LevelBarR1.Xo ) / .MaxLevelXd; } //----------------------------------------------------------------------------- function ^MainWindow.DrawLevelBar( v ) // 音量レベルバーを描画 { switch( .RecState ) { default: case #REC_OFF: case #REC_READY: color = .Playing? ? #COLOR_PLAYING : #COLOR_MONITORING; break; case #REC_WAITING: color = #COLOR_WAITING; break; case #REC_STOPPING: case #REC_BUSY: color = #COLOR_RECORDING; break; } #pc "DrawLevelBar: v=": v, "rec=": .RecState, "play=": .Playing?, "color=": color'x(6); if( v != null ) { max = .Playing? ? #WO_MAX_LEVEL : #WI_MAX_LEVEL; .LevelBarR1.Xd = .MaxLevelXd * v'cut( 0, max ) / max; } .gc.DrawEdge( .LevelBarR0, #EDGE_BUMP, #BF_RECT | #BF_MIDDLE | #BF_FLAT ); .gc.ClearRect( .LevelBarR1, color ); .gc.DrawEdge( .MarkR0, 0, #BF_MIDDLE ); if( .AutoRec? ) // 自動録音中? { // 自動録音レベルを示すマークを表示 ( X, Y ) = ( .MarkXc, .MarkR1.Yo + .MarkR1.Yd ); .MarkPoly.P = { X, Y, X - 6, Y - 10, X + 6, Y - 10 }; .gc.DrawPolygon( .MarkPoly ); .gc.MoveTo( X, Y + 4 ); .gc.LineTo( X, Y + 6 + .LevelBarR1.Yd ); } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.DrawRecTime() // 録音時間を描画 { switch( .RecState ) { case #REC_BUSY: case #REC_STOPPING: if( ! .AutoRec? ) { T = ( .WaveIn.GetPos() - .StartPos ) + .PrevRecBytes; break; } default: case #REC_OFF: case #REC_READY: case #REC_WAITING: T = .InpBuf.Size(); break; } T = ( T * 100.0 / #WIS_BN / #WIS_RATE )'int; #pc "DrawRecTime: T=": T, "rec=": .RecState, "ar=": .AutoRec?; DRAW_TIME: .gc.DrawEdge( .RecTimeRect, 0, #BF_MIDDLE ); .gc.DrawText( .RecTimeRect, "%02d:%02d.%02d"'fmt( T / (60*100), T / 100 % 60, T % 100 ) ); return; OnError: #pc "DrawRecTime: OnError --- rec=": .RecState, "ar=": .AutoRec?; T = 0; goto DRAW_TIME; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.DrawPlayTime() // 再生時間を描画 { T = ( 100.0 * .CurOutBytes / #WOS_BN / #WOS_RATE )'int; DRAW_TIME: .gc.DrawEdge( .PlayTimeRect, 0, #BF_MIDDLE ); .gc.DrawText( .PlayTimeRect, "%02d:%02d.%02d"'fmt( T / (60*100), T / 100 % 60, T % 100 ) ); return; OnError: #pc "DrawPlayTime: OnError --- T=": T; T = 0; goto DRAW_TIME; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.DrawSpeedVal() // 再生速度のパーセント値を描画 { .gc.DrawEdge( .SpeedValRect, 0, #BF_MIDDLE ); .gc.DrawText( .SpeedValRect, .SpeedBar.GetPos()'d's(3) + " %", #DT_LEFT | #DT_SINGLELINE ); } //----------------------------------------------------------------------------- function ^MainWindow.ExecCmd( cmd ) { switch( cmd ) { case #CMD_START_RECORD: .StartRecord(); break; case #CMD_STOP_RECORD: .StopRecord(); break; case #CMD_CLEAR_RECORD: .ClearRecord(); break; case #CMD_AUTO_RECORD: .AutoRecord(); break; case #CMD_START_PLAY: .StartPlay(); break; case #CMD_STOP_PLAY: .StopPlay(); break; case #CMD_REV_PLAY: .Rev? = .RevPlayBtn.GetCheck(); break; case #CMD_SAVE_FILE: .SaveFile(); break; case #CMD_LOAD_FILE: .LoadFile(); break; } } //----------------------------------------------------------------------------- function ^MainWindow.SetButtonState() { switch( .RecState + 10 * .Playing? ) { // ------ 録音 ------- ------ 再生 ------- ファイル // 開始 停止 消去 自録 開始 停止 位置 逆再 保存 読込 case #REC_OFF: S = { 1, 0, 1, 1, 1, 0, 1, 1, 1, 1 }; break; case #REC_READY: S = { 1, 0, 1, 1, 1, 0, 1, 1, 1, 1 }; break; case #REC_WAITING: S = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; break; case #REC_BUSY: S = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; break; case #REC_STOPPING: S = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; break; case 10+#REC_OFF: S = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; break; case 10+#REC_READY: S = { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 }; break; default: // 以下は、不正状態 case 10+#REC_WAITING: case 10+#REC_BUSY: case 10+#REC_STOPPING: S = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; break; } .StartRecBtn .Enable( S[0] && ! .InpBufFul? ); .StopRecBtn .Enable( S[1] ); .ClearRecBtn .Enable( S[2] && .InpBuf.Size() > 0 ); .AutoRecBtn .Enable( S[3] && ! .InpBufFul? ); .StartPlayBtn.Enable( S[4] && .InpBuf.Size() > 0 ); .StopPlayBtn .Enable( S[5] && .InpBuf.Size() > 0 ); .PlayPosBar .Enable( S[6] && .InpBuf.Size() > 0 ); .RevPlayBtn .Enable( S[7] ); .SaveFileBtn .Enable( S[8] && .InpBuf.Size() > 0 ); .LoadFileBtn .Enable( S[9] ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.ResetCond() { .InpBuf.Seek( 0 ); .CurOutBytes = .PrevOutBytes = .TotalOutBytes = 0; .PlayPosBar.SetPos( 0 ); .SpeedBar.SetPos( 100 ); .DrawPlayPos(); .DrawRecTime(); .DrawSpeedVal(); .SetButtonState(); } //----------------------------------------------------------------------------- function ^MainWindow.StartRecord() { #pc "StartRecord [1]: rec=": .RecState, "play=": .Playing?; switch( .RecState ) { case #REC_OFF: case #REC_READY: if( .Playing? ) return; break; case #REC_WAITING: case #REC_BUSY: case #REC_STOPPING: default: return; } if( .InpBufFul? ) { //.MessageBox( "音声記録バッファが満杯です!", // "音声記録", #MB_ICONEXCLAMATION | #MB_OK ); print "音声記録バッファが満杯です!"; return; } if( .AutoRec? ) { .RecState = #REC_WAITING; .SetButtonState(); .InpBuf.Seek( 0, "end" ); return; } .RecState = #REC_BUSY; .SetButtonState(); #pc "StartRecord [2]: Pos=": .StartPos = .WaveIn.GetPos(); if( .StartPos < .TotalInBytes ) // ← 本来無い筈だが偶に起こる { #pc "StartRecord [3]: Pos=": .StartPos = .TotalInBytes; } delete .StopPos; .PrevRecBytes = .InpBuf.Seek( 0, "end" ); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.StopRecord() { #pc "StopRecord [1]: rec=": .RecState, "play=": .Playing?; switch( .RecState ) { default: case #REC_OFF: case #REC_READY: return; case #REC_WAITING: break; case #REC_BUSY: break; case #REC_STOPPING: return; } .RecState = #REC_STOPPING; .SetButtonState(); if( .AutoRec? ) { .B = .TotalInBytes; return; } .StopPos = .WaveIn.GetPos(); #pc "StopRecord [2]: Pos=": .StopPos, "Len=": .StopPos - .StartPos, "rec=": .RecState; if( .StopPos < .TotalInBytes ) // ← 本来無い筈だが偶に起こる { .StopPos = .TotalInBytes; #pc "StopRecord [3]: Pos=": .StopPos, "Len=": .StopPos - .StartPos, "rec=": .RecState; } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.ClearRecord() { #pc "ClearRecord rec=": .RecState, "play=": .Playing?; switch( .RecState ) { case #REC_OFF: case #REC_READY: if( .Playing? ) return; break; default: case #REC_WAITING: case #REC_BUSY: case #REC_STOPPING: return; } .InpBuf.Size( 0 ); .InpBuf.BuffSize( #WIS_RATE * #WIS_BN * 10 ); .InpBufFul? = #FALSE; .TotalInBytes = 0; .CurOutBytes = 0; .TotalOutBytes = 0; .B = .E = 0; delete .StartPos, .StopPos; .DrawLevelBar( 0 ); .DrawRecTime(); .DrawPlayTime(); .PlayPosBar.SetPos( 0 ); .WaveIn.Reset(); .WaveIn.Start(); .RecState = #REC_READY; .SetButtonState(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.AutoRecord() { #pc "AutoRecord rec=": .RecState, "play=": .Playing?; switch( .RecState ) { case #REC_OFF: case #REC_READY: if( .Playing? || .InpBufFul? ) return; break; default: case #REC_WAITING: case #REC_BUSY: case #REC_STOPPING: return; } .AutoRec? = .AutoRecBtn.GetCheck(); .DrawLevelBar(); } //----------------------------------------------------------------------------- function ^MainWindow.OnDataInput( sp, sn ) { #pc "OnDataInput: sp=": sp, "sn=": sn, "Total=": .TotalInBytes, "rec=": .RecState, "play=": .Playing?, "Start=": .StartPos, "Stop=": .StopPos; if(( N = sn / #WIS_BN ) <= 0 ) return; // 音声記録バッファの空き容量が 3 秒未満の場合、15 秒分増やす if( ! .InpBufFul? && .InpBuf.BuffSize() - .InpBuf.Size() < #WIS_RATE * #WIS_BN * 3 ) { if( .InpBuf.BuffSize( .InpBuf.BuffSize() + #WIS_RATE * #WIS_BN * 15 ) <= 0 ) // メモリーが足りない(拡張不能)? { .InpBufFul? = #TRUE; .RecState = #REC_READY; .AutoRec? = #FALSE; .AutoRecBtn.SetCheck( #FALSE ); .B = .TotalInBytes; .SetButtonState(); print "音声記録バッファが満杯です!"; } } max = 0; if( .AutoRec? && .RecState >= #REC_WAITING ) goto AUTO_REC; // 自動録音監視中ではない場合 for( i = 0 ; i < N ; i++ ) { v = sp(i) - #WIS_BIAS; if( max < v ) max = v; } .DrawLevelBar( max ); if( .InpBufFul? ) return; .TotalInBytes += sn; #pc " ": .TotalInBytes / (#WIS_BUFLEN * #WIS_BN) : ": max=": max ; if( ! .StartPos'exist? || .StartPos < 0 ) return; if(( bn = .TotalInBytes - .StartPos ) <= sn ) // 音声記録開始点が現バッファ内にある? { .InpBuf.Keep( bn ); .InpBuf'addr'memcpy( sp( ( sn - bn ) / #WIS_BN )'addr, bn ); .InpBuf.Seek( 0, "end" ); if( .RecState != #REC_BUSY ) { .RecState = #REC_BUSY; .SetButtonState(); } } else if( ! .StopPos'exist? ) { .InpBuf.Write( sp ); } else if( .StopPos < 0 ) ; else if(( bn = .TotalInBytes - .StopPos ) < 0 ) { .InpBuf.Write( sp ); } else if( bn <= sn ) // 音声記録終了点が現バッファ内にある? { .InpBuf.Keep( sn - bn ); .InpBuf'addr'memcpy( sp'addr, sn - bn ); .InpBuf.Seek( 0, "end" ); .RecState = #REC_READY; .SetButtonState(); } else return; .DrawRecTime(); return; // 自動録音監視中の場合 AUTO_REC: Lv = .MarkLv; Io = .TotalInBytes; Ie = Io + sn; I = Io; for( i = 0 ; i < N ; i++ ) { v = sp(i) - #WIS_BIAS; if( max < v ) max = v; if( Lv <= v ) // 自動録音境界以上? { a = I - #AUTO_REC_BFR_BYTES; //if( a < .B ) // 現区間の終了前に今回の開始が発生? // ; // 現区間の終了位置を延長する //else // 前区間の終了後に、今回の開始が発生した場合 if( a < .B + #AUTO_REC_GAP_BYTES ) // 前区間の終了と今回の区間の開始との間隔が短か過ぎる? ; // 前区間の終りから短い区間を跨いで今回の区間を連続させる( ここで、.B <= a ) else // 前区間の終了と今回の区間の開始との間隔が充分に長い場合 { if( .E < .B ) // 前区間で未挿入の区間あり? { // 前区間の残りの区間を挿入する ← 既に挿入済の筈 } // 所定の無音区間を、今回の区間の最初に(前区間の最後に)挿入する // ( この無音区間の先頭には現在の年月日時分秒を示す UNICODE 文字列を配置 ) .InpBuf.Keep( #AUTO_REC_GAP_BYTES, #WIS_BIAS ); .InpBuf.Seek( 16, "cur" ); ::GetTime( .Year, .Month, .Day, , .Hour, .Minute, .Second ); .Date'puts( "*** %04d.%02d.%02d %02d:%02d:%02d ***" 'fmt( .Year, .Month, .Day, .Hour, .Minute, .Second ) ); .InpBuf.Write( .Date ); .InpBuf.Seek( 0, "end" ); .E = I - #AUTO_REC_GAP_BYTES; // 今回の区間を新たに開始する .A = a; } .B = I + #AUTO_REC_AFT_BYTES; // 終了位置を更新(いずれの場合も共通) } I += #WIS_BN; } if( .B <= Io || Ie <= .A ) // 自動録音区間は、現ブロック区間と重なっていない? { switch( .RecState ) { default: case #REC_WAITING: break; case #REC_BUSY: .RecState = #REC_WAITING; .SetButtonState(); break; case #REC_STOPPING: .RecState = #REC_READY; .SetButtonState(); break; } goto AR_FIN; } if( .A < Io ) // 自動録音区間の開始は、現ブロックよりも前? { if( .E < Io ) // 前ブロックで未挿入の区間あり? { ( pp, pn ) = .WaveIn.GetPrevBuf( sp ); if( pp != null && pn == #WIS_BUFLEN * #WIS_BN ) // 前ブロックあり? { // 前ブロックの未挿入の区間を挿入する Jo = Io - pn; //前ブロックの先頭位置 q = ( .E <= Jo) ? Jo : .E ; if(( d = Io - q ) > 0 ) { .InpBuf.Keep( d ); .InpBuf'addr'memcpy( pp( ( q - Jo )/ #WIS_BN )'addr, d ); .InpBuf.Seek( 0, "end" ); .E = Io; } } } a = Io; } else a = .A; if( a < .E ) a = .E; if( Ie < .B ) // 自動録音区間の終了は、現ブロックよりも後? b = Ie; else b = .B; // 現ブロック内の a から b までのデータを挿入 if(( d = b - a ) > 0 ) { .InpBuf.Keep( d ); .InpBuf'addr'memcpy( sp( ( a - Io )/ #WIS_BN )'addr, d ); .InpBuf.Seek( 0, "end" ); .E = b; } switch( .RecState ) { default: case #REC_WAITING: .RecState = #REC_BUSY; .SetButtonState(); case #REC_BUSY: break; case #REC_STOPPING: .RecState = #REC_READY; .SetButtonState(); break; } .DrawRecTime(); AR_FIN: .TotalInBytes = Ie; #pc " ": .TotalInBytes / (#WIS_BUFLEN * #WIS_BN) : ": max=": max; .DrawLevelBar( max ); return; } //----------------------------------------------------------------------------- function ^MainWindow.StartPlay() { #pc "StartPlay [1]: rec=": .RecState, "play=": .Playing?; switch( .RecState ) { case #REC_OFF: case #REC_READY: if( .Playing? ) return; break; default: case #REC_WAITING: case #REC_BUSY: case #REC_STOPPING: return; } if( .Rev? ) // 逆再生? { if( .InpBuf.Seek() == 0 ) // 逆再生データの残りなし? .InpBuf.Seek( 0, "end" ); // 逆再生の開始は最後からに bn = .InpBuf.Size() - .InpBuf.Seek(); } else // 順再生 { if( .InpBuf.Seek() >= .InpBuf.Size() ) // 順再生データの残りなし? .InpBuf.Seek(0); // 順再生の開始は最初からに bn = .InpBuf.Seek(); } .CurOutBytes = .PrevOutBytes = .TotalOutBytes = (( bn * ( .SpeedBar.GetPos() / 100.0 ))'int ) / #WIS_BN * #WIS_BN; .PlayPosBar.SetPos(( .InpBuf.Seek() * 100.0 / .InpBuf.Size())'int ); delete .PrevTi; .DrawPlayTime(); #pc "StartPlay [2]: total=": .TotalOutBytes, "seek=": .InpBuf.Seek(), "size=": .InpBuf.Size(); if( .WaveOut.Open( this, -1, 1, #WOS_RATE, #WOS_BITS, #WOS_BUFLEN ) >= 0 ) { .Playing? = #TRUE; .RecState = #REC_OFF; .WaveIn.Stop(); #pc "StartPlay [3]: volume=": .WaveOut.Volume(); } .PrevRev? = .Rev?; .SetButtonState(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.StopPlay() { #pc "StopPlay rec=": .RecState, "play=": .Playing?; if( ! .Playing? ) return; if( ! .StopReqTime'exist? ) .StopReqTime = 'ticks; else if( .StopReqTime - 'ticks >= 1000 ) .OnDataEnd(); // 強制的に再生終了状態へ移行 .WaveOut.Close(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.DrawPlayPos() { if( .Playing? || .RecState >= #REC_WAITING ) return; bi = ( .InpBuf.Size() * ( .PlayPosBar.GetPos() / 100.0 ))'int / #WIS_BN * #WIS_BN; .InpBuf.Seek( bi ); bn = .Rev? ? ( .InpBuf.Size() - bi ) : bi ; .CurOutBytes = .PrevOutBytes = .TotalOutBytes = ( bn * 100.0 / .SpeedBar.GetPos() )'int / #WIS_BN * #WIS_BN; .DrawPlayTime(); #pc "PlayPos: pos=": .PlayPosBar.GetPos(), "seek=": .InpBuf.Seek(), "size=": .InpBuf.Size(), "total=": .TotalOutBytes ; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnDataReq( dp ) { #pc "OnDataReq [1]: rec=": .RecState, "play=": .Playing?, "N=": dp'count; min = max = 0; if( ! .Playing? ) { i = 0; goto DRAW_INFO; } Td = .SpeedBar.GetPos(); Sk = .InpBuf.Seek(); Sz = .InpBuf.Size(); .PlayPosBar.SetPos( (( Sk * 100.0 ) / Sz )'int ); cur_vp = vp = .InpBuf'addr; top_vp = vp - Sk; lim_vp = top_vp + Sz; Ib = Sk / #WOS_BN; vp #WOS_IODF; if( .Rev? ) // 逆再生? { if( lim_vp <= vp ) // 終端? { vp = lim_vp - #WOS_BN; Ib = Sz / #WOS_BN - 1; } if( vp < top_vp ) return 0; Kb = ( Sz / #WOS_BN - 1 ) - Ib; Tb = #RIOS * Kb; Vb = vp'pv; if( Kb == 0 ) { Ti = Td; Ta = 0; Va = #WOS_BIAS; } else if( Kb > 0 ) { Ti = .PrevTi'exist? ? .PrevTi : Tb ; Ta = Tb - #RIOS; Va = ( vp + #WOS_BN ) #WOS_IODF 'pv; } else return 0; } else // 順再生の場合 { if( lim_vp <= vp ) return 0; Tb = #RIOS * Ib; Vb = vp'pv; if( Ib == 0 ) { Ti = Td; Ta = 0; Va = #WOS_BIAS; } else if( Ib > 0 ) { Ti = .PrevTi'exist? ? .PrevTi : Tb ; Ta = Tb - #RIOS; Va = ( vp - #WOS_BN ) #WOS_IODF 'pv; } else return 0; } #pc "OnDataReq [2]: Rev=": .Rev?, "Ib=": Ib, "Kb=": Kb, "Va=": Va, "Vb=": Vb, "Ta=": Ta, "Tb=": Tb, "Ti=": Ti; for( i = 0 ; i < #WOS_BUFLEN ; i++ ) { while( Tb < Ti ) { if( .Rev? ) // 逆再生? { if(( vp -= #WOS_BN ) < top_vp ) goto SAVE_COND; } else // 順再生 { if( lim_vp <= ( vp += #WOS_BN )) goto CHK_REC_DATE; } Va = Vb; Vb = vp'pv; Ta = Tb; Tb += #RIOS; } dp(i) = v = Va + ( Ti - Ta ) * ( Vb - Va ) / #RIOS; Ti += Td; v -= #WOS_BIAS; if( min > v ) min = v; if( max < v ) max = v; } if( ! .Rev? ) // 順再生? { CHK_REC_DATE: #set RDBN ( 2 * ( #REC_DATE_CN - 1 )) .InpBuf.Seek( - #RDBN, "cur" ); for( fr = .InpBuf.Find( , vp - cur_vp + #RDBN ) ; fr > 0 ; fr = .InpBuf.FindNext() ) { .InpBuf.Read( .Date ); print .Date'gets; } } SAVE_COND: #pc " .InpBuf: Size=": Sz, "Pos=": .InpBuf.Seek( vp - top_vp ); .PrevTi = Ti; DRAW_INFO: #pc "OnDataReq [3]: i=": i, "max=": max, "min=": min, "play=": .Playing?; .DrawLevelBar( max > -min ? max : -min ); .CurOutBytes = .WaveOut.GetPos() + .PrevOutBytes; FIN: .DrawPlayTime(); #pc " TotalOutBytes=": .TotalOutBytes += i * #WOS_BN; return i * #WOS_BN; OnError: #pc "OnDataReq: Error! --- "; goto FIN; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - function ^MainWindow.OnDataEnd() { #pc "OnDataEnd"; delete .StopReqTime; .Playing? = #FALSE; .RecState = #REC_READY; .CurOutBytes = .PrevOutBytes = .TotalOutBytes; .DrawPlayTime(); .PlayPosBar.SetPos( ( .InpBuf.Seek() * 100.0 / .InpBuf.Size())'int ); .DrawLevelBar( 0 ); .SetButtonState(); .WaveIn.Start(); } //----------------------------------------------------------------------------- function ^MainWindow.SaveFile() { if( .RecState >= #REC_WAITING && .Playing? ) return; OFN.Title = "録音データ保存ファイル"; OFN.FileSpec'UBYTE( #MAX_PATH + 4 ); OFN.FileSpec'puts( .RecFileName ); OFN.InitialDir = .RecFilePath; OFN.Filter'UBYTE( 256 ); i = 0; i += OFN.Filter'puts( "音声ファイル(*.wav)", i ) + 1; i += OFN.Filter'puts( "*.wav", i ) + 1; i += OFN.Filter'puts( "全て(*.*)", i ) + 1; i += OFN.Filter'puts( "*.*", i ) + 1; OFN.Flags = #OFN_OVERWRITEPROMPT | #OFN_HIDEREADONLY | #OFN_NOCHANGEDIR | #OFN_ENABLESIZING | #OFN_PATHMUSTEXIST; if( ! .GetSaveFileName( OFN ) ) return; #pc "File Name = ": OFN.FileSpec'gets; file = ::File.Open( OFN.FileSpec'gets, "out" ); if( file == null ) { .MessageBox( ::ErrorMessage( $err_code, $err_info ), "録音データ保存ファイル", #MB_ICONERROR | #MB_OK ); return; } wfh ::= // ヘッダー部 { .cRiffId 'C(4) = "RIFF"; // Riff chunk id ( = 'RIFF' ) .nRiffSize 'ULONG; // Riff chunk size ( = - 8 ) .cWaveId 'C(4) = "WAVE"; // = 'WAVE' .cFormatId 'C(4) = "fmt "; // format chunk id ( = 'fmt ' ) .nFormatSize 'ULONG = 16; // format chunck size ( = 16 ) .nFormatType 'USHORT = 1; // format type ( =1: PCM ) .nChannels 'USHORT = 1; // number of channels ( =1: mono, =2:stereo ) .nSamplesPerSec 'ULONG = #WIS_RATE; // sample rate [Hz] ( = 11025, 22050, or 44100 ) .nAvgBytesPerSec'ULONG = 1 * #WIS_RATE * #WIS_BN; // average data-transfer rate in bytes per second .nBlockAlign 'USHORT = 1 * #WIS_BN; // = nChannels * nBitsPerSample / 8 .nBitsPerSample 'USHORT = #WIS_BITS; // # of bits of sampling data .cDataId 'C(4) = "data"; // data chunk id ( = 'data' ) .nDataSize 'ULONG; // data chunk size // // sampled PCM data area } wfh.nDataSize = .InpBuf.Size(); #pc "nRiffSize = ": wfh.nRiffSize = wfh'size + wfh.nDataSize - 8 ; if( file.Write( wfh ) != wfh'size ) goto ERR_FILE_WRITE; .InpBuf.Seek(0); if( file.Write( .InpBuf ) != .InpBuf.Size() ) goto ERR_FILE_WRITE; FIN: .RecFileName = file.name; .RecFilePath = file.path; file.Close(); return; ERR_FILE_WRITE: .MessageBox( file.name + "\nファイル書き込みエラー!", "録音データ保存ファイル", #MB_ICONEXCLAMATION | #MB_OK ); goto FIN; } //----------------------------------------------------------------------------- function ^MainWindow.LoadFile() { if( .RecState >= #REC_WAITING && .Playing? ) return; OFN.Title = "音声ファイル"; OFN.FileSpec'UBYTE( #MAX_PATH + 4 ); OFN.FileSpec'puts( .RecFileName ); OFN.InitialDir = .RecFilePath; OFN.Filter'UBYTE( 256 ); //OFN.Filter'puts( "全て(*.*)\0*.*\0音声ファイル(*.wav)\0*.wav\0" ); i = 0; i += OFN.Filter'puts( "音声ファイル(*.wav)", i ) + 1; i += OFN.Filter'puts( "*.wav", i ) + 1; i += OFN.Filter'puts( "全て(*.*)", i ) + 1; i += OFN.Filter'puts( "*.*", i ) + 1; OFN.Flags = #OFN_PATHMUSTEXIST | #OFN_FILEMUSTEXIST; if( ! .GetOpenFileName( OFN ) ) return; .InpBuf.Size( 0 ); .InpBuf.BuffSize( 0 ); #pc "File Name = ": OFN.FileSpec'gets; file = ::File.Open( OFN.FileSpec'gets, "in" ); wfh ::= { .cRiffId 'C(4); // Riff chunk id ( = 'RIFF' ) .nRiffSize 'ULONG; // Riff chunk size ( = - 8 ) .cWaveId 'C(4); // = 'WAVE' } file.Read( wfh ); #pc wfh.cRiffId, wfh.nRiffSize, wfh.cWaveId; if( wfh.cRiffId != "RIFF" || wfh.cWaveId != "WAVE" ) goto ILL_DATA; chunk ::= { .Id 'C(4); .Size 'ULONG; } READ_CHUNK: file.Read( chunk ); #pc chunk.Id, chunk.Size; if( chunk.Id == "fmt " ) goto FMT_CHUNK; if( chunk.Id == "data" ) goto DATA_CHUNK; NEXT_CHUNK: file.Seek( chunk.Size, "cur" ); if( file.IsEnd() ) goto ILL_DATA; goto READ_CHUNK; FMT_CHUNK: fmt ::= { .FormatType 'USHORT; // format type ( =1: PCM ) .Channels 'USHORT; // number of channels ( =1: mono, =2:stereo ) .SamplesPerSec 'ULONG; // sample rate [Hz] ( = 11025, 22050, or 44100 ) .AvgBytesPerSec 'ULONG; // = .Channels * .SamplesPerSec * 16 / 8 ; .BlockAlign 'USHORT; // = .Channels * .BitsPerSample / 8 .BitsPerSample 'USHORT; // # of bits of sampling data ( = 16 or 8 ) } if(( chunk.Size -= fmt'size ) < 0 ) goto ILL_DATA; file.Read( fmt ); #pc fmt.FormatType, fmt.Channels, fmt.SamplesPerSec, fmt.BitsPerSample; #pc fmt.AvgBytesPerSec, fmt.BlockAlign, fmt.BitsPerSample; if( fmt.FormatType != 1 ) goto ILL_DATA; switch( fmt.SamplesPerSec ) { case 11025: K = 1; break; case 22050: K = 2; break; case 44100: K = 4; break; default: goto ILL_DATA; } goto NEXT_CHUNK; DATA_CHUNK: if( ! fmt'exist? ) goto ILL_DATA; Ns = chunk.Size / fmt.BlockAlign; #pc ##${ file.name }: Fs=${ fmt.SamplesPerSec / 1000.0 }[KHz], Ns=${ Ns }, TT=${ 1000.0 * Ns / fmt.SamplesPerSec }[ms]##; if( Ns > #WI_MAX_SAMPLES ) { .MessageBox( file.name + "\nファイルのサイズが大き過ぎます!", "録音データ読込ファイル", #MB_ICONEXCLAMATION | #MB_OK ); goto RENEW_COND; } if( Ns < 0 ) goto ILL_DATA; .InpBuf.BuffSize( Ns * 2 ); if( Ns == 0 ) goto RENEW_COND; Nd = Ns'cut( , 64*1024 ); sv'SHORT( Nd ); goto WAVDAT[ fmt.BitsPerSample ][ fmt.Channels ]; goto ILL_DATA; WAVDAT[8][1]: // 8-bit 1-channel の場合 bv'UBYTE( Nd * K ); for( ; Ns > 0 ; Ns -= n ) { N = file.Read( bv ); n = N / K; for( i = 0 ; i < n ; i++ ) sv(i) = ( bv( i*K ) - 128 ) * 200; if( n == 0 ) break; if( n < Nd ) sv'SHORT( n ); .InpBuf.Write( sv ); } goto RENEW_COND; WAVDAT[8][2]: // 8-bit 2-channel の場合 bv'UBYTE( Nd * K, 2 ); for( ; Ns > 0 ; Ns -= n ) { N = file.Read( bv ); n = N / 2 / K; for( i = 0 ; i < n ; i++ ) sv(i) = (( bv( i*K, 0 ) + bv( i*K, 1 )) / 2 - 128 ) * 200; if( n == 0 ) break; if( n < Nd ) sv'SHORT( n ); .InpBuf.Write( sv ); } goto RENEW_COND; WAVDAT[16][1]: // 16-bit 1-channel の場合 if( K == 1 ) { file.Read( .InpBuf ); goto RENEW_COND; } tv'SHORT( Nd * K ); for( ; Ns > 0 ; Ns -= n ) { N = file.Read( tv ); n = N / 2 / K; for( i = 0 ; i < n ; i++ ) sv(i) = tv( i*K ); if( n == 0 ) break; if( n < Nd ) sv'SHORT( n ); .InpBuf.Write( sv ); } goto RENEW_COND; WAVDAT[16][2]: // 16-bit 2-channel の場合 tv'SHORT( Nd * K, 2 ); for( ; Ns > 0 ; Ns -= n ) { N = file.Read( tv ); n = N / 4 / K; for( i = 0 ; i < n ; i++ ) sv(i) = ( tv( i*K, 0 ) + tv( i*K, 1 )) / 2; if( n == 0 ) break; if( n < Nd ) sv'SHORT( n ); .InpBuf.Write( sv ); } goto RENEW_COND; RENEW_COND: .ResetCond(); .RecFileName = file.name; .RecFilePath = file.path; file.Close(); return; OnFileError: .MessageBox( ::ErrorMessage( $err_code, $err_info ), "録音データ読込ファイル", #MB_ICONERROR | #MB_OK ); if( file == null ) return; goto RENEW_COND; OnFileEnd: ILL_DATA: .MessageBox( file.name + "\nファイルの内容が想定外です!", "録音データ読込ファイル", #MB_ICONEXCLAMATION | #MB_OK ); goto RENEW_COND; } //-----------------------------------------------------------------------------