Link to home
Start Free TrialLog in
Avatar of Booth882
Booth882

asked on

making noises

I am using MSVC++ 4.0 and I would like to make sound waves that I can actually hear based on digital or analog data.  how would I go about doing this?  I use mainly console applications but realize that I may need to go to windows for this one.  I know windows has a way to record from a microphone and play back sounds using .wav files.  could I possibly make a .wav file from my programs output and play it using the sound player?  this would be ideal.  thank you.
Avatar of scrapdog
scrapdog
Flag of United States of America image

Yes, it can be done, even with console applications.  However, you have to put together the outputted .wav file yourself.

If you want to skip making the .wav file and go directly to playing it, you could use sndPlaySound, or use functions found in DirectSound.

All of the sound data coming in is in the form of bytes or words (depending on whether you use 8 or 16 bit resolution).  These values represent the amplitude of the sound at that point in time.  The higher the value, the greater the compression or rarefraction the speaker produces.  This value is determined several times per second (depending on sampling rate, 22050, 44100, etc.).  

All you have to do is send this stream of data from the source to the destination.  I've never used any of the API functions for playing/recording sound, so I couldn't tell you how exactly how to use them.  However, if you you making a wav file, you would simply write these values to disk in sequence.

WAV files contain a header consisting of data such as sampling rate and sample resolution used.  After the header, the remainder of the file is the raw sound data.
Avatar of Booth882
Booth882

ASKER

alright, thank you scrapdog, that was very helpful.  so can you or anybody tell me explicitly how I would go about making a .wav file from raw data and then use that file to play the sound please?  I take it the sound data is binary but how would I format the file so that the .wav player can play it and then play the .wav file?  with console application would be preferred.  thank you.
I could show you have to process sound data in Pascal, but I couldn't show you in C++ :)

I can explain the WAV file header to you though, if that will help.


The WAV file header contains 44 bytes:
---------------------------------------

0-3:    "RIFF"
4-7:    size of file(including header)
8-15:   "WAVEfmt "
16:     $10
17:     $00
18:     $01 for Mono,  $02 for Stereo
19:     $00
20:     $01
21:     $00
22:     $01
23:     $00
24-27:  Sample Rate
28-31:  Sample Rate * BitsPerSecond / 8
32:     BitsPerSecond / 8
33:     $00
34:     BitsPerSecond
35:     $00
36-39:  "data"
40-43:  length of raw sound data in bytes

The values contained in strings are constant. Most of the explicit values given
are also generally constant.


scrapdog
thanks for the help scrapdog but I dont know the first thing about pascal.  anybody know how to do this with c++?  thanks
FYI

This wasn't Pascal :)

It was simply a description of the WAV file header.
really?!  then what is the $00 or $01 mean?  I'm sorry I have no idea!  
also, I still need to know how to set this all up, I mean making the file and using it once I have it.  thanks.  
$00 and $01 are hex for the value 0 and 1.

In the file header, this means that these values will be written into the file header directly.
just curious...

what is the point of having all of these $00 and $01 if they are common to every .wav file?  couldnt the program that uses the files just assume them?  what happens if I change them around?

thanks for your help by the way scrapdog.  its too bad you dont know c++...
They are not common to every .wav file, but they are common to MOST .wav files.  The .wav files you will write will most likely need these values.

It is not a good idea to change them around!!
You can find a WAVE file format description at http://www.wotsit.org/wmusic/wav.zip and http://www.wotsit.org/wmusic/wave.zip.

If you use the low-level waveform playback services or DirectSound, you don't need to form a complete WAVE file. Check out the Win32 Multimedia Samples "Lowpass: Low Pass Filter for Waveform Audio" and "Reverse: Play a Waveform in Reverse" that come with Visual C++ or DirectSound samples.

