Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Playing background WAVE files in a QBASIC RPG

Status
Not open for further replies.

sandmann999

Programmer
Apr 28, 2001
52
US
For the last year or so, I've been working on an RPG of mine. I've put a lot of work into it, and the only thing I feel that keeps it from having that final "professional" touch is the fact that it's dead silent. I would love to use wave files for sound effects and for background music. Can anyone guide me in the right direction as to how to go about doing this?
Thanks for any help that you can give.

 
there are lots of wave players out there for qb. try dmawave, or check out the ff2 clone. try a google search for qb and wave.
 
I have no idea where to find any of those programs you mentioned. I did internet searches and came up with nothing.
As far as just searching for a qbasic wave file player on google, we're looking at about 5000 or more matches, and about 1% of them have anything of any interest, if that.
No offense, but any more concrete help would be greatly appreciated.
 
This plays wave files flawlessly on every computer that I have tried it on. The wavs can be any length that you want.

These are the specifications of the wav
PCM format
176kbps bit rate
8 bit sample size
mono
22kHz sample rate

This format can be made by Windows Sound recorder, I could give you a sample wav to try if you would like.



DECLARE SUB wave (wav$)
DECLARE SUB SBDETECT ()
DECLARE SUB RESETDSP ()
DECLARE SUB DMAPLAY (Segment&, Offset&, buflen&)
DECLARE SUB GetBLASTER (DMA, BasePort, IRQ)
DECLARE SUB WRITEDSP (ibyte)
DECLARE SUB playmusic ()
COMMON SHARED repeat, buflen&
COMMON SHARED BasePort, LenPort, Channel, nosound
DIM SHARED aa!
CONST timeconst = 256 - 1000000 \ 22050
'$DYNAMIC
DIM SHARED WavBuffer(0 TO 0) AS STRING * 32767
'$STATIC
DIM SHARED snds(1 TO 6) AS STRING * 30
wave wav$
a! = LOF(2)
DO
IF nosound = 0 THEN playmusic
aa! = aa! + 32767
LOOP UNTIL aa! >= a!

SUB DMAPLAY (Segment&, Offset&, buflen&)
Page = 0
MemLoc& = Segment& * 16 + Offset&
SELECT CASE Channel
CASE 0
PgPort = &H87
AddPort = &H0
LenPort = &H1
ModeReg = &H48
CASE 1
PgPort = &H83
AddPort = &H2
LenPort = &H3
ModeReg = &H49
CASE 2
PgPort = &H81
AddPort = &H4
LenPort = &H5
ModeReg = &H4A
CASE 3
PgPort = &H82
AddPort = &H6
LenPort = &H7
ModeReg = &H4B
END SELECT
OUT &HA, &H4 + Channel
OUT &HC, &H0
OUT &HB, ModeReg
OUT AddPort, MemLoc& AND &HFF
OUT AddPort, (MemLoc& AND &HFFFF&) \ &H100
IF (MemLoc& AND 65536) THEN Page = Page + 1
IF (MemLoc& AND 131072) THEN Page = Page + 2
IF (MemLoc& AND 262144) THEN Page = Page + 4
IF (MemLoc& AND 524288) THEN Page = Page + 8
OUT PgPort, Page
OUT LenPort, buflen& AND &HFF
OUT LenPort, (buflen& AND &HFFFF&) \ &H100
OUT &HA, Channel
WRITEDSP &H40
WRITEDSP timeconst
WRITEDSP &H14
WRITEDSP (buflen& AND &HFF)
WRITEDSP ((buflen& AND &HFFFF&) \ &H100)
END SUB

SUB GetBLASTER (DMA, BasePort, IRQ)
IF LEN(ENVIRON$("BLASTER")) = 0 THEN PRINT "BLASTER environment variable not set.": EXIT SUB
FOR Length = 1 TO LEN(ENVIRON$("BLASTER"))
SELECT CASE MID$(ENVIRON$("BLASTER"), Length, 1)
CASE "A"
BasePort = VAL("&H" + MID$(ENVIRON$("BLASTER"), Length + 1, 3))
CASE "I"
IRQ = VAL(MID$(ENVIRON$("BLASTER"), Length + 1, 1))
CASE "D"
DMA = VAL(MID$(ENVIRON$("BLASTER"), Length + 1, 1))
END SELECT
NEXT
END SUB

