WINAMP input module writing detailed

zhaozj2021-02-08  429

Write a friend who is written to the C / C basic class: I haven't taken seriously in the version for a long time, I talk about my netizens. I occasionally turned to turn it, only the version. Some of these days came to you, I sent me a short message, asked "Hey, buddy (most of the time is the word 'boss', but I don't like this name, I feel a bit like a black society? ), How can you not see you, what is your favor? "And I replied in extremely perfunctory:" I am busy with my own live, I am sorry. "I also felt very guilty, but whenever I think of now Doing the work, you can realize those who have contributed to the C / C version, I also feel that my heart is getting peaceful.

I still want to say some nonsense between the official start. If you are old, drag the rolling bar 30-50 pixels on the right. Winamp is a well-known world's best and most popular music (media) playing software, which is supported by other similar software. This extensive support is also because it uses a very reasonable and smart plug-in design. Each person can play the audio format invented by WINAMP, as long as you write the corresponding input plugin. However, this article will not teach you high-difficult mathematics algorithms such as wavelet transform to write more excellent audio coding than MP3 or APE, if you try to find these things in the article, I still advise you to turn off, turn off The browser, then go to the library to look at "Signal". In this article, I will teach you how to write an output plugin that meets the WinAmp plug-in specification, you will be asked, isn't I just enter a plug-in? In fact, these two plugins are integrated, you will get answers down. While exercising, I will help you understand the basic principle of electroacoustic, understand Winamp's design structure, familiar with WAVE (PCM) file format, basic working mechanism of the sound card, etc. Finally, we will use DirectX to complete the output module of this audio. This is a very exciting topic, but please don't rush to boil, and the cold water below is certain. After continuing to see, please make sure you have these conditions or capabilities: You can open the VC.NET to write the test program, we have DirectX8.1sdk, you can skilled the development of a complex SDK program, familiar with C ... well I want to want to want to ask for so much, I am trying to use a large number of readers or try to use a warranty. One thing to say is, I hope that you can read the full text in a pleasant state, not like reading "quantum mechanics" in the toilet, make sure we can start?

In order to start this question, I hurt my brain. I want to go after all, this part is published online, then start from your mobile mouse, now open a browser, go to the following URL: http: // Www.winamp.com/nsdn/winamp2x/dev/plugins/out.jhtml This is the home page developed by WinAmp plug-in. He is named NSDN, but it is nothing more than MSDN. What you need to do now is to download that out_minisdk, link in: http://ftpwa.newaol.com/customize/component/nsdn/winamp2x/out_minisdk.zip, you open this compressed package you will see three files in2.h , Out.h and out_raw.c, what, have you seen other files? Oh, there is still some other files, but we only need these three now. Unzip them, then open them, because you need to look at the code implemented when listening to me that WINAMP is designed. When I first saw this plug-in package structure, I was shocked. It turned out that WinAmp was more lazy than I thought, it did not do anything, just an interface, called some input interface, only this . Let's take a look at the structure of IN_MODULE and OUT_MODULE:

