#include "sim_ses.hpp" #include #include // waveOut* — winmm #include #include #include #include namespace fiko { namespace { constexpr int kSR = 44100; // örnekleme constexpr int kTamponSay = 6; // ring tampon adedi constexpr int kTamponOrn = 512; // tampon başına örnek (~11.6 ms) } // namespace struct SimSes::Ic { HWAVEOUT hwo = nullptr; HANDLE olay = nullptr; // waveOut CALLBACK_EVENT (tampon boşaldı) std::thread is; std::atomic kosuyor{false}; WAVEHDR hdr[kTamponSay]{}; std::vector tampon[kTamponSay]; // her tamponun PCM'i // Hedef parametreler (ayarla) — atomik; synth per-örnek yumuşatır. std::atomic hedefFreq{120.0f}; std::atomic hedefAmp{0.0f}; std::atomic hedefKesme{false}; // Synth durumu (yalnız iş parçacığı okur/yazar). f64 faz = 0.0; f32 freq = 120.0f, amp = 0.0f, kesmeK = 0.0f; u32 rngDurum = 0x1234567u; f32 gurultu() { // xorshift → [-1,1] rngDurum ^= rngDurum << 13; rngDurum ^= rngDurum >> 17; rngDurum ^= rngDurum << 5; return (f32)((i32)rngDurum) / 2147483648.0f; } void tamponDoldur(std::vector& buf) { const f32 hF = hedefFreq.load(std::memory_order_relaxed); const f32 hA = hedefAmp.load(std::memory_order_relaxed); const f32 hK = hedefKesme.load(std::memory_order_relaxed) ? 1.0f : 0.0f; for (int i = 0; i < kTamponOrn; ++i) { // Per-örnek tek-kutuplu yumuşatma (tık önler): ~5ms sabit. freq += (hF - freq) * 0.0025f; amp += (hA - amp) * 0.0015f; kesmeK += (hK - kesmeK) * 0.0015f; faz += 2.0 * 3.14159265358979 * (f64)freq / kSR; if (faz > 6.28318530717959) faz -= 6.28318530717959; const f32 s = (f32)std::sin(faz); const f32 s2 = (f32)std::sin(faz * 2.0); const f32 ig = s * 0.55f + s2 * 0.22f; // iğ whir + harmonik const f32 grind = gurultu() * (0.10f + 0.45f * kesmeK); // kesmede gıcırtı artar f32 v = (ig * 0.5f + grind) * amp; if (v > 1.0f) v = 1.0f; if (v < -1.0f) v = -1.0f; buf[(size_t)i] = (i16)(v * 30000.0f); } } void isDongusu() { // İlk doldurma + kuyruğa ver. for (int b = 0; b < kTamponSay; ++b) { tamponDoldur(tampon[b]); hdr[b] = {}; hdr[b].lpData = (LPSTR)tampon[b].data(); hdr[b].dwBufferLength = kTamponOrn * sizeof(i16); waveOutPrepareHeader(hwo, &hdr[b], sizeof(WAVEHDR)); waveOutWrite(hwo, &hdr[b], sizeof(WAVEHDR)); } while (kosuyor.load(std::memory_order_relaxed)) { WaitForSingleObject(olay, 50); for (int b = 0; b < kTamponSay; ++b) { if (hdr[b].dwFlags & WHDR_DONE) { waveOutUnprepareHeader(hwo, &hdr[b], sizeof(WAVEHDR)); tamponDoldur(tampon[b]); hdr[b].dwFlags = 0; hdr[b].dwBufferLength = kTamponOrn * sizeof(i16); waveOutPrepareHeader(hwo, &hdr[b], sizeof(WAVEHDR)); waveOutWrite(hwo, &hdr[b], sizeof(WAVEHDR)); } } } } }; SimSes& SimSes::instance() { static SimSes ses; return ses; } SimSes::~SimSes() { kapat(); } bool SimSes::acikMi() const { return d_ && d_->kosuyor.load(std::memory_order_relaxed); } void SimSes::baslat() { if (d_ && d_->kosuyor.load()) return; if (!d_) d_ = new Ic(); d_->olay = CreateEventW(nullptr, FALSE, FALSE, nullptr); WAVEFORMATEX wf{}; wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 1; wf.nSamplesPerSec = kSR; wf.wBitsPerSample = 16; wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; if (waveOutOpen(&d_->hwo, WAVE_MAPPER, &wf, (DWORD_PTR)d_->olay, 0, CALLBACK_EVENT) != MMSYSERR_NOERROR) { // Ses cihazı yok/açılamadı → sessiz no-op (asla çökme). if (d_->olay) { CloseHandle(d_->olay); d_->olay = nullptr; } return; } for (int b = 0; b < kTamponSay; ++b) d_->tampon[b].assign(kTamponOrn, 0); d_->kosuyor.store(true); d_->is = std::thread([this] { d_->isDongusu(); }); } void SimSes::kapat() { if (!d_) return; if (d_->kosuyor.exchange(false)) { if (d_->olay) SetEvent(d_->olay); if (d_->is.joinable()) d_->is.join(); if (d_->hwo) { waveOutReset(d_->hwo); for (int b = 0; b < kTamponSay; ++b) if (d_->hdr[b].dwFlags & WHDR_PREPARED) waveOutUnprepareHeader(d_->hwo, &d_->hdr[b], sizeof(WAVEHDR)); waveOutClose(d_->hwo); d_->hwo = nullptr; } if (d_->olay) { CloseHandle(d_->olay); d_->olay = nullptr; } } delete d_; d_ = nullptr; } void SimSes::ayarla(f32 freqHz, f32 amp, bool kesme) { if (!d_ || !d_->kosuyor.load(std::memory_order_relaxed)) return; if (freqHz < 30.0f) freqHz = 30.0f; if (freqHz > 4000.0f) freqHz = 4000.0f; if (amp < 0.0f) amp = 0.0f; if (amp > 1.0f) amp = 1.0f; d_->hedefFreq.store(freqHz, std::memory_order_relaxed); d_->hedefAmp.store(amp, std::memory_order_relaxed); d_->hedefKesme.store(kesme, std::memory_order_relaxed); } } // namespace fiko