SUB playmusic
DEF SEG
RESETDSP
IF repeat >= 0 THEN DMAPLAY VARSEG(WavBuffer(0)), VARPTR(WavBuffer(0)), buflen&
IF EOF(2) THEN
SEEK #2, 44
IF repeat <= 0 THEN repeat = repeat - 1
END IF
buflen& = LOF(2) - LOC(2)
IF buflen& > 32767 THEN buflen& = 32767
GET #2, , WavBuffer(0)
END SUB

SUB RESETDSP
OUT BasePort + 6, 1
FOR Count = 1 TO 4
junk = INP(BasePort + 6)
NEXT
OUT BasePort + 6, 0
END SUB

SUB SBDETECT
nosound = 1
FOR port = &H210 TO &H280 STEP &H10
OUT port + &H6, 1
FOR Count = 1 TO 100
OUT port + &H6, 0
Stat = INP(port + &HE)
Stat = INP(port + &HA)
IF Stat = &HAA THEN nosound = 0: EXIT SUB
NEXT Count
NEXT port
IF nosound = 1 THEN
PRINT &quot;Sound Blaster not detected. Do you&quot;
PRINT &quot;wish to disable sound? (y,n)&quot;
DO: sel$ = INKEY$: LOOP WHILE UCASE$(sel$) <> &quot;Y&quot; AND UCASE$(sel$) <> &quot;N&quot;
IF UCASE$(sel$) = &quot;N&quot; THEN END
END IF
END SUB

SUB wave (wav$)
IF wav$ = &quot;&quot; THEN EXIT SUB
DEF SEG
SBDETECT
IF nosound = 0 THEN
GetBLASTER Channel, BasePort, IRQ
RESETDSP
END IF
OPEN wav$ + &quot;.wav&quot; FOR BINARY AS #2
GET #2, , WavBuffer(0): buflen& = 32767
END SUB

SUB WRITEDSP (ibyte)
DO: LOOP WHILE INP(BasePort + 12) AND &H80
OUT BasePort + 12, ibyte
END SUB
 
Thanks for the music player!
Although, I have some questions. Your specifications said:

8-bit
22k Hz
mono

But then you said it had to be of 176kbps. When you convert a wave to 8bit mono, 22kHz, it's only 21kbps. The only reason I'm questioning this is because when I play a song that I've converted, it sounds right, but it skips. It'll play a 2 minute song in about 15 seconds because it keeps jumping through the song. It'll play 3 seconds of the song, skip, play another 3 seconds, skip, etc...

Do you know why this might be? Or what I can do to convert the song just right?
Thanks for your help, it's much appreciated. :)

(Secondly, I haven't tried it, is there any way that this wave file code can be running in the background if my main code? Thanks again.)
 
Actually its 22 KBps, kiloBYTES = 176 kiloBITs.


Your second question, yes. You will just have to count the ticks of the TIMER, every 27 ticks you will have to call playmusic. The way that the wav player above works is it send 32767 bits to the sound card, which lays it automatically, qbasic doesn't actually play anything. You will notice that if you end the program in the middle of one of the 32767 bit sections it will continue playing that section.

I don't see why it skips through the song, it doesn't for me. You must have the wrong format; I copy and pasted the code directly, so its not wrong.
 
Would you be able to send me that sample wave file? I probably just need to do some tweaking with the format and could use the example to base it on.

 
I've been trying some things.
I'm not sure if it's the format that's incorrect (although, I'd still appreciate a working sample. Email address milesaway1980@juno.com).

But if I change the kHz to anything but 22050, the speed is wrong, if I change the bits to 16 instead of 8, it's really staticy, and if I change it to stereo instead of mono, it's really slow. So I know that the format has to be correct through process of elimination. It's just that it plays about 2 seconds of the song and then skips ahead like 30 seconds. (It doesn't start from the beginning of the wave file either.)

Could that possibly have anything to do with me using Windows XP? I doubt it, but no harm in checking.
 
