N88モードにてMP3ファイルの再生方法をご教授いただけないでしょうか?
WAVファイルについては「Win32プログラミング講座 ~ Step29. WAVEファイルを再生する ~」
にて記述されておりますが、WAVだとサイズも大きいのでMP3を扱いたいのです。
次の項「Win32プログラミング講座 ~ Step30. MCIコマンドで音楽ファイルを再生する ~」
にてMP3ファイルの取り扱いについて触れていますが、N88モードにてどのように扱ったらよいのかが分かりません。
希望としては、以下のように使いたいと考えております
・非同期にてファイルを指定すると再生する
・現在再生しているMP3の再生を終了する
例えば以下の様に使用できればと思います。
Playmp3Stop("C:\Winnt\Media\The Microsoft Sound.wav") '再生
'何か別の処理をする
Playmp3Sound() '現在再生しているMP3の再生を終了
宜しくお願いいたします。
N88モードでMP3の再生方法とは?
自己レスです
「Win32プログラミング講座 ~ Step30. MCIコマンドで音楽ファイルを再生する ~」
のサンプルコードをN88BASICモードとして以下の様に置き換えてみました。
試行錯誤にて、「どうにか、動作出来るレベル」になった程度なので、本来の手法(?)とは違うのだろうなぁと感じています。
「ここはこうすべき」、「この方が効率的」、「ここが変」
などなど、アドバイスをいただけますと幸いです。
よろしくお願いいたします。
#N88BASIC
#include <api_mmsys.sbp>
Dim I$ AS STRING
Dim filename(500) AS BYTE
' ----------------------------------ここから----------------------------------
Dim mop As MCI_OPEN_PARMS
*loop
Openfile()
Playsound()
I$=Input$(1)
Playstop()
goto *loop
end
' ----------------------------------ここまで----------------------------------
'↑先頭部分には、MCIデバイスのオープン情報のための構造体を用意します。
Sub Openfile()
Dim ofn As OPENFILENAME
Dim buffer[MAX_PATH-1] As Byte
'ファイル名を取得
ofn.lStructSize=76
ofn.hwndOwner=_PromptSys_hWnd'hMainWnd
ofn.lpstrFilter=Ex"音楽 ファイル(*.wav;*.mid;*.mp3)\0*.wav;*.mid;*.mp3\0すべてのファイル(*.*)\0*\0\0"
ofn.nFilterIndex=1
ofn.nMaxFile=MAX_PATH
ofn.lpstrFile=buffer
GetOpenFileName(ofn)
lstrcpy(filename,buffer)
End Sub
'↑OPENボタンがクリックされたときに呼び出されるイベントです。GetOpenFileName関数でファイル名を取得し、テキストボックス(Static_Path)にファイルパスを表示します。
Sub Playsound()
Dim bErr As Long
Dim mpp As MCI_PLAY_PARMS
Dim buffer[MAX_PATH-1] As Byte
'再生中の場合は停止させる
If mop.wDeviceID Then Playstop()
'サウンド ファイルのパスを設定します。
mop.lpstrElementName=buffer
'メッセージ通知のためのウィンドウを指定
mop.lpstrElementName=filename
mop.dwCallback=_PromptSys_hWnd
'MCIデバイスをオープン
bErr=mciSendCommand(0,MCI_OPEN,MCI_OPEN_ELEMENT,mop)
If bErr Then
MessageBox(_PromptSys_hWnd,"デバイスのオープンに失敗","error",MB_OK)
Exit Sub
End If
'再生
mpp.dwCallback=_PromptSys_hWnd
bErr=mciSendCommand(mop.wDeviceID,MCI_PLAY,MCI_NOTIFY,mpp)
If bErr Then
MessageBox(_PromptSys_hWnd,"デバイスの再生に失敗","error",MB_OK)
Exit Sub
End If
End Sub
'↑PLAYボタンがクリックされたときに呼び出されるイベントです。MCI_OPENコマンドでMCIデバイスを開き、MCI_PLAYコマンドでデバイスを再生します。
Sub Playstop()
Dim bErr As Long
Dim dwCallback As DWord
'停止
bErr=mciSendCommand(mop.wDeviceID,MCI_STOP,MCI_WAIT,dwCallback)
If bErr Then
MessageBox(_PromptSys_hWnd,"デバイスの停止に失敗","error",MB_OK)
Exit Sub
End If
'デバイスを閉じる
mciSendCommand(mop.wDeviceID,MCI_CLOSE,MCI_WAIT,dwCallback)
mop.wDeviceID=0
End Sub
'↑STOPボタンがクリックされたときに呼び出されるイベントです。MCI_STOPコマンドでデバイスを停止させ、MCI_CLOSEコマンドでデバイスをクローズしています。
Sub MainWnd_MciNotify(flags As Long, DevID As Long)
Dim dwCallback As DWord
If flags=MCI_NOTIFY_SUCCESSFUL Then
'デバイスを閉じる
mciSendCommand(DevID,MCI_CLOSE,MCI_WAIT,dwCallback)
mop.wDeviceID=0
MessageBox(_PromptSys_hWnd,"再生終了","Sound test",MB_OK)
End If
End Sub
/*
↑MciNotifyは、再生が終了した場合などにMCIコマンドから送られるイベントです。RADツールの「イベント処理」ダイアログのMMSYSタブで表示されています。
再生が正常に終了した場合(flagsパラメータに MCI_NOTIFY_SUCCESSFUL フラグがセットされていたとき)はMCI_CLOSEコマンドを送り、MCIデバイスをクローズします。DevID変数とmop.wDeviceID変数には同じ値がセットされています。
のサンプルコードをN88BASICモードとして以下の様に置き換えてみました。
試行錯誤にて、「どうにか、動作出来るレベル」になった程度なので、本来の手法(?)とは違うのだろうなぁと感じています。
「ここはこうすべき」、「この方が効率的」、「ここが変」
などなど、アドバイスをいただけますと幸いです。
よろしくお願いいたします。
#N88BASIC
#include <api_mmsys.sbp>
Dim I$ AS STRING
Dim filename(500) AS BYTE
' ----------------------------------ここから----------------------------------
Dim mop As MCI_OPEN_PARMS
*loop
Openfile()
Playsound()
I$=Input$(1)
Playstop()
goto *loop
end
' ----------------------------------ここまで----------------------------------
'↑先頭部分には、MCIデバイスのオープン情報のための構造体を用意します。
Sub Openfile()
Dim ofn As OPENFILENAME
Dim buffer[MAX_PATH-1] As Byte
'ファイル名を取得
ofn.lStructSize=76
ofn.hwndOwner=_PromptSys_hWnd'hMainWnd
ofn.lpstrFilter=Ex"音楽 ファイル(*.wav;*.mid;*.mp3)\0*.wav;*.mid;*.mp3\0すべてのファイル(*.*)\0*\0\0"
ofn.nFilterIndex=1
ofn.nMaxFile=MAX_PATH
ofn.lpstrFile=buffer
GetOpenFileName(ofn)
lstrcpy(filename,buffer)
End Sub
'↑OPENボタンがクリックされたときに呼び出されるイベントです。GetOpenFileName関数でファイル名を取得し、テキストボックス(Static_Path)にファイルパスを表示します。
Sub Playsound()
Dim bErr As Long
Dim mpp As MCI_PLAY_PARMS
Dim buffer[MAX_PATH-1] As Byte
'再生中の場合は停止させる
If mop.wDeviceID Then Playstop()
'サウンド ファイルのパスを設定します。
mop.lpstrElementName=buffer
'メッセージ通知のためのウィンドウを指定
mop.lpstrElementName=filename
mop.dwCallback=_PromptSys_hWnd
'MCIデバイスをオープン
bErr=mciSendCommand(0,MCI_OPEN,MCI_OPEN_ELEMENT,mop)
If bErr Then
MessageBox(_PromptSys_hWnd,"デバイスのオープンに失敗","error",MB_OK)
Exit Sub
End If
'再生
mpp.dwCallback=_PromptSys_hWnd
bErr=mciSendCommand(mop.wDeviceID,MCI_PLAY,MCI_NOTIFY,mpp)
If bErr Then
MessageBox(_PromptSys_hWnd,"デバイスの再生に失敗","error",MB_OK)
Exit Sub
End If
End Sub
'↑PLAYボタンがクリックされたときに呼び出されるイベントです。MCI_OPENコマンドでMCIデバイスを開き、MCI_PLAYコマンドでデバイスを再生します。
Sub Playstop()
Dim bErr As Long
Dim dwCallback As DWord
'停止
bErr=mciSendCommand(mop.wDeviceID,MCI_STOP,MCI_WAIT,dwCallback)
If bErr Then
MessageBox(_PromptSys_hWnd,"デバイスの停止に失敗","error",MB_OK)
Exit Sub
End If
'デバイスを閉じる
mciSendCommand(mop.wDeviceID,MCI_CLOSE,MCI_WAIT,dwCallback)
mop.wDeviceID=0
End Sub
'↑STOPボタンがクリックされたときに呼び出されるイベントです。MCI_STOPコマンドでデバイスを停止させ、MCI_CLOSEコマンドでデバイスをクローズしています。
Sub MainWnd_MciNotify(flags As Long, DevID As Long)
Dim dwCallback As DWord
If flags=MCI_NOTIFY_SUCCESSFUL Then
'デバイスを閉じる
mciSendCommand(DevID,MCI_CLOSE,MCI_WAIT,dwCallback)
mop.wDeviceID=0
MessageBox(_PromptSys_hWnd,"再生終了","Sound test",MB_OK)
End If
End Sub
/*
↑MciNotifyは、再生が終了した場合などにMCIコマンドから送られるイベントです。RADツールの「イベント処理」ダイアログのMMSYSタブで表示されています。
再生が正常に終了した場合(flagsパラメータに MCI_NOTIFY_SUCCESSFUL フラグがセットされていたとき)はMCI_CLOSEコマンドを送り、MCIデバイスをクローズします。DevID変数とmop.wDeviceID変数には同じ値がセットされています。
Re: ループ再生について
> 上記にて再生は出来たと書きましたが、「MCI_NOTIFY」の取り扱いが理解出来ず、ループ再生 (BGMのように扱いたい) の方法が分かりません。
>
> 検討違いの質問かもしれませんが、ご教授の程宜しくお願いいたします。
トモカズさんがされようとしていることはAB3.X以降のN88BASICモードでの
実装は正攻法では無理だと思います。
勿論、正攻法でできないだけでN88BASIC互換モードの裏側を理解している者に
とってはそんなに難しいことではありませんので少し時間を頂ければ
ループ再生のサンプルをのせたいと思います。
ということでサンプルできましたのでのせときます。
肝心のMP3の再生ですが、"waveaudio"の部分を"mpegvideo"にすれば再生できるはずです。
内容的に難しくなってしまったので分からないことがあれば質問してください。
ソースはこちら [ここをクリックすると内容が表示されます]
コード: 全て選択
#N88BASIC
#include <api_mmsys.sbp>
'----------------オマジナイ-----------------
Dim proc As DWord
proc = SetWindowLong(_PromptSys_hWnd,GWL_WNDPROC,AddressOf(MyCallback))
'-------------------------------------------
'ウィンドウメッセージ
Const WM_MCIOPEN = WM_USER + 1
Const WM_MCICLOSE = WM_USER + 2
Const WM_MCIPLAY = WM_USER + 3
Const WM_MCISTOP = WM_USER + 4
'構造体の準備
Dim mop As MCI_OPEN_PARMS
Dim mpp As MCI_PLAY_PARMS
'MCIデバイスを開く
Dim device1 As Long
device1 = SendMessage(_PromptSys_hWnd,WM_MCIOPEN,"waveaudio","C:\WINDOWS\Media\Windows XP Startup.wav")
Dim device2 As Long
device2 = SendMessage(_PromptSys_hWnd,WM_MCIOPEN,"waveaudio","C:\WINDOWS\Media\notify.wav")
'device1をループ再生
SendMessage(_PromptSys_hWnd,WM_MCIPLAY,device1,MCI_NOTIFY)
'device2を1秒おきに10回再生
Dim cnt As Long
For cnt = 1 To 10
SendMessage(_PromptSys_hWnd,WM_MCIPLAY,device2,0)
Sleep(1000)
Next
'MCIデバイスを閉じる
SendMessage(_PromptSys_hWnd,WM_MCICLOSE,device1,0)
device1 = 0
SendMessage(_PromptSys_hWnd,WM_MCICLOSE,device2,0)
device2 = 0
End
'--------------------コールバック関数---------------------------------------------------
Function MyCallback(hWnd As HWND, Msg As DWord, wParam As WPARAM, lParam As LPARAM) As LRESULT
Select Case Msg
Case WM_DESTROY '必ず実装してください。
'プログラムの終了時に開いたままのデバイスを必ずここで全て閉じること!
'複数のBGMやサウンドを再生する場合はdeviceを配列にしてくといいかも。
If device1 <> 0 Then mciSendCommand(device1,MCI_CLOSE,MCI_WAIT,hWnd)
If device2 <> 0 Then mciSendCommand(device2,MCI_CLOSE,MCI_WAIT,hWnd)
Case WM_MCIOPEN 'デバイスを開く処理
mop.lpstrDeviceType = wParam
mop.lpstrElementName = lParam
mop.dwCallback = _PromptSys_hWnd
mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_TYPE or MCI_OPEN_ELEMENT,mop)
MyCallback = mop.wDeviceID
Exit Function
Case WM_MCICLOSE 'MCIデバイスを閉じる処理
mciSendCommand(wParam,MCI_CLOSE,MCI_WAIT,hWnd)
MyCallback = 0
Exit Function
Case WM_MCIPLAY 'MCIデバイスを再生する処理
mpp.dwCallback = hWnd
mpp.dwFrom = 0
mciSendCommand(wParam,MCI_PLAY,lParam or MCI_FROM,mpp)
MyCallback = 0
Exit Function
Case WM_MCISTOP 'MCIデバイスを停止する処理
mciSendCommand(wParam,MCI_STOP,MCI_WAIT,hWnd)
MyCallback = 0
Exit Function
Case MM_MCINOTIFY 'MCI_NOTIFYの処理
Select Case wParam
Case MCI_NOTIFY_SUCCESSFUL
mpp.dwCallback = hWnd
mpp.dwFrom = 0
'MCIデバイスの再生(notifyを指定しているのでループ再生になる)
mciSendCommand(wParam,MCI_PLAY,MCI_FROM or MCI_NOTIFY,mpp)
End Select
End Select
MyCallback = CallWindowProc(proc,hWnd,Msg,wParam,lParam)
End Function
肝心のMP3の再生ですが、"waveaudio"の部分を"mpegvideo"にすれば再生できるはずです。
内容的に難しくなってしまったので分からないことがあれば質問してください。
試行錯誤しています。
NoWestさん、早々にサンプログラムまで作成していただき、ありがとうございます。
N88モードでは、そもそも正攻法では出来なかったのですね。
さて、サンプルプログラムを色々と自分なりに(使い方を)理解しようと
試してみました。
サンプルをベースに簡易的に、手を入れて自分なりのサンプルを色々作成してみました。(Device1を途中で止める・・・だとか、ここでDvice2を再生するだとか・・・試してみました)
とりあえず、扱い方は理解したので(と思います)、自分のソフトに実装してみました。
「思った通りの動作をした」・・・と思いきや、暫く実行していると、強制終了してしまいました。
その後も、MICデバイスを開くタイミングだとかMICデバイスを閉じるタイミングなど変更して試しています。
それでも、未だに正常に動作しないのですが、(Device1がループ再生しているかと思いきや4~5回再生すると終了してしまったり・・・)
私の実装方法に問題があると思いますので、もうちょっと頑張ってみます。
(自作簡易サンプルだとうまく動作するのに、それと同様に組み込んでるんですけどね・・)
N88モードでは、そもそも正攻法では出来なかったのですね。
さて、サンプルプログラムを色々と自分なりに(使い方を)理解しようと
試してみました。
サンプルをベースに簡易的に、手を入れて自分なりのサンプルを色々作成してみました。(Device1を途中で止める・・・だとか、ここでDvice2を再生するだとか・・・試してみました)
とりあえず、扱い方は理解したので(と思います)、自分のソフトに実装してみました。
「思った通りの動作をした」・・・と思いきや、暫く実行していると、強制終了してしまいました。
その後も、MICデバイスを開くタイミングだとかMICデバイスを閉じるタイミングなど変更して試しています。
それでも、未だに正常に動作しないのですが、(Device1がループ再生しているかと思いきや4~5回再生すると終了してしまったり・・・)
私の実装方法に問題があると思いますので、もうちょっと頑張ってみます。
(自作簡易サンプルだとうまく動作するのに、それと同様に組み込んでるんですけどね・・)
Re: 試行錯誤しています。
AB2.Xの頃はMCISound命令というのが実装されていまして> N88モードでは、そもそも正攻法では出来なかったのですね。
> さて、サンプルプログラムを色々と自分なりに(使い方を)理解しようと
> 試してみました。
こいつを使ってMCIを操作していたんですが、AB3.X系からは実装されていません。
何が問題なのかもう少し具体的な情報がないと判断できません。> 「思った通りの動作をした」・・・と思いきや、暫く実行していると、強制終了してしまいました。
> その後も、MICデバイスを開くタイミングだとかMICデバイスを閉じるタイミングなど変更して試しています。
>
> それでも、未だに正常に動作しないのですが、(Device1がループ再生しているかと思いきや4~5回再生すると終了してしまったり・・・)
> 私の実装方法に問題があると思いますので、もうちょっと頑張ってみます。
> (自作簡易サンプルだとうまく動作するのに、それと同様に組み込んでるんですけどね・・)
OSは?
ABのバージョンは?
プログラムの流れは?
何が問題なのか整理をつけながらデバックするとよいでしょう。
私が良く使う手はプログラムの同一機能の部分を「'」コメントアウトして
プログラムの全体に問題点がないかを探しておき、
今度は細かな部分を徐々に確認していきます。
今回のサンプルで言えば、
1.まずはMCIに関するAPI関連部分を全てコメントアウトして
MCIの再生部分以外に問題がないことを確認します。
2.続いてMCIデバイスを開く部分と閉じる部分のコメントアウトを外し
確実に開かれたデバイスが閉じられているかを確認します。
3.最後にその他の機能を少しずつ回復させながらデバックを行います。
全体を通していえますが、コメントアウトを外しながらその部分を記述を
確認しましょう。引数を間違えるとか大ポカをやらかしていることも多々あります。
あとはGetLastError関数とmciGetErrorString関数を使うのも手ですね。
コイツを使うとエラーの詳細情報を得られます。
約1年半が経過
NoWestさん、ありがとうございました。
約1年半が経過してしまいましたが、ようやく解決いたしました。
全然、分からなくって半ば諦めかけていました。
> 「思った通りの動作をした」・・・と思いきや、暫く実行していると、強制終了してしまいました。
MP3のループ再生のプログラムを実装すると、確かにこのような症状になるのですが、
原因は (なのかどうかは、定かではありませんが・・・) 文字列変数(String)を多用していたからだったのかもしれません。
これを修正したら、強制終了が起こる事は一切無くなりました。
String多用であっても、MP3再生をしなければ強制終了が起こらないというのも謎です。
文字列変数を使うと不安定になるのでしょうか?
約1年半が経過してしまいましたが、ようやく解決いたしました。
全然、分からなくって半ば諦めかけていました。
> 「思った通りの動作をした」・・・と思いきや、暫く実行していると、強制終了してしまいました。
MP3のループ再生のプログラムを実装すると、確かにこのような症状になるのですが、
原因は (なのかどうかは、定かではありませんが・・・) 文字列変数(String)を多用していたからだったのかもしれません。
これを修正したら、強制終了が起こる事は一切無くなりました。
String多用であっても、MP3再生をしなければ強制終了が起こらないというのも謎です。
文字列変数を使うと不安定になるのでしょうか?