If you form a complete WAVE file (either on disk or in memory), you may play it using the PlaySound function.
I'm sorry chensu but I still do not know how to make my own wav file or how to use the PlaySound function.  I downloaded the zip file you specified but it was more confusing than helpful.  even if I understood it I still do not know how to set up the file in this way.  would it be done with simple fstream techniques or is there other formatting that needs to take place?  and what do I include for PlaySound and what parameters does it take?  if you would please elaborate?...
ASKER CERTIFIED SOLUTION
Avatar of chensu
chensu
Flag of Canada image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
ah, beautiful!  but where do I find a hex editor?  is there anyway I can do that with msvc++ 4.0?  or something else?
If you give me your e-mail address I will mail you one right now.
Booth866@aol.com.  thanks scrapdog, I really appreciate it.
thanks alot for that hex editor it is extremely useful.  I have noticed some odd things however.

I looked at a wav file, and comparing what scrapdog said about the file header there are some discrepancies.

first of all, the file header has 58 bytes, not 44.  it all follows right until byte 36, and picks back up where byte 36 is supposed to be (the 'd' in "data") at byte 50.  here is what the bytes in between look like

36:  $00
37:  &00
38 - 41:  "fact"
42:  $04
43:  $00
44:  $00
45:  $00
46 - 49:  are the same as what 40 - 43 are supposed to be, which is the length of raw sound data in bytes

then it picks up where it left off

50 - 53:  "data"
54 - 57:  length of raw sound data in bytes

and then starts the sound data.  is this some special kind of header?  what is going on?

also the hex editor says that the length of the file is 5562 bytes, but the hex value of the length of the file specified in bytes 4 - 7 of the header say it is 15B2 bytes long, which translates into 5554 bytes in decimal!  where are the other 8 bytes I wonder?

can you guys tell me whats going on?
The "data" in the file header specifies where the raw data starts.  Different .wav files can have different headers.  The program that reads it will read "fact" instead of "data".  This will indicate that what follows is NOT raw sound data.  "Fact" may be some other data section, but it is not required.  After this information, "data" is found, which means to start reading the actual sound data.

"WAVEfmt " specifies that the format of the wave file is to follow, and "RIFF" specifies that it is a wave file.  

If I were you, I wouldn't worry about these unless you are reading the file.  If this is the case, search for the strings in the file.  If you want to get to the data, keep reading the file until "data" is present.  For your purposes, any data sections besides the format and the data are irrelevant.

When you are writing the file, you can skip these extra sections and use the file header I gave you.  It will work in any wave player.
ah I see.  so what about the mysterious 8 byte discrepancy between the hex editors point of view and the headers point of view?  what is that all about?  its not really important I would just like to understand whats going on.
by the way, what is a LPSTR that chensu is talking about?  I'm sorry my specialty is algorithms and concepts, I'm an idiot when it comes to all of this technical jargon, and I like to know what exactly it is that I'm using before I use it.  

and what do you need to include to use PlaySound?
thanks scrapdog by the way.  you have been most helpful
Consider those 8 bytes as filler material.  Read all of the information you want, and after that skip over everything until you hit one of the data sections (i.e. "data").  Then resume reading.

Are you going to read .wav files from your program?  If not, do not worry about it.
I know exactly how you feel about technical jargon.  I hate it too!  I like to concentrate on algorithms and concepts, so that is why I use Pascal!!  

But, I suppose I need a little practice in C, so I will try to help you out.  I'll get back to you.


The editor that comes with Visual C++ 5.0 or above is able to view hex data. I don't remember if Visual C++ 4.0 could do it. You may find a hex editor at http://www.ultraedit.com.

There is also some decription on WAVE file format in the documentation "Waveform Audio" that comes with Visual C++ 4.0. A WAVE file is a RIFF file. Another useful tool is RiffWalk. You can download it at http://msdn.microsoft.com/developer/sdk/sdktools.htm.


#include <windows.h>

???
alright you guys I am almost there.  

I made a class, wavemaker, that oversees the making of the wav data.  I am storing the data in a char array which is dynamically allocated.  I tried using the LPSTR even though I dont know what they are but I couldnt assign to it with int consts like 54 or 6 or something, so I chose char instead.  everything works except when I send the array into the PlaySound function like so:

  PlaySound(WaveData, NULL, SND_MEMORY | SND_SYNC);

