// Use -lwinmm for linking to the winmm library in GCC/G++ #include "../DFPSR/api/soundAPI.h" #include #include #include "../DFPSR/implementation/math/scalar.h" #include "../DFPSR/base/simd.h" namespace dsr { static const int samplesPerChannel = 2048; static int bufferElements = 0; static Buffer outputBuffer, floatBuffer; static SafePointer outputData[2]; static SafePointer floatData; // Aligning memory to allow using the widest available floating-point SIMD vector. static const int soundBufferAlignment = DSR_FLOAT_ALIGNMENT; static void allocateBuffers(int neededElements) { int64_t roundedElements = roundUp(neededElements, soundBufferAlignment / 2); int64_t outputSize = roundedElements * sizeof(int16_t); outputBuffer = buffer_create(outputSize * 2); floatBuffer = buffer_create(roundedElements * sizeof(float), soundBufferAlignment); SafePointer allOutputData = buffer_getSafeData(outputBuffer, "Output data"); outputData[0] = allOutputData.slice("Output data 0", 0, outputSize); outputData[1] = allOutputData.slice("Output data 1", outputSize, outputSize); floatData = buffer_getSafeData(floatBuffer, "Output data"); bufferElements = neededElements; } static HWAVEOUT waveOutput; static WAVEHDR header[2] = {0}; static HANDLE bufferEndEvent = 0; static bool running = true; static void terminateSound() { printText("Terminating sound.\n"); running = false; if (waveOutput) { waveOutReset(waveOutput); for (int b = 0; b < 2; b++) { waveOutUnprepareHeader(waveOutput, &header[b], sizeof(WAVEHDR)); } waveOutClose(waveOutput); waveOutput = 0; } if (bufferEndEvent) { CloseHandle(bufferEndEvent); bufferEndEvent = 0; } } bool sound_streamToSpeakers(int32_t channels, int32_t sampleRate, std::function data, int32_t length)> soundOutput) { bufferEndEvent = CreateEvent(0, FALSE, FALSE, 0); if (bufferEndEvent == 0) { terminateSound(); sendWarning(U"Failed to create buffer end event!"); return false; } int totalSamples = samplesPerChannel * channels; allocateBuffers(totalSamples); WAVEFORMATEX format; ZeroMemory(&format, sizeof(WAVEFORMATEX)); format.nChannels = (WORD)channels; format.nSamplesPerSec = (DWORD)sampleRate; format.wFormatTag = WAVE_FORMAT_PCM; format.wBitsPerSample = 16; format.nBlockAlign = format.nChannels * sizeof(int16_t); format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign; format.cbSize = 0; if(waveOutOpen(&waveOutput, WAVE_MAPPER, &format, (DWORD_PTR)bufferEndEvent, (DWORD_PTR)NULL, CALLBACK_EVENT) != MMSYSERR_NOERROR) { terminateSound(); sendWarning(U"Failed to open wave output!"); return false; } if(waveOutSetVolume(waveOutput, 0xFFFFFFFF) != MMSYSERR_NOERROR) { terminateSound(); sendWarning(U"Failed to set volume!"); return false; } for (int b = 0; b < 2; b++) { ZeroMemory(&header[b], sizeof(WAVEHDR)); header[b].dwBufferLength = totalSamples * sizeof(int16_t); header[b].lpData = (LPSTR)(outputData[b].getUnsafe()); if (waveOutPrepareHeader(waveOutput, &header[b], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { terminateSound(); sendWarning(U"Failed to prepare buffer for streaming!"); return false; } } running = true; while (running) { for (int b = 0; b < 2; b++) { if ((header[b].dwFlags & WHDR_INQUEUE) == 0) { // When one of the buffers is done playing, generate new sound and write more data to the output. safeMemorySet(floatData, 0, totalSamples * sizeof(float)); running = soundOutput(floatData, samplesPerChannel); //for (int i = 0; i < totalSamples; i++) { // outputData[b][i] = sound_convertF32ToI16(floatBuffer[i]); //} SafePointer target = outputData[b]; // Convert to target format so that the sound can be played for (uint32_t t = 0; t < totalSamples; t+=8) { // SIMD vectorized sound conversion with scaling and clamping to signed 16-bit integers. F32x4 lowerFloats = F32x4::readAligned(floatData + t, "sound_streamToSpeakers: Reading lower floats"); F32x4 upperFloats = F32x4::readAligned(floatData + t + 4, "sound_streamToSpeakers: Reading upper floats"); I32x4 lowerInts = truncateToI32(clamp(F32x4(-32768.0f), lowerFloats * 32767.0f, F32x4(32767.0f))); I32x4 upperInts = truncateToI32(clamp(F32x4(-32768.0f), upperFloats * 32767.0f, F32x4(32767.0f))); // TODO: Create I16x8 SIMD vectors for processing sound as 16-bit integers? // Or just move unzip into simd.h with a fallback solution and remove simdExtra.h. // Or just implement reading and writing of 16-bit signed integers using multiple SIMD registers or smaller memory regions. IVector4D lower = lowerInts.get(); IVector4D upper = upperInts.get(); target[t+0] = (int16_t)lower.x; target[t+1] = (int16_t)lower.y; target[t+2] = (int16_t)lower.z; target[t+3] = (int16_t)lower.w; target[t+4] = (int16_t)upper.x; target[t+5] = (int16_t)upper.y; target[t+6] = (int16_t)upper.z; target[t+7] = (int16_t)upper.w; } if (waveOutWrite(waveOutput, &header[b], sizeof(WAVEHDR)) != MMSYSERR_NOERROR) { terminateSound(); sendWarning(U"Failed to write wave output!"); return false; } if (!running) { break; } } } WaitForSingleObject(bufferEndEvent, INFINITE); } terminateSound(); return true; } }