News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Making a simple WAV player

Started by NoCforMe, January 09, 2012, 12:45:57 AM

Previous topic - Next topic

NoCforMe

I'd like a reallllly simple WAV player. Just something I can fire up, select the file to play and have it play in the background while I do other stuff.

I tried PlaySound():


INVOKE PlaySoundA, ADDR SoundFilename, NULL, SND_FILENAME ;OR SND_ASYNC


It works--it plays the sound--but it has problems, and I don't think it's the right thing to use here.

No problem for short sound files, but when I tried to play a song of a few minutes length (~23 MB), it played to a certain point, then stopped. Plus I couldn't get the "async" flag to work (no sound at all if I use this flag). The one page I found in MSDN about this function says:

QuoteThe sound specified by pszSound must fit into available physical memory and be playable by an installed waveform-audio device driver.

So it apparently does little or no buffering of the WAV data. (I used PlaySoundA() because this was all I could find in the MASM32 include and lib files; no PlaySound().)

The documentation on MSDN is very confusing. All the descriptions I found seem to apply to weird things like Windows CE and Windows Mobile, or else they give C++ or C# examples that need a certain SDK or language-specific features. (Then there's the whole "core audio" API, which requires Vista or later.)

I think I want to use some of the wavexxxx() functions instead. All I want is a really simple player; all it needs is to be able to select a WAV file, and maybe a "stop" button to kill it. Don't really even need positioning controls, pause, etc., though these would probably be easy enough to add.

Can someone point me to a simple tutorial on this, or maybe a working example somewhere, that shows how to use these functions?


Bill Cravener

Dave has you on the correct path and with my simple mp3 player example you should have no trouble making your own wave file player. Simply change the DlgDirList functions lpPathSpec in my example to "*.wav" and it will play wave files.

http://www.quickersoft.com/examples/PlayMp3.zip
My MASM32 Examples.

"Prejudice does not arise from low intelligence it arises from conservative ideals to which people of low intelligence are drawn." ~ Isaidthat

jj2007

Try if it stops early with your 23 MB file.
include \masm32\include\masm32rt.inc
include \masm32\include\winmm.inc
includelib \masm32\lib\winmm.lib

.data
playthis db "play "
buffer db 128 dup(?)

.code
start:
mov esi, offset buffer
; drag a valid wav file over the executable,
; e.g. C:\WINDOWS\Media\Windows XP Startup.wav
invoke GetCL, 1, esi
.if rv(GetShortPathName, esi, esi, 127)
sub esi, 5
invoke mciSendString, esi, 0, 0, 0
.endif
inkey "bye"
exit
end start

NoCforMe

Hmm; I tried that, jj, but it doesn't work at all. Here's the bulk of my program:


.data

SoundCommand DB "play \SAVE\Sounds\Music\Balkan-E. European\Bulgarian\Gumzoviana.wav", 0
ErrorMsgBuffer DB 200 DUP (?)


;============================================
; CODE LIVES HERE
;============================================
.code


start: INVOKE mciSendString, OFFSET SoundCommand, NULL, 0, 0
OR EAX, EAX
JZ @F
INVOKE mciGetErrorString, EAX, OFFSET ErrorMsgBuffer, SIZEOF ErrorMsgBuffer
INVOKE MessageBox, 0, OFFSET ErrorMsgBuffer, NULL, MB_OK

@@: INVOKE ExitProcess, EAX


All I get is an error message that says "The specified device is not open or is not recognized by MCI".

Bill, I'm going to look at your example next.

NoCforMe

Bill, I just edited and built your little app. It works, but I've got some questions.

I just changed ".mp3" to ".wav" as you suggested, and it does play WAV files (all the way through, too). But even though I can select and play MP3 files as well, they don't play ... correctly. Sound all chopped up.

I notice some minor buffering glitches too, which must be due to the way the file is being handled by the mcixxx() functions.

I notice you open a device called "MPEGVideo"; shouldn't this be changed if I want to play WAVs? or not? (seems to work OK).

By the way, I hate your user interface (and I mean this in the nicest possible way). Very hard to drill back up through a disk directory structure.

Thanks for the app, and thanks in advance for any further answers.

Update: Just remade the app changing the ".wav" back to ".mp3". Did you know it makes no difference? You can play WAV or MP3 files in either case.