Unfortunately there is no pure QB way of playing a waw IN THE BACKGROUND.
Qbasicking code will work as long as you dont ask QB to do other things as draw tiles, move sprites and read keyboard. And even if the code is tweaked to match your sample it may not work ok in all computers.
To work OK a Soundblaster needs an Interrupt Service Routine (ISR) filling its buffer at the right pace, independently of what your program is doing. If you don't do it the sound skips, ckicks and does weird things.
An ISR CAN'T be done in QB, it must be done in assembler or C. So the best way of having sound in QB is by using a pre-made library. You have multichannel mixers in DirectQB or UGL, and a single channel in Future.

Before deciding to use a pure QB solution try it in real-life conditions, merge it with your game and see how the sound and the game behave...


Antoni
 
Sandman,
I agree with Antoni that a premade library is the best way to go, but I've recently read some posts by v1ctor (author of the UGL library) on the NeoBASIC board that suggests that a Pure QB solution is possible. However, it doesn't have the benefit of being able to run in the IDE. Here are the links to the posts (they will disappear soon, so read it now).

The basic trick is that you write QB code that has a call to the (QB ISR) function immediately after a ProcPtr() call. The ProcPtr function then gets the address of the instruction in the caller subroutine (that called both the QBISR() and ProcPtr()) where the ProcPtr() call was executed. Now it scans downward in the machine code in memory to find the call to the QBISR() to get its function pointer, and removes the call (to prevent the ISR from being executed before the interrupt vector table is set up). It's a hack, but it solves the &quot;getting the address of a QB function in PureQB&quot; problem. Now the other problem is that the QBISR() function doesn't have an IRET instruction at the end of the function. This would cause the OS (or DOS Box) to crash when the ISR is called. Now, there are two ways to solve this. One is to modify the machine code at the end of the QBISR() sub to replace a RETF with an IRET (might also have to change the prologue and epilogue code to save extra registers). The other way is to write an assembler ISR stub to save the important registers, call the QB subroutine, and restore registers and IRET. This can be stored into memory using a QB string or array, and the interrupt vector table can be modified to point to this SUB. There is no need to call the subroutine directly, so no CALL ABSOLUTE should be needed. (If there is a need to call an assembler subroutine without using CALL ABSOLUTE, Plasma offered a solution which involves overwriting the machine code of a QB library function e.g. SLEEP and calling the library function).

Don't you think this is too much work just to get background sound playing working? However, if you still want to go the QB way, here is some code that uses an assembler ISR and polling to achieve very good background sound in compiled programs. But don't expect it to be compatible with all sound cards.

For SB4.0 compatibles, use
For WinXP or SB2.0 compatibles, use
 
Thanks for all the information on that. Definitely a bit too much work for a pure QB method of playing wave files. If I went that route, I don't know enough about how sound cards work to be able to do all the things you mentioned. I'm not really all about having pure QB code playing the wave file, just as long as I can get the program/game itself to get some wave files playing (without having to shrink down the game [Qbasic] and play them with Winamp or something.)
Something like qbasicking's wave file player would also be good if I could just somehow tweak it to get it to work right on my computer. It plays wave files, but it plays just pieces of them. It'll play about 3 seconds worth, skip 30 seconds, play another 3 seconds worth, etc.. and a 3 minute song will be done in about 15 seconds. (I'm using a Sound Blaster Live 5.1 soundcard with Windows XP if anyone has any ideas.)
Secondly, if a library works too, that'd be great, although, I've never used a library before in QBasic, so some help with that would be appreciated. Also, I don't know where to find such libraries. I've done searches on the internet &quot;Qbasic Wave library&quot; and other such variations but haven't come up with much.
Thanks for everyone's help.
 
Might I add that even a pure DOS solution would cause more problems than it would solve? Look up DS4QB or it's descendants, DS4QB2 and DS4QB++. One of them (I can't remember which) will run flawlessly on all windows systems from 95 to XP. Speaking from experience, if you don't get a perfect system the first time around, you'll end up switching a lot to find one that works, so be careful with your choice.
 
Glad to meet you, Toshi!

And to complete the information:

To use a soundcard from a DOS program in Windows 2000 or XP is not possible, unless you install VDMSound. This thing emulates the API of a SoundBlaster 16 in any soundcard that's currently working with Windows Media.

Antoni
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top