Results 1 to 3 of 3

Thread: [VB6] Play MP3s from either file or byte array w/ PlaySound (sndPlaySound)

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Jul 2010
    Location
    NYC
    Posts
    2,547

    Lightbulb [VB6] Play MP3s from either file or byte array w/ PlaySound (sndPlaySound)

    .wav is actually a container format, and if you put MP3 audio into a WAV container, it turns out PlaySound (or sndPlaySound) is able to play it.

    This allows playing MP3s from a file, or byte array, with just that simple API instead of more complex methods or additional dependencies.

    Credit to autoitscript.com's Melba23, this is based off their autoitscript implementation of this idea.

    ---

    First, the main function plays from memory with an MP3 loaded into a byte array, then placed into the WAV container, so you'd be able to play one from a resource or anywhere else where you have an mp3 without an actual file.

    Code:
    Private btPlay() As Byte 'The full structure passed to PlaySound. It must be module-level, otherwise it will go out of scope and crash.
                             'Alternative, you could use GlobalAlloc and play from that, but then you need a module-level anyway for GlobalFree.
    
    Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long)
    Private Declare Function PlaySoundW Lib "winmm.dll" (ByVal pszSound As Long, ByVal hModule As Long, ByVal dwFlags As SND_FLAGS) As Long
    Private Enum SND_FLAGS
        SND_SYNC = &H0 ' play synchronously (default)
        SND_ASYNC = &H1 ' play asynchronously
        SND_NODEFAULT = &H2 ' silence not default, if soundnot found
        SND_MEMORY = &H4 ' lpszSoundName points to a memoryFile
        SND_LOOP = &H8 ' loop the sound until nextsndPlaySound
        SND_NOSTOP = &H10 ' don't stop any currently playingsound
        SND_PURGE = &H40 ' purge non-static events forTask. Not supported.
        SND_APPLICATION = &H80 ' look for applicationspecific association
        SND_NOWAIT = &H2000 'Not supported
        SND_ALIAS = &H10000 ' name is a WIN.INI [sounds] entry
        SND_FILENAME = &H20000 ' name is a file name
        SND_SENTRY = &H80000      ' /* Generate a SoundSentry event with this sound */
        SND_RING = &H100000          ' /* Treat this as a "ring" from a communications app - don't duck me */
        SND_SYSTEM = &H200000        ' /* Treat this as a system sound */
        SND_ALIAS_ID = &H110000 ' name is a WIN.INI [sounds]Entry identifier
        SND_ALIAS_START = 0 ' must be > 4096 to keep strings insame section of resource file
        SND_RESERVED = &HFF000000 ' In particular these flags areReserved
        SND_RESOURCE = &H40004 ' name is a resource name or atom
        SND_TYPE_MASK = &H170007
        SND_VALID = &H1F ' valid flags / ;Internal /
        SND_VALIDFLAGS = &H17201F ' Set of valid flag bits.
    End Enum
    
    
    Private Sub PlayMp3Hackertastically(btMp3() As Byte, Optional dwFlags As SND_FLAGS = 0&)
    'Since WAV is a container format, we can force MP3 audio into a WAV container, which PlaySound
    'then is able to play.
    'The structure is built as follows:
    '1) Initial part of the WAV format header,
    '2) WAV header size: The file size plus both parts of the header,
    '3) The remainder of the WAV container header
    '4) The size of the MP3 data, and then finally
    '5) The bytes of the MP3 file.
    
    Dim btHdr1(3) As Byte 'First part of header
    btHdr1(0) = &H52: btHdr1(1) = &H49: btHdr1(2) = &H46: btHdr1(3) = &H46
    
    Dim btHdr2(57) As Byte 'Remainder of WAV container header
    btHdr2(0) = &H57: btHdr2(1) = &H41: btHdr2(2) = &H56: btHdr2(3) = &H45: btHdr2(4) = &H66: btHdr2(5) = &H6D: btHdr2(6) = &H74: btHdr2(7) = &H20: btHdr2(8) = &H1E: btHdr2(9) = &H0: btHdr2(10) = &H0: btHdr2(11) = &H0: btHdr2(12) = &H55: btHdr2(13) = &H0: btHdr2(14) = &H2: btHdr2(15) = &H0: btHdr2(16) = &H44: btHdr2(17) = &HAC: btHdr2(18) = &H0: btHdr2(19) = &H0: btHdr2(20) = &H58: btHdr2(21) = &H1B: btHdr2(22) = &H0: btHdr2(23) = &H0: btHdr2(24) = &H1: btHdr2(25) = &H0: btHdr2(26) = &H0: btHdr2(27) = &H0: btHdr2(28) = &HC: btHdr2(29) = &H0: btHdr2(30) = &H1: btHdr2(31) = &H0
    btHdr2(32) = &H2: btHdr2(33) = &H0: btHdr2(34) = &H0: btHdr2(35) = &H0: btHdr2(36) = &HB6: btHdr2(37) = &H0: btHdr2(38) = &H1: btHdr2(39) = &H0: btHdr2(40) = &H71: btHdr2(41) = &H5: btHdr2(42) = &H66: btHdr2(43) = &H61: btHdr2(44) = &H63: btHdr2(45) = &H74: btHdr2(46) = &H4: btHdr2(47) = &H0: btHdr2(48) = &H0: btHdr2(49) = &H0: btHdr2(50) = &H64: btHdr2(51) = &HE: btHdr2(52) = &H6: btHdr2(53) = &H0: btHdr2(54) = &H64: btHdr2(55) = &H61: btHdr2(56) = &H74: btHdr2(57) = &H61
    
    'Sizes: Normally a Long, a Long is 4 bytes, so we'll use 4-byte arrays to add each size
    Dim iFileSize As Long
    Dim iWaveSize As Long
    Dim btFS(3) As Byte
    Dim btWS(3) As Byte
    
    iFileSize = UBound(btMp3) + 1
    CopyMemory btFS(0), ByVal VarPtr(iFileSize), 4&
    
    iWaveSize = iFileSize + 63
    CopyMemory btWS(0), ByVal VarPtr(iWaveSize), 4&
    
    
    
    ReDim btPlay((4 + 58 + 4 + 4 + (UBound(btMp3) + 2))) 'The sizes of all the arrays we're about to combine.
                                                         'The extra byte will ensure a null-terminated structure.
    Dim lOff As Long 'Copying offset
    
    CopyMemory btPlay(0), btHdr1(0), 4&: lOff = 4
    
    CopyMemory btPlay(lOff), btWS(0), 4&: lOff = 8
    
    CopyMemory btPlay(lOff), btHdr2(0), 58&: lOff = 66
    
    CopyMemory btPlay(lOff), btFS(0), 4&: lOff = 70
    
    CopyMemory btPlay(lOff), btMp3(0), UBound(btMp3) + 1
    
    'Add mandatory flags
    dwFlags = dwFlags Or SND_MEMORY Or SND_NODEFAULT
    
    PlaySoundW VarPtr(btPlay(0)), 0&, dwFlags
    
    End Sub
    What the code is doing here, it first takes the main header for the WAV container, then the total header size which includes the variable file size, then the rest of the header, the size of the MP3 data, and finally the MP3 data itself; combining them all into a single byte array. I've broken the copying and array size calculations down so it's easier to see what's going on.

    Finally, copying into globally allocated memory was the trick here, you'd get a crash with an access violation if you tried to just play from the local byte array, which for normal .wav data you can do. Note that the issue here is the data going out of scope. If you moved btPlay to a module-level variable, that would work too.


    Now, if you did want to play from a .mp3 file, you can just use a standard method to read it into a byte array, then use the play from byte array code above:

    Code:
    Private Declare Function CreateFileW Lib "kernel32" (ByVal lpFileName As Long, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Any, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
    Private Const FILE_READ_DATA = &H1
    Private Const FILE_SHARE_READ = &H1&
    Private Const OPEN_EXISTING = 3&
    Private Declare Function GetFileSize Lib "kernel32" (ByVal hFile As Long, lpFileSizeHigh As Long) As Long
    Private Declare Function ReadFile Lib "kernel32" (ByVal hFile As Long, lpBuffer As Any, ByVal nNumberOfBytesToRead As Long, lpNumberOfBytesRead As Long, ByVal lpOverlapped As Any) As Long
    Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
    
    
    Private Sub PlayMP3HackertasticallyFromFile(sPath As String)
    Dim hFile   As Long
    Dim bWave() As Byte
    Dim lSize     As Long
    Dim mf2 As Long
    
    hFile = CreateFileW(StrPtr(sPath), FILE_READ_DATA, FILE_SHARE_READ, ByVal 0&, OPEN_EXISTING, 0, 0)
    
    lSize = GetFileSize(hFile, mf2)
    ReDim bWave(lSize - 1)
    ReadFile hFile, bWave(0), lSize, mf2, 0&
    CloseHandle hFile
    PlayMp3Hackertastically bWave
    
    End Sub

    That's all there is to it, but I'll attach a project anyway:


    sndPlaySound is the older and more common API; it's a subset of PlaySound though. If you wanted to, you could switch them out if you wanted to.

    Update: Forgot to mention, if you're playing Async, you can stop playback at any time by calling PlaySoundW 0&, 0&, 0&
    The attached project has been changed to add a 'Stop' button, options for Loop and Async, and automatically stopping when exiting. Also cleaned up and commented code.

    Update: Using GlobalAlloc would probably leak memory since GlobalFree was never called; the issue was the data going out of scope, so I just switched to putting btPlay as a module-level var.
    Attached Files Attached Files

  2. #2
    Addicted Member
    Join Date
    Jun 2016
    Location
    Espaņa
    Posts
    148

    Re: [VB6] Play MP3s from either file or byte array with PlaySound (sndPlaySound)

    good job

  3. #3
    New Member
    Join Date
    Aug 2006
    Posts
    9

    Re: [VB6] Play MP3s from either file or byte array w/ PlaySound (sndPlaySound)

    Just wanted to say thanks for posting this, I've used it in a game I am working on for the background music.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width