TYPEDEF STRUCT {Int NVER; // Module version number char * szdesc; // module description information hwnd hmainwnd; // Winamp's main window handle (filled in WinAmp) Hinstance HDLLINSTANCE; // DLL instance handle (fill in WinAmp) CHAR * SZFILEXT; / / Extension filter, format See getopenFileName Int nISserteekable; // If you can index the media, you can drag the progress bar, no - Int users INT UsesoutputPlug; / / Do you use the output plugin? Do you want to get everything in this module?

// The following is a function pointer, will be called Void (* config) (hwnd hwndparent) (hwnd hwndparent) (* about) () () () (); // about dialog void (* init) (); // Initialize VOID (* quit) (); // Exit // SZFile - Introduced file name, SZTITLE - The title, NLEN - the length of time, milliseconds. // If the SZFILE passes NULL, return the information void (* getfile, char * sztitle, int * szfile) (CHAR * SZFILE, HWND HWNDPARENT); // Pop-up in Int (* infobox); File Information Dialog INT (* isourfile); // Check file format int (* szfile); // Start play file SZFILE, return 0 normal, -1 error Void (* Pause) (); // Pause the processed void (* unpause) (); // cancel the int (* ispaused) (); // Is it stopped? 1 is pause, 0 is not void (* stop) (); // Stop playing int (* getLength) (); // get the length, millisecond unit int (* getOutputtime) (); // Get the current time, generally call OUT The same name function of the module can void (* setoutputtime) (Int ntime) (Int Volume) (INT VOLUME) (INT VOLUME) (INT PAN) (INT PAN) (INT PAN) (INT PAN) (INT PAN) (INT PAN) (INT PAN) (INT PAN) ); // Left and right channel balance, from -127 - 127 // below the function of the AVS, visualization effect, equalizer, etc., I don't talk about it, I don't talk about it. Void (* savsainit) (int size); void (* savsadeinit) (); // call in stop () void (* saaddpcmdata) (Void * pcmdata, int nch, int bps, int timestamp); int (Int); int * SageTMode) (); void (* saadd) (void * data, int timestamp, int CSA); void (* vsaaddpcmdata) (vid * pcmdata, int nch, int bps, int timestamp); int (* vsagetmode) (int * SPECNCH, INT * WAVENCH; VOID (* VSAADD) (Void * Data, INT TimeStamp); Void (* vsasetInfo); int (* dsp_isactive) (); int (* dsp_dosamples) (SHORT INT * SAMPLES, INT NUMSAMPLES, INT BPS, INT NCH, INT SRATE); Void (* EQSET) (int on, char data [10], int preamp); void (* setInfo) (int Bitrate, int SRate, int STEREO , int synched; out_module * outmod; // Take a look, WinAMP finally reveals the horse's foot? } In_module;

Typedef struct {// Some of the following is in the same way, it will not be described. Int NVER; char * szdesc; int NID; // I give an ID, I don't know what to use, anyway, greater than 65536. Hinstance HDLINSTANCE; VOID (* config) (hwnd hwndparent); void (* @); void (* quit) (); // nsample - sampling rate, nchannels - Number of channels, 1 or 2 // nbitpersamp - the bit rate per sampling, nbuflen, nprebufflen - buffer length, we can not use // return larger than 0 normal play, less than 0 failed int (* open) (int nsample, int NChannels, int NbitPersamp, INT NBUFLEN, INT NPREBUFLEN; VOID (* Close) (); // Turns the output device // PBUF - memory data block, NLEN - Data block length int (* write) (char * PBUF, INT) LEN); // Returns 0 success, other unsuccessful int (* canwrite) (); // Indicates if the current state can be written int (* isplaying) (); // Indicate whether it is playing int (* Pause) (int PAUSE) ); // Pause, detail void (* setVolume); void (* flush) (INT T) (INT T); // Refresh buffer INT (* getOutputTime) ); // Get the output time int (* getWrittentime) (); // Return the time} OUT_MODULE; you should also see this code in Out_Raw.c: __ declspec (dllexport) OUT_MODULE * WINAMPGETMODULE () {Return & Out;}

I believe that people who have some experiences have seen this is really like a big white, but considering the "compatibility" of the article, I still have to be detailed. Take the output module, Winamp will go to the Plugins directory when starting, you will find all available output plugins. If you find the available output module, you will load this DLL first. When this DLL is loaded, fill in an OUT_MODULE global object when the writer is defined, and the function pointer member points to the function code that has been implemented. WINAMP calls these functions are the pointers of the OUT_MODULE global object through this DLL-exported WinampGetoutModule () function, then it can call those functions through this global object's member function pointer. Is it very clever? The feedback mechanism of the plug-in is done by the WINDOWS standard message mechanism. PostMessage is the easiest way, which is why every plug-in will have a HWND HMAINDOW main form handle. In fact, WINAMP is only the interface of the in_module when playing music, because IN_MODULE includes an OUT_MODULE pointer, so IN_MODULE automatically calls OUT_MODULE's corresponding function, complete all things for WINAMP, OUT_MODULE is generally only used.

After understanding WINAMP's working principle, then we will start writing the first WINAMP output module. In order to facilitate debugging, and avoid accidental damage to your WinAmp, we will write a Winamp analog program, which will call Winamp's plugin. Let's do it as follows, create a new MFC application in VC.NET, remember the selection dialog. Add two files MusicPlayer.h and MusicPlayer.cpp. Then add the two big structures OUT_MODULE and IN_Module's statement to the header, then write some functions we need to call: void outinit (); void outconfig (hwnd hwnd); void Outabout; int OutOpen (int nSampleRate, int nChannels, int nBitPerSample, int nBufLen, int nPreBufLen); void OutClose (void); void OutQuit (void); int OutWrite (char * pBuf, int nLen); int OutCanWrite (void); int OutIsPlaying (void ); int outpause; void outflush (int NT); int outgetOutputtime (void); void outsetVolume (int NVolume); void outsetpan;

We also need to declare the following in the header file:

// Used to get the function pointer TypedEf in_module * (Void) (Void) (IN_MODULE * PINMODULE, OUT_MODULE * POUTMODULE) (IN_MODULE * POUTMODULE);

Now return to MusicPlayer.cpp, implement those functions of the above declaration, first write the braces, return 0 to 0, let the entire project can be compiled smoothly, then we will come back to fill in these functions. Code. Prior to this, we first had a function pointer that was not filled in some input modules. If we don't fill, the input module will call the empty address, and the result can be known. The following is the statement and implementation of these functions, directly put it on the top of the MusicPlayer.cpp file, will not occupy too many space? :)

void SAVSAInit (int maxlatency_in_ms, int srate) {} void SAVSADeInit () {} void SAAddPCMData (void * PCMData, int nch, int bps, int timestamp) {} int SAGetMode () {return 0;} void SAAdd (void * data , int timestamp, int csa) {} void VSAAddPCMData (void * PCMData, int nch, int bps, int timestamp) {} int VSAGetMode (int * specNch, int * waveNch) {return 0;} void VSAAdd (void * data, int timestamp) {} void VSASetInfo (int nch, int srate) {} int dsp_isactive () {return 0;} int dsp_dosamples (short int * samples, int numsamples, int bps, int nch, int srate) {return 0;} Void EQSET (int on, char data [10], int preamp) {} void setInfo (int synched) {} ​​above these functions are used for visual effects, and we certainly don't need it. So use the simplest code to implement it! Let's implement the initModules function below:

Void Initmodules (IN_MODULE * PINMODULE) {// If you use the MFC, you can get the main window pointer // If not, then you need to find another way, such as global variable g_hwnd = :: afxgetmainwnd () -> getSafehWnd (); // The output module structure is given to the structure, so that it will call to the corresponding function at the corresponding function at the time of its call to PoutModule-> description = "create"; " pOutModule-> About = OutAbout; pOutModule-> CanWrite = OutCanWrite; pOutModule-> Open = OutOpen; pOutModule-> Close = OutClose; pOutModule-> Config = OutConfig; pOutModule-> Flush = OutFlush; pOutModule-> GetOutputTime = OutGetOutputTime; pOutModule -> GetWrittenTime = OutGetWrittenTime; pOutModule-> hDllInstance = :: AfxGetInstanceHandle (); pOutModule-> hMainWindow = g_hWnd; pOutModule-> id = 32; pOutModule-> Init = OutInit; pOutModule-> IsPlaying = OutIsPlaying; pOutModule-> Pause = Outpause; poutmoduit; poutModule-> setModule-> setmodule = outsetVolume; poutModule-> version = 100; poutmodule-> write = outmodule; poutmodule-> init ();

// Most of the function functions of the input module have been filled with DLL // This only needs to fill in the "useless and danger" function pointer pinModule-> outmod = poutmodule; pinModule-> hmainwindow = poutmodule-> hmainwindow ; pInModule-> hDllInstance = pOutModule-> hDllInstance; pInModule-> SAVSAInit = SAVSAInit; pInModule-> SAVSADeInit = SAVSADeInit; pInModule-> SAAddPCMData = SAAddPCMData; pInModule-> SAGetMode = SAGetMode; pInModule-> SAAdd = SAAdd; pInModule-> VSAAddPCMData = VSAAddPCMData; pInModule-> VSAGetMode = VSAGetMode; pInModule-> VSAAdd = VSAAdd; pInModule-> VSASetInfo = VSASetInfo; pInModule-> dsp_isactive = dsp_isactive; pInModule-> dsp_dosamples = dsp_dosamples; pInModule-> EQSet = EQSet; pInModule-> SetInfo = Setinfo; // Final initialize pinModule-> init ();} Can you get normal compilation? If you can, start our theme, now go back to fill in the important functions on the header files. You still don't expect this time we can broadcast music, because for some beginners, their basic knowledge is worse than this, so I need to tell the basic principle of electroacupase first, if you are old bird If you want to drag the scroll bar on the right again.

As we all know, the sound is caused by the vibration of the air, and the frequency of the vibration is the frequency of the acoustic wave. The acceptance of the general human ear is 20 Hz to 20000 Hz, which is lower than this extensive called second sound wave, higher than this Call ultrasound. Different tones are of course different, such as do, RE, MI is three different frequencies. Then, different instruments have issued the sound of the same frequency, but we can hear their differences, what is going on? This is the difference in tone. In fact, the difference between the different instruments is not in the frequency but in the waveform, such as sound waves of the same frequency, the steep increase in the 1/4 stage of one cycle, in the 3/4 stage The sounds generated by the sudden rise in the next portion of the 1/4 stage and the sound generated in the 3/4 stage is very different.

The principle of speakers in the speaker is actually very simple, that is, through electromagnetic induction phenomenon, the change in current is turned to the vibration of the pot membrane, so as long as there is a frequency of the human ear can accept, a powerful vibration of vibration Enter the speaker, the human ear can get a sound pressure in a sufficiently near voice field (hear sound). Of course, in the multimedia active speakers except for the speaker, there are devices such as op amp (current amplifiers) and power amplifiers (power amplifiers), which are generally enlarged to enlarge the input current and power to the speaker can emit a sound. The role of the sound card on the computer is to transmit the digital audio data transmitted to the sound card through the buffer, processing, shunt, conversion, etc., and transmit the current carries the analog signal to the speaker through the interface to make it sound.

Now that the principle of electroacours is clear, it is clear that the sound wave electrical signal is a smooth curve, which is the computer data represented by only 1 and 0 to represent this analog signal? Below we will introduce a concept "sampling rate". For example, a sound wave frequency is 1000 Hz, which means it will vibrate 1000 times in a second, that is, 1000 cycles, then if you need to restore this sound from digital wave to the sound wave, you can listen to a sound, then each cycle You need to record at least 8 sampling points in tens of more than 8 sample points. Then the sample rate of this data wave is 8 * 1000 = 8000 SAMP / SEC. Let's look at a concept "bit rate", this is very understandable, if it is 8 bits, then use a char type data to record each sampling point, the maximum amplitude is of course -128 - 127, if it is 16, then Record with a Short, the extensive is -32768 - 32767, don't worry that the amplitude is reduced with a small bit rate, and the sound is reduced, because the sound card is not allowed to be data, it will map one In the extensive, that is, the volume of 127 in 8 digits is the same as the volume of 32767 in the 16-bit, only 16 bits can show more dynamic details. Now you will ask you anxiously, how much is it, how did the data do? Don't worry, I will give an example, such as a sine wave of 1000Hz, we have digital samples for it, the sample rate is 8000, the bit rate is 16bit (every sample point data is a short type), mono, then its Sampling point data is thus 0, 16384, 32768, 16384, 0, -16384, -32768, -16384, 0, 16384 ... which represent 1 second 1000 Hz sound waves. The current sound card generally supports the sampling rate such as 16 and 8 bit rates, 44100, 22050, 11025, as for why the sampling rate is not an integer, this is to effectively reduce the hoe, this principle is a bit complicated, if you are interested, you can refer to it. Book, I haven't described here. The applet I wrote below can generate a specified frequency, sampling rate, bit rate, and time length of data sound waves.

Typedef struct tagwaveinfo {word cbsize; double dseconds; word wording dsels; word wbitspersample; dword dwhz; byte byte byvolume; dWord dwmode;} WaveInfo, *

// Specify the type of sound waves you need with PWaveInfo, the function will create data // PWF according to your requirements to pass the wavestand information that meets the WAVE standard, and the data length // function generated by PBUFlen is returned to the generated data. pointer void * CreateWaveData (const PWAVEINFO pwi, LPWAVEFORMATEX pwf, DWORD * pBufLen) {if (pwi-> cbSize = sizeof (WAVEINFO)!) {return NULL;} double dCurTime; int nCurValue; double dCircle = 1.0 / (double) pwi -> dwhz; // cycle time double dsecpersample = 1.0 / (double) PWI-> dwsamplepersec; // sampling point distance word wbytepersample = pwi-> wbitsample / 8; // per sampling byte DWORD DWSAMPLECOUNT = (DWORD) CEIL (PWI-> DSECONDS * (Double) PWI-> dwsamplepersec); // All sampling points * pbufflen = dwsample address * wbytepersample * pwi-> wchannels; // data length void * pdata = new char [* Pbufflen]; for (DWord i = 0; i wbitsample) / 2.0 * (double) PWI-> BYVOLUME / 255.0); for (Word J = 0; J WChannels; J ) {for (Word K = 0; K wchannels j) * wbytepersample k] = (NCurValue << (((3 - k) * 8)) >> 24;}}} PWF- > cbSize = sizeof (WAVEFORMATEX); pwf-> wFormatTag = WAVE_FORMAT_PCM; pwf-> nChannels = pwi-> wChannels; pwf-> nSamplesPerSec = pwi-> dwSamplePerSec; pwf-> wBitsPerSample = pwi-> wBitsPerSample; pwf-> nBlockAlign = Wbytepersample * pwi-> wchannels; pwf-> navgbytespesec = pwi-> dwsamplepersec * pwf-> nblockalign; return pdata;

After you know that you know enough to know what the electroacoustic sounds mentioned above, I will take you to the Win32 Wave (PCM) format to travel. The full name of PCM is: Pulse Code Modulation (pulse coding), it is the mechanism of interval sampling. The structure of the Wave file is as follows: Scope Length Type Default Note File Identifier 4 Char [4] "Riff" Resource Interchange File Format Wave is one of the file data length 4 DWORD N / A does not include file identity The length format of the bare data of the data length 4 CHAR [4] "Wave" to determine if the WAVE file information header ID 4 CHAR [4] "FMT" means the information header description information header length 4 DWORD 16 or 12 The following information header data has multiple longitudes 2 Word 1 or 2 represents a single channel or stereo sample rate 4 DWORD 44100 or the like per second DWORD N / A bit rate / 8 * sampling rate * the number of channels Packet length 4 Word N / A bit rate / 8 * channel Number rate 2 Word 16 or 8 Data ID 4 CHAR [4] "DATA" means that the block data length 4 DWORD N / A indicates more data Long Data N / A N / A N / A Data Other Information Does Not Require Its information is sequentially stored, and the information head is just a bit of WaveFormatex structure, but the order is a bit chaos, but must not be bad. Very simple? Now you can generate a sound data with the sound wave generator above, then write a WAVE file to try it, see if you can put it out of WinAMP.

Said so much, I believe that the rookie is also aware of these stuff, so now we go back to continue writing our output modules. Let's make sure we want to write an output module? Is output to the sound card? That's too difficult, let us first click on the simple test, output a Wave file. File Operation Module I personally think that the FStream using the STL database is very convenient, so you have to first set a file global variable in the top of MusicPlayer.cpp to open the file you need to write:

Ofstream File ("File Name", iOS_BASE :: Out | iOS_BASE :: binary);

There is also two global variables:

DWORD g_dwwrited; // The length of the currently written data DWORD g_dwbytepersec; // Dynamic per second

After calling the in_module's Play function, after the music starts playback, IN_MODULE calls OUT_MODULE's Open function, and specifies data information, so we should write the header information of the WAVE file here, follow the Wave file format that is mentioned above, we Write this Open function:

INT OUTOPEN (int NSAMPLERATE, INT NCHANNELS, INT NBITPERSAMPLE, INT NBUFLEN, INT NPREBUFLEN) {DWORD DWFLAG; DWFLAG = (DWORD) 'Riff'; File.Write ((Char *) & DWFLAG, 4); File.Write ((char *) & dwflag, 4); // Empty 4 byte write data length DWFLAG = (dword) 'Wave'; file.write ((char *) & dwflag, 4); dwflag = (dword) 'FMT'; file .write ((char *) & dwFlag, 4); WAVEFORMATEX wf; ZeroMemory (& wf, sizeof (wf)); wf.cbSize = sizeof (wf); wf.nSamplesPerSec = nSampleRate; wf.wBitsPerSample = nBitPerSample; wf.nChannels = NChannels; wf.nblockalign = nbitpersample / 8 * nchaneneless; wf.navgbytespersec = wf.nblockalign * nsample; wf.wformattag = Wave_Format_PCM;

g_dwbytepersec = wf.navgbytespersec;

File.Write ((char *) & wf.cbsize, sizeof (wf.cbsize)); // Write low WF.cbsize = 0; file.write ((char *) & wf.cbsize, sizeof (wf.cbsize)); // Write the high, 0 file.write ((char *) & wf.wformattag, sizeof (wf)); // write dwflag = (dword) 'data' with offset one-time File.Write ((char *) & dwflag, 4);

After starting playback, IN_MODULE will repeatedly perform the following procedure: call the OUT_MODULE's OutcanWrite function to determine if it can be written, then the OUT_MODULE's Outwrite function will write data to it. Because here is a file operation, we have returned 0x1000 in the OutcanWrite function.

INT OUTCANWRITE (VOID) {Return 0x1000;}

Outwrite functions are of course simpler, just write data to files, and add data length.

INT OUTWRITE (CHAR * PBUF, INT NLEN) {file.write (PBUF, NLEN); g_dwwrited = Nlen;

This data will be written in a section of the Wave file, but there is still a problem. OUT_MODULE does not know when I end, and IN_MODULE can still end up_module on the buffer in the buffer after writing. However, IN_MODULE sends a user 2 message to the main form after writing, indicating the end of the write, and then the main form is here to call out_module's getOutputTime to get the output module for the mechanism of the usage timer. This function is written by the current playback:

INT OUTGETOUTPUTTIME (VOID) {RETURN G_DWWWWRITED / G_DWBYTEPERSEC * 1000;} When the time in which the main form finds the output, the OUT_MODULE's OUTCLOSE event is called immediately. Outclose fills in the file in this file, one is the length of the file data, one is the length of the naked data. Void Outclose (void) {dWord dwsize = (dword) file.tellg () - 8; file.seekg (4, ios_base :: beg); file.write ((char *) & dwsize, sizeof (dwsize)); file. Seekg (32, iOS_BASE :: Cur); File.Write ((char *) & g_dwwrited, sizeof (g_dwwrited)); file.close ();}

It is completely written to this Wave file. There are also some code in the call of the main program, but it is very simple. When the application is initialized, you need to load WINAMP IN_MP3.DLL.

IN_MODULE * PINMODULE; HMODULE HIN2MOD; HIN2MOD = LOADLIBRARY ("in_mpg123.dll"); if (null == hin2mod) {MessageBox (NULL, "Unable to load the input module!", "Error", MB_ICONHAND; RETURN FALSE;} PWINAMPGETIN2MOD PINFUNC = (PWINAMPGETIN2MOD) getProcaddress (Hin2Mod, "WinampGetInmodule2"); pinModule = Pinfunc (); // This allows you or get the in_module object from WinAmp in_mp3.dll. // and OUT_MODULE is the stuff just now, instantiate one. OUT_MODULE OUTMODULE; // The following is initialized with our previously written function. Initmodules (PinModule, & Outmodule);

Now you can call PinModule-> Play; "File Name"); come to play - the fact is replicating music. After the end, you can use Winamp to open a good file to listen, because there is no problem. If you think that you have seen so long, you still feel hunger, let's take a more exciting theme, let us use DirectX to write a real media player based on test programs. First of all, you must be sure, do you know DirectX? If you have never written DirectX programs, you may have a bit difficult for you, at least you want to make sure you have written COM applications under VC.NET. But the problem has appeared, what should I talk about such a long code? Simply posted the original code first, and then talk about it later.

//MusicPlayer.cpp#include "stdafx.h" #include "musicplayer.h"

HWND g_hWnd; HANDLE g_hThread; IDirectSound8 * g_pDS8; // DirectSount pointer IDirectSoundBuffer8 * g_pDSB; // DirectSound buffer pointer volatile DWORD dwWritePointer; bool bStop, bPause; DWORD dwBytePerSec; DWORD dwWrapCount; DWORD dwBufLen = 0x200000; // buffer length

DWORD WINAPI ThreadProc (LPVOID lpParameter) {DWORD dwStatus, dwCurPlay; while (! BStop && g_pDSB) {Sleep (50); g_pDSB-> GetStatus (& dwStatus); if (bPause || 0 == (dwStatus & DSBSTATUS_PLAYING)) {continue ;} g_pDSB-> GetCurrentPosition (& dwCurPlay, NULL); double dTemp = ((double) dwWrapCount * dwBufLen dwCurPlay) * 1000.0 / dwBytePerSec; PostMessage (g_hWnd, WM_USER 4, dwWrapCount, (DWORD) dTemp);} PostMessage (g_hWnd , WM_USER 4, 0, 0); g_hthread = null; return 0;} // Configuration dialog Void Outconfig (hwnd hwnd) {MessageBox (hwnd, "config", "about", mb_ok);}

/ / About dialog Void Outabout (HWND HWND) {MessageBox (hwnd, "about create create", "about", mb_ok);

// Initialize Void Outinit () {Coinitialize (NULL); DirectSoundCreate8 (NULL, & G_PDS8, NULL); g_pds8-> setcooperativeelevel (g_hwnd, dsscl_priority);}

// Open the device int outopen (int NSampleRate, int nchannels, int nbitpersample, int nbufflen, int nprebufflen) {ified (! Outisplaying ()) {return -1;}

BSTOP = false; bpause = false; dwwritepointer = 0; dwwrapcount = 0;

WAVEFORMATEX wf; ZeroMemory (& wf, sizeof (wf)); wf.cbSize = sizeof (wf); wf.nSamplesPerSec = nSampleRate; wf.wBitsPerSample = nBitPerSample; wf.nChannels = nChannels; wf.nBlockAlign = nBitPerSample / 8 * nChannels; Wf.navgbytespersec = wf.nblockalign * nsampleRate; wf.wformattag = Wave_format_pcm; dwbytepersec = wf.navgbytespersec;

DSBUFFERDESC BufDesc; ZeroMemory (& BufDesc, sizeof (BufDesc)); BufDesc.dwSize = sizeof (BufDesc); BufDesc.dwFlags = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_LOCSOFTWARE | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_STICKYFOCUS; BufDesc.lpwfxFormat = & wf; BufDesc.dwBufferBytes = dwBufLen; BufDesc. Guid3Dalgorithm = ds3dalg_default; hResult HR = g_pds8-> createSoundBuffer (& BUFDESC, (LPDIRECTSOUFFER *) & g_pdsb, null); if (s_ok! = HR) {Return -1;}

g_hthread = CreateThread (NULL, 0, ThreadProc, 0, 0, NULL);

IF (FAILED (G_PDSB-> Play)))) {return -1;} PostMessage (g_hwnd, wm_user 3, (wparam) WF.NAVGBYTESPERSEC, (LPARAM) 0); Return 0;}

// prepare data block write int OutWrite (char * pBuf, int nLen) {VOID * pMem1, * pMem2; DWORD dwSize1, dwSize2; if (FAILED (g_pDSB-> Lock (dwWritePointer% dwBufLen, nLen, & pMem1, & dwSize1, & pMem2 , & dwsize2, 0))) {g_pdsb-> restore (); g_pdsb-> lock (dwwritepointer% dwbufflen, nlen, & pmem1, & dwsize1, & pmem2, & dwsize2, 0);} CopyMemory (PMEM1, PBUF, DWSIZE1); if (PMEM2 ) {CopyMemory (PMEM2, PBUF DWSIZE1, DWSIZE2);} g_pdsb-> unlock (pmem1, dwsize1, pmem2, dwsize2); dwwritepointer = nlen; return 0;}

int OutCanWrite (void) {DWORD dwCurPlay; static DWORD dwLastPlay = 0; if (bPause || NULL == g_pDSB) {return 0;} g_pDSB-> GetCurrentPosition (& dwCurPlay, NULL); if (dwCurPlay (long) (dwBufLen / 2)) {return 0;} Return dwbufflen / 2;} int output dwstatus = 0; if (g_pdsb) {g_pdsb-> getStatus (& dwstatus);} return 0 == (DWSTATUS & DSBSTATUS_PLAYING);}

int OutPause (int nPause) {if {return -1 (g_pDSB!);} DWORD dwStatus; g_pDSB-> GetStatus (& dwStatus); if (0 == (dwStatus & DSBSTATUS_PLAYING)) {bPause = false; if (FAILED (g_pDSB -> Play (0, 0, DSBPLAY_LOOPING)))))))))) {RETURN-1;}} else {bpause = true; g_pdsb-> stop ();}