it gives me the compile error

  cannot convert parameter one from unsigned char * to const char *

how may I go about fixing this without radically altering my char array structure?  I really want to use the char array just because the way the data that I want to make the wav data from is in this format.  

thank you both for your help
never mind about that last comment, I got it to compile by changiing it from an unsigned char to a char.  but now I'm getting a linker error!

unresolved external symbol __imp__PlaySoundA@12

and its for PlaySoundA not PlaySound!  I have gotten this kind of error before when using windows and it caused me to completely abandon the project!!  aghk!!!  can you tell me whats going on?  I really dont want to abandon this project too!!!

I HATE WINDOWS!!!!!!!!!!!!!
anybody?  this is all that is keeping my program from working!  maybe I havent sacrificed enough goats...
LPSTR is a pointer to a string.
thanks scrapdog!  any idea what the unresolved external symbol __imp__PlaySoundA@12 is all about?
That is one damn ugly error message!  (typical C++)

No, I can't tell you what that means...
LPSTR is simply char *.

To use PlaySound, #include <mmsystem.h> in your source code. And link with winmm.lib. Go to the Project Settings and the Link tab, add winmm.lib in the Object/library modules edit box.

Like many other Win32 functions, PlaySound has two version, one is the ANSI version PlaySoundA, the other is the Unicode version PlaySoundW. PlaySound is just a preprocessor macro.

WINMMAPI BOOL WINAPI PlaySoundA(LPCSTR pszSound, HMODULE hmod, DWORD fdwSound);
WINMMAPI BOOL WINAPI PlaySoundW(LPCWSTR pszSound, HMODULE hmod, DWORD fdwSound);
#ifdef UNICODE
#define PlaySound  PlaySoundW
#else
#define PlaySound  PlaySoundA
#endif // !UNICODE

PlaySoundA requires the first parameter to be of type LPCSTR, which is const char *.
alright, I did everything you said, I am calling play sound with an LPSTR that contains the header as its first 44 elements and all of the data after, and the whole thing compiles and links, but when I execute I get the following error:

Unhandled exception in wavetest.exe (WINMM.DLL):  0xC0000005: Access Violation

and then it goes to the following assembly segment in WINMM! bfe38f61()

bfe38f61  mov        eax, dword ptr [esi + 04]

can you please tell me whats going on?
oops I meant a LPCSTR.  
Did you allocate all of the necessary memory before reading/writing to it?
The instruction that caused the exception is one that reads a double word from the address 4 higher than the contents of the ESI register.  The ESI register contains a memory location that your program doesn't have access to, and reading from it will raise the exception.  This could be because you didn't allocate your memory properly.
as far as I can tell I allocated the memory correctly.  but tell me if this could be the problem.  the LPCSTR I am sending in to the  PlaySound function contains as its data the actual wav file rather than the name of the wav file.  do I need to put my data in a file, put the name of the file into the LPCSTR and call the function with the name of the file containing the wav data rather than the actual wav data?  is that the problem?  until then I will investigate the memory allocation hypothesis.
alright I have pinpointed the problem to be with the PlaySound function.  can you tell me whats going on?
Probably the header of the WAVE data is wrong. For example, the header says the data size is 64 bytes. But in fact the data size is 32 bytes only. When PlaySound tries to access the data, it will cause the access violation since you don't allocate such a buffer. Create a WAVE file (by recording or editing), try playing it using Windows Sound Recorder to make sure the file is correct. Then use a hex editor to view the data and copy the data to the buffer.

You can play a WAVE file. For example,

PlaySound(_T("test.wav"), NULL, SND_FILENAME | SND_SYNC);
alright heres the deal.  I did what you said I made a file and looked at it in the hex editor.  the hex editor says that there are 4044 bytes in the file.  in the header it says there are 4044 bytes in the file.  but when I try to play it it says the file has been corrupted and cannot be opened!  

