Solved

making noises

Posted on 1998-10-05
53
316 Views
Last Modified: 2012-05-04
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.
0
Comment
Question by:Booth882
  • 28
  • 19
  • 6
53 Comments
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174465
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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174466
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.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174467
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
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174468
thanks for the help scrapdog but I dont know the first thing about pascal.  anybody know how to do this with c++?  thanks
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174469
FYI

This wasn't Pascal :)

It was simply a description of the WAV file header.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174470
really?!  then what is the $00 or $01 mean?  I'm sorry I have no idea!  
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174471
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.  
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174472
$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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174473
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++...
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174474
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!!
0
 
LVL 23

Expert Comment

by:chensu
ID: 1174475
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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174476
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?...
0
 
LVL 23

Accepted Solution

by:
chensu earned 100 total points
ID: 1174477
I would suggest you take a small WAVE file and use a hex editor to view it. Try to analyze the format according to the format description. To play it, allocate a buffer and fill it with the data as a byte stream.

LPSTR lpBuf[1024];  // using dynamical allocation is better

// fill the buffer
// you may try using a readymade WAVE file first

// play the WAVE file from the memory buffer
PlaySound(lpBuf, NULL, SND_MEMORY | SND_SYNC);
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174478
ah, beautiful!  but where do I find a hex editor?  is there anyway I can do that with msvc++ 4.0?  or something else?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174479
If you give me your e-mail address I will mail you one right now.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174480
Booth866@aol.com.  thanks scrapdog, I really appreciate it.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174481
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?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174482
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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174483
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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174484
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?
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174485
thanks scrapdog by the way.  you have been most helpful
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174486
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.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174487
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.


0
 
LVL 23

Expert Comment

by:chensu
ID: 1174488
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.


0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174489
#include <windows.h>

???
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174490
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
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 1

Author Comment

by:Booth882
ID: 1174491
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!!!!!!!!!!!!!
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174492
anybody?  this is all that is keeping my program from working!  maybe I havent sacrificed enough goats...
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174493
LPSTR is a pointer to a string.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174494
thanks scrapdog!  any idea what the unresolved external symbol __imp__PlaySoundA@12 is all about?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174495
That is one damn ugly error message!  (typical C++)

No, I can't tell you what that means...
0
 
LVL 23

Expert Comment

by:chensu
ID: 1174496
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 *.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174497
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?
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174498
oops I meant a LPCSTR.  
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174499
Did you allocate all of the necessary memory before reading/writing to it?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174500
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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174501
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.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174502
alright I have pinpointed the problem to be with the PlaySound function.  can you tell me whats going on?
0
 
LVL 23

Expert Comment

by:chensu
ID: 1174503
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);
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174504
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?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174505
Send me your wave file and I will see how it is corrupted.  You have my e-mail address.
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174506
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?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174507
meat@wctc.net
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174508
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?
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174509
never mind that last comment, EE crosseyed me.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174510
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]



0
 
LVL 1

Author Comment

by:Booth882
ID: 1174511
but why 36 greater when the header is 44 bytes?
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174512
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)
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174513
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.
0
 
LVL 5

Expert Comment

by:scrapdog
ID: 1174514
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.
0
 
LVL 23

Expert Comment

by:chensu
ID: 1174515
>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

0
 
LVL 1

Author Comment

by:Booth882
ID: 1174516
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! :)
0
 
LVL 1

Author Comment

by:Booth882
ID: 1174517
chensu you are the master.  thanks again, both of you!!!  this opens up a whole new world in programming for me!!!!! :)
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
Container Orchestration platforms empower organizations to scale their apps at an exceptional rate. This is the reason numerous innovation-driven companies are moving apps to an appropriated datacenter wide platform that empowers them to scale at a …
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now