With Direct3D9 I did the type library and module support functions for DirectSound. The archive contains a type library dsvb.tlb and module DS_Functions.bas. In the future, I add a class module to support asynchronous notification until you can use clsTrickWait.cls. The module DS_Functions contains the following functions:
DSCreateSoundBufferFromFile - creates an object with interface IDirectSoundBuffer8 from a file. Supported only WAVE and MP3 files is. MP3 files can contain only the ID3v1 and ID3v2 tags, any other may not be recognized/will not work. Too long (by time) files are not supported. For streaming you need to write streaming decoding based on the function code DSCreateSoundBufferFromMemory;
DSCreateSoundBufferFromMemory - the same, but instead of the file is passed a pointer to the data file in memory and size.
Also in the archive contains an example of a player that implements some methods IDirectSoundBuffer8 interface (volume, pan, frequency, effects). TLB especially did not well tested, so something may not work. If something is not working please write here.
Im testing this code, and it works well, except for this mp3.
its a small mp3, 390 bytes, the mp3 works in media player / directshow, but not with this code.
it gives me error when I try to load it (automation)
all other mp3 I have works well.
anyone have a clue why?
baka, that's because you use effects. Just remove the DSBCAPS_CTRLFX flag from the DSBUFFERDESC structure. If you want to use effects you should make the sound bigger (just test the outSize variable and make it little big bigger).
That isn't applicable to this code because it doesn't use threading so we don't have such situation when a thread can change the memory when we read it. Thus, we can safely use this function.
so, I have done a lot of changes, this to load from an Offset and also to create multiple sounds from 1 source.
I create 3 functions instead of 1, the first one:
Code:
Private Sub DSCreateOpen(ByRef strFileName$)
Dim hFile As Long
Dim hMap As Long
hFile = CreateFile(StrPtr(strFileName), GENERIC_READ, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
bufSize = GetFileSize(hFile, ByVal 0)
hMap = CreateFileMapping(hFile, ByVal 0&, PAGE_READONLY, 0, 0, 0): CloseHandle hFile
buflpData = MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0): CloseHandle hMap
End Sub
the next function is used to create IDirectSoundBuffer , and can be called multiple times if needed:
Public variables: buflpData, bufSize, buffer(), lpData and bufDesc
Code:
Private Function DSCreateSoundBufferFromMemory(Optional ByVal Offset&, Optional ByVal szData&) As IDirectSoundBuffer
lpData = buflpData + Offset
If szData = 0 Then szData = bufSize
(just the important part, the rest is like the original)
and the last function is to remove the Public buffer and to Unmap
Code:
Private Sub DSCreateClose()
Erase buffer
UnmapViewOfFile lpData
End Sub
everything works, but Im not sure its the best way,
- first the "offset", right now I read the entire file, the Offset will jump ahead to a position in the memory address.
what I want is to read "only" the part of the file that are used, but I dont know how, everything I tried resulted in crash or not working!
- second is the "multiple" creating, is there any other way? or is that the right way to add the same sounds multiple times from 1 source?
(I create 5 hit-sounds so when I hit many monsters theres multiple hit-sounds not just one)
everything works, but Im not sure its the best way,
- first the "offset", right now I read the entire file, the Offset will jump ahead to a position in the memory address.
what I want is to read "only" the part of the file that are used, but I dont know how, everything I tried resulted in crash or not working!
You can map the only the needed part of the file (see dwFileOffsetLow in MapViewOfFile function). Note it should be multiple to memory allocation granularity. The better way is to encapsulate the logic to a class. You could also use no file mapping at all just read the content thru ReadFile. (FileMapping was added in order to make the same logic of opening a file either from a physical file or memory.
second is the "multiple" creating, is there any other way? or is that the right way to add the same sounds multiple times from 1 source?
(I create 5 hit-sounds so when I hit many monsters theres multiple hit-sounds not just one)
ah thanks a lot! I now use _lopen and _llseek and it works well!
here's the code if anyone else want to use it: (its a class, and u need to call Init first)
Code:
Dim ds As DirectSound8
Dim format As MPEGLAYER3WAVEFORMAT
Dim dstFormat As WAVEFORMATEX
Dim acmHdr As ACMSTREAMHEADER
Dim bufDesc As DSBUFFERDESC
Sub init(ByVal hWnd&)
Dim b As curBuffer
Set ds = New DirectSound8
ds.Initialize ByVal 0
ds.SetCooperativeLevel hWnd, DSSCL_NORMAL
b.b(0) = 450377142658.6656@: b.b(1) = 900743977448.248@: b.b(2) = 1351114248211.6672@
b.b(3) = 1801487954948.9248@: b.b(4) = 2702228496423.3344@: b.b(5) = 3602975909897.8496@
b.b(6) = 4503737067267.712@: b.b(7) = 18941235272.0895@: b.b(8) = 4735201446.045@
b.b(9) = 10307921515.2@: b.b(10) = 13743895348.4@: b.b(11) = 3435973838.4@
memcpy Constants.bitrate(0, 1), b.b(0), 96
With format
.wFormatTag = WAVE_FORMAT_MPEGLAYER3
.cbSize = MPEGLAYER3_WFX_EXTRA_BYTES
.wBitsPerSample = 0
.nBlockAlign = 1
.nFramesPerBlock = 1
.nCodecDelay = 0
.fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF
.wID = MPEGLAYER3_ID_MPEG
End With
With dstFormat
.cbSize = Len(dstFormat)
.wBitsPerSample = 16
.wFormatTag = WAVE_FORMAT_PCM
End With
acmHdr.cbStruct = Len(acmHdr)
bufDesc.dwFlags = DSBCAPS_CTRLVOLUME
bufDesc.dwSize = Len(bufDesc)
bufDesc.lpwfxFormat = VarPtr(dstFormat)
End Sub
Private Sub Class_Terminate()
Set ds = Nothing
End Sub
Function DSReadFile(ByRef FileName$, Optional ByVal Offset&, Optional ByVal FileSize&) As IDirectSoundBuffer
Dim hFile As Long
Dim b() As Byte
Dim buffer() As Byte
Dim L As Long
Dim D As Long
Dim ptr As Long
Dim hStream As Long
Dim index As Long
Dim AcmSize As Long
Dim AcmTotal As Long
hFile = lOpen(FileName, 0)
If FileSize = 0 Then FileSize = GetFileSize(hFile, ByVal 0)
If Offset > 0 Then llseek hFile, Offset, FILE_BEGIN
ReDim b(FileSize - 1)
lread hFile, b(0), FileSize
lclose hFile
D = FileSize - 128
If b(D) = &H54 And b(D + 1) = &H41 And b(D + 2) = &H47 Then FileSize = FileSize - 128
If b(0) = &H49 And b(1) = &H44 And b(2) = &H33 Then
If b(5) And &H10 Then FileSize = FileSize - 10
hFile = b(6) * &H200000
hFile = hFile Or (b(7) * &H4000&)
hFile = hFile Or (b(8) * &H80&)
hFile = hFile Or b(9)
hFile = hFile + 10
L = L + hFile
FileSize = FileSize - hFile
Else
D = FileSize - 10
If b(D) = &H33 And b(D + 1) = &H44 And b(D + 2) = &H49 Then
hFile = b(D + 6) * &H200000
hFile = hFile Or (b(D + 7) * &H4000&)
hFile = hFile Or (b(D + 8) * &H80&)
hFile = hFile Or b(D + 9)
FileSize = FileSize - hFile - 20
End If
End If
Do
If b(L) = &HFF And (b(L + 1) And &HE0) = &HE0 Then
D = (b(L + 1) And &H18) \ 8
With format
If D = 3 Then
.nAvgBytesPerSec = Constants.bitrate(0, (b(L + 2) And &HF0) \ &H10)
.nSamplesPerSec = Constants.smprate(0, (b(L + 2) And &HC) \ &H4)
Else
.nAvgBytesPerSec = Constants.bitrate(1, (b(L + 2) And &HF0) \ &H10)
If D = 2 Then .nSamplesPerSec = Constants.smprate(1, (b(L + 2) And &HC) \ &H4) Else .nSamplesPerSec = Constants.smprate(2, (b(L + 2) And &HC) \ &H4)
End If
If D = 3 Then
.nBlockSize = Int(144000 * .nAvgBytesPerSec / .nSamplesPerSec) + CInt((b(L + 2) And &H2) \ 2)
Else
.nBlockSize = Int(72000 * .nAvgBytesPerSec / .nSamplesPerSec) + CInt((b(L + 2) And &H2) \ 2)
End If
.nChannels = -(((b(L + 3) And &HC0) \ 64) <> 3) + 1
.nAvgBytesPerSec = .nAvgBytesPerSec * 128
End With
Exit Do
End If
L = L + 1
FileSize = FileSize - 1
Loop
With dstFormat
.nChannels = format.nChannels
.nSamplesPerSec = format.nSamplesPerSec
.nBlockAlign = (.wBitsPerSample \ 8) * .nChannels
.nAvgBytesPerSec = .nBlockAlign * .nSamplesPerSec
End With
acmStreamOpen hStream, 0, format, dstFormat, ByVal 0&, ByVal 0&, ByVal 0&, 0
Do While FileSize > 0
acmStreamSize hStream, FileSize, AcmSize, ACM_STREAMSIZEF_SOURCE
AcmTotal = AcmTotal + AcmSize
ReDim Preserve buffer(AcmTotal - 1)
With acmHdr
.lppbDst = VarPtr(buffer(index))
.lppbSrc = VarPtr(b(L))
.cbDstLength = AcmSize
.cbSrcLength = FileSize
End With
acmStreamPrepareHeader hStream, acmHdr, 0
acmStreamConvert hStream, acmHdr, ACM_STREAMCONVERTF_BLOCKALIGN
acmStreamUnprepareHeader hStream, acmHdr, 0
FileSize = FileSize - acmHdr.cbSrcLengthUsed
L = L + acmHdr.cbSrcLengthUsed
index = index + acmHdr.cbDstLengthUsed
Loop
acmStreamClose hStream, 0
bufDesc.dwBufferBytes = index
ds.CreateSoundBuffer bufDesc, DSReadFile, ByVal 0&
DSReadFile.Lock 0, 0, ptr, index, 0, 0, DSBLOCK_ENTIREBUFFER
memcpy ByVal ptr, buffer(0), index
DSReadFile.Unlock ptr, index, 0, 0
End Function
ok, another problem, that I can not recreate (since its working for me), but theres one user that can not use it.
the reason is: acmStreamSize
that is not returning the right size, so its impossible to continue.
it could be a "codec" problem, that is missing on the system.
I read in the internet that if that person doesn't have the right codec in the "list" it will not work,
so I wonder, how to check if the person have WAVE_FORMAT_PCM and/or WAVE_FORMAT_MPEGLAYER3
Microsoft IMA ADPCM CODEC
Microsoft CCITT G.711 A-Law and u-Law CODEC
Microsoft GSM 6.10 Audio CODEC
Microsoft ADPCM CODEC
Fraunhofer IIS MPEG Layer-3 Codec (decode only)
Microsoft PCM Converter
so, Microsoft PCM Converter should work with WAVE_FORMAT_PCM
and Fraunhofer IIS MPEG Layer-3 Codec should work with WAVE_FORMAT_MPEGLAYER3
but how do I know that?
Im asking that person to check what codecs he has in his computer.
edit;
that user tried that program and the result is:
Code:
Microsoft IMA ADPCM CODEC
Microsoft CCITT G.711 A-Law and u-Law CODEC
Microsoft GSM 6.10 Audio CODEC
Microsoft ADPCM CODEC
Microsoft PCM Converter
that means Fraunhofer IIS MPEG Layer-3 Codec is missing.
what can be done?
instruction to install the codec?
include the codec with the program?
some other solution?
a solution would be to encode the mp3 to pcm and save them in a file.
this is possible with small mp3, sound-effects.
I have around 50 sound-effects, 161kb in size total, in pcm format I get 2.3MB total, its acceptable size.
but background music would increase size too much, since most of the time mp3>pcm would increase around 15x.
using ADPCM CODEC could also be another way, convert .mp3 to .wav, would increase size 2.5x, still too much.
I tried using ADPCM wav directly into the directsound8, it start playing but not the right speed, I can't change much in dstFormat,
no changes helps, and most of the time it crashes on me. so I suppose only PCM works.