「mpglib.dllを使ったmp3再生」の編集履歴(バックアップ)一覧はこちら
追加された行は緑色になります。
削除された行は赤色になります。
前回は素のwaveファイルお再生しました。
これを使えばほかのものでも応用ができます。
今回はmp3をやります。
mp3はデコーダやプレイやがあって今さらですけど、
ABでもできるんですよ。
今回使うのは定番のmpglib.dllです。
mpglib.dllのある場所[[http://mpesch3.de1.cc/misc.html]]
こいつはABから使うことを想定していないしドキュメントも不親切です
だから元となったmpg123というライブラリも調べてみるといいかもしれませんね。
まずこのDLLは3つの関数しかないので、簡単ですけど、便利な昨日がないので
自分で作ります。
ID3V2も自力で読み飛ばさないといけません。
**abmpglib.abp
#asciiart(blockquote){
Const AUDIOBUFSIZE = 16384
Const SBLIMIT = 32
Const SSLIMIT = 18
Const SCALE_BLOCK = 12 /* Layer 2 */
Const MPG_MD_STEREO = 0
Const MPG_MD_JOINT_STEREO = 1
Const MPG_MD_DUAL_CHANNEL = 2
Const MPG_MD_MONO = 3
Const MAXFRAMESIZE = 1792
Const MP3_ERR = -1
Const MP3_OK = 0
Const MP3_NEED_MORE = 1
Type al_table
bits As Word
d As Word
End Type
Type frame
stereo As Long
jsbound As Long
single As Long
lsf As Long
mpeg25 As Long
header_change As Long
lay As Long
error_protection As Long
bitrate_index As Long
sampling_frequency As Long
padding As Long
extension As Long
mode As Long
mode_ext As Long
copyright As Long
original As Long
emphasis As Long
framesize As Long /* computed framesize */
II_sblimit As Long /* Layer 2 */
alloc As *al_table /* Layer 2 */
do_layer As VoidPtr /* Layer 2 */
End Type
Type mpglib_buf
pnt As *Byte
size As Long
pos As Long
pnext As *mpglib_buf
prev As *mpglib_buf
End Type
Type framebuf
buf As *mpglib_buf
pos As Long
pnext As *frame
prev As *frame
End Type
Type mpstr
head As *mpglib_buf
tail As *mpglib_buf
bsize As Long
framesize As Long
fsizeold As Long
fr As frame
bsspace[ELM(2*(MAXFRAMESIZE+512))] As Byte /* MAXFRAMESIZE */
hybrid_block[ELM(2*2*SBLIMIT*SSLIMIT)] As Single
hybrid_blc[ELM(2)] As Long
header As DWord
bsnum As Long
synth_buffs[ELM(2*2*&h110)] As Single
synth_bo As Long
End Type
Type ID3V2
ID3[2] As Byte
majorversion As Byte
revision As Byte
flag As Byte
size[3] As Byte
End Type
Declare Function InitMP3 CDECL Lib"mpglib.dll" Alias "_InitMP3" (mp As *mpstr) As Long
Declare Sub ExitMP3 CDECL Lib"mpglib.dll" Alias "_ExitMP3" (mp As *mpstr)
Declare Function decodeMP3 CDECL Lib"mpglib.dll" Alias "_decodeMP3" (mp As *mpstr, inmemory As *Byte, inmemsize As Long, outmemory As *Byte, outmemsize As Long, done As *Long) As Long
Dim br_tbl[1,14] = [
[ 0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320],
[ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160]
] As Long
Dim rate_tbl[9] = [44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000, 0] As Long
'ID3V2のサイズを詰める
Function unpack_sint28 (ptr As *Byte) As DWord
Dim value As DWord
If (ptr[0] And &h80) Then Exit Function
value = value Or (ptr[0] And &h7f)
value = (value << 7) Or (ptr[1] And &h7f)
value = (value << 7) Or (ptr[2] And &h7f)
value = (value << 7) Or (ptr[3] And &h7f)
unpack_sint28 = value
End Function
Type ABMP3DEC_CTX
mp As mpstr
buf[AUDIOBUFSIZE] As Byte
hF As HANDLE
rate As Long
ch As Long
pcmb As DWord
startpos As DWord
more As Long
status As Long
End Type
Dim mc As ABMP3DEC_CTX
'mp3ファイルを開いてデコード準備
Function OpenMp3(mp3file As *Byte) As Long
Dim size As Long
Dim out[8192] As Byte
Dim len As Long
Dim ret As Long
Dim id3 As ID3V2
If mc.status = TRUE Then Exit Function
ZeroMemory(VarPtr(mc), SizeOf(ABMP3DEC_CTX))
mc.hF = CreateFile(mp3file, GENERIC_READ, FILE_SHARE_READ, ByVal 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
If mc.hF = INVALID_HANDLE_VALUE Then Exit Function
'ID3V2
ReadFile(mc.hF, VarPtr(id3), SizeOf(ID3V2), VarPtr(len), ByVal 0)
If strstr(id3.ID3, "ID3") Then
If Not (id3.size[0] And &h80) Then mc.startpos = unpack_sint28(id3.size)+10
End If
SetFilePointer(mc.hF, mc.startpos, 0, FILE_BEGIN)
InitMP3(VarPtr(mc.mp))
'一度読み込んでフォーマットを取得
If ReadFile(mc.hF, mc.buf, 1024, VarPtr(len), ByVal 0) = FALSE Or len <= 0 Then
'読み込みエラー
End If
ret = decodeMP3(VarPtr(mc.mp), mc.buf, len, out, 8192, VarPtr(size))
If ret <> MP3_OK Then
'デコードエラー
End If
mc.rate = rate_tbl[mc.mp.fr.sampling_frequency]
mc.ch = mc.mp.fr.stereo
'リセット
SetFilePointer(mc.hF, mc.startpos, 0, FILE_BEGIN)
ExitMP3(VarPtr(mc.mp))
InitMP3(VarPtr(mc.mp))
mc.status = TRUE
OpenMp3 = TRUE
End Function
'サンプリングレート、チャンネル数を得る
Function GetFormat(rate As *Long, ch As *Long) As Long
If mc.status = FALSE Then Exit Function
SetDWord(rate, mc.rate)
SetDWord(ch, mc.ch)
End Function
'デコードし、そのサイズを返す。
Function ReadMp3(pcm As *Byte, buflen As Long) As DWord
Dim pos As DWord
Dim len As DWord
Dim siz As DWord
Dim ret As Long
If mc.status = FALSE Then Exit Function
pos = 0
siz = 0
Do
If mc.more = FALSE Then
ret = ReadFile(mc.hF, mc.buf, 16384, VarPtr(len), ByVal 0)
If ret = FALSE Or len = 0 Then *JUMP
ret = decodeMP3(VarPtr(mc.mp), mc.buf, len, VarPtr(pcm[pos]), 8192, VarPtr(siz))
If ret = MP3_OK Then mc.more = TRUE
End If
While (ret = MP3_OK)
pos = pos + siz
If pos+8192 > buflen Then *JUMP
ret = decodeMP3(VarPtr(mc.mp), NULL, 0, VarPtr(pcm[pos]), 8192, VarPtr(siz))
Wend
mc.more = FALSE'次に読み込みが必要
Loop While pos+8192 < buflen
*JUMP
ReadMp3 = pos
mc.pcmb = mc.pcmb + pos
End Function
'終わり
Function CloseMp3() As Long
If mc.status = FALSE Then Exit Function
CloseHandle(mc.hF)
ExitMP3(VarPtr(mc.mp))
mc.status = FALSE
End Function
}
つぎに再生部
#asciiart(blockquote){
#include <api_mmsys.sbp>
#include "abmpglib.abp"
Declare Function waveOutOpen Lib "winmm" (phwo As *HWAVEOUT, uDeviceID As DWord, pwfx As *WAVEFORMATEX, dwCallback As DWord, dwInstance As DWord, fdwOpen As DWord) As MMRESULT
Declare Function waveOutClose Lib "winmm" (hwo As HWAVEOUT) As MMRESULT
Declare Function waveOutPrepareHeader Lib "winmm" (hwo As HWAVEOUT, pwh As *WAVEHDR, cbwh As DWord) As MMRESULT
Declare Function waveOutUnprepareHeader Lib "winmm" (hwo As HWAVEOUT, pwh As *WAVEHDR, cbwh As DWord) As MMRESULT
Declare Function waveOutWrite Lib "winmm" (hwo As HWAVEOUT, pwh As *WAVEHDR, cbwh As DWord) As MMRESULT
Declare Function waveOutPause Lib "winmm" (hwo As HWAVEOUT) As MMRESULT
Declare Function waveOutRestart Lib "winmm" (hwo As HWAVEOUT) As MMRESULT
Declare Function waveOutReset Lib "winmm" (hwo As HWAVEOUT) As MMRESULT
Declare Function waveOutGetPosition Lib "winmm" (hwo As HWAVEOUT, pmmt As *MMTIME, cbmmt As DWord) As MMRESULT
Declare Function MulDiv Lib "kernel32" (nNumber As Long, nNumerator As Long, nDenominator As Long) As Long
Const WAVE_MAPPER = (-1)
Const CALLBACK_FUNCTION = &H00030000l
Const WOM_OPEN = &H3BB
Const WOM_CLOSE = &H3BC
Const WOM_DONE = &H3BD
Const WHDR_DONE = &H00000001
Const WHDR_PREPARED = &H00000002
Const WHDR_BEGINLOOP = &H00000004
Const WHDR_ENDLOOP = &H00000008
Const WHDR_INQUEUE = &H00000010
Const TIME_MS = &H0001
Const TIME_SAMPLES = &H0002
Const TIME_BYTES = &H0004
Const TIME_SMPTE = &H0008
Const TIME_MIDI = &H0010
Const TIME_TICKS = &H0020
TypeDef MMRESULT = DWord
Typedef HWAVEOUT = VoidPtr
Type WAVEHDR
lpData As *Byte
dwBufferLength As DWord
dwBytesRecorded As DWord
dwUser As DWord
dwFlags As DWord
dwLoops As DWord
lpNext As *WAVEHDR
reserved As *DWord
End Type
Type MMTIME
wType As DWord
u As DWord
u2 As DWord
End Type
Class Mp3Player
Private
buffer[2] As *Byte
hwo As HWAVEOUT
wfe As WAVEFORMATEX
switch As Long
Function GetMp3Header() As Long
Dim head[3] As Byte
Dim r As DWord
Dim c As DWord
GetFormat(VarPtr(r), VarPtr(c))
wfe.wFormatTag = 1
wfe.nChannels = c
wfe.nSamplesPerSec = r
wfe.wBitsPerSample = 16
wfe.cbSize = Sizeof(WAVEFORMATEX)
wfe.nBlockAlign = wfe.nChannels * wfe.wBitsPerSample/8
wfe.nAvgBytesPerSec = wfe.nSamplesPerSec * wfe.nBlockAlign
GetMp3Header = 1
End Function
Sub waveOutProc(hwo As HWAVEOUT, uMsg As DWord, dwInstance As *DWord, dwParam1 As DWord, dwParam2 As DWord)
Dim x As *Mp3Player
x = dwInstance
Select Case uMsg
Case WOM_CLOSE
Case WOM_DONE
x->wh[1].dwUser = x->wh[1].dwUser - 1
If x->wh[0].dwUser = 0 Then x->read(hwo)
Case WOM_OPEN
End Select
End Sub
Public
wh[2] As WAVEHDR
Sub read(hwo As HWAVEOUT)
Dim r As DWord
If hwo = NULL Or wh[0].dwUser = 1 Or wh[1].dwUser > 1 Then Exit Sub
waveOutUnprepareHeader(hwo, VarPtr(wh[switch]), SizeOf(WAVEHDR))
r = ReadMp3(buffer[switch], wfe.nAvgBytesPerSec)
wh[switch].lpData = buffer[switch]
wh[switch].dwBufferLength = r
If r = 0 Then wh[0].dwUser = 1'これ以上再生しません
waveOutPrepareHeader(hwo, VarPtr(wh[switch]), SizeOf(WAVEHDR))
waveOutWrite(hwo, VarPtr(wh[switch]), SizeOf(WAVEHDR))
wh[1].dwUser = wh[1].dwUser + 1
switch = switch + 1
If switch = 2 Then switch = 0
End Sub
Function play(infile As *Byte) As Long
If hwo <> NULL Then Exit Function
If OpenMp3(infile) = FALSE Then Exit Function
If GetMp3Header() = 0 Then
CloseMp3()
Exit Function
End If
buffer[0] = malloc(wfe.nAvgBytesPerSec)
buffer[1] = malloc(wfe.nAvgBytesPerSec)
waveOutOpen( VarPtr(hwo), WAVE_MAPPER, VarPtr(wfe), AddressOf(waveOutProc) ,VarPtr(this), CALLBACK_FUNCTION)
read(hwo)
Sleep(500)
read(hwo)
play = 1
End Function
Sub stop() As Long
If hwo = NULL Then Exit Sub
wh[0].dwUser = 1
waveOutReset(hwo)
While wh[1].dwUser > 0
Sleep(1)
Wend
waveOutUnprepareHeader(hwo, VarPtr(wh[0]), SizeOf(WAVEHDR))
waveOutUnprepareHeader(hwo, VarPtr(wh[0]), SizeOf(WAVEHDR))
free(buffer[0])
free(buffer[1])
waveOutClose(hwo)
CloseMp3()
hwo = NULL
End Sub
Function state(t As *DWord) As Long
state = wh[1].dwUser
If t = NULL Then Exit Function
Dim mmt As MMTIME
mmt.wType = TIME_SAMPLES
waveOutGetPosition(hwo, VarPtr(mmt), SizeOf(MMTIME))
SetDWord(t,MulDiv(mmt.u , 1000, wfe.nSamplesPerSec))
End Function
End Class
#define SELFTEST
#ifdef SELFTEST
#N88BASIC
Dim wp As Mp3Player
Dim time As DWord
Print "再生"
wp.play("test.mp2")
While wp.state(VarPtr(time))
Locate 4,1
Print time\1000;"sec"
Sleep(1000)
Wend
wp.stop()
Print "停止"
#endif
}
ファイル名は好きに変えるようにして、
美しい音楽が聴こえてくるはずです。