int cellSurMixerCreate(vm::ptr<const CellSurMixerConfig> config) { libmixer->Warning("cellSurMixerCreate(config_addr=0x%x)", config.addr()); surMixer = *config; AudioPortConfig& port = m_config.m_ports[SUR_PORT]; if (port.m_is_audio_port_opened) { return CELL_LIBMIXER_ERROR_FULL; } port.channel = 8; port.block = 16; port.attr = 0; port.level = 1.0f; libmixer->Warning("*** audio port opened(default)"); port.m_is_audio_port_opened = true; port.tag = 0; m_config.m_port_in_use++; libmixer->Warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", (u32)surMixer.chStrips1, (u32)surMixer.chStrips2, (u32)surMixer.chStrips6, (u32)surMixer.chStrips8); mixcount = 0; surMixerCb = 0; thread t("Surmixer Thread", []() { AudioPortConfig& port = m_config.m_ports[SUR_PORT]; CPUThread* mixerCb = &Emu.GetCPU().AddThread(CPU_THREAD_PPU); mixerCb->SetName("Surmixer Callback"); while (port.m_is_audio_port_opened) { if (Emu.IsStopped()) { libmixer->Warning("Surmixer aborted"); break; } if (mixcount > (port.tag + 0)) // adding positive value (1-15): preemptive buffer filling (hack) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; } if (port.m_is_audio_port_started) { //u64 stamp0 = get_system_time(); memset(mixdata, 0, sizeof(mixdata)); if (surMixerCb) mixerCb->ExecAsCallback(surMixerCb, true, surMixerCbArg, mixcount, 256); //u64 stamp1 = get_system_time(); { std::lock_guard<std::mutex> lock(mixer_mutex); for (auto& p : ssp) if (p.m_active && p.m_created) { auto v = vm::lptrl<s16>::make(p.m_addr); // 16-bit LE audio data float left = 0.0f; float right = 0.0f; float speed = fabs(p.m_speed); float fpos = 0.0f; for (int i = 0; i < 256; i++) if (p.m_active) { u32 pos = p.m_position; int pos_inc = 0; if (p.m_speed > 0.0f) // select direction { pos_inc = 1; } else if (p.m_speed < 0.0f) { pos_inc = -1; } int shift = i - (int)fpos; // change playback speed (simple and rough) if (shift > 0) { // slow playback pos_inc = 0; // duplicate one sample at this time fpos += 1.0f; fpos += speed; } else if (shift < 0) { // fast playback i--; // mix two sample into one at this time fpos -= 1.0f; } else { fpos += speed; } p.m_position += (u32)pos_inc; if (p.m_channels == 1) // get mono data { left = right = (float)v[pos] / 0x8000 * p.m_level; } else if (p.m_channels == 2) // get stereo data { left = (float)v[pos * 2 + 0] / 0x8000 * p.m_level; right = (float)v[pos * 2 + 1] / 0x8000 * p.m_level; } if (p.m_connected) // mix { // TODO: m_x, m_y, m_z ignored mixdata[i * 8 + 0] += left; mixdata[i * 8 + 1] += right; } if ((p.m_position == p.m_samples && p.m_speed > 0.0f) || (p.m_position == ~0 && p.m_speed < 0.0f)) // loop or stop { if (p.m_loop_mode == CELL_SSPLAYER_LOOP_ON) { p.m_position = p.m_loop_start; } else if (p.m_loop_mode == CELL_SSPLAYER_ONESHOT_CONT) { p.m_position -= (u32)pos_inc; // restore position } else // oneshot { p.m_active = false; p.m_position = p.m_loop_start; // TODO: check value } } } } } //u64 stamp2 = get_system_time(); auto buf = vm::get_ptr<be_t<float>>(m_config.m_buffer + (128 * 1024 * SUR_PORT) + (mixcount % port.block) * port.channel * 256 * sizeof(float)); for (u32 i = 0; i < (sizeof(mixdata) / sizeof(float)); i++) { // reverse byte order buf[i] = mixdata[i]; } //u64 stamp3 = get_system_time(); //ConLog.Write("Libmixer perf: start=%lld (cb=%lld, ssp=%lld, finalize=%lld)", stamp0 - m_config.start_time, stamp1 - stamp0, stamp2 - stamp1, stamp3 - stamp2); } mixcount++; } { std::lock_guard<std::mutex> lock(mixer_mutex); ssp.clear(); } Emu.GetCPU().RemoveThread(mixerCb->GetId()); }); t.detach(); return CELL_OK; }