|
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
|
|
|
|
|
Hi Ruslan,
Great article and good code examples. I ran your program on my computer and its working perfectly fine. However it would be great if we could avoid going to the dos prompt and then running the program i.e. when I want to run the application then I can directly launch the .exe file and it displays all the devices and lines present on the system. After that I could select any of the line and start recording. I was trying out this for myself but could not do it( I am very poor at coding ). Hope you could help me with this..Thanks.
|
|
|
|
|
Hi,
You suggest having a GUI for this application? Well, idea is all these come with open sources. If I implement a GUI, I can sell it for (e.g.) 10$ per unit. Since Lame is a light GPL, I can do that with no problems, except I must mention somewhere (visible in GUI) that I am using Lame (with the URL to their site). Or someone else can do that with my code , which will definitely be "not at least kind of effort" if I do GUI as well. I have seen lots of similar applications and people buying them.
So, this is the main reason I didn't do GUI. Plus, if you know a bit of C# or Visual Basic and C/C++, you can wrap my sources in a DLL and use C#/VB for the GUI stuff only, that's really half a day work maximum. Otherwise, in the ZIP with binaries I included a BAT file which you can use as a fast start option.
Regards,
Ruslan
|
|
|
|
|
Ya, you are right Ruslan. But how do I get rid of the dos prompt. I mean to say, I dont need a complete GUI but what I need is that once I launch the program from Debug folder, the application just reads the WaveInDevice and displays the list of all the available lines. I just select one line and start recording. I tried to change the code but its too complex for me to handle.
Read this scenario
I launch the application and it displays the WaveInDevice and the lines on this device.Now I select one line and start recording from that point of time on that line.
I appreicate your comments.
|
|
|
|
|
I see what you mean. This reminds me of the university ages ... "Please specify the array size:" ... "Please enter 1st element:" ... etc . Well, to be honest, I haven't thought about this. Idea of the article was to have as set of usable classes, a simple application as a proof of concept and, next, to move to the internet streaming ideas. It isn't difficult at all to implement your scenario, but very annoying actually . That's right, very annoying because:
1. Application must handle user input - if something is wrong, then ask user to re-enter again.
2. Some combinations of bitrate and simple rate may not be accepted by Lame engine. So, ask user to re-specify again.
3. Combine user input and command line parameters. What is not set in command line – ask user to specify. Or leave default, but default options (e.g. Volume level = 0) may not work perfectly for all the PC configurations (as I wrote in the article).
Well, the code must be re-thought as well, to make it look nice. So, definitely it's easier
- to have a GUI or
- take Linux/Unix approach where command line tool has a bunch of command line options and works "from the top to the bottom, if something is wrong – report and quit". Easier to code, less errors and if user finds the best combination of command line options – they create a shell script which will make life easier (approach I usually take ).
I don't say no, but I promise to think about
Regards,
Ruslan
|
|
|
|
|
I got around the command prompt. Now I am able to disply the Device name and the lines. nothing much to do in it. Great work Ruslan. 3 cheers to you.
|
|
|
|
|
You are welcome
Regards,
Ruslan
|
|
|
|
|
Hai rtybase
I found ur article intresting but if u feel free to explain how this process Recording the sound from the soundcard little bit more and if you can provide some helpfull link i would be thankfull.
jobythomas24@gmail.com
|
|
|
|
|
Hi,
Well, one of the ideas of this article is to present some classes that help with recording from the sound card. You can use them directly, no problems. However, if you want to dig deeper inside this topic, I posted sources as well and I did my best to comment them. But, as a good start to understand the entire "kitchen behind" I would recommend these links:
http://www.borg.com/~jglatt/tech/lowaud.htm
http://www.borg.com/~jglatt/tech/mixer.htm
Once you understand the concepts, check the sources inside "waveIN_simple.h" file. Start with the constructor "CWaveINSimple::CWaveINSimple(...)", check "CWaveINSimple::_Start(...)" method next and, at the end, check "CWaveINSimple::waveInProc(...)" method. This is the core and code for those methods is not complex.
Regards,
Ruslan.
|
|
|
|
|
hai Ruslan Ciurca
Fine article i included ur class in my app and i was able to get stereo mix in dialog based app i was looking for it for a long time thanks once more
jobythomas24@gmail.com
|
|
|
|
|
That's a really good little program but I'm wondering if you would give me a pointer in the right direction please?
I program in VB.NET and am a bit poor on C++/C#. I understand most of what you did in the code but you have put on the bottom that you could modify the code:
[quote]
If you wish, you can adjust the code, so application will support two more extra command line options, e.g. "-bitrate" to indicate at what bitrate to perform MP3 encoding (my demo is encoding at 128Kbps) and "-volume" to manage Line's volume from the command line (my application is setting it to "50%", but ... see "Remark 1" above)[/quote]
I have REALLY tried (for a fair few days now!) to be able to implement these but have been unsuccesful and I would appreciate just a pointer if possible.
I was also wondering if the sample rate can be changed as well from 44.1kHz
Thanks very much in advance
Rob
|
|
|
|
|
Ok, regarding volume. In the "mp3_stream.cpp" you can find the lines:
mixerline.UnMute();
mixerline.SetVolume(50);
mixerline.Select();
mixer.Close();
"mixerline.SetVolume(50);" is the one which sets the volume to 50%. So, this is easy.
Regarding bitrate, in the same file, see:
class mp3Writer: public IReceiver {
private:
CMP3Simple m_mp3Enc;
FILE *f;
public:
mp3Writer(): m_mp3Enc(128) {
f = fopen("music.mp3", "wb");
if (f == NULL) throw "Can't create MP3 file.";
};
...
You can adjust the contructor of the "mp3Writer" class as follows:
mp3Writer(unsigned int bitrate = 128): m_mp3Enc(bitrate) {
f = fopen("music.mp3", "wb");
if (f == NULL) throw "Can't create MP3 file.";
};
so, you can build instances of "mp3Writer" by indicating required bitrate as well (128 still default).
Regarding sample rate, see "INCLUDE\mp3_simple.h". Costructor of the class "CMP3Simple":
CMP3Simple(unsigned int nBitRate, unsigned int nInputSampleRate = 44100,
unsigned int nOutSampleRate = 0);
Also there you will find a description for each parameter, but just a short remark, "nInputSampleRate" is the sample rate of the PCM to be encoded (!!!) where "nOutSampleRate" is the final rate (== 0 says to not re-sample). If you need to re-sample, better to use that parameter. So, you can change constructor of the class "mp3Writer" as (final version) follows:
mp3Writer(unsigned int bitrate = 128, unsigned int finalSimpleRate = 0):
m_mp3Enc(bitrate, 44100, finalSimpleRate) {
f = fopen("music.mp3", "wb");
if (f == NULL) throw "Can't create MP3 file.";
};
Coming back to the "mp3_stream.cpp", now you can change the code like:
mp3Wr = new mp3Writer(64, 32000);
and you will get the output at 64Kbps and 32KHz.
I hope this will help. If not, I will set a new version of the sources and binaries with this article, but, not earlier than after next week (in a business trip now).
|
|
|
|
|
Thanks very much for the quick reply. I will look at this over the next couple of days and let you know how I get on. A brief look at it looks like it will answer my questions.
Cheers
Rob
-- modified at 11:01 Wednesday 8th November, 2006
|
|
|
|
|
I updated the article, source code and demo project links. It supports additional 3 parameters now. Feel free to use it
Ruslan
|
|
|
|
|
Hi Ruslan,
thanks very much for the updated file. I had actually battled through and written my own code to provide these functions but realise after looking at yours that it was a little messy - probably because I'm not used to C!! One thing that has occurred in both the code I wrote and the new code that you have written is that changing the sample rate doesn't affect the size of the recording i.e. a recording in 44100Hz produces the same file size as one recorded in 8000hz. I have been trying to sort this out but have been unable to so far. Is it because of this?
[code]
virtual void ReceiveBuffer(LPSTR lpData, DWORD dwBytesRecorded) {
BYTE mp3Out[44100 * 4];
DWORD dwOut;
m_mp3Enc.Encode((PSHORT) lpData, dwBytesRecorded/2, mp3Out, &dwOut);
fwrite(mp3Out, dwOut, 1, f);
};
[/code]
any help with this much appreciated.
Thanks again
Rob
|
|
|
|
|
Hello Rob,
Everything is fine with that code. It just sends the buffer with PCM (lpData) data, indicating the size (dwBytesRecorded), to the Lame encoder. Since PCM buffer is in BYTEs and Lame API requires SHORTs, code just casts BYTEs to SHORTs (pointers are meant of course) and set the size to dwBytesRecorded/2 (2* BYTE = SHORT). Recording is in stereo mode, so dwBytesRecorded is divisible by 2.
Lame encoder copies encoded data to the mp3Out buffer and sets the buffer size to the dwOut. Exactly dwOut bytes from the mp3Out buffer are written to the MP3 output file due to
fwrite(mp3Out, dwOut, 1, f);
Back to your question, there is a big difference between bitrate and sample rate so far. To predict the output file size having the bitrate (br) and playing time (t, sec) use the formula:
file_size = (br * t) / 8;
For more details see:
- few details regarding above formula on http://www.mp3-converter.com/bitrates.htm
- same formula on http://www.codeproject.com/audio/mpegaudioinfo.asp
- http://webhome.idirect.com/~nuzhathl/mp3-faq.html#ques17
- http://webhome.idirect.com/~nuzhathl/mp3-faq.html#ques97
- http://groups.google.co.uk/group/alt.music.mp3/browse_thread/thread/c89578e645572864/cb2ce06e13a32b14%23cb2ce06e13a32b14
So far, there is nothing about sample rate in that formula. All goes to the internal encoding algorithm of the Lame, where simple rate only affect the quality, not the size.
Another remark is; if to use Lame pre-settings, you will see a dependency picture like:
- if setting 128kbps => Lame will set sample rate to 44.1Khz
- if setting 96kbps => Lame will set sample rate to 32Khz
- if setting 64kbps => Lame will set sample rate to 24Khz
- if setting 32kbps => Lame will set sample rate to 16Khz
...
All this is done for the best MP3 quality. Very amazing, but with (e.g.) 96kbps/32Khz you may (but not as a rule) get a better MP3 sound quality than with 96kbps/44.1Khz due to different compressions (as the final MP3 file size will be the same). See the logic below:
1) 96Kbps/32Khz => 96Kbps = 12Kbytes/sec, which decoded should give 32Khz, 16 bits, stereo PCM sound (32000 * 16/8 * 2 = 128000 Bytes/sec = 125 Kbyte/sec). At the end we have 12Kbytes MP3 decompressed to 125Kbytes PCM, all per second.
2) 96Kbps/44.1Khz => 96Kbps = 12Kbytes/sec, which decoded should give 44.1Khz, 16 bits, stereo PCM sound (44100 * 16/8 * 2 = 176400 Bytes/sec ~ 172 Kbyte/sec). At the end we have 12Kbytes MP3 decompressed to 172Kbytes PCM, all per second.
96Kbps/32Khz: 12Kbytes MP3 <-> 125Kbytes PCM
96Kbps/44.1Khz: 12Kbytes MP3 <-> 172Kbytes PCM
So, 96Kbps/44.1Khz uses a "harder" compression than 96Kbps/32Khz.
Obvious question would be why not 96Kbps/24Khz? And the answer would be; 24Khz is a lower quality than 32Khz. So 96Kbps/32Khz seems to be the optimal compression-quality value for the 96Kbps bitrate.
Thanks for this question; I have discovered few interesting things for myself too
Regards,
Ruslan.
|
|
|
|
|
thanks again for your quick reply and really appreciate your help. I'm going to check out the links you supplied in your reply now.
Keep up the good work
Rob
|
|
|
|
|
nice work... works great...
how can i use your functions to play a mp3-file?
|
|
|
|
|
Well, you can't, since they do just recording (from Wave In) and encoding (as title of the article states). If you need to implement playing from your application, I recommend you to check http://www.arbingersys.com/madxlib.html. You can also download "Aumplib" from their site (http://www.arbingersys.com/concerns.html), it's a nice suite of classes and an application for converting from/to different audio formats (in C# however). Idea is, with "madxlib" you can decode MP3 and obtain raw PCM sound which you can play through Wave Out device. Problem is, their decoder may crash if "MP3 file" is not a valid MP3 file. "Aumplib" has a nice class "MP3Check.cs" which you can use as a "how to" to validate MP3 files. Other way would be to play via DirectSound. I haven't implemented decoding because I use these (from the article) classes for MP3 streaming only, where as a client application can be considered WinAmp or Windows Media Player.
-- modified at 6:48 Monday 18th September, 2006
|
|
|
|
|