HRESULT SaveWavToFile( MyDirectSoundBuffer* o, const void* data, DWORD size, const char* filepath ) { // The WAVEFORMATEX structure can have a variable length that depends on the details of the format. // Before retrieving the format description, the application should query the DirectSoundBuffer object // for the size of the format by calling this method and specifying NULL for the lpwfxFormat parameter. // The size of the structure will be returned in the lpdwSizeWritten parameter. DWORD bytesToAllocate = 0; V_RET(o->m_pDSoundBuffer->GetFormat(NULL, 0, &bytesToAllocate)); WAVEFORMATEX* lpwfxFormat = (WAVEFORMATEX*) alloca( bytesToAllocate ); V_RET(o->m_pDSoundBuffer->GetFormat(lpwfxFormat, bytesToAllocate, NULL)); #ifdef FOLDER_TO_SAVE_WAVEFORMATEX { const UINT64 format_hash = MurmurHash64( lpwfxFormat, bytesToAllocate ); // generate unique file name char buffer[32]; sprintf(buffer, "%016llX", format_hash); char filepath[MAX_PATH]; strcpy_s(filepath, FOLDER_TO_SAVE_WAVEFORMATEX); strcat_s(filepath, buffer); strcat_s(filepath, ".wav_header"); FILE* pFile = ::fopen( filepath, "w" ); if( pFile ) { ::fwrite( lpwfxFormat, sizeof(char), bytesToAllocate, pFile ); ::fclose( pFile ); pFile = NULL; } } #endif CWaveFile waveFile; V_RET(waveFile.Open( (char*)filepath, lpwfxFormat, WAVEFILE_WRITE )); UINT bytesWritten; V_RET(waveFile.Write( size, (BYTE*)data, &bytesWritten )); V_RET(waveFile.Close()); return S_OK; }
// export thread static DWORD __stdcall export_rendering_thread(void *parameter) { const int samples_per_sec = 44100; uint progress = 0; WAVEFORMATEX wfxInput; ZeroMemory( &wfxInput, sizeof(wfxInput)); wfxInput.wFormatTag = WAVE_FORMAT_PCM; wfxInput.nSamplesPerSec = samples_per_sec; wfxInput.wBitsPerSample = 16; wfxInput.nChannels = 2; wfxInput.nBlockAlign = wfxInput.nChannels * (wfxInput.wBitsPerSample / 8); wfxInput.nAvgBytesPerSec = wfxInput.nBlockAlign * wfxInput.nSamplesPerSec; HRESULT hr; CWaveFile wavFile; hr = wavFile.Open((char*)parameter, &wfxInput, WAVEFILE_WRITE); if (FAILED(hr)) { goto done; } // stop playing song_stop_playback(); // skip 5 seconds for (int samples = samples_per_sec * 5; samples > 0; samples -= 32) { float temp_buffer[2][32]; // update song song_update(1000.0 * (double)32 / (double)samples_per_sec); // update effect vsti_update_config((float)samples_per_sec, 32); // call vsti process func vsti_process(temp_buffer[0], temp_buffer[1], 32); } song_start_playback(); for (;;) { const int samples = 32; float temp_buffer[2][samples]; short output_buffer[2 * samples]; // update song song_update(1000.0 * (double)samples / (double)samples_per_sec); // update effect vsti_update_config((float)samples_per_sec, samples); // call vsti process func vsti_process(temp_buffer[0], temp_buffer[1], samples); short* output = output_buffer; float volume = config_get_output_volume() / 100.0; for (int i = 0; i < samples; i++) { float l = output[0] = convertSample(temp_buffer[0][i] * 32767.0f * volume); output[1] = convertSample(temp_buffer[1][i] * 32767.0f * volume); output += 2; } UINT sizeWrote = 0; hr = wavFile.Write(sizeof(output_buffer), (BYTE*)output_buffer, &sizeWrote); if (!song_is_playing()) break; if (!gui_is_exporting()) break; uint new_progress = 100 * song_get_time() / song_get_length(); if (new_progress != progress) { progress = new_progress; gui_update_export_progress(progress); } } done: song_stop_playback(); gui_close_export_progress(); wavFile.Close(); return hr; }