// // Created by Administrator on 2025/1/17. // #include "AudioOutput.h" #include #include #include #include // Static Helpers (WAV Header Parser) static bool hasWavHeader(const QByteArray& data) { if (data.size() < 44) return false; return data.startsWith("RIFF") && data.mid(8, 4) == "WAVE"; } // StreamAudioWorker 实现 StreamAudioWorker::~StreamAudioWorker() { // 确保析构时资源释放 stop(); } void StreamAudioWorker::start(int sampleRate, int channelCount, int bitDepth) { if (m_sink) { m_sink->stop(); m_sink.reset(); } m_firstChunk = true; // 配置音频格式 QAudioFormat format; format.setSampleRate(sampleRate); format.setChannelCount(channelCount); if (bitDepth == 8) format.setSampleFormat(QAudioFormat::UInt8); else if (bitDepth == 16) format.setSampleFormat(QAudioFormat::Int16); else if (bitDepth == 32) format.setSampleFormat(QAudioFormat::Float); // 32位通常为Float else format.setSampleFormat(QAudioFormat::Int16); // 默认回退 // 检查设备是否支持 auto device = QMediaDevices::defaultAudioOutput(); if (!device.isFormatSupported(format)) { qWarning() << "[Worker] Device does not support format, using preferred format."; format = device.preferredFormat(); } // 创建 Sink (必须在 Worker 线程中创建) m_sink.reset(new QAudioSink(device, format)); // 监听状态 connect(m_sink.data(), &QAudioSink::stateChanged, this, [this](QAudio::State state){ if (state == QAudio::IdleState) { emit playbackFinished(); } else if (state == QAudio::StoppedState) { if (m_sink->error() != QAudio::NoError) { emit errorOccurred("Audio Sink Error: " + QString::number(m_sink->error())); } } }); // 启动,获取 IO 设备 m_ioDevice = m_sink->start(); if (!m_ioDevice) { emit errorOccurred("Failed to start audio device"); } else { qDebug() << "[Worker] Stream started:" << sampleRate << "Hz"; } } void StreamAudioWorker::processChunk(const QByteArray& chunk) { if (chunk.isEmpty() || !m_ioDevice || !m_sink) return; QByteArray dataToWrite = chunk; // 智能处理 WAV 头 if (m_firstChunk) { if (hasWavHeader(chunk)) { qDebug() << "[Worker] Detected WAV header, stripping 44 bytes."; dataToWrite = chunk.mid(44); } m_firstChunk = false; } // 写入音频设备 (QAudioSink 内部有缓冲区,这里直接 write 即可) // 如果数据量过大,write 可能会阻塞,但在独立线程中这是可以接受的 qint64 written = m_ioDevice->write(dataToWrite); if (written != dataToWrite.size()) { qWarning() << "[Worker] Incomplete write:" << written << "/" << dataToWrite.size(); } } void StreamAudioWorker::stop() { if (m_sink) { m_sink->stop(); m_sink.reset(); // 删除对象 } m_ioDevice = nullptr; qDebug() << "[Worker] Stream stopped"; } // AudioOutput 主类实现 QScopedPointer AudioOutput::m_instance; QMutex AudioOutput::m_mutex; AudioOutput* AudioOutput::getInstance() { if (m_instance.isNull()) { QMutexLocker locker(&m_mutex); if (m_instance.isNull()) { m_instance.reset(new AudioOutput()); } } return m_instance.data(); } void AudioOutput::destroy() { QMutexLocker locker(&m_mutex); if (!m_instance.isNull()) { m_instance.reset(); } } AudioOutput::AudioOutput(QObject *parent) : QObject(parent) { // 初始化文件播放器 m_player = new QMediaPlayer(this); m_audioOutput = new QAudioOutput(this); m_player->setAudioOutput(m_audioOutput); connect(m_player, &QMediaPlayer::mediaStatusChanged, this, [this](QMediaPlayer::MediaStatus status){ if (status == QMediaPlayer::EndOfMedia) emit playbackFinished(); }); // 预先初始化流式 Worker 线程 // 保持一个常驻线程Worker,通过信号控制 m_streamWorker = new StreamAudioWorker(); // 不能指定 parent,因为要 moveToThread m_workerThread = new QThread(this); m_streamWorker->moveToThread(m_workerThread); // 连接信号槽 // 主线程 -> Worker connect(this, &AudioOutput::sigOperateStreamStart, m_streamWorker, &StreamAudioWorker::start); connect(this, &AudioOutput::sigOperateStreamChunk, m_streamWorker, &StreamAudioWorker::processChunk); connect(this, &AudioOutput::sigOperateStreamStop, m_streamWorker, &StreamAudioWorker::stop); // Worker -> 主线程 connect(m_streamWorker, &StreamAudioWorker::errorOccurred, this, &AudioOutput::errorOccurred); // 线程启动 m_workerThread->start(); } AudioOutput::~AudioOutput() { stopPlayback(); // 清理线程 if (m_workerThread) { m_workerThread->quit(); m_workerThread->wait(3000); // 等待退出 delete m_streamWorker; } } // 对外接口 void AudioOutput::stopPlayback() { // 停止文件播放 if (m_player->playbackState() != QMediaPlayer::StoppedState) { m_player->stop(); } // 停止流播放 if (m_isStreaming) { stopStream(); } } void AudioOutput::setVolume(int volume) { if (m_audioOutput) m_audioOutput->setVolume(volume / 100.0); // 注意:流式播放的音量控制需要在 Worker 内单独实现 } bool AudioOutput::isPlaying() const { return (m_player->playbackState() == QMediaPlayer::PlayingState) || m_isStreaming; } // 文件播放 void AudioOutput::playUrl(const QUrl& url) { stopPlayback(); // 互斥,播放新文件前停止旧的 m_player->setSource(url); m_player->play(); } void AudioOutput::playData(const QByteArray& data) { // 这个方法对于 QMediaPlayer 比较麻烦,需要自定义 QIODevice // 建议直接走 stream 接口,或者使用 QBuffer + StreamWorker 的一次性模式 // 为了简单,这里将 buffer 视为 stream 播放 stopPlayback(); startStream(44100, 2, 16); // 假设默认 wav 格式,Worker 会自动解析头 pushStreamData(data); // 不需要显式 stopStream,让它播完 } // 流式播放 void AudioOutput::startStream(int sampleRate, int channelCount, int bitDepth) { stopPlayback(); // 确保干净的状态 m_isStreaming = true; // 通过信号跨线程调用 Worker 的 start emit sigOperateStreamStart(sampleRate, channelCount, bitDepth); } void AudioOutput::pushStreamData(const QByteArray& chunk) { if (!m_isStreaming) return; // 直接发射信号,Qt 会把 chunk copy 到子线程事件队列 emit sigOperateStreamChunk(chunk); } void AudioOutput::stopStream() { if (!m_isStreaming) return; m_isStreaming = false; emit sigOperateStreamStop(); }