Static int nlastpause = 0; int nTemp = NLASTPAUSE; NLASTPAUSE = NPAUSE; RETURN NTEMP;}

// Close the device void outclose (void) {if (g_hthread) {bstop = true; waitforsingleObject (g_hthread, infinite);} if (g_pdsb) {g_pdsb-> stop (); g_pdsb-> release (); g_pdsb = null; }

Void Outquit () {Outclose (); if (g_pds8) {g_pds8-> release ();}}

Void OutSetVolume (INT NVOLUME) {IF (g_pdsb) {g_pdsb-> setvolume ((NVOLUME * (DSBVOLUME_MAX - DSBVOLUME_MIN) / 255 DSBVOLUME_MIN);}}

Void outsetpan (int np) {}

void OutFlush (int nT) {DWORD dwFlushPos = (nT / 1000) * dwBytePerSec; dwWritePointer = dwFlushPos; dwWrapCount = dwWritePointer / dwBufLen; g_pDSB-> SetCurrentPosition (dwWritePointer% dwBufLen);}

int OutGetWrittenTime (void) {return 0;} void InitModules (In_Module * pInModule, Out_Module * pOutModule) {g_hWnd = :: AfxGetMainWnd () -> GetSafeHwnd (); pOutModule-> About = OutAbout; pOutModule-> CanWrite = OutCanWrite; pOutModule -> Open = OutOpen; pOutModule-> Close = OutClose; pOutModule-> Config = OutConfig; pOutModule-> description = "Creamdog WaveOut v1.0"; pOutModule-> Flush = OutFlush; pOutModule-> GetOutputTime = OutGetWrittenTime; pOutModule-> GetWrittenTime = OutGetWrittenTime; pOutModule-> hDllInstance = :: AfxGetInstanceHandle (); pOutModule-> hMainWindow = g_hWnd; pOutModule-> id = 32; pOutModule-> Init = OutInit; pOutModule-> IsPlaying = OutIsPlaying; pOutModule-> Pause = OutPause; PoutModule-> quit = Outquit; PoutModule-> setModule-> setModule = outsetVolume; poutModule-> version = 100; poutmodule-> write = outmodule; poutmodule-> init ();

pInModule-> outMod = pOutModule; pInModule-> hMainWindow = pOutModule-> hMainWindow; pInModule-> hDllInstance = pOutModule-> hDllInstance; pInModule-> SAVSAInit = SAVSAInit; pInModule-> SAVSADeInit = SAVSADeInit; pInModule-> SAAddPCMData = SAAddPCMData; pInModule- > SAGetMode = SAGetMode; pInModule-> SAAdd = SAAdd; pInModule-> VSAAddPCMData = VSAAddPCMData; pInModule-> VSAGetMode = VSAGetMode; pInModule-> VSAAdd = VSAAdd; pInModule-> VSASetInfo = VSASetInfo; pInModule-> dsp_isactive = dsp_isactive; pInModule-> DSP_DOSMPLES = DSP_DOSMPLES; PINMODULE-> EQSet = EQSET; pinModule-> setInfo = setInfo; pinModule-> init ();

void SAVSAInit (int maxlatency_in_ms, int srate) {} void SAVSADeInit () {} void SAAddPCMData (void * PCMData, int nch, int bps, int timestamp) {} int SAGetMode () {return 0;} void SAAdd (void * data , int timestamp, int csa) {} void VSAAddPCMData (void * PCMData, int nch, int bps, int timestamp) {} int VSAGetMode (int * specNch, int * waveNch) {return 0;} void VSAAdd (void * data, int timestamp) {} void VSASetInfo (int nch, int srate) {} int dsp_isactive () {return 0;} int dsp_dosamples (short int * samples, int numsamples, int bps, int nch, int srate) {return 0;} Void EQSET (int on, char data [10], int preamp) {} void setInfo (int bitrate, int srate, int switch, int synched) {} ​​// EOF

In the initialization function, the following three sentences are initializing COM and DirectSound8, I will not say much.

Coinitialize (NULL); DirectSoundCreate8 (NULL, & g_pds8, null); g_pds8-> setcooPERATIVELEVEL (G_HWND, DSSCL_PRIORITY);

The job that is done in the Outopen function is to create a loop playback cache, where you want to pay attention to the created parameters.

BUFDESC.DWFLAGS = DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_LOCSOFTWARE | DSBCAPS_GETCURRENTPSITION2 | DSBCAPS_STICKYFOCUS

DSBCAPS_CTRLPAN and DSBCAPS_CTRLVOLUME do not matter, you can change the volume via g_pdsb-> setvolume. DSBCAPS_LOCSOFTWARE is for a broader compatibility, there is no big hard cache on many board sound cards and old-fashioned ISA sound cards, so it is used to force the cache to create in memory. After dsbcaps_getcurrentposition2 is specified, you can get a more accurate value for g_pdsb-> getcurrentpos (). DSBCAPS_STICKYFOCUS guarantees that your application can still be released in an inactive state. Keep in mind that these three markers are not available.

g_pdsb-> Play (0, 0, DSBPLAY_LOOPING)

This sentence is to let the sound start looping. For example, the buffer you created has 0x200000 bytes, if dsbplay_looping is specified, when playing to 0x200000, it is looped to the beginning of the play. Of course, it is not easy to get the length of the media in OUT_MODULE, so we create a cache that can be looped, and the speed is of course fast than the speed of play, the Outwrite function is written in front, g_pdsb is back, in order to avoid Outwrite Write a circle and catching up with G_PDSB, we must define the speed of the G_PDSB, which of course is implemented by OutcanWrite.

VOID * pMem1, * pMem2; DWORD dwSize1, dwSize2; if (FAILED (g_pDSB-> Lock (dwWritePointer% dwBufLen, nLen, & pMem1, & dwSize1, & pMem2, & dwSize2, 0))) {g_pDSB-> Restore (); g_pDSB-> Lock (dwWritePointer% dwBufLen, nLen, & pMem1, & dwSize1, & pMem2, & dwSize2, 0);} CopyMemory (pMem1, pBuf, dwSize1); if (pMem2) {CopyMemory (pMem2, pBuf dwSize1, dwSize2);} g_pDSB-> Unlock (PMEM1, DWSIZE1, PMEM2, DWSIZE2); DWRITEPOINTER = Nlen; Return 0; The code in the Outwrite function is clearly clear that the method of writing buffers is cyclically, such as your buffer has 0x30000 words. Section, and where you of course write is the 0x20000 byte, then you lock the content of the 0x20000 byte length, then the remaining 0x10000 bytes will return from the header of the buffer to the PMEM2. DwritePointer records all written bytes, in order to use dwwritepointer% dwbufflen to calculate which location in the loop in the loop in order to calculate. In addition, that thread is critical, it will send the current playback time and status through the message to the main form. The main form is also only relying on this message to implement the management of OUT_MODULE. As for other pauses, the code implementation of functions such as the end of information is not complicated. If you are interested, you can understand that the source code can be understood. If you will write a DLL, then you now have written because you already know how to match the WinAmp plug-in interface standard. To this full text is coming, is you a bit depressed? Is it too much to do? What is the texture? Still following the shit, don't you say eight? I am ready to criticize, tell me short messages!

Creamdog was Wednesday, September 10, 2003, and did not enter rice in seven hours.

Disclaimer: 1. This article refers to WinAmp to Nullsoft Winamp2.x series products, which is copyrighted by Nullsoft, if the article infringes your copyright if the article is invaded, please call us. 2. The source code involved in this article has not been tested. I don't guarantee its correctness and stability. If you have any destructive impact on your computer and data, I will not be responsible. 3. If there is no license by Nullsoft, publish applications that use Winamp dedicated plugins will not be protected by law and may be accused by Nullsoft. 4. If the explanation or code in the text is wrong, please refer to! 5. The copyright of this article belongs to Creamdog, and by CreamDog first in the CBS C / C Basic Forum, if you want to post this article to other places, keep all the formats and characters of the original text. 6. Thanks to the CBS C / C Basic Forum Forum, all netizens have long supported for me.

转载请注明原文地址:https://www.9cbs.com/read-487.html

New Post(0)