|
Hi,
Just to let you know: your sample project works perfectly fine when compiled with Visual Studio 2005, and no change is required (other than going through the VS upgrade wizard).
|
|
|
|
|
Nice ... thank you for this update.
Regards,
Ruslan
|
|
|
|
|
I feel this article very good. I want to use lame_enc.dll file in my project. Can you show me how to use it. Thank you very much.
Note : I want that when music is playing then program begins work and when music have just stoped then program stops work.
Thank you very much once more
-- modified at 2:39 Wednesday 28th February, 2007
|
|
|
|
|
Hi,
Well, regarding showing how to use. I think best you download sources and have a look at the "mp3_simple.h" file. Anyway I will end up explaining that file. Or, even better use those sources in your project.
Regarding what you want to implement ... biggest problem is detecting when music is stopped. This is a silence detection algorithm and if you google, you can find code or good advices how to do, I don't have one to be honest. But, keep in mind, access to the soundcard's recording device (Wave IN) is exclusive. So, if there is another application recording, your program will not be able to record. Those are two technical issues you may encounter.
Regards,
Ruslan
|
|
|
|
|
Hi Ruslan,
I am working on a new project in which i need to,
Record from both Microphone and waveout simultaniously and either save it into a file or send through socket ( something like inserting an audio comment on what u r listening ).
How can i record from MIC and WAVEOUT simultaniously ?
Can you plz give me some advice regarding this?
Mujeeb.
|
|
|
|
|
Hi Mujeeb,
Well, almost impossible, but depends on sound card. So far, I haven't seen cards with options to record from multiple sources. You can test it by opening Volume Control -> Options -> Properties -> Recording -> OK.
However, there is a solution and it is related to the microphone only. It is so called microphone routing and again depends on sound card. Check
Volume Control -> Options -> Properties -> Playback. If you see microphone there then you can route the microphone. This means that recorded sound from the microphone will be routed to the WAVEOUT by the sound card's driver. This also means that sound card will mix music going to WAVEOUT (from e.g. WinAmp) with the sound from the microphone. The quality depends on the soundcard but in many cases it is fine.
Other solution would be to write a sound card driver, but this isn't the easiest one.
Regards,
Ruslan
|
|
|
|
|
Thanks Ruslan,
you give me the right advice
i think the second option is possible bcz, now all sound cards have stereo Mix in Recording source and Microphone playback, I tried it in my system
1. Enable Mic in Playback
2. Set Recording source as stereo mix
3. Start playback using winamp
4. Record stereo mix using sound recorder
and i got the result i want.
Now i need some help on implementing this in my VB.NET project. Am i use DirectX for the same or is there is any other technology exist for doing it?
if you can give me some samples, i will be thankful.
Regards,
Mujeeb
|
|
|
|
|
Unfortunately, I am helpless regarding VB.NET. I am using C++ and Java most of the time ... some C#, but most for Windows Smart Phones. I suggest you find some starting point applications in C#, so you'll translate them to VB. Otherwise (in case you don't find anything good) you can use [DllImport] (or equivalent in VB.NET) for importing relevant functions from the relevant DLL's. Sorry about that.
Regards,
Ruslan
|
|
|
|
|
waveform audio interface you can hear in your .net apps[^][^]
Testing your code against performance will keep you running with good scalability and maintenance for years.
NTime.exe - the free tool for real developers of high scalability applications!
Testing your code against performance will keep you running with good scalability and maintenance for years.
NTime.exe - the free tool for real developers of high scalability applications!
|
|
|
|
|
Hi, is it possible to write IDv2 tags through mp3_simple or wavein_simple?
Thanks in advance.
Rafael
|
|
|
|
|
Nope, I haven't designed those classes to support writing IDv1/2 tags. I need to think on re-adapting them, as, much probably, I will need tagging too.
Regards,
Ruslan
|
|
|
|
|
Hi!
I was wondering if there is much to to get the project running on a pocket pc
|
|
|
|
|
Honestly, I don't know, I haven't investigated (yet) this option. Apart ... maybe ... if to port LAME to Pocket PC. I am not sure this will work well, LAME API uses processor pack instructions (MMX, SSE1/2) for a good reason and CPU power of the PPC's are yet too weak.
Regards,
Ruslan
|
|
|
|
|
Hi,
I've been looking for LAME in the last weeks but my c++ is not good enough to appreciate the source (i'm more on the c# side), the incipit of your article is very near to what i need, i.e. listen to a microphone on computer 1 and get the live voice on computer 2.
Thanks to your sample (much easier to read) now i can easily intercept microphone or linein audio and stock it in a file, but what if i must listen to it live for many minutes from another pc, while not creating a gicantic mp3 ?
Can you give me some directions and/or anticipations on what aspects i must study to reach this ? I'm... quite pressed
Thanks in advance, and good year!
Fabrizio
|
|
|
|
|
Hello Fabrizio,
First big change in your application would be not to save recorded sound in a file, but in memory buffers. Let say, you need to create some buffers to hold last X (e.g. 5) seconds of the recorded sound. For example, I created a class with X buffers where each buffer holds 1 second of the recorded sound. Each buffer also contains timestamp, so I always know which one is the most recent and which one is oldest. If new sound data is coming and there are no free buffers, the oldest one is overridden (call it cycle buffering). If you are encoding at a specific bitrate, you will know of what size should be the buffer to hold 1 second of sound (e.g. 128Kbps * 1 sec = 128 Kilo bits = 128000 bits => (divide by 8) => 16000 bytes).
Second. This depends on how you want to implement your server application. If you have you own client application, then it is enough to be well familiar with sockets API. If you want WinAmp and Windows Media Player to be able to play that sound, you will need some basic knowledge of (e.g.) HTTP protocol with few additional IceCast commands (e.g. google for "icy-name:", "icy-pub:" and "icy-br:"). Most important here is, you should implement a tracking mechanism, so you will always know what was the last sound buffer you sent to a particular socket (see buffering idea above).
Third. You need to be familiar with at least one of the patterns for developing server applications. Easiest one is "one thread serves one client connection" which may work fine for ~100-1000 concurrent connection (depends on hardware as well as how you implement your application). I recommend you to start with this one. However, later you can switch to asynchronous sockets ("one thread serves few (10, 20 etc.) connections" or "event based sockets"). For really scalable server application, you can also try IO Completion Ports, but this will be more complex. By the way, I have posted the second article of this series at http://www.codeproject.com/useritems/LikeJavaThreads.asp. There you will find sources of the thread pool class I am using in my streaming server application. However, I am sure .NET may provide better thread pool classes.
Regards,
Ruslan
|
|
|
|
|
Hello Ruslan,
thanks for your quick and exaustive response, i saw you wrote another article but by the title (Java threads) i guessed it was about something different and didn't check. doh.
First: Even if i never played with them in c++m cycle buffering is a kind of procedure i know and i hope i won't create me big troubles. I'm more lost on how to save 1 sec music in a buffer element.
Second: here i'm a bit more lost, what i need is my own client application and through tcp i know how to chat between apps. what i don't know is how to send the 1 sec buffer element to the speakers and how to be sure to have sound without silence gaps or start singing before the previous has ended.
Third: I'm quite confident with async threading (having built more than a service with tcp client/server), still i have few experience in c++ and your 2nd article will be surely useful.
Again, thanks for the time you can spend on the argument, you've already been very helpful.
Fabrizio
|
|
|
|
|
Ok, let's see:
1. If you are implementing based on the code from this article, mind
CWaveINSimple::CWaveINSimple(...) {
...
this->m_waveFormat.nAvgBytesPerSec = this->m_waveFormat.nSamplesPerSec * this->m_waveFormat.nBlockAlign;
...
this->m_WaveHeader[1].dwBufferLength = this->m_WaveHeader[0].dwBufferLength = this->m_waveFormat.nAvgBytesPerSec << 1;
}
This means that every returned buffer with PCM from sound card will contain max 2 seconds of sound (m_waveFormat.nAvgBytesPerSec << 1 == m_waveFormat.nAvgBytesPerSec * 2). Every second of PCM encoded in MP3 will be exactly 1 second of MP3 sound. So, cycle buffering class must hold at least 2 seconds of MP3, otherwise there is a risk (99.9%) to override sound that wasn't yet sent (so 5 seconds should be fine). You are right about saving 1 second of data in a buffer, you need to analyse incoming buffer size and split it in 1 second buffers and pass each of them to the cycling buffer class. Not a big problem, but you should be very careful with C/C++ pointers here. Best (easy) way to avoid this is to use pipes (see MSDN for CreatePipe() function). So re-adapted IReceiver would look like:
enum IO_MODES {READ, WRITE};
class mp3Writer: public IReceiver {
private:
CMP3Simple m_mp3Enc;
HANDLE pipes[2];
public:
mp3Writer(unsigned int bitrate = 128, unsigned int finalSimpleRate = 0):
m_mp3Enc(bitrate, 44100, finalSimpleRate) {
if (!(CreatePipe(&pipes[READ], &pipes[WRITE], NULL, 44100 * 4))) {
throw "wavein_listener, no resources.";
}
};
~mp3Writer() {
CloseHandle(pipes[WRITE]);
CloseHandle(pipes[READ]);
};
HANDLE getReadPipeEnd() { return pipes[READ]; };
virtual void ReceiveBuffer(LPSTR lpData, DWORD dwBytesRecorded) {
BYTE mp3Out[44100 * 4];
DWORD dwOut;
DWORD dwRet;
m_mp3Enc.Encode((PSHORT) lpData, dwBytesRecorded/2, mp3Out, &dwOut);
WriteFile(pipes[WRITE], mp3Out, dwOut, &dwRet, NULL)
};
};
This time mp3Writer will push encoded sound to a pipe. Now you need another thread that will actually read sound from that pipe, something like:
// OneSecondSize must be equal to 1 second of MP3, it depends on bitrate!!!
BYTE MP3Buffer[OneSecondSize];
ReadPipe = mp3WriterInstance->getReadPipeEnd();
while (1) {
memset(MP3Buffer, 0, sizeof(MP3Buffer));
if (::ReadFile(ReadPipe, MP3Buffer, sizeof(PCMBuffer), &dwRead, NULL))
{
// normally it must be so dwRead == sizeof(PCMBuffer), reading from/writing
// to pipe is blocking operation. dwRead < sizeof(PCMBuffer) only when reading
// last bytes from pipe, but not a big problem anyway.
// Now MP3Buffer contains 1 second of MP3. Cache it in a cycle buffer class!!!
}
else {
// most probably pipe was closed
break;
}
}
All the above are not checked/compiled code, if you find errors – sorry for that. But these pseudo codes should highlight the idea.
Next answers i will post separately, this one is too big already.
Regards,
Ruslan
|
|
|
|
|
>> Second: here i'm a bit more lost, what i need is my own client application and through tcp i know how to chat between apps. what i don't know is how to send the 1 sec buffer element to the speakers and how to be sure to have sound without silence gaps or start singing before the previous has ended.
At the client side you need to know at what bitrate the sound was compressed. Otherwise you will not be able to guess the 1 second buffer size. But that shouldn't be a problem anyway. Problem is decoding, to be more precise the way sound will be decoded. With MadXLib (see other messages I posted here, you will find the link to that lib) you can do it like other players, allocate a buffer and cache incoming data there (pre-loading). Send a part of that buffer to the MadXLib decoding function and it will tell you is it enough data or not to decode a complete chunk. If not enough, get another part from the buffer or wait for data to come from server. Once you obtain PCM sound from decoder, send it to the soundcard (check Wave Out functions). With other decoding libraries - you should check the API. Regarding gaps, I would say you must consider them (even theoretically) because they may happen (network problems, other download/upload operations increasing/reducing the traffic. etc).
>>Third: I'm quite confident with async threading (having built more than a service with tcp client/server), still i have few experience in c++ and your 2nd article will be surely useful.
Then it should be easier for you. However, I would try to find some C# classes wrapping LAME API and would implement all in C#. By the way, regarding MadXLib API, guys created MadXLib have few nice classes and implementations in C# (you will find on their site).
Regards,
Ruslan
|
|
|
|
|
Just two more lines to thank you again.
From here i'll try to not abuse of your help and walk on my legs; if i get lost i'll scream for help
Regards,
Fabrizio
|
|
|
|
|
Ruslan,
Forgive me for asking simple questions. But I am new this this MP3 stuff, and what I want to basically get started with is to record my WINAMP radio music. I used the MP3_STREAM with different line types and it seems to record but with play back it has no sound.
What I am missing here?
I tried:
mp3_stream "-device=Realtek AC97 Audio" "-line=Stereo Mix"
mp3_stream "-device=Realtek AC97 Audio" "-line=Line In"
mp3_stream "-device=Realtek AC97 Audio" "-line=CD Player"
Each gives me a MP3 file but it has no sound when I play it back via WINAMP or with my new MP3 player I just got for christmas.
I'm going to read your references and begin more R&D like you did, but I would like to know why I couldn't get it to work just with the demo.
Thanks
---
HLS
|
|
|
|
|
Hi Hector,
Have a look at the "Remark 1". Application sets sound volume to 0 (zero) by default. I recommend you doing something like:
mp3_stream "-device=Realtek AC97 Audio" "-line=Stereo Mix" -v=50
If you (should ) hear sound, adjust "-v=50" to a value more appropriate, e.g. "-v=10" or "-v=15" for a better sound.
Regards,
Ruslan
|
|
|
|
|
Hello,
The code looks promising. However, my compiler can't find the QMutex class (Using MS Visual Studio 2005 Express). Can you give me any hint on where is it included?
Thanks,
Peter
|
|
|
|
|
Hello Peter,
Check sources for the "sync_simple.h" file in the INCLUDE folder. I am sorry, that's my fault. I forgot to add:
#include "INCLUDE/sync_simple.h"
to the "waveIN_simple.h".
Regards,
Ruslan
|
|
|
|
|
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 !
|
|
|
|
|
Wow, huge code . Why are you using GSM encoding? Could it be that you are implementing a voice communication (PC to mobile phone, mobile phone to mobile phone, mobile phone to PC) application? You can use Java (J2SE, J2ME and JMF). JMF contains GSM encoder/decoder and it works fine.
Anyway, that's a bit too far of the topic. However, it is clear that you are using an existing code without understanding it. So far, I can't understand why you are calling GSM encoding from
CRecordSound::OnSoundBlock(...)
where you encode the PCM into GSM and after that send a message to another thread via
m_Writer->PostThreadMessage(WM_WRITESOUNDFILE_WRITEBLOCK,GetCurrentThreadId(),(LPARAM) pWriteHdr);
which at the end is caught due to
ON_THREAD_MESSAGE(WM_WRITESOUNDFILE_WRITEBLOCK, WriteToSoundFile)
by
(!!!) CWriteSoundFile::WriteToSoundFile(...)
and there you are doing GSM encoding again?
By the way, that method should look like
LRESULT CWriteSoundFile::WriteToSoundFile(WPARAM wParam, LPARAM lParam)
{
LPWAVEHDR lpHdr = (LPWAVEHDR) lParam;
int cbLength = lpHdr->dwBufferLength;
if(lpHdr)
{
short *soundbuffer = (short*) lpHdr->lpData;
if(m_hFile && soundbuffer) {
//*********************************************
gsm_state *gsmencode = gsm_create();
gsm_signal src[160];
gsm_frame dst;
gsm_encode(gsmencode, soundbuffer, dst);
//*********************************************
::mmioWrite(m_hFile, (const char *) &dst, sizeof(dst));
if(soundbuffer) delete (BYTE*) soundbuffer;
if(lpHdr) delete lpHdr;
}
}
return ERROR_SUCCESS;
}
And I am sure you can do all these just having one thread!
That's it ... no more helps
Regards,
Ruslan Ciurca
|
|
|
|
|