ART6 (11)









mmio




Strumienie audio

Każdy odtwarzany plik dźwiękowy
musi być przekazywany do strumienia audio. Taki strumień ma swój adres. Plik
jest częściowo ładowany do buforu znajdującego się w pamięci w obrębie
danego strumienia. W miarę dalszego odtwarzania ładowane są następne części
pliku.

Za pomocą VB możemy odtwarzać pliki dźwiękowe za pomocą sndPlaySound w
Windows 32 API, lub innych zewnętrznych bibliotek. Funkcja API nie odgrywa
tylko dźwięk i nie zezwala na żadną inną operację typu zatrzymania czy
zmiany pozycji. Takie operacje są jednak przewidziane w funkcjach obsługi
strumienia audio. Co więcej funkcje te pozwalają na dużo więcej. Często
widziałem (na grupach dyskusyjnych itp.), że ktoś chciałby zrobić widmo dźwięku.
Te funkcje i procedury pozwalają na to, choć jest to proces bardzo
skomplikowany.

Do takich operacji potrzebne nam jest Windows API, tym razem użyjemy funkcji
zawartych w winmm.dll. Ich nazwy zaczynają się od mmio. Muszę na początku
powiedzieć, że deklaracje w API Viewer nie są kompatybilne. Chociaż są
prawidłowe, to jedynie z punktu widzenia składni. VB nie może przekazywać
danych strukturalnych jako wskaźnik o wartości 0, ale zawsze adresu jakiejś
zmiennej, co powoduje, że pewne operacje działałyby błędnie.
Musiałem więc zmienić pewne deklaracje, aby można było przekazać pewne
parametry jako wskaźniki o wartości 0.

Architektura plików multimedialnych
Każdy plik multimedialny Windows typu RIFF (Wave, AVI) składa się z
sekcji zwanych po angielsku Chunk (w skrócie ck). Nazwa sekcji ma zawsze długość
4 znaków ('RIFF','fmt ','data') i przekazywana jest jako zmienna typu long. W
prawdzie (w rozumieniu C++) jest to oddzielny typ zmiennej Four Character Char
FOURCC. I rzeczywiście, konwersja BSTR na FOURCC opiera się na operacjach
logiczno/bitowych w tym także przy pomocy funkcji shr, shl lub operatorów
<< i >>. Pomimo tego, że istnieje funkcja mmioStringToFOURCC
przekształcająca BSTR na FOURCC to ja i tak napisałem własną funkcję
MAKEFOURCC. Pomysł ten podsunął mi plik nagłówkowy VC++ w którym funkcja
ta była przedstawiona (w krótszym zapisie niż w VB, bo w VB trzeba tworzyć
funkcje równe z shr, shl).

Niestety, do widma dźwięku jeszcze nie doszedłem i niestety prawdopodobnie
nie dojdę, bo wywołanie niektórych funkcji powoduje krytyczny błąd. Poniżej
przedstawię jak pobrać informacje o pliku Wave, wbrew pozorom nie jest to
zadanie takie proste jak to się by mogło wydawać.
W module:


Public Declare Function mmioOpen Lib
"winmm.dll" Alias "mmioOpenA" (ByVal
szFileName As String, ByRef lpmmioinfo As
MMIOINFO, ByVal dwOpenFlags As Long) As Long
Public Declare Function mmioClose Lib "winmm.dll" (ByVal
hmmio As Long, ByVal uFlags As Long) As Long
Public Declare Function mmioDescend Lib "winmm.dll" (ByVal
hmmio As Long, ByRef lpck As Any, ByRef
lpckParent As Any, ByVal uFlags As Long) As Long
Public Declare Function mmioStringToFOURCC Lib "winmm.dll" Alias
"mmioStringToFOURCCA" (ByVal sz As String, ByVal
uFlags As Long) As Long
Public Declare Function mmioRead Lib "winmm.dll" (ByVal
hmmio As Long, ByRef pch As Any, ByVal
cch As Long) As Long
Public Declare Function mmioAscend Lib "winmm.dll" (ByVal
hmmio As Long, ByRef lpck As
MMCKINFO, ByVal uFlags As Long) As Long

Public Type MMCKINFO
    ckid As Long
    ckSize As Long
    fccType As Long
    dwDataOffset As Long
    dwFlags As Long
