Привет всем ! PlaySound с параметром SND_MEMORY не подходит. Желательно через какой-нибудь низкоуровневый способ типа mciwioOpen/mciwioPlay
Почему? Можно к примеру через mciSendString с дополнительными манипуляциями. Можно через waveOut-функции, но нужно будет вручную многое делать.
Thetrik, как делать через waveOut примерно представляю, еще можно через DirectSound (IDirectSound8::CreateSoundBuffer и IDirectSoundBuffer8::Play), а вот о "mciSendString с дополнительными манипуляциями" можно более развернуто?
Накидал небольшой пример: Код (C++): #include <Windows.h> #include <mmreg.h> #pragma comment (linker, "/ENTRY:main") #pragma comment (lib, "Winmm.lib") static PBYTE m_pbBuffer = NULL; static SIZE_T m_szBuffer = 0; LRESULT WINAPI MMIOProc(MMIOINFO* lpmmioinfo, UINT uMsg, LONG lParam1, LONG lParam2) { switch (uMsg) { case MMIOM_OPEN: case MMIOM_CLOSE: lpmmioinfo->lDiskOffset = 0; return 0; case MMIOM_READ: SIZE_T szRead; if (lpmmioinfo->lDiskOffset + lParam2 > m_szBuffer) szRead = m_szBuffer - lpmmioinfo->lDiskOffset; else szRead = lParam2; memcpy((PVOID)lParam1, m_pbBuffer + lpmmioinfo->lDiskOffset, szRead); lpmmioinfo->lDiskOffset += szRead; return szRead; case MMIOM_SEEK: switch (lParam2) { case SEEK_SET: lpmmioinfo->lDiskOffset = lParam1; break; case SEEK_CUR: lpmmioinfo->lDiskOffset += lParam1; break; case SEEK_END: lpmmioinfo->lDiskOffset = m_szBuffer - lParam1 - 1; } if (lpmmioinfo->lDiskOffset < 0) lpmmioinfo->lDiskOffset = 0; else if (lpmmioinfo->lDiskOffset > m_szBuffer) lpmmioinfo->lDiskOffset = m_szBuffer; return lpmmioinfo->lDiskOffset; } return -1; } void main() { // Load from resource MPEGLAYER3WAVEFORMAT wave HRSRC hRes = FindResource(NULL, MAKEINTRESOURCE(101), RT_RCDATA); if (!hRes) ExitProcess(HRESULT_FROM_WIN32(GetLastError())); HGLOBAL hMem = LoadResource(NULL, hRes); if (!hMem) ExitProcess(HRESULT_FROM_WIN32(GetLastError())); m_pbBuffer = (PBYTE)LockResource(hMem); if (!m_pbBuffer) ExitProcess(HRESULT_FROM_WIN32(GetLastError())); m_szBuffer = SizeofResource(NULL, hRes); if (!m_szBuffer) ExitProcess(HRESULT_FROM_WIN32(GetLastError())); // Install IO proc if (NULL == mmioInstallIOProc(' ZYX', (LPMMIOPROC)MMIOProc, MMIO_INSTALLPROC)) ExitProcess(E_FAIL); // Open in-memory file MCIERROR merr = mciSendString(L"open x.XYZ+ type waveaudio alias music", NULL, 0, NULL); if (merr) ExitProcess(merr); // Play merr = mciSendString(L"play music wait", NULL, 0, NULL); ExitProcess(merr); } --- Сообщение объединено, 3 мар 2022 --- Нужно еще поменять определение MMIOProc для 64 бит если планируется делать. В MSDN бардак, в одном месте такое объявление, в другом другое.
А я у себя на HD нашел вот такой вариант (когда-то эксперементировал). Может тоже пригодится. Код (C): #include <windows.h> #include <iostream> #include <string> #pragma comment(lib, "winmm.lib") enum mode {unknown, open, playing, paused, stopped }; class MCI { private: std::string filename; unsigned int lenght; int volume; int balance; public: MCI() : lenght(0),volume(1000),balance(0) { }; MCI(std::string filename): volume(1000),balance(0) { Open(filename); }; ~MCI() { Close(); } void Open(std::string filename) { Close(); this->filename = filename; std::string msg = "open \"" + filename + "\" type mpegvideo alias " + filename; mciSendString(msg.c_str(), NULL, 0, 0); lenght = Lenght(true); } void Play(int pos = 0) { char *buff = new char[10]; std::string msg = "play " + filename + " from " + itoa(pos,buff,10); mciSendString(msg.c_str(), NULL, 0, 0); delete [] buff; } void Seek(int pos) { char *buff = new char[10]; std::string msg = "seek " + filename + " to " + itoa(pos,buff,10); mciSendString(msg.c_str(), NULL, 0, 0); delete [] buff; } void Pause() { std::string msg = "pause " + filename; mciSendString(msg.c_str(), NULL, 0, 0); } void Resume() { std::string msg = "resume " + filename; mciSendString(msg.c_str(), NULL, 0, 0); } void Stop() { std::string msg = "stop " + filename; mciSendString(msg.c_str(), NULL, 0, 0); } void Close() { std::string msg = "close " + filename; mciSendString(msg.c_str(), NULL, 0, 0); } int Volume() { return volume; } void Volume(int vol) { vol = (vol < 0 ? 0 : (vol > 1000 ? 1000 : vol)); char *buff = new char[5]; std::string msg = "setaudio " + filename + " volume to " + itoa(vol,buff,10); mciSendString(msg.c_str(), NULL, 0, 0); delete [] buff; } int Balance() { return balance; } void Balance(int bal) { bal = (bal < -1000 ? -1000 : (bal > 1000 ? 1000 : bal)); char *buff = new char[5]; std::string msg = "setaudio " + filename + "left volume to " + itoa((bal<0?1000:1000-bal),buff,10); mciSendString(msg.c_str(), NULL, 0, 0); msg = "setaudio " + filename + "right volume to " + itoa((bal>0?1000:1000+bal),buff,10); mciSendString(msg.c_str(), NULL, 0, 0); delete [] buff; } int Lenght(bool refresh = false) { if (refresh) { char *buff = new char[128]; std::string msg = "status " + filename + " length"; mciSendString(msg.c_str(), buff, 128, 0); int t = atoi(buff); delete [] buff; return t; } else { return lenght; } } int Position() { char *buff = new char[16]; std::string msg = "status " + filename + " position"; mciSendString(msg.c_str(), buff, 16, 0); int t = atoi(buff); delete [] buff; return t; } int Position(int ms) { if (status() == playing || status() == paused) Seek(ms); else Play(ms); } mode status() { char *buff = new char[8]; std::string msg = "status " + filename + " mode"; mciSendString(msg.c_str(), buff, 8, 0); mode ret; if (strncmp(buff,"open",4) == 0) ret = open; else if (strncmp(buff,"playing",7) == 0) ret = playing; else if (strncmp(buff,"paused",6) == 0) ret = paused; else if (strncmp(buff,"stopped",7) == 0) ret = stopped; else ret = unknown; delete [] buff; return ret; } }; // Example int main() { MCI mp3("C:\\1.mp3"); std::cout << mp3.Lenght()/60000.0 << std::endl; // 1 минута mp3.Play(); std::cout << "Press F8 to exit" << std::endl; while (!GetAsyncKeyState(VK_F8)) { std::cout << "\r" << mp3.Position()/60000.0; Sleep(500); } mp3.Stop(); return 0; } Thetrik, так глядишь и постепенно, полностью перейдете на C/C++ и напишете пару тройку каких-нибудь интересных VST-шек.
Ну то, что вы на VB много чего интересного написали я в курсе. В том числе и VST. И даже на FASM'е умудрились VST написать - от чего я, вообще, обалдел, в хорошем смысле слова разумеется. Интересно было бы узнать, кто-нибудь, что-нибудь подобное делал или нет. Мне кажется, что маловероятно. И я всегда, видя ваши интересные посты на VB, которые вы размещали сначала на cyberforum'е ( https://www.cyberforum.ru/blogs/354370/ ), а чуть позже и на WASM'е - страдал от того, что не знаю VB. А так бы, возможно, что было бы что обсудить с интересным и увлеченным человеком. Одно время я даже, вроде как, уже даже настроился изучать VB. Даже самоучитель скачал, но в последний момент схлюздил и подумал, что в наше время Питон чуть поважнее будет, чем VB. Ну и начал изучать Питон, а с VB решил пока повременить.
Воспроизвожу в синхронном режиме wav-файл из буфера в памяти. Используются низкоуровневые функции waveOutPrepareHeader, waveOutUnprepareHeader, waveOutOpen, waveOutClose, waveOutWrite. В аттаче wav-файл, исходник и ехе-файл Код (ASM): ; GUI # include win64a.inc WAVEHDR struct lpData QWORD ? dwBufferLength DWORD ? dwBytesRecorded DWORD ? dwUser QWORD ? dwFlags DWORD ? dwLoops DWORD ? lpNext QWORD ? reserved qword ? WAVEHDR ends WHDR_DONE equ 1 .code WinMain proc local hGout:qword local p:qword local FSize:dword local hFile:dword local szReadWrite:qword ;number of bytes actually read or write local hWaveOut:qword local lpwiocb:WAVEHDR mov ecx,offset wav_file invoke CreateFile,,GENERIC_READ,0,0,OPEN_EXISTING,\ FILE_ATTRIBUTE_ARCHIVE,0 mov hFile,eax invoke GetFileSize,eax,0 mov FSize,eax invoke GlobalAlloc,GHND or GMEM_DDESHARE,eax mov hGout,rax invoke GlobalLock,eax mov p,rax lea r9d,szReadWrite invoke ReadFile,hFile,eax,FSize,,0 invoke CloseHandle,hFile invoke GlobalUnlock,hGout lea ecx,hWaveOut; указатель на идентификатор устройства xor r9,r9 ; адрес функции обратного вызова или идентификатор окна mov [rsp+20h],r9 ; данные для функции обратного вызова mov qword ptr[rsp+28h],WAVE_ALLOWSYNC;режим открытия устройства mov r8,p add r8d,14h; указатель на структуру WAVEFORMAT or edx,WAVE_MAPPER; номер открываемого устройства invoke waveOutOpen ; Подготавливаем заголовок для вывода lea edx,lpwiocb ; указатель на структуру WAVEHDR mov edi,edx xor eax,eax mov ecx,(sizeof WAVEHDR)/8 rep stosq mov rax,p add rax,2Ch ; пропускаю "RIFF" и размер (8 байт), "WAVE", "fmt ", размер формата ; данных, тип формата, количество каналов (моно или стерео), частоту дискретизации, ; скорость потока данных, выравнивание блока данных ;заполняем структуру WAVEHDR mov [rdx].WAVEHDR.lpData,rax mov eax,FSize sub eax,2Ch mov [rdx].WAVEHDR.dwBufferLength,eax invoke waveOutPrepareHeader,hWaveOut,,sizeof WAVEHDR ; Запускаем проигрывание блока lea edx,lpwiocb invoke waveOutWrite,hWaveOut,,sizeof WAVEHDR @@: test lpwiocb.dwFlags,WHDR_DONE jz @b; ждем пока музыка доиграет до конца ; После того как приложение получило сообщение WHDR_DONE, оно должно ; передать указатель на структуру WAVEHDR, соответствующей проигранному ; блоку функции waveOutUnprepareHeader lea edx,lpwiocb invoke waveOutUnprepareHeader,hWaveOut,,sizeof WAVEHDR ; После завершения работы с устройством его необходимо закрыть. Через ; единственный параметр функции waveOutClose передают идентификатор ; закрываемого устройства вывода invoke waveOutClose,hWaveOut ; завершаю программу invoke RtlExitUserProcess,NULL WinMain endp wav_file db 'имя_какого-нибудь_wav-файла',0 end