 |
|
 |
Thank you for your great code.
Can I use your code in my commercial project?
Let me know if I have your permission.
Thank you in advance.
Hojin, Lee
Eoneo Inc
http://www.eoneo.co.kr
leigh@eoneo.co.kr
|
|
|
|
 |
|
 |
Hi Pierre Canthelou,
Thank you for making this code available.
But i noticed one very problematic issue:
In the Callback, you add new headers. According to MSDN, you should not call any system functions except some allowed ones
''Applications should not call any system-defined functions from inside a callback function, except for EnterCriticalSection, LeaveCriticalSection, midiOutLongMsg, midiOutShortMsg, OutputDebugString, PostMessage, PostThreadMessage, SetEvent, timeGetSystemTime, timeGetTime,timeKillEvent, and timeSetEvent. Calling other wave functions will cause deadlock.
''
In fact, i got reproducable deadlocks when playing different waves shortly one after the other at least on windows mobile 6.0.
The solution could be rather complicated, just as a start, i delegated the callback back to the WaveOut class and i used a thread to call AddNewHeaders. For this, we need an event to be set in the callback (MSDN allows it!):
void CALLBACK waveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
if(dwInstance)
((CWaveOut*)(dwInstance))->WaveOutProc(hwo,uMsg,dwParam1,dwParam2);
}
void CWaveOut::WaveOutProc(HWAVEOUT hwo, UINT uMsg, DWORD dwParam1, DWORD dwParam2)
{
switch(uMsg)
{
...
case MM_WOM_DONE:
if(m_hEventFromThreadController)
SetEvent(m_hEventFromThreadController); break;
}
}
Do not forget to give the this pointer to the callback:
bool CWaveOut::Open()
{
return !IsError( waveOutOpen(&m_hWaveOut, m_waveDevice.GetDevice(), &m_wave.GetFormat(), (DWORD)waveOutProc,(DWORD) this, CALLBACK_FUNCTION) );
}
The Thread controller is a little bit larger, but essentially, i do this:
m_hEvent is an array, containing other events, one of them is m_hEventFromThreadController , i gave to the CWaveOut instance (see above)
DWORD CWavePlayer::ThreadFunction()
{
while(!m_bStopThread)
{
DWORD dwWait = WaitForMultipleObjects(3, m_hEvent, FALSE, 4500);
switch(dwWait)
{
case WAIT_OBJECT_0 + The-Index-of-m_hEventFromThreadController-From-Above:
m_waveOut.PlayNextHdr();
...
}
PlayNextHdr() is basically the code i had removed from the callback function.
Another tip:
* in the Play(...) methods, prepare only 2 of the 3 headers, and after preparing, write them to wave out. Reason: for small buffers and heavy CPU load, the callback might return too early and tries to add new headers before Play has finished adding it's own headers. This may spoil your indexing variable m_nIndexWaveHdr and one of both AddNewHeader calls might use wrong headers. (This should happen rarely, but might be possible, as far as i see...)
If you like, i can send you the changes and the thread controller, but i made a lot of changes there, it might need some work for you to merge it back into your code.
|
|
|
|
 |
|
 |
Beware that the Wave routines will crash on large files. The function MakeWave() will GPF when copying data becuase the following math overflows:
CopyMemory( (char*)pBuffer + dwPosInBuffer, p_....
In general this is a problem handling large recordings, because MakeWave() will try to allocate memory large enough to hold the entire recording before writing it. One way around is to skip MakeWave() altogether and have waveIn() write out the data, like this:
//////////////////////////////////////////////////////////////////////
void CWaveIn::Save(CFile *f)
{
long bytes = 0;
long samples = 0;
ASSERT( GetNumSamples() > 0 );
f->Write("RIFF", 4) ;
DWORD dwFileSize = GetNumSamples() * m_pcmWaveFormat.nBlockAlign + 36 ;
f->Write(&dwFileSize, sizeof(dwFileSize)) ;
f->Write("WAVEfmt ", 8) ;
DWORD dwFmtSize = 16L;
f->Write(&dwFmtSize, sizeof(dwFmtSize)) ;
f->Write(&m_pcmWaveFormat.wFormatTag, sizeof(m_pcmWaveFormat.wFormatTag)) ;
f->Write(&m_pcmWaveFormat.nChannels, sizeof(m_pcmWaveFormat.nChannels)) ;
f->Write(&m_pcmWaveFormat.nSamplesPerSec, sizeof(m_pcmWaveFormat.nSamplesPerSec)) ;
f->Write(&m_pcmWaveFormat.nAvgBytesPerSec, sizeof(m_pcmWaveFormat.nAvgBytesPerSec)) ;
f->Write(&m_pcmWaveFormat.nBlockAlign, sizeof(m_pcmWaveFormat.nBlockAlign)) ;
f->Write(&m_pcmWaveFormat.wBitsPerSample, sizeof(m_pcmWaveFormat.wBitsPerSample)) ;
f->Write("data", 4) ;
DWORD dwNum = GetNumSamples() * m_pcmWaveFormat.nBlockAlign;
f->Write(&dwNum, sizeof(dwNum)) ;
//
POSITION pos = m_listOfBuffer.GetHeadPosition();
while (pos) {
CWaveBuffer* p_waveBuffer = (CWaveBuffer*) m_listOfBuffer.GetNext(pos);
f->Write(p_waveBuffer->GetBuffer(), p_waveBuffer->GetNumSamples() * p_waveBuffer->GetSampleSize() );
bytes += p_waveBuffer->GetNumSamples() * p_waveBuffer->GetSampleSize();
samples += p_waveBuffer->GetNumSamples();
TRACE("\nWriting %u (%u %u) bytes %u samples %u",p_waveBuffer->GetNumSamples() * p_waveBuffer->GetSampleSize(),p_waveBuffer->GetNumSamples() , p_waveBuffer->GetSampleSize(), bytes, samples);
}
}
|
|
|
|
 |
|
 |
Dear Pirerre
I want to write a program which records data from microphone in 100 milli seconds chunks and then
play it on the speaker.
But before I begin this job I want to know is it possible to do this task with your classes.
Do your classes support concurrency.
for example some thing like this
while( !ActionTerminated )
{
switch(WaitForMultipleObjects(2,Handles,FALSE,INFINITE))
{
case WAIT_OBJECT_0 : m_queue->AddToQueue(m_Record.buffer,ONE_SECOND/10);
if ( m_Record.IsOpen() )
{
m_Record.Record();
}
ResetEvent(Handles[0]);
break;
case WAIT_OBJECT_0+1 : m_queue->Remove(sbuf,ONE_SECOND/10);
m_Play.CopyBuffer(sbuf, 1);
if (m_Play.IsOpen())
{
m_Play.Play();
}
ResetEvent(Handles[1]);
break;
}
}
Thanx in advance
Mahdi Monhi
|
|
|
|
 |
|
 |
Hello
Ur Code sample works great for me.
But I have been needing a new feature on my wave recorder and player.
I need to make appending record to existing wave file.
how can u help me on this.
I would greatly appreciate if u give me a quick answer for that.
thanks and reagrds
Bharatha
|
|
|
|
 |
|
 |
In order to append a Wave file to an existing one, assuming they're the same format (channels, frequency and bits per sample) you can just add the data from the second one to the 1st one (the data only, without the header) and change the header of the 1st file. You'll have to change 2 values: the chunk size (the 2nd param @ offset 4) and the next to the last parameter, for sub-chunk 2 size (offset 40).
However, if the format is not the same, you'll have to reencode either one of them to make them the same format and then do the appending like i described above
Cheers
|
|
|
|
 |
|
 |
Can I use these class in commercial project ?
Can I add or remove code & comments ?
|
|
|
|
 |
|
 |
m_nIndexWaveHdr is not initiated in CWaveIn().
CWaveIn::CWaveIn() : m_hWaveIn(0), m_bResetRequired(true)
Shall be:
CWaveIn::CWaveIn() : m_hWaveIn(0), m_nIndexWaveHdr(NUMWAVEINHDR - 1), m_bResetRequired(true)
Otherwise a call to CWaveIn::IsRecording() may cause crash at this line
if (m_nIndexWaveHdr > -1 && m_tagWaveHdr[m_nIndexWaveHdr].dwFlags != 0) {
|
|
|
|
 |
|
 |
ive got it on a Wave_Class_for_playing_and_recording_src. file but how do i open up the setup?
|
|
|
|
 |
|
 |
I need to record some audio off my soundcard -- your example seems promising.
One thing I do not see in your example is the ability to create a sound meter (one that "lights up" (from yellow to orange to red, etc.) as sound data is collected.
Is information available in the Wave Class you developed to accomplish this? If not, can you suggest a way to accomplish this?
celoftis
|
|
|
|
 |
|
 |
Hi,
The application is working fine in Pocket PC 2003. But it is not working in WinCE 5.0. The Problem is in Recording only. The problem is with device handle.
How to solve this problem?
Regards,
Lakshman.
|
|
|
|
 |
|
 |
how to encode the audio stream after record by GSM 6.10 codec . i want to pass stream audio into gsm_encode() function . My project have many erros . Who can help me to correct it. Download my code at : http://www.mediafire.com/?enemyzidznz . plz contact to me : kesitinhluoibieng@yahoo.com . After correct the project plz send it to me . thank you !
|
|
|
|
 |
|
 |
this implementation does not work the correct way.
1)
you are calling wave* functions out of the callback function this is not allowed.
see documentation of "waveOutProc"
2)
your "CWaveOut::Play" function for example is calling AddNewHeader for each of the buffers.
AddNewHeader is calling "waveOutWrite".
After the first call of "waveOutWrite" the callback function may be called (from system thread) at the same time. now your callback function is calling AddNewHeader at the same time. in worst case both threads are in the function "AddNewHeader". this must be synchronized. but because of 1) AddNewHeader should never be called out of the callback
function.
3)
some variables missing initialization
...
if using the callback function there should be some other thread feeding new output data.
|
|
|
|
 |
|
 |
Hi!
i have seen through whole code but can't figure out where the device selection take effect?
i have two sound devices (Logitech Mic(PTZ) and Sound Max
Your code is handling both but i dont know where the device is selected
Kindly help me out.
Thanks n regards
Jabeen
|
|
|
|
 |
|
 |
I’ve been working with this set of classes for some time now. The desired effect was to stream an audio devices output to another audio device’s input.
Basically I took your example extracted the necessary code for record and play into a single class. This process started with making a single class for the record data and with only two data element added to the buffer class (a tri-state value, and a timestamp of the tri-state’s last change). This has worked fine. I then added the play audio code to the record class.
The end result: It works functionally. However realistically it leaves a lot to be desired.
I ended up setting both the Header and the Buffer into list links in a FIFO type setting, calculating the dwEndPos each time a new buffer node was added to the list.
I was wondering if you might have some other solution to the method I was using, or suggestions on how to increase efficiency of this method.
The tri-state and timestamp help to make certain the play, record and list node clean-up do not create a race condition. I’m still working on this.
I have run the application for several hours and have noticed something. The dwEndPos and dwStartPos are not necessarily in numerical order; this caused some problems on the attempt to clean up the nodes. I was originally just watching the dwStartPos. Now it watches for both the dwStartPos and dwEndPos, before cleaning the corresponding nodes. The problem is that when there is a system-pause/slowdown the record seems to continue just fine, however the playback is effected.
This causes a significant problem when the code runs for several hours. The list links become rather large and the delay between the record and play sections increases. This is a most undesirable effect.
I was hopping you might have some suggestions on how to keep this from occurring or if there’s another solution I should be looking at all-together.
This is the first time I have done any programming dealing with multi-media, let alone media streaming.
Thank you
Matt
|
|
|
|
 |
|
 |
I need a win32 API to set the default recording device for windows. Do you know?
PLease help me? Tks!!!!
|
|
|
|
 |
|
 |
Great Article! Is it possible to be able to have like a COM or another type of a component, so I can use these functions in VB .Net? I really need to be able to do some very basic recording through my VB.net software. Any ideas? Anything?
-Parik.
http://parik.in
|
|
|
|
 |
|
 |
hi, i am doing a project on TAPI. for that i require to recored a wav file with the following requirements:
Bits per sample : 16 bits
Sampling rate : 8000KHz
Channels : 1(mono)
Format : PCM, (.wav)
how can i do that?
is there any software which can convert a simple wav file to the above required format?
|
|
|
|
 |
|
 |
Please help me how to overload + operator of CWave Class.
Please Help me as soon as possible.
or you can mail that piece of code to my id
ambrish50@hotmail.com
ambrish50@rediffmail.com
Thanks in advance
VC++,MFC,COM,ATL
-- modified at 7:06 Friday 21st April, 2006
Ya!!!, I overloaded it. But it have some memory leaks plz some one check it and give the suggestion to avoid these leaks.
Ambrish
|
|
|
|
 |
|
 |
I want to compare two audio streams, I can get the buffer but it appears to be difficult to interpret or all the same value.
whatsmyid
|
|
|
|
 |
|
 |
It can not play/record on win2000 22:43 9 Nov '05
I tested it on the WindowsXp, It can play/record well.
But when i test it on the Windows 2000, it can not play/record and it showed the bug message: "TestAudio.exe has generated errors and will be closed by Windows.You need to restart the program".
Have you tested it on Windows 2000, is it OK?
I want to make sure that is it OK on the windows 2000?
Please, reply me soon.Thanks!
Seven Pham
Seven Pham
|
|
|
|
 |
|
 |
Hi,
I read your article and code, very nice. I am currently working in one application in which i would like to record audio, replay and convert audio from one format to another format. could you give me some tips to do.. ? thanks in advance
with regards
Muthukumar
|
|
|
|
 |
|
 |
I've update my website where the last sources are available : it's on http://code.cmatoile.com. You'll be able to post comments and question about the framework and I'll be better advices of questions
pierre.canthelou @ cmatoile.com
webog // www.cmatoile.com
site // www.codesign.fr
|
|
|
|
 |
|
 |
Sorry, I can't login your homepage. http://code.cmatoile.com is webmail homepage, which means I have to have an account.
Could you help me to get in?
Thank you.
ps> your code is great. I tried to port it to Windows CE, but somehow it didn't work. It seems me that WaveInReset() isn't work in CE. Do you have any idea?
|
|
|
|
 |
|
 |
i am recording a .wav file directly from my sound card.so i am able to record the file.but i am not sure that it contain the data..inorder to test it,i want to play that file in mp3 player.but it is not supported by that.so for that i want wav header to be added to the start of the file.please specify the wave header format ( a class ).....
|
|
|
|
 |