some suspicious things.  when I look at another wav file in the hex editor it says there are 5562 bytes in the file, but according to  the header it says there are only 5554 bytes in the file!  I am thinking those 8 bytes may be the key.  and also, every other wav file I have opened has a 58 byte header with a fact inserted in the middle (see previous comments for complete description).  

any ideas as to what is going on and what I should do about it?
Send me your wave file and I will see how it is corrupted.  You have my e-mail address.
scrapdog, I had it but I deleted the message once I got the hex editor.  mind posting it or can you send me another message?
meat@wctc.net
scrapdog, I deleted the message you sent after I downloaded the hex editor.  do you mind posting it or can you send me another message?
never mind that last comment, EE crosseyed me.
I looked at your wave file.  Are you sure you wanted to set the sample rate at 20514?  My guess is that you wanted 22500 and enter $22 $50 here.  However $22 $50 would be interpreted as $5022, or 20514 in decimal.

To correct this, enter $e4 and $57 in these bytes.  ($57e4 = 22500 decimal).  Make sure you also place this in the avg bytes second part of the data header (bytes 28-31), [same as SampleRate * BitsPerSec / 8].

Also your the size of your header+data (specified in the beginning of the file) and the size of the data (specified after "data") did not match.

Generally you want the value stored in the first size DWORD to be 36 greater than the size DWORD after "data".

Size of entire file = Size of data + 36

Also, you set the file to stereo.  I don't think you want to do this.  If you really want to play around with stereo waveforms, you have to store two samples.  I can not help you, because I don't know how stereo waveforms are stored.

I would assume, however, that you want mono.  If this is the case, set byte 18 to $00 or $01 rather than $02.

Other than that your header looked fine.  After making these changes, it should work (it did on my computer).  [It is not audible because it appears as if you filled whole file with one value -- it IS, however, a valid and uncorrupted wave file]



but why 36 greater when the header is 44 bytes?
This is not counting "RIFF" and the first size DWORD itself.

The second size DWORD is 36 bytes from it ("RIFF" and size together take up 8 bytes; 44-8=36)
I see.  thats that missing 8 bytes I was always talking about!!  the mystery is revealed!!

the file you sent me does not work on my machine.  it still says it is corrupted.  it worked on yours you say?  well then what the hell is going on!!!!   I am starting to get peeved.
Note: That is NOT the 8 bytes that you were talking about before.  This is just a coincidence.

Check the properties of the wave file, so if it can tell that its a wav file.  If so it should say the sample rate, bps, etc.
>when I look at another wav file in the hex editor it says there are 5562 bytes in the file, but according to  the header it says there are only 5554 bytes in the file!  I am thinking those 8 bytes may be the key.

The size stated by the "RIFF" chunk refers to the size of the "RIFF" chunk data, which does not include the four-character code "RIFF" and the size itself (a doubleword).

>every other wav file I have opened has a 58 byte header with a fact inserted in the middle.

The "fact" chunk is just an additional chunk, which has no effect for playing the WAVE files.


Try the following data. It is a complete PCM 22,050 Hz, 8 bits, Mono WAVE.

52 49 46 46 78 00 00 00 57 41 56 45 66 6D 74 20
12 00 00 00 01 00 01 00 22 56 00 00 22 56 00 00
01 00 08 00 00 00 66 61 63 74 04 00 00 00 45 00
00 00 64 61 74 61 45 00 00 00 81 80 80 81 81 7E
7F 7E 7E 83 87 77 8E 86 62 9F 7A 49 84 A3 B6 92
45 58 9C B9 73 72 62 83 C5 65 4B 94 A8 81 59 67
8F A9 86 5B 81 8B 80 8E 88 84 6D 68 80 78 80 80
8A 85 7B 83 83 8E 95 7F 75 76 78 80 80 7D 7E 00

ah thank you both so much for your help!!!!  I am victorious!!!!  I just wish I had points to give you both, scrapdog I will have to make it up to you man, I couldnt have done it without you.  thanks a million! :)
chensu you are the master.  thanks again, both of you!!!  this opens up a whole new world in programming for me!!!!! :)