I tried playing some MP3s. The files I have don't play well at all (all chopped up, as noted above), but your sample MP3 plays just fine. For some reason it doesn't seem to like my MP3s. (Mine were made with a Sound Forge plug-in and play on every other player I've used.) No big deal, just thought you'd like to know.

jj2007

Quote from: NoCforMe on January 10, 2012, 06:13:36 AM
Hmm; I tried that, jj, but it doesn't work at all.

Your filename has spaces. Won't work with spaces.

donkey

Hi NoCforMe,

Many MP3 files are VBR encoded, these wll have problems with mciXXX functions as far as I remember (its been quite a while since I've used them). You can check for a XING signature in the MP3 file to see if it is VBR, I posted a program to extract the information from MP3 files that demonstrates how to do this. It will also allow you to display other information about the MP3 at the same time.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

NoCforMe

Quote from: jj2007 on January 10, 2012, 06:37:34 AM
Quote from: NoCforMe on January 10, 2012, 06:13:36 AM
Hmm; I tried that, jj, but it doesn't work at all.

Your filename has spaces. Won't work with spaces.

And yet PlaySound() can play that same file, coded the same way.

I thought the problem might be that you never opened the sound device, like Bill does in his little app (using mciSendCommand(MCI_OPEN) ).

jj2007

NoC,

There could be a problem with your audio setup or Windows version. I forgot that my example should work with spaces, too, since it uses GetShortPathName. On my puter, WinXP SP2, it works just fine, you can drag *.wav or *.mp3 over the exe and it starts playing until you hit the key.

See also Edgar's comment concerning mp3 files (but your problem is wav...).

donkey

#10
You know if you're going to be serious about playing MP3's the mci is very limited. You should think about DirectShow. Unfortunately (for some) it is only available as a COM interface but it is extremely easy to use. I have cut the appropriate parts out of a project I was working on (but have shelved indefinitely) and pasted them here. It is in GoAsm syntax and requires my headers but I am sure someone here can translate them for you. It plays MP3s very well and if you want to get deeper you can control pretty much everything about your player.

Note that this should play WAVs as well but I have never tried it.

Sorry about the length of this code, I didn't want to build a project.

STRINGS UNICODE
#DEFINE UNICODE

#define WIN32_LEAN_AND_MEAN 

#DEFINE WINVER NTDDI_WINXP
#DEFINE FILTERAPI

#DEFINE LINKFILES
#DEFINE LINKVCRT

#DEFINE CCUSEORDINALS

#include "WINDOWS.H"
#include "macros.h"
#include "mmsystem.h"
#include "strmif.h"
#include "control.h"
#include "uuids.h"

#IF X64
S=8
#ELSE
S=4
#ENDIF

DATA SECTION
pigb PTR NULL
pimc PTR NULL
pimex PTR NULL
piba PTR NULL
pims PTR NULL
ready BOOL FALSE
duration LONGLONG 0

IID_IGraphBuilder GUID GUID_IID_IGraphBuilder
IID_IMediaControl GUID GUID_IID_IMediaControl
IID_IMediaEventEx GUID GUID_IID_IMediaEventEx
IID_IBasicAudio GUID GUID_IID_IBasicAudio
IID_IMediaSeeking GUID GUID_IID_IMediaSeeking
CLSID_FilterGraph GUID GUID_CLSID_FilterGraph
TIME_FORMAT_MEDIA_TIME GUID GUID_TIME_FORMAT_MEDIA_TIME

MP3File DUS "Some.mp3",0

evcode DD 0

CODE SECTION
// Be sure to initialize COM
invoke CoInitialize,NULL

// To play a file just do this
invoke Load,offset MP3File
// Volume is 0 (full) to -10000 (mute)
invoke SetVolume,0
invoke Play
// Wait for it to complete
invoke WaitForCompletion,10000000,offset evcode
// Cleanup the interfaces
call Cleanup

// When your program is done shut down COM
invoke CoUninitialize

// The functions (I have a few more but they require a lot of setup code)

Cleanup:
cmp S[pimc],0
je >
CoInvoke(pimc,IMediaControl.Stop)
:
cmp S[pigb],0
je >
CoInvoke(pigb,IGraphBuilder.Release)
mov S[pigb],NULL
:
cmp S[pimc],0
je >
CoInvoke(pimc,IMediaControl.Release)
mov S[pimc],NULL
:
cmp S[pimex],0
je >
CoInvoke(pimex,IMediaEventEx.Release)
mov S[pimex],NULL
:
cmp S[piba],0
je >
CoInvoke(piba,IBasicAudio.Release)
mov S[piba],NULL
:
cmp S[pims],0
je >
CoInvoke(pims,IMediaSeeking.Release)
mov S[pims],NULL
:
mov D[ready],FALSE
RET


Load FRAME szFile
LOCAL LocResult:D

call Cleanup

mov D[ready],FALSE
invoke CoCreateInstance,offset CLSID_FilterGraph,NULL,CLSCTX_INPROC_SERVER,offset IID_IGraphBuilder,offset pigb
test eax,eax

jnz >>.EXIT
xor eax,eax
mov [LocResult],eax
CoInvoke(pigb,IGraphBuilder.IUnknown.QueryInterface,offset IID_IMediaControl,offset pimc)
or [LocResult],eax
CoInvoke(pigb,IGraphBuilder.IUnknown.QueryInterface,offset IID_IMediaEventEx,offset pimex)
or [LocResult],eax
CoInvoke(pigb,IGraphBuilder.IUnknown.QueryInterface,offset IID_IBasicAudio,offset piba)
or [LocResult],eax
CoInvoke(pigb,IGraphBuilder.IUnknown.QueryInterface,offset IID_IMediaSeeking,offset pims)
or [LocResult],eax
cmp D[LocResult],0
jne >.EXIT

CoInvoke(pigb,IGraphBuilder.RenderFile,[szFile],NULL)
test eax,eax
jnz >.EXIT
mov D[ready],TRUE
cmp S[pims],0
je >>.EXIT
CoInvoke(pims,IMediaSeeking.SetTimeFormat,offset TIME_FORMAT_MEDIA_TIME)
CoInvoke(pims,IMediaSeeking.GetDuration,offset duration)


    .EXIT
    mov eax,[ready]
    ret
ENDF

Play:
cmp D[ready],FALSE
je >>.EXIT
cmp S[pimc],0
je >>.EXIT
CoInvoke(pimc,IMediaControl.Run)
ret
.EXIT
mov eax,-1
ret

Pause:
cmp D[ready],FALSE
je >>.EXIT
cmp S[pimc],0
je >>.EXIT
CoInvoke(pimc,IMediaControl.Pause)
ret
.EXIT
mov eax,-1
ret

Stop:
cmp D[ready],FALSE
je >>.EXIT
cmp S[pimc],0
je >>.EXIT
CoInvoke(pimc,IMediaControl.Stop)
ret
    .EXIT
mov eax,-1
ret

WaitForCompletion FRAME msTimeout, pEvCode
cmp D[ready],FALSE
je >>.EXIT
cmp S[pimex],0
je >>.EXIT
CoInvoke(pimex,IMediaEventEx.WaitForCompletion,[msTimeout],[pEvCode])
cmp eax,0
jne >.EXIT
mov eax,1
ret
.EXIT
mov eax,0
ret
ENDF

SetVolume FRAME vol
cmp D[ready],FALSE
je >>.EXIT
cmp S[piba],0
je >>.EXIT
CoInvoke(piba,IBasicAudio.put_Volume,[vol])
ret
    .EXIT
mov eax,-1
ret
ENDF

GetVolume FRAME pvol
cmp D[ready],FALSE
je >>.EXIT
cmp S[piba],0
je >>.EXIT
CoInvoke(piba,IBasicAudio.get_Volume,[pvol])
ret
    .EXIT
mov eax,-1
ret
ENDF

GetCurrentPosition FRAME pInt64curpos
cmp D[ready],FALSE
je >>.EXIT
cmp S[pims],0
je >>.EXIT
mov eax,[pInt64curpos]
mov D[eax],-1
mov D[eax+4],-1
CoInvoke(pims,IMediaSeeking.GetCurrentPosition,[pInt64curpos])
ret
    .EXIT
mov eax,-1
ret
ENDF

SetPositions FRAME pINT64Current, pINT64Stop, bAbsolutePositioning
LOCAL Posflags:D

mov D[Posflags],0
cmp D[ready],FALSE
je >>.EXIT
cmp S[pims],0
je >>.EXIT
cmp D[bAbsolutePositioning],0
je >
mov D[Posflags], AM_SEEKING_AbsolutePositioning | AM_SEEKING_SeekToKeyFrame
jmp >>.SETPOS
        :
mov D[Posflags], AM_SEEKING_RelativePositioning | AM_SEEKING_SeekToKeyFrame
.SETPOS
CoInvoke(pims,IMediaSeeking.SetPositions,[pINT64Current], [Posflags], [pINT64Stop], [Posflags])
ret
.EXIT
mov eax,-1
ret
ENDF
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

Bill Cravener

Quote from: NoCforMe on January 10, 2012, 06:32:44 AM
I notice you open a device called "MPEGVideo"; shouldn't this be changed if I want to play WAVs? or not? (seems to work OK).

Using the MPEGVideo type you can play almost all of the newest media files such as divx, xvid, mp3, avi, wav, mpeg, mpg, wma and wmv. If I recall correctly earlier versions of Windows win.ini file had a list of supported media file types that the MPEGVideo type supported. The MPEGVideo type refers to Windows system default media driver which as we know can play most anything including MP3 using I think DirectShow which is the technology that replaced MCI some 10 years or more ago. If you have a third-party driver with a higher precedence that claims to support MP3 but in fact doesn't or a third-party installer that has broke MPEGVideo's registration you may get stuttering in your playback. It is not the fault of my simple media player example.

Quote from: NoCforMe on January 10, 2012, 06:32:44 AM
By the way, I hate your user interface (and I mean this in the nicest possible way). Very hard to drill back up through a disk directory structure.

Come on NoCforMe just what did you expect for free its intention is to be simple. What you do with it from there is up to you. Now if you want to open up your wallet I'll do you up something real fancy. :bg
My MASM32 Examples.

"Prejudice does not arise from low intelligence it arises from conservative ideals to which people of low intelligence are drawn." ~ Isaidthat

bomz


Bill Cravener

My MASM32 Examples.

"Prejudice does not arise from low intelligence it arises from conservative ideals to which people of low intelligence are drawn." ~ Isaidthat

Bill Cravener

First time I've ever tested my simple media player example on a full length mp3 and it appears to work perfectly. :bg

Crank it up!! HillbillyHerald.mp3
My MASM32 Examples.

"Prejudice does not arise from low intelligence it arises from conservative ideals to which people of low intelligence are drawn." ~ Isaidthat