End Type
Public Type MMIOINFO
    dwFlags As Long
    fccIOProc As Long
    pIOProc As Long
    wErrorRet As Long
    htask As Long
    cchBuffer As Long
    pchBuffer As String
    pchNext As String
    pchEndRead As String
    pchEndWrite As String
    lBufOffset As Long
    lDiskOffset As Long
    adwInfo(4) As Long
    dwReserved1 As Long
    dwReserved2 As Long
    hmmio As Long
End Type
Public Type WAVEFORMAT
    wFormatTag As Integer
    nChannels As Integer
    nSamplesPerSec As Long
    nAvgBytesPerSec As Long
    nBlockAlign As Integer
End Type
Public Type PCMWAVEFORMAT
    wf As WAVEFORMAT
    wBitsPerSample As Integer
End Type
Public Const MMIO_ALLOCBUF = &H10000
Public Const MMIO_COMPAT = &H0
Public Const MMIO_CREATE = &H1000
Public Const MMIO_CREATELIST = &H40
Public Const MMIO_CREATERIFF = &H20
Public Const MMIO_DEFAULTBUFFER = 8192
Public Const MMIO_DELETE = &H200
Public Const MMIO_DENYNONE = &H40
Public Const MMIO_DENYREAD = &H30
Public Const MMIO_DENYWRITE = &H20
Public Const MMIO_DIRTY = &H10000000
Public Const MMIO_EMPTYBUF = &H10
Public Const MMIO_EXCLUSIVE = &H10
Public Const MMIO_EXIST = &H4000
Public Const MMIO_FHOPEN = &H10
Public Const MMIO_FINDCHUNK = &H10
Public Const MMIO_FINDLIST = &H40
Public Const MMIO_FINDPROC = &H40000
Public Const MMIO_FINDRIFF = &H20
Public Const MMIO_GETTEMP = &H20000
Public Const MMIO_INSTALLPROC = &H10000
Public Const MMIO_OPEN_VALID = &H3FFFF
Public Const MMIO_PARSE = &H100
Public Const MMIO_PUBLICPROC = &H10000000
Public Const MMIO_READ = &H0
Public Const MMIO_READWRITE = &H2
Public Const MMIO_REMOVEPROC = &H20000
Public Const MMIO_RWMODE = &H3
Public Const MMIO_SHAREMODE = &H70
Public Const MMIO_TOUPPER = &H10
Public Const MMIO_UNICODEPROC = &H1000000
Public Const MMIO_VALIDPROC = &H11070000
Public Const MMIO_WRITE = &H1

Public Function LShift(ByVal lThis As
Long, ByVal lBits As
Long) As Long
    If (lBits <= 0)
Then
        'bledny parametr ...
        LShift = lThis
    ElseIf (lBits > 31)
Then
        'bledny parametr ...
        LShift = 0
    Else
        If (lThis
And (2 ^ (31 - lBits))) = (2 ^ (31 - lBits)) Then
            LShift = (lThis
And (2 ^ (31 - lBits) - 1)) * (2 ^ lBits) Or &H80000000
        Else
            LShift = (lThis
And (2 ^ (31 - lBits) - 1)) * (2 ^ lBits)
        End If
    End If
End Function

Public Function RShift(ByVal lThis
As Long, ByVal lBits
As Long) As Long
    If (lBits <= 0)
Then
        'bledny parametr ...
        RShift = lThis
    ElseIf (lBits > 31)
Then
        'bledny parametr ...
        RShift = 0
    Else
        If (lThis
And &H80000000) = &H80000000 Then
            RShift = (lThis
And &H7FFFFFFF) \ (2 ^ lBits) Or (2 ^ (31 - lBits))
        Else
            RShift = lThis \ (2 ^ lBits)
        End If
    End If
End Function
Function MAKEFOURCC(ch0, ch1, ch2, ch3)
MAKEFOURCC = Asc(ch0) Or LShift(Asc(ch1), 8)
Or LShift(Asc(ch2), 16) Or LShift(Asc(ch3), 24)
End Function

W Form_Load:

Private Sub Form_Load()
Show
' przykładowa aplikacja operująca na plikach WAVE
Dim hmmioBuff As Long
'// bufor strumienia
Dim hmmioinfo As MMIOINFO
'// info
Dim RIFFchunk As MMCKINFO
'// RIFF chunk info (informacje o początkowym ck)
Dim Chunk As MMCKINFO
'// chunk info (tutaj będziemy ładować poszczególne ck)
Dim lRet As Long '// zmienna przyjmująca wartości zwrócone
Dim MyWaveFormat As PCMWAVEFORMAT '// kontekst zmiennej Chunk

Const File = "C:\WINDOWS\MEDIA\ding.wav"

' Teraz ładujemy plik do buforu hmmioBuff, z zezwoleniem na alokowanie buforu i na
' czytanie
hmmioBuff = mmioOpen(File, hmmioinfo, MMIO_ALLOCBUF Or MMIO_READ)

' pobieramy początkowe informacje o pliku do RIFFchunk
lRet = mmioDescend(hmmioBuff, RIFFchunk, 0, MMIO_READ)

' sprawdzamy, czy plik jest typy RIFF WAVE
' gdyby funkcje API deklarowane byłyby tak jak w API Viewer, to poniższy kod
' poinformowałby nas o błędzie.
' spowodowane jest to tym, że nie można przekazać w mmioDescend jako nadrzędny bufor
' wskaźnika = 0, ale należałoby podać jakąś zmienną, która ma wskaźnik i cały
' projekt legnie w gruzach!

If RIFFchunk.ckid <> MAKEFOURCC("R", "I", "F", "F")
_
Or RIFFchunk.fccType <> MAKEFOURCC("W", "A", "V", "E")
Then
    MsgBox "Plik nie jest plikiem typu WAVE"
    End
End If

' ckid - to chunk ID, identyfikuje się z "nagłówkiem" pliku,
' poprzez jego zmianę, możemy przenieść się do innego jego miejsca

' teraz poszukamy ckid o nazwie 'fmt ', i zapiszemy jego info do Chunk
RIFFchunk.ckid = MAKEFOURCC("f", "m", "t", " ")
lRet = mmioDescend(hmmioBuff, Chunk, RIFFchunk, MMIO_FINDCHUNK)

If lRet <> 0 Then
    MsgBox "Plik ma niekompatybilny format (prawdopodobnie została użyta inna kompresja niż PCM)"
    End
End If


If Chunk.ckSize < Len(MyWaveFormat) Then
    MsgBox "Plik wydaje się być uszkodzony"
    End
End If

' ładujemy informacje nagłówka 'fmt ' do MyWaveFormat
lRet = mmioRead(hmmioBuff, MyWaveFormat, Len(MyWaveFormat))
' sprawdzamy, czy nagłówek 'fmt ' jest zgodny z formatem PCM WAVE
If lRet <> Len(MyWaveFormat) Then
    MsgBox "Plik wydaje się być uszkodzony"
    End
End If

' prawdę mówiąc, nie wiem co robi poniższa użyta funkcja :)
lRet = mmioAscend(hmmioBuff, Chunk, 0)
If lRet <> 0 Then
    MsgBox "Krytyczny błąd"
    End
End If

' wyświetlamy informacje o pliku (niestety, nie wiem, czemu nie wyświetla próbkowania)
MsgBox "Informacje o pliku " & File & vbCrLf _
& "Format: PCM" & vbCrLf _
& "Ilość kanałów: " & MyWaveFormat.wf.nChannels & vbCrLf _
& "Próbkowanie: " & MyWaveFormat.wBitsPerSample & vbCrLf _
& "Bitów/sekundę: " & MyWaveFormat.wf.nAvgBytesPerSec & vbCrLf
_
& "Próbek/sekundę: " & MyWaveFormat.wf.nSamplesPerSec

' po zrobionej pracy zamykamy plik!
Call mmioClose(hmmioBuff, 0)
End Sub

Chociaż nie wyświetlana jest informacja
o próbkowaniu, to można to obliczyć dzieląc MyWaveFormat.wf.nSamplesPerSec
przez MyWaveFormat.wf.nAvgBytesPerSec.

Marcin Porębski ( Doogie )
marcin.porebski@interia.pl







Wyszukiwarka

Podobne podstrony:
11 (311)
ZADANIE (11)
Psychologia 27 11 2012
359 11 (2)
11
PJU zagadnienia III WLS 10 11
Wybrane przepisy IAAF 10 11
06 11 09 (28)
info Gios PDF Splitter And Merger 1 11

więcej podobnych podstron