Cocos2d-x 音频暂停/恢复与频道管理全解析

举报
William 发表于 2025/12/12 10:04:04 2025/12/12
【摘要】 1. 引言音频系统的暂停/恢复与频道管理是游戏开发中的重要环节,直接影响用户体验和应用性能。在实际开发中,我们经常会遇到需要临时暂停所有音频(如接听电话)、恢复特定频道音频(如游戏继续)、或者管理多个并发音频流(如背景音乐与多个音效同时播放)的场景。Cocos2d-x的SimpleAudioEngine虽然提供了基础的暂停/恢复功能,但在复杂的多频道管理场景下显得力不从心。本文将深入探讨如何...


1. 引言

音频系统的暂停/恢复与频道管理是游戏开发中的重要环节,直接影响用户体验和应用性能。在实际开发中,我们经常会遇到需要临时暂停所有音频(如接听电话)、恢复特定频道音频(如游戏继续)、或者管理多个并发音频流(如背景音乐与多个音效同时播放)的场景。Cocos2d-x的SimpleAudioEngine虽然提供了基础的暂停/恢复功能,但在复杂的多频道管理场景下显得力不从心。本文将深入探讨如何构建一个健壮的音频管理系统,实现精细化的频道控制和状态管理。

2. 技术背景

2.1 音频系统核心概念

  1. 音频频道(Channel):独立的音频播放单元,每个频道可以同时播放一个音频流
  2. 音频状态机:音频的生命周期状态(初始化→加载→播放→暂停→停止→销毁)
  3. 音频焦点(Audio Focus):系统级音频控制权,决定哪个应用可以播放音频
  4. 混音(Mixing):将多个音频流合并到扬声器输出的过程
  5. 优先级管理:当资源冲突时决定哪个音频优先播放的策略

2.2 Cocos2d-x音频架构局限

Cocos2d-x的SimpleAudioEngine主要局限:
  • 单一BGM频道:只能同时播放一个背景音乐
  • 有限SFX频道:早期版本仅支持少量音效并发
  • 全局暂停/恢复:无法选择性暂停特定音频
  • 缺乏优先级:无法基于重要性管理音频播放
  • 状态跟踪不足:难以精确跟踪每个音频的状态

2.3 系统音频焦点机制

现代操作系统都实现了音频焦点管理:
  • Android:AudioManager.OnAudioFocusChangeListener
  • iOS:AVAudioSession Interruption
  • Windows:Core Audio APIs
  • Web:Web Audio API + Page Visibility API

3. 应用使用场景

3.1 典型应用场景分类

场景类型
需求描述
技术要求
应用生命周期
切入后台暂停,返回前台恢复
系统事件监听、状态保存
游戏内暂停
暂停游戏时保留BGM,暂停SFX
选择性暂停、状态隔离
模态对话框
显示对话框时暂停背景音频
临时静音、快速恢复
多频道混音
同时播放BGM+环境音+UI音效
频道分配、优先级控制
资源冲突
内存不足时停止低优先级音频
动态管理、优雅降级
网络中断
在线音频流中断时切换本地资源
故障转移、无缝切换

3.2 场景复杂度分析

  • 简单场景:全局暂停/恢复,无状态保持
  • 中级场景:分类暂停(BGM保持,SFX暂停)
  • 复杂场景:多频道独立控制、优先级管理、故障恢复
  • 企业级场景:跨平台音频焦点协调、网络自适应、实时监控

4. 核心原理与流程图

4.1 音频频道管理架构

graph TD
    A[Audio Manager] --> B[BGM Channel]
    A --> C[SFX Channel Pool]
    A --> D[Ambient Channel]
    A --> E[Voice Channel]
    
    B --> B1[State: Playing/Paused/Stopped]
    B --> B2[Volume Control]
    B --> B3[Loop Control]
    
    C --> C1[Channel 1]
    C --> C2[Channel 2]
    C --> C3[... Channel N]
    C1 --> C1a[State Management]
    C1 --> C1b[Priority Queue]
    
    A --> F[Audio Focus Manager]
    F --> F1[System Event Listener]
    F --> F2[Focus State Tracking]
    
    A --> G[Resource Manager]
    G --> G1[Memory Monitoring]
    G --> G2[Dynamic Loading/Unloading]

4.2 暂停/恢复工作流程

sequenceDiagram
    participant App as Application
    participant AM as AudioManager
    participant SE as SimpleAudioEngine
    participant SF as SystemFocus
    
    Note over App,SF: 应用切入后台
    App->>SF: onPause()
    SF->>AM: onAppPause()
    AM->>AM: 保存当前音频状态
    AM->>SE: pauseBackgroundMusic()
    AM->>SE: pauseAllEffects()
    AM->>AM: 标记所有频道为Paused
    
    Note over App,SF: 应用返回前台
    App->>SF: onResume()
    SF->>AM: onAppResume()
    AM->>AM: 检查音频焦点状态
    alt 获得焦点
        AM->>SE: resumeBackgroundMusic()
        AM->>SE: resumeAllEffects()
        AM->>AM: 恢复所有频道状态
    else 未获得焦点
        AM->>AM: 保持暂停状态
    end

4.3 工作原理详解

  1. 频道抽象:将不同类型的音频分配到独立频道,实现精细控制
  2. 状态持久化:暂停时保存音频进度、音量等状态信息
  3. 焦点协调:监听系统音频焦点变化,做出相应响应
  4. 优先级队列:基于重要性管理音频播放顺序和资源分配
  5. 资源监控:实时监控内存和CPU使用,动态优化资源分配

5. 环境准备

5.1 开发环境配置

# 创建Cocos2d-x项目(v3.17+)
cocos new AudioChannelDemo -p com.yourcompany.audiochannel -l cpp -d ./projects

# 目录结构规划
AudioChannelDemo/
├── Resources/
│   ├── audio/            # 音频资源
│   │   ├── bgm/          # 背景音乐
│   │   ├── sfx/          # 音效
│   │   ├── ambient/      # 环境音
│   │   └── voice/        # 语音
│   ├── fonts/           # 字体文件
│   └── textures/        # 图片资源
├── Classes/             # 源代码
│   ├── audio/           # 音频管理模块
│   │   ├── AudioManager.h
│   │   ├── AudioManager.cpp
│   │   ├── AudioChannel.h
│   │   ├── AudioChannel.cpp
│   │   ├── AudioFocusManager.h
│   │   ├── AudioFocusManager.cpp
│   │   └── AudioTypes.h
│   ├── scenes/          # 场景类
│   └── platforms/       # 平台特定代码
│       ├── PlatformAudioFocus.h
│       └── PlatformAudioFocus.cpp
└── proj.*               # 各平台工程文件

5.2 平台特定配置

Classes/platforms/PlatformAudioFocus.h
#ifndef __PLATFORM_AUDIO_FOCUS_H__
#define __PLATFORM_AUDIO_FOCUS_H__

#include "AudioTypes.h"
#include <functional>

NS_CC_BEGIN

class PlatformAudioFocus {
public:
    static PlatformAudioFocus* getInstance();
    static void destroyInstance();
    
    // 初始化音频焦点监听
    bool init();
    
    // 焦点状态查询
    bool hasAudioFocus();
    
    // 焦点请求与放弃
    bool requestAudioFocus();
    bool abandonAudioFocus();
    
    // 焦点变化回调设置
    void setFocusChangeCallback(const std::function<void(AudioFocusState)>& callback);
    
    // 平台特定实现
    void platformSpecificInit();
    void platformSpecificCleanup();
    
private:
    PlatformAudioFocus() = default;
    ~PlatformAudioFocus() = default;
    
    static PlatformAudioFocus* _instance;
    std::function<void(AudioFocusState)> _focusCallback;
};

NS_CC_END

#endif // __PLATFORM_AUDIO_FOCUS_H__

5.3 项目配置

CMakeLists.txt 补充
# 添加音频管理模块
file(GLOB_RECURSE AUDIO_MODULE_FILES 
    Classes/audio/*.h 
    Classes/audio/*.cpp
    Classes/platforms/*.h
    Classes/platforms/*.cpp
)

list(APPEND GAME_SRC ${AUDIO_MODULE_FILES})

# 平台特定编译选项
if(ANDROID)
    add_definitions(-DANDROID_AUDIO_FOCUS_ENABLED)
elseif(IOS)
    add_definitions(-DIOS_AUDIO_SESSION_ENABLED)
endif()

6. 详细代码实现

6.1 音频类型定义

Classes/audio/AudioTypes.h
#ifndef __AUDIO_TYPES_H__
#define __AUDIO_TYPES_H__

#include "cocos2d.h"
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>

NS_CC_BEGIN

// 音频频道类型
enum class AudioChannelType {
    BGM,        // 背景音乐
    SFX,        // 音效
    AMBIENT,    // 环境音
    VOICE,      // 语音
    UI,         // UI音效
    SYSTEM      // 系统音效
};

// 音频状态
enum class AudioState {
    INITIALIZED,
    LOADING,
    READY,
    PLAYING,
    PAUSED,
    STOPPED,
    ERROR
};

// 音频焦点状态
enum class AudioFocusState {
    NO_FOCUS,           // 无焦点
    FOCUS_GAINED,       // 获得焦点
    FOCUS_LOST,         // 暂时失去焦点
    FOCUS_LOST_TRANSIENT, // 短暂失去焦点
    FOCUS_LOST_TRANSIENT_CAN_DUCK // 可压低音量
};

// 音频播放模式
enum class AudioPlayMode {
    ONCE,       // 单次播放
    LOOP,       // 循环播放
    BATCH       // 批次播放
};

// 音频优先级
enum class AudioPriority {
    CRITICAL = 0,    // 关键(如游戏结束音效)
    HIGH = 1,        // 高(如重要UI反馈)
    NORMAL = 2,      // 普通(如一般音效)
    LOW = 3,         // 低(如装饰性环境音)
    BACKGROUND = 4   // 背景(可随时停止)
};

// 音频资源信息
struct AudioResource {
    std::string resourceId;        // 资源ID
    std::string filePath;          // 文件路径
    AudioChannelType channelType;  // 频道类型
    AudioPriority priority;        // 优先级
    float baseVolume;              // 基础音量
    bool streamable;               // 是否可流式加载
    int estimatedDurationMs;       // 预估时长
    
    AudioResource()
        : channelType(AudioChannelType::SFX)
        , priority(AudioPriority::NORMAL)
        , baseVolume(1.0f)
        , streamable(false)
        , estimatedDurationMs(0) {}
};

// 音频实例信息
struct AudioInstance {
    int instanceId;                // 实例ID
    std::string resourceId;        // 资源ID
    AudioState state;              // 当前状态
    AudioChannelType channelType;  // 频道类型
    AudioPriority priority;        // 优先级
    float currentVolume;           // 当前音量
    float pitch;                   // 音调
    bool looping;                  // 是否循环
    double startTime;              // 开始播放时间
    double pausePosition;          // 暂停位置(秒)
    unsigned int nativeSoundId;    // 原生音频ID
    
    AudioInstance()
        : instanceId(0)
        , state(AudioState::INITIALIZED)
        , channelType(AudioChannelType::SFX)
        , priority(AudioPriority::NORMAL)
        , currentVolume(1.0f)
        , pitch(1.0f)
        , looping(false)
        , startTime(0.0)
        , pausePosition(0.0)
        , nativeSoundId(0) {}
};

// 频道统计信息
struct ChannelStats {
    AudioChannelType type;
    int activeCount;
    int totalCapacity;
    float averageVolume;
    float cpuUsage;
};

NS_CC_END

#endif // __AUDIO_TYPES_H__

6.2 音频频道实现

Classes/audio/AudioChannel.h
#ifndef __AUDIO_CHANNEL_H__
#define __AUDIO_CHANNEL_H__

#include "AudioTypes.h"
#include "cocos2d.h"
#include <queue>
#include <mutex>

NS_CC_BEGIN

class AudioChannel {
public:
    AudioChannel(AudioChannelType type, int maxConcurrent = 1);
    ~AudioChannel();
    
    // 频道管理
    bool initialize();
    void update(float dt);
    void cleanup();
    
    // 音频播放控制
    int playAudio(const AudioResource& resource, 
                  float volume = -1.0f, 
                  bool loop = false,
                  float pitch = 1.0f);
    void stopAudio(int instanceId);
    void stopAllAudio();
    void pauseAudio(int instanceId);
    void pauseAllAudio();
    void resumeAudio(int instanceId);
    void resumeAllAudio();
    
    // 状态查询
    bool isPlaying(int instanceId) const;
    bool isPaused(int instanceId) const;
    AudioState getAudioState(int instanceId) const;
    std::vector<int> getActiveInstanceIds() const;
    
    // 音量控制
    void setChannelVolume(float volume);
    void setAudioVolume(int instanceId, float volume);
    float getChannelVolume() const { return _channelVolume; }
    
    // 优先级管理
    void setAudioPriority(int instanceId, AudioPriority priority);
    
    // 统计信息
    ChannelStats getStats() const;
    
private:
    // 内部方法
    int generateInstanceId();
    void removeFinishedInstances();
    void prioritizeInstances();
    void limitConcurrentPlays();
    
    // 数据结构
    AudioChannelType _type;
    int _maxConcurrent;
    float _channelVolume;
    
    std::unordered_map<int, AudioInstance> _instances;
    std::unordered_map<std::string, AudioResource> _cachedResources;
    std::priority_queue<int, std::vector<int>, std::function<bool(int, int)>> _priorityQueue;
    
    int _nextInstanceId;
    mutable std::mutex _mutex;
    
    // 原生音频引擎引用
    class SimpleAudioEngine* _audioEngine;
};

NS_CC_END

#endif // __AUDIO_CHANNEL_H__
Classes/audio/AudioChannel.cpp
#include "AudioChannel.h"
#include "SimpleAudioEngine.h"
#include <algorithm>

USING_NS_CC;

AudioChannel::AudioChannel(AudioChannelType type, int maxConcurrent)
: _type(type)
, _maxConcurrent(maxConcurrent)
, _channelVolume(1.0f)
, _nextInstanceId(1) {
    
    _audioEngine = CocosDenshion::SimpleAudioEngine::getInstance();
    
    // 初始化优先级队列的比较函数
    _priorityQueue = std::priority_queue<int, std::vector<int>, std::function<bool(int, int)>>(
        [this](int a, int b) {
            auto itA = _instances.find(a);
            auto itB = _instances.find(b);
            if (itA != _instances.end() && itB != _instances.end()) {
                // 优先级数字越小优先级越高
                return itA->second.priority < itB->second.priority;
            }
            return false;
        }
    );
}

AudioChannel::~AudioChannel() {
    cleanup();
}

bool AudioChannel::initialize() {
    CCLOG("AudioChannel: Initializing channel type %d with capacity %d", 
          (int)_type, _maxConcurrent);
    return true;
}

void AudioChannel::update(float dt) {
    std::lock_guard<std::mutex> lock(_mutex);
    
    // 移除已完成的实例
    removeFinishedInstances();
    
    // 重新排序优先级
    prioritizeInstances();
    
    // 限制并发数量
    limitConcurrentPlays();
}

void AudioChannel::cleanup() {
    std::lock_guard<std::mutex> lock(_mutex);
    stopAllAudio();
    _instances.clear();
    _cachedResources.clear();
}

int AudioChannel::playAudio(const AudioResource& resource, 
                             float volume, 
                             bool loop,
                             float pitch) {
    std::lock_guard<std::mutex> lock(_mutex);
    
    // 检查资源缓存
    if (_cachedResources.find(resource.resourceId) == _cachedResources.end()) {
        _cachedResources[resource.resourceId] = resource;
    }
    
    // 检查并发限制
    int currentPlaying = 0;
    for (const auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING) {
            currentPlaying++;
        }
    }
    
    if (currentPlaying >= _maxConcurrent) {
        // 达到并发上限,根据优先级决定是否替换
        CCLOG("AudioChannel: Max concurrent reached (%d/%d), considering priority replacement", 
              currentPlaying, _maxConcurrent);
        
        // 找到最低优先级的播放中实例
        int lowestPriorityInstance = -1;
        AudioPriority lowestPriority = AudioPriority::CRITICAL;
        
        for (const auto& pair : _instances) {
            if (pair.second.state == AudioState::PLAYING && 
                pair.second.priority > lowestPriority) {
                lowestPriority = pair.second.priority;
                lowestPriorityInstance = pair.first;
            }
        }
        
        // 如果新音频优先级更高,替换最低优先级实例
        if (lowestPriorityInstance != -1 && resource.priority < lowestPriority) {
            stopAudio(lowestPriorityInstance);
            CCLOG("AudioChannel: Replaced low priority instance %d with higher priority %s", 
                  lowestPriorityInstance, resource.resourceId.c_str());
        } else {
            CCLOG("AudioChannel: Cannot play %s due to concurrency limit", 
                  resource.resourceId.c_str());
            return -1; // 无法播放
        }
    }
    
    // 生成实例ID
    int instanceId = generateInstanceId();
    
    // 确定音量
    float finalVolume = (volume > 0) ? volume : resource.baseVolume;
    finalVolume *= _channelVolume;
    finalVolume = cocos2d::clampf(finalVolume, 0.0f, 1.0f);
    
    // 播放音频
    unsigned int nativeSoundId = 0;
    bool success = false;
    
    if (_type == AudioChannelType::BGM) {
        // BGM频道使用背景音乐接口
        _audioEngine->playBackgroundMusic(resource.filePath.c_str(), loop);
        success = true;
        nativeSoundId = 1; // BGM使用特殊ID
    } else {
        // SFX等其他频道使用音效接口
        nativeSoundId = _audioEngine->playEffect(resource.filePath.c_str(), loop, finalVolume, pitch);
        success = (nativeSoundId != 0);
    }
    
    if (!success) {
        CCLOG("AudioChannel: Failed to play audio %s", resource.resourceId.c_str());
        return -1;
    }
    
    // 创建实例记录
    AudioInstance instance;
    instance.instanceId = instanceId;
    instance.resourceId = resource.resourceId;
    instance.state = AudioState::PLAYING;
    instance.channelType = resource.channelType;
    instance.priority = resource.priority;
    instance.currentVolume = finalVolume;
    instance.pitch = pitch;
    instance.looping = loop;
    instance.startTime = cocos2d::utils::getTimeInMilliseconds() / 1000.0;
    instance.pausePosition = 0.0;
    instance.nativeSoundId = nativeSoundId;
    
    _instances[instanceId] = instance;
    _priorityQueue.push(instanceId);
    
    CCLOG("AudioChannel: Started playing %s (instance: %d, volume: %.2f)", 
          resource.resourceId.c_str(), instanceId, finalVolume);
    
    return instanceId;
}

void AudioChannel::stopAudio(int instanceId) {
    std::lock_guard<std::mutex> lock(_mutex);
    
    auto it = _instances.find(instanceId);
    if (it != _instances.end()) {
        if (_type == AudioChannelType::BGM) {
            _audioEngine->stopBackgroundMusic();
        } else {
            _audioEngine->stopEffect(it->second.nativeSoundId);
        }
        
        it->second.state = AudioState::STOPPED;
        CCLOG("AudioChannel: Stopped instance %d", instanceId);
    }
}

void AudioChannel::stopAllAudio() {
    std::lock_guard<std::mutex> lock(_mutex);
    
    for (auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING || pair.second.state == AudioState::PAUSED) {
            if (_type == AudioChannelType::BGM) {
                _audioEngine->stopBackgroundMusic();
            } else {
                _audioEngine->stopEffect(pair.second.nativeSoundId);
            }
            pair.second.state = AudioState::STOPPED;
        }
    }
    
    CCLOG("AudioChannel: Stopped all audio in channel %d", (int)_type);
}

void AudioChannel::pauseAudio(int instanceId) {
    std::lock_guard<std::mutex> lock(_mutex);
    
    auto it = _instances.find(instanceId);
    if (it != _instances.end() && it->second.state == AudioState::PLAYING) {
        // 注意:SimpleAudioEngine的pauseEffect不支持单个音效暂停
        // 这里采用停止并记录位置的方式模拟暂停
        if (_type != AudioChannelType::BGM) {
            _audioEngine->stopEffect(it->second.nativeSoundId);
            it->second.pausePosition = cocos2d::utils::getTimeInMilliseconds() / 1000.0 - it->second.startTime;
        } else {
            _audioEngine->pauseBackgroundMusic();
        }
        
        it->second.state = AudioState::PAUSED;
        CCLOG("AudioChannel: Paused instance %d at position %.2fs", 
              instanceId, it->second.pausePosition);
    }
}

void AudioChannel::pauseAllAudio() {
    std::lock_guard<std::mutex> lock(_mutex);
    
    for (auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING) {
            if (_type == AudioChannelType::BGM) {
                _audioEngine->pauseBackgroundMusic();
            } else {
                _audioEngine->stopEffect(pair.second.nativeSoundId);
                pair.second.pausePosition = cocos2d::utils::getTimeInMilliseconds() / 1000.0 - pair.second.startTime;
            }
            pair.second.state = AudioState::PAUSED;
        }
    }
    
    CCLOG("AudioChannel: Paused all audio in channel %d", (int)_type);
}

void AudioChannel::resumeAudio(int instanceId) {
    std::lock_guard<std::mutex> lock(_mutex);
    
    auto it = _instances.find(instanceId);
    if (it != _instances.end() && it->second.state == AudioState::PAUSED) {
        auto resourceIt = _cachedResources.find(it->second.resourceId);
        if (resourceIt != _cachedResources.end()) {
            // 重新播放音频(从暂停位置开始)
            // 注意:由于SimpleAudioEngine的限制,这里无法实现真正的断点续播
            // 实际项目中需要使用支持位置控制的音频库
            float volume = it->second.currentVolume;
            bool loop = it->second.looping;
            float pitch = it->second.pitch;
            
            // 重新播放
            unsigned int newNativeId = 0;
            if (_type == AudioChannelType::BGM) {
                _audioEngine->resumeBackgroundMusic();
                newNativeId = 1;
            } else {
                newNativeId = _audioEngine->playEffect(resourceIt->second.filePath.c_str(), loop, volume, pitch);
            }
            
            if (newNativeId != 0) {
                it->second.nativeSoundId = newNativeId;
                it->second.state = AudioState::PLAYING;
                it->second.startTime = cocos2d::utils::getTimeInMilliseconds() / 1000.0 - it->second.pausePosition;
                it->second.pausePosition = 0.0;
                
                CCLOG("AudioChannel: Resumed instance %d", instanceId);
            } else {
                CCLOG("AudioChannel: Failed to resume instance %d", instanceId);
            }
        }
    }
}

void AudioChannel::resumeAllAudio() {
    std::lock_guard<std::mutex> lock(_mutex);
    
    for (auto& pair : _instances) {
        if (pair.second.state == AudioState::PAUSED) {
            resumeAudio(pair.first);
        }
    }
    
    CCLOG("AudioChannel: Resumed all audio in channel %d", (int)_type);
}

bool AudioChannel::isPlaying(int instanceId) const {
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _instances.find(instanceId);
    return it != _instances.end() && it->second.state == AudioState::PLAYING;
}

bool AudioChannel::isPaused(int instanceId) const {
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _instances.find(instanceId);
    return it != _instances.end() && it->second.state == AudioState::PAUSED;
}

AudioState AudioChannel::getAudioState(int instanceId) const {
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _instances.find(instanceId);
    return it != _instances.end() ? it->second.state : AudioState::ERROR;
}

std::vector<int> AudioChannel::getActiveInstanceIds() const {
    std::lock_guard<std::mutex> lock(_mutex);
    std::vector<int> activeIds;
    
    for (const auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING || pair.second.state == AudioState::PAUSED) {
            activeIds.push_back(pair.first);
        }
    }
    
    return activeIds;
}

void AudioChannel::setChannelVolume(float volume) {
    std::lock_guard<std::mutex> lock(_mutex);
    _channelVolume = cocos2d::clampf(volume, 0.0f, 1.0f);
    
    // 更新所有活跃实例的音量
    for (auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING) {
            float newVolume = pair.second.currentVolume / _channelVolume * _channelVolume; // 保持相对音量
            // 注意:SimpleAudioEngine不支持动态音量调整,实际项目需要平台特定实现
            CCLOG("AudioChannel: Would set volume for instance %d to %.2f", 
                  pair.first, newVolume);
        }
    }
    
    // 设置BGM音量
    if (_type == AudioChannelType::BGM) {
        _audioEngine->setBackgroundMusicVolume(_channelVolume);
    } else {
        _audioEngine->setEffectsVolume(_channelVolume);
    }
}

void AudioChannel::setAudioVolume(int instanceId, float volume) {
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _instances.find(instanceId);
    if (it != _instances.end() && it->second.state == AudioState::PLAYING) {
        it->second.currentVolume = cocos2d::clampf(volume, 0.0f, 1.0f);
        // 注意:SimpleAudioEngine不支持动态音量调整单个音效
        CCLOG("AudioChannel: Set volume for instance %d to %.2f (limited by engine)", 
              instanceId, it->second.currentVolume);
    }
}

void AudioChannel::setAudioPriority(int instanceId, AudioPriority priority) {
    std::lock_guard<std::mutex> lock(_mutex);
    auto it = _instances.find(instanceId);
    if (it != _instances.end()) {
        it->second.priority = priority;
        // 重新排序优先级队列
        // 注意:标准priority_queue不支持动态更新,实际项目需要使用更复杂的数据结构
        CCLOG("AudioChannel: Updated priority for instance %d to %d", 
              instanceId, (int)priority);
    }
}

ChannelStats AudioChannel::getStats() const {
    std::lock_guard<std::mutex> lock(_mutex);
    
    ChannelStats stats;
    stats.type = _type;
    stats.totalCapacity = _maxConcurrent;
    
    int activeCount = 0;
    float totalVolume = 0.0f;
    int volumeSamples = 0;
    
    for (const auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING || pair.second.state == AudioState::PAUSED) {
            activeCount++;
        }
        if (pair.second.state == AudioState::PLAYING) {
            totalVolume += pair.second.currentVolume;
            volumeSamples++;
        }
    }
    
    stats.activeCount = activeCount;
    stats.averageVolume = volumeSamples > 0 ? totalVolume / volumeSamples : 0.0f;
    stats.cpuUsage = 0.0f; // 实际项目中需要测量
    
    return stats;
}

int AudioChannel::generateInstanceId() {
    return _nextInstanceId++;
}

void AudioChannel::removeFinishedInstances() {
    // 由于SimpleAudioEngine的限制,无法准确检测音效何时播放完成
    // 这里采用简单的时长估算方法
    double currentTime = cocos2d::utils::getTimeInMilliseconds() / 1000.0;
    
    auto it = _instances.begin();
    while (it != _instances.end()) {
        if (it->second.state == AudioState::PLAYING) {
            auto resourceIt = _cachedResources.find(it->second.resourceId);
            if (resourceIt != _cachedResources.end()) {
                double elapsed = currentTime - it->second.startTime;
                if (!it->second.looping && elapsed >= (resourceIt->second.estimatedDurationMs / 1000.0)) {
                    CCLOG("AudioChannel: Instance %d finished playing (elapsed: %.2fs)", 
                          it->first, elapsed);
                    it->second.state = AudioState::STOPPED;
                    it = _instances.erase(it);
                    continue;
                }
            }
        }
        ++it;
    }
}

void AudioChannel::prioritizeInstances() {
    // 重建优先级队列
    std::vector<int> instances;
    for (const auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING || pair.second.state == AudioState::PAUSED) {
            instances.push_back(pair.first);
        }
    }
    
    std::sort(instances.begin(), instances.end(), 
        [this](int a, int b) {
            auto itA = _instances.find(a);
            auto itB = _instances.find(b);
            if (itA != _instances.end() && itB != _instances.end()) {
                return itA->second.priority < itB->second.priority;
            }
            return false;
        });
    
    // 清空并重新填充优先级队列
    std::priority_queue<int, std::vector<int>, std::function<bool(int, int)>> newQueue(
        [this](int a, int b) {
            auto itA = _instances.find(a);
            auto itB = _instances.find(b);
            if (itA != _instances.end() && itB != _instances.end()) {
                return itA->second.priority < itB->second.priority;
            }
            return false;
        }
    );
    
    for (int id : instances) {
        newQueue.push(id);
    }
    
    _priorityQueue = std::move(newQueue);
}

void AudioChannel::limitConcurrentPlays() {
    // 确保播放中的实例不超过最大并发数
    int playingCount = 0;
    std::vector<int> playingInstances;
    
    for (const auto& pair : _instances) {
        if (pair.second.state == AudioState::PLAYING) {
            playingCount++;
            playingInstances.push_back(pair.first);
        }
    }
    
    if (playingCount > _maxConcurrent) {
        // 按优先级排序,保留高优先级实例
        std::sort(playingInstances.begin(), playingInstances.end(),
            [this](int a, int b) {
                auto itA = _instances.find(a);
                auto itB = _instances.find(b);
                if (itA != _instances.end() && itB != _instances.end()) {
                    return itA->second.priority > itB->second.priority; // 降序排列,保留前面的
                }
                return false;
            });
        
        // 停止超出限制的实例(从低优先级开始)
        int excessCount = playingCount - _maxConcurrent;
        for (int i = 0; i < excessCount && i < playingInstances.size(); i++) {
            stopAudio(playingInstances[playingInstances.size() - 1 - i]); // 停止最后的(最低优先级)
        }
    }
}

6.3 音频焦点管理器

Classes/audio/AudioFocusManager.h
#ifndef __AUDIO_FOCUS_MANAGER_H__
#define __AUDIO_FOCUS_MANAGER_H__

#include "AudioTypes.h"
#include "AudioChannel.h"
#include "cocos2d.h"
#include <functional>
#include <vector>

NS_CC_BEGIN

class AudioFocusManager {
public:
    static AudioFocusManager* getInstance();
    static void destroyInstance();
    
    bool initialize();
    void update(float dt);
    
    // 焦点状态管理
    AudioFocusState getFocusState() const { return _currentFocusState; }
    bool hasAudioFocus() const { 
        return _currentFocusState == AudioFocusState::FOCUS_GAINED ||
               _currentFocusState == AudioFocusState::FOCUS_LOST_TRANSIENT_CAN_DUCK;
    }
    
    // 焦点变化处理
    void onFocusLost(AudioFocusState reason);
    void onFocusGained();
    
    // 响应策略配置
    void setFocusResponseStrategy(AudioChannelType channelType, 
                                  AudioFocusState focusState,
                                  const std::function<void(AudioChannel*)>& strategy);
    
    // 频道注册
    void registerChannel(AudioChannelType type, AudioChannel* channel);
    void unregisterChannel(AudioChannelType type);
    
private:
    AudioFocusManager();
    ~AudioFocusManager();
    
    static AudioFocusManager* _instance;
    
    AudioFocusState _currentFocusState;
    std::unordered_map<AudioChannelType, AudioChannel*> _channels;
    std::unordered_map<std::string, std::function<void(AudioChannel*)>> _responseStrategies;
    
    // 平台特定实现
    void platformSpecificInit();
    void platformSpecificCleanup();
    void notifySystemFocusChange(AudioFocusState state);
};

NS_CC_END

#endif // __AUDIO_FOCUS_MANAGER_H__
Classes/audio/AudioFocusManager.cpp
#include "AudioFocusManager.h"
#include "PlatformAudioFocus.h"

USING_NS_CC;

AudioFocusManager* AudioFocusManager::_instance = nullptr;

AudioFocusManager::AudioFocusManager() 
: _currentFocusState(AudioFocusState::FOCUS_GAINED) {
    
}

AudioFocusManager::~AudioFocusManager() {
    _channels.clear();
    _responseStrategies.clear();
}

AudioFocusManager* AudioFocusManager::getInstance() {
    if (!_instance) {
        _instance = new (std::nothrow) AudioFocusManager();
        if (_instance && _instance->initialize()) {
            // 初始化成功
        } else {
            CC_SAFE_DELETE(_instance);
        }
    }
    return _instance;
}

void AudioFocusManager::destroyInstance() {
    CC_SAFE_DELETE(_instance);
}

bool AudioFocusManager::initialize() {
    CCLOG("AudioFocusManager: Initializing...");
    
    // 初始化平台特定音频焦点
    auto platformFocus = PlatformAudioFocus::getInstance();
    if (platformFocus) {
        platformFocus->init();
        platformFocus->setFocusChangeCallback(
            [this](AudioFocusState state) {
                this->onFocusLost(state);
            }
        );
    }
    
    // 设置默认响应策略
    setFocusResponseStrategy(AudioChannelType::BGM, 
                           AudioFocusState::FOCUS_LOST_TRANSIENT_CAN_DUCK,
                           [](AudioChannel* channel) {
                               channel->setChannelVolume(0.3f); // 压低BGM音量
                           });
    
    setFocusResponseStrategy(AudioChannelType::SFX, 
                           AudioFocusState::FOCUS_LOST_TRANSIENT_CAN_DUCK,
                           [](AudioChannel* channel) {
                               channel->pauseAllAudio(); // 暂停SFX
                           });
    
    setFocusResponseStrategy(AudioChannelType::AMBIENT, 
                           AudioFocusState::FOCUS_LOST,
                           [](AudioChannel* channel) {
                               channel->stopAllAudio(); // 停止环境音
                           });
    
    platformSpecificInit();
    return true;
}

void AudioFocusManager::update(float dt) {
    // 检查系统焦点状态
    auto platformFocus = PlatformAudioFocus::getInstance();
    if (platformFocus && !platformFocus->hasAudioFocus() && 
        _currentFocusState == AudioFocusState::FOCUS_GAINED) {
        onFocusLost(AudioFocusState::FOCUS_LOST);
    }
}

void AudioFocusManager::onFocusLost(AudioFocusState reason) {
    CCLOG("AudioFocusManager: Focus lost - %d", (int)reason);
    _currentFocusState = reason;
    
    // 应用响应策略
    for (const auto& channelPair : _channels) {
        AudioChannelType type = channelPair.first;
        AudioChannel* channel = channelPair.second;
        
        std::string strategyKey = StringUtils::format("%d_%d", (int)type, (int)reason);
        auto it = _responseStrategies.find(strategyKey);
        if (it != _responseStrategies.end()) {
            it->second(channel);
        } else {
            // 应用默认策略
            switch (reason) {
                case AudioFocusState::FOCUS_LOST_TRANSIENT_CAN_DUCK:
                    if (type == AudioChannelType::BGM) {
                        channel->setChannelVolume(0.3f);
                    } else {
                        channel->pauseAllAudio();
                    }
                    break;
                case AudioFocusState::FOCUS_LOST_TRANSIENT:
                case AudioFocusState::FOCUS_LOST:
                    channel->pauseAllAudio();
                    break;
                default:
                    break;
            }
        }
    }
    
    notifySystemFocusChange(reason);
}

void AudioFocusManager::onFocusGained() {
    CCLOG("AudioFocusManager: Focus gained");
    AudioFocusState previousState = _currentFocusState;
    _currentFocusState = AudioFocusState::FOCUS_GAINED;
    
    // 恢复音频
    for (const auto& channelPair : _channels) {
        AudioChannel* channel = channelPair.second;
        
        switch (previousState) {
            case AudioFocusState::FOCUS_LOST_TRANSIENT_CAN_DUCK:
                if (channelPair.first == AudioChannelType::BGM) {
                    channel->setChannelVolume(1.0f); // 恢复BGM音量
                } else {
                    channel->resumeAllAudio(); // 恢复SFX
                }
                break;
            case AudioFocusState::FOCUS_LOST_TRANSIENT:
            case AudioFocusState::FOCUS_LOST:
                channel->resumeAllAudio(); // 恢复所有音频
                break;
            default:
                break;
        }
    }
    
    notifySystemFocusChange(_currentFocusState);
}

void AudioFocusManager::setFocusResponseStrategy(AudioChannelType channelType, 
                                               AudioFocusState focusState,
                                               const std::function<void(AudioChannel*)>& strategy) {
    std::string key = StringUtils::format("%d_%d", (int)channelType, (int)focusState);
    _responseStrategies[key] = strategy;
    CCLOG("AudioFocusManager: Set strategy for channel %d, focus state %d", 
          (int)channelType, (int)focusState);
}

void AudioFocusManager::registerChannel(AudioChannelType type, AudioChannel* channel) {
    _channels[type] = channel;
    CCLOG("AudioFocusManager: Registered channel type %d", (int)type);
}

void AudioFocusManager::unregisterChannel(AudioChannelType type) {
    auto it = _channels.find(type);
    if (it != _channels.end()) {
        _channels.erase(it);
        CCLOG("AudioFocusManager: Unregistered channel type %d", (int)type);
    }
}

void AudioFocusManager::platformSpecificInit() {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        // Android特定初始化
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // iOS特定初始化
    #endif
}

void AudioFocusManager::platformSpecificCleanup() {
    // 平台特定清理
}

void AudioFocusManager::notifySystemFocusChange(AudioFocusState state) {
    // 通知系统焦点变化(用于调试或统计)
    CCLOG("AudioFocusManager: Notified system of focus change to %d", (int)state);
}

6.4 主音频管理器

Classes/audio/AudioManager.h
#ifndef __AUDIO_MANAGER_H__
#define __AUDIO_MANAGER_H__

#include "AudioTypes.h"
#include "AudioChannel.h"
#include "AudioFocusManager.h"
#include "cocos2d.h"
#include <memory>

NS_CC_BEGIN

class AudioManager {
public:
    static AudioManager* getInstance();
    static void destroyInstance();
    
    bool initialize();
    void update(float dt);
    void cleanup();
    
    // 资源管理
    void registerAudioResource(const AudioResource& resource);
    AudioResource* getAudioResource(const std::string& resourceId);
    
    // 播放控制
    int playAudio(const std::string& resourceId, 
                  AudioChannelType channelType = AudioChannelType::SFX,
                  float volume = -1.0f, 
                  bool loop = false,
                  float pitch = 1.0f);
    
    void stopAudio(int instanceId);
    void stopAudioByChannel(AudioChannelType channelType);
    void stopAllAudio();
    
    void pauseAudio(int instanceId);
    void pauseAudioByChannel(AudioChannelType channelType);
    void pauseAllAudio();
    
    void resumeAudio(int instanceId);
    void resumeAudioByChannel(AudioChannelType channelType);
    void resumeAllAudio();
    
    // 状态查询
    bool isPlaying(int instanceId) const;
    AudioState getAudioState(int instanceId) const;
    std::vector<int> getActiveInstances(AudioChannelType channelType = AudioChannelType::SFX) const;
    
    // 音量控制
    void setMasterVolume(float volume);
    void setChannelVolume(AudioChannelType channelType, float volume);
    void setAudioVolume(int instanceId, float volume);
    float getMasterVolume() const { return _masterVolume; }
    
    // 焦点管理
    AudioFocusState getFocusState() const;
    void requestAudioFocus();
    void abandonAudioFocus();
    
    // 统计信息
    struct AudioStats {
        int totalChannels;
        int totalActiveInstances;
        float memoryUsageMB;
        float cpuUsagePercent;
        std::unordered_map<AudioChannelType, ChannelStats> channelStats;
    };
    
    AudioStats getStats() const;
    
private:
    AudioManager();
    ~AudioManager();
    
    static AudioManager* _instance;
    
    // 核心组件
    std::unique_ptr<AudioFocusManager> _focusManager;
    std::unordered_map<AudioChannelType, std::unique_ptr<AudioChannel>> _channels;
    std::unordered_map<std::string, AudioResource> _audioResources;
    
    // 全局设置
    float _masterVolume;
    bool _initialized;
    
    // 内部方法
    void createDefaultChannels();
    AudioChannel* getChannel(AudioChannelType type) const;
    void applyMasterVolume();
};

NS_CC_END

#endif // __AUDIO_MANAGER_H__
Classes/audio/AudioManager.cpp
#include "AudioManager.h"

USING_NS_CC;

AudioManager* AudioManager::_instance = nullptr;

AudioManager::AudioManager() 
: _masterVolume(1.0f)
, _initialized(false) {
    
    _focusManager = std::make_unique<AudioFocusManager>();
}

AudioManager::~AudioManager() {
    cleanup();
}

AudioManager* AudioManager::getInstance() {
    if (!_instance) {
        _instance = new (std::nothrow) AudioManager();
        if (_instance && _instance->initialize()) {
            // 初始化成功
        } else {
            CC_SAFE_DELETE(_instance);
        }
    }
    return _instance;
}

void AudioManager::destroyInstance() {
    CC_SAFE_DELETE(_instance);
}

bool AudioManager::initialize() {
    if (_initialized) {
        return true;
    }
    
    CCLOG("AudioManager: Initializing...");
    
    // 创建默认频道
    createDefaultChannels();
    
    // 初始化焦点管理器
    if (_focusManager) {
        _focusManager->initialize();
    }
    
    // 注册系统更新
    Director::getInstance()->getScheduler()->scheduleUpdate(this, 0, false);
    
    _initialized = true;
    CCLOG("AudioManager: Initialization complete");
    return true;
}

void AudioManager::update(float dt) {
    if (!_initialized) return;
    
    // 更新焦点管理器
    if (_focusManager) {
        _focusManager->update(dt);
    }
    
    // 更新所有频道
    for (const auto& channelPair : _channels) {
        channelPair.second->update(dt);
    }
}

void AudioManager::cleanup() {
    if (!_initialized) return;
    
    stopAllAudio();
    
    // 清理频道
    _channels.clear();
    
    // 清理资源
    _audioResources.clear();
    
    // 停止更新
    Director::getInstance()->getScheduler()->unscheduleUpdate(this);
    
    _initialized = false;
    CCLOG("AudioManager: Cleanup complete");
}

void AudioManager::createDefaultChannels() {
    // 创建BGM频道(单实例)
    auto bgmChannel = std::make_unique<AudioChannel>(AudioChannelType::BGM, 1);
    bgmChannel->initialize();
    _channels[AudioChannelType::BGM] = std::move(bgmChannel);
    
    // 创建SFX频道(多实例,根据平台调整)
    int sfxCapacity = 16; // 默认容量
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        sfxCapacity = 8; // Android通常限制较多
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        sfxCapacity = 32; // iOS性能较好
    #endif
    
    auto sfxChannel = std::make_unique<AudioChannel>(AudioChannelType::SFX, sfxCapacity);
    sfxChannel->initialize();
    _channels[AudioChannelType::SFX] = std::move(sfxChannel);
    
    // 创建环境音频道
    auto ambientChannel = std::make_unique<AudioChannel>(AudioChannelType::AMBIENT, 4);
    ambientChannel->initialize();
    _channels[AudioChannelType::AMBIENT] = std::move(ambientChannel);
    
    // 创建语音频道
    auto voiceChannel = std::make_unique<AudioChannel>(AudioChannelType::VOICE, 2);
    voiceChannel->initialize();
    _channels[AudioChannelType::VOICE] = std::move(voiceChannel);
    
    // 创建UI频道
    auto uiChannel = std::make_unique<AudioChannel>(AudioChannelType::UI, 8);
    uiChannel->initialize();
    _channels[AudioChannelType::UI] = std::move(uiChannel);
    
    // 注册频道到焦点管理器
    if (_focusManager) {
        for (const auto& channelPair : _channels) {
            _focusManager->registerChannel(channelPair.first, channelPair.second.get());
        }
    }
    
    CCLOG("AudioManager: Created %zu default channels", _channels.size());
}

AudioChannel* AudioManager::getChannel(AudioChannelType type) const {
    auto it = _channels.find(type);
    return it != _channels.end() ? it->second.get() : nullptr;
}

void AudioManager::registerAudioResource(const AudioResource& resource) {
    _audioResources[resource.resourceId] = resource;
    CCLOG("AudioManager: Registered resource %s", resource.resourceId.c_str());
}

AudioResource* AudioManager::getAudioResource(const std::string& resourceId) {
    auto it = _audioResources.find(resourceId);
    return it != _audioResources.end() ? &it->second : nullptr;
}

int AudioManager::playAudio(const std::string& resourceId, 
                           AudioChannelType channelType,
                           float volume, 
                           bool loop,
                           float pitch) {
    if (!_initialized) {
        CCLOG("AudioManager: Not initialized, cannot play audio");
        return -1;
    }
    
    // 获取音频资源
    AudioResource* resource = getAudioResource(resourceId);
    if (!resource) {
        CCLOG("AudioManager: Resource not found - %s", resourceId.c_str());
        return -1;
    }
    
    // 强制使用资源指定的频道类型(如果提供)
    AudioChannelType actualChannelType = resource->channelType != AudioChannelType::SFX ? 
                                        resource->channelType : channelType;
    
    // 获取对应频道
    AudioChannel* channel = getChannel(actualChannelType);
    if (!channel) {
        CCLOG("AudioManager: Channel not found - %d", (int)actualChannelType);
        return -1;
    }
    
    // 播放音频
    int instanceId = channel->playAudio(*resource, volume, loop, pitch);
    if (instanceId > 0) {
        CCLOG("AudioManager: Successfully started playing %s (instance: %d, channel: %d)", 
              resourceId.c_str(), instanceId, (int)actualChannelType);
    }
    
    return instanceId;
}

void AudioManager::stopAudio(int instanceId) {
    if (!_initialized) return;
    
    // 在所有频道中查找并停止该实例
    for (const auto& channelPair : _channels) {
        if (channelPair.second->isPlaying(instanceId) || channelPair.second->isPaused(instanceId)) {
            channelPair.second->stopAudio(instanceId);
            CCLOG("AudioManager: Stopped instance %d from channel %d", 
                  instanceId, (int)channelPair.first);
            return;
        }
    }
    
    CCLOG("AudioManager: Instance %d not found for stopping", instanceId);
}

void AudioManager::stopAudioByChannel(AudioChannelType channelType) {
    AudioChannel* channel = getChannel(channelType);
    if (channel) {
        channel->stopAllAudio();
        CCLOG("AudioManager: Stopped all audio in channel %d", (int)channelType);
    }
}

void AudioManager::stopAllAudio() {
    if (!_initialized) return;
    
    for (const auto& channelPair : _channels) {
        channelPair.second->stopAllAudio();
    }
    
    CCLOG("AudioManager: Stopped all audio across all channels");
}

void AudioManager::pauseAudio(int instanceId) {
    if (!_initialized) return;
    
    for (const auto& channelPair : _channels) {
        if (channelPair.second->isPlaying(instanceId)) {
            channelPair.second->pauseAudio(instanceId);
            CCLOG("AudioManager: Paused instance %d in channel %d", 
                  instanceId, (int)channelPair.first);
            return;
        }
    }
    
    CCLOG("AudioManager: Instance %d not found for pausing", instanceId);
}

void AudioManager::pauseAudioByChannel(AudioChannelType channelType) {
    AudioChannel* channel = getChannel(channelType);
    if (channel) {
        channel->pauseAllAudio();
        CCLOG("AudioManager: Paused all audio in channel %d", (int)channelType);
    }
}

void AudioManager::pauseAllAudio() {
    if (!_initialized) return;
    
    for (const auto& channelPair : _channels) {
        channelPair.second->pauseAllAudio();
    }
    
    CCLOG("AudioManager: Paused all audio across all channels");
}

void AudioManager::resumeAudio(int instanceId) {
    if (!_initialized) return;
    
    for (const auto& channelPair : _channels) {
        if (channelPair.second->isPaused(instanceId)) {
            channelPair.second->resumeAudio(instanceId);
            CCLOG("AudioManager: Resumed instance %d in channel %d", 
                  instanceId, (int)channelType);
            return;
        }
    }
    
    CCLOG("AudioManager: Instance %d not found for resuming", instanceId);
}

void AudioManager::resumeAudioByChannel(AudioChannelType channelType) {
    AudioChannel* channel = getChannel(channelType);
    if (channel) {
        channel->resumeAllAudio();
        CCLOG("AudioManager: Resumed all audio in channel %d", (int)channelType);
    }
}

void AudioManager::resumeAllAudio() {
    if (!_initialized) return;
    
    for (const auto& channelPair : _channels) {
        channelPair.second->resumeAllAudio();
    }
    
    CCLOG("AudioManager: Resumed all audio across all channels");
}

bool AudioManager::isPlaying(int instanceId) const {
    if (!_initialized) return false;
    
    for (const auto& channelPair : _channels) {
        if (channelPair.second->isPlaying(instanceId)) {
            return true;
        }
    }
    
    return false;
}

AudioState AudioManager::getAudioState(int instanceId) const {
    if (!_initialized) return AudioState::ERROR;
    
    for (const auto& channelPair : _channels) {
        AudioState state = channelPair.second->getAudioState(instanceId);
        if (state != AudioState::ERROR) {
            return state;
        }
    }
    
    return AudioState::ERROR;
}

std::vector<int> AudioManager::getActiveInstances(AudioChannelType channelType) const {
    std::vector<int> instances;
    
    if (!_initialized) return instances;
    
    if (channelType == AudioChannelType::SFX) {
        // 如果是SFX类型,返回所有频道的活跃实例
        for (const auto& channelPair : _channels) {
            auto channelInstances = channelPair.second->getActiveInstanceIds();
            instances.insert(instances.end(), channelInstances.begin(), channelInstances.end());
        }
    } else {
        // 返回指定频道的活跃实例
        AudioChannel* channel = getChannel(channelType);
        if (channel) {
            instances = channel->getActiveInstanceIds();
        }
    }
    
    return instances;
}

void AudioManager::setMasterVolume(float volume) {
    _masterVolume = cocos2d::clampf(volume, 0.0f, 1.0f);
    applyMasterVolume();
    CCLOG("AudioManager: Master volume set to %.2f", _masterVolume);
}

void AudioManager::setChannelVolume(AudioChannelType channelType, float volume) {
    AudioChannel* channel = getChannel(channelType);
    if (channel) {
        channel->setChannelVolume(volume);
        CCLOG("AudioManager: Channel %d volume set to %.2f", (int)channelType, volume);
    }
}

void AudioManager::setAudioVolume(int instanceId, float volume) {
    for (const auto& channelPair : _channels) {
        if (channelPair.second->isPlaying(instanceId) || channelPair.second->isPaused(instanceId)) {
            channelPair.second->setAudioVolume(instanceId, volume);
            CCLOG("AudioManager: Instance %d volume set to %.2f", instanceId, volume);
            return;
        }
    }
}

void AudioManager::applyMasterVolume() {
    // 应用主音量到所有频道
    for (const auto& channelPair : _channels) {
        float channelVolume = channelPair.second->getChannelVolume();
        channelPair.second->setChannelVolume(channelVolume * _masterVolume);
    }
}

AudioFocusState AudioManager::getFocusState() const {
    return _focusManager ? _focusManager->getFocusState() : AudioFocusState::NO_FOCUS;
}

void AudioManager::requestAudioFocus() {
    if (_focusManager) {
        // 实际项目中需要实现具体的焦点请求逻辑
        CCLOG("AudioManager: Requesting audio focus");
    }
}

void AudioManager::abandonAudioFocus() {
    if (_focusManager) {
        // 实际项目中需要实现具体的焦点放弃逻辑
        CCLOG("AudioManager: Abandoning audio focus");
    }
}

AudioManager::AudioStats AudioManager::getStats() const {
    AudioStats stats;
    stats.totalChannels = _channels.size();
    stats.totalActiveInstances = 0;
    stats.memoryUsageMB = 0.0f;
    stats.cpuUsagePercent = 0.0f;
    
    for (const auto& channelPair : _channels) {
        auto channelStats = channelPair.second->getStats();
        stats.totalActiveInstances += channelStats.activeCount;
        stats.channelStats[channelPair.first] = channelStats;
    }
    
    return stats;
}

6.5 平台特定音频焦点实现

Classes/platforms/PlatformAudioFocus.cpp
#include "PlatformAudioFocus.h"

#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
#include <jni.h>
#include "platform/android/jni/JniHelper.h"
#endif

USING_NS_CC;

PlatformAudioFocus* PlatformAudioFocus::_instance = nullptr;

PlatformAudioFocus::PlatformAudioFocus() {
    
}

PlatformAudioFocus::~PlatformAudioFocus() {
    platformSpecificCleanup();
}

PlatformAudioFocus* PlatformAudioFocus::getInstance() {
    if (!_instance) {
        _instance = new (std::nothrow) PlatformAudioFocus();
    }
    return _instance;
}

void PlatformAudioFocus::destroyInstance() {
    CC_SAFE_DELETE(_instance);
}

bool PlatformAudioFocus::init() {
    CCLOG("PlatformAudioFocus: Initializing platform-specific audio focus");
    platformSpecificInit();
    return true;
}

bool PlatformAudioFocus::hasAudioFocus() {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        // Android实现
        JniMethodInfo methodInfo;
        bool hasMethod = JniHelper::getStaticMethodInfo(methodInfo,
            "org/cocos2dx/cpp/AppActivity", "hasAudioFocus", "()Z");
        if (hasMethod) {
            jboolean result = methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID);
            methodInfo.env->DeleteLocalRef(methodInfo.classID);
            return result;
        }
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // iOS实现 - 简化版
        // 实际项目中需要调用AVAudioSession的API
        return true; // 简化返回
    #else
        // 其他平台默认返回true
        return true;
    #endif
    
    return true;
}

bool PlatformAudioFocus::requestAudioFocus() {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        JniMethodInfo methodInfo;
        bool hasMethod = JniHelper::getStaticMethodInfo(methodInfo,
            "org/cocos2dx/cpp/AppActivity", "requestAudioFocus", "()Z");
        if (hasMethod) {
            jboolean result = methodInfo.env->CallStaticBooleanMethod(methodInfo.classID, methodInfo.methodID);
            methodInfo.env->DeleteLocalRef(methodInfo.classID);
            return result;
        }
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // iOS实现
        // [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {}];
        return true;
    #else
        return true;
    #endif
    
    return false;
}

bool PlatformAudioFocus::abandonAudioFocus() {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        JniMethodInfo methodInfo;
        bool hasMethod = JniHelper::getStaticMethodInfo(methodInfo,
            "org/cocos2dx/cpp/AppActivity", "abandonAudioFocus", "()V");
        if (hasMethod) {
            methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID);
            methodInfo.env->DeleteLocalRef(methodInfo.classID);
            return true;
        }
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // iOS实现
        // [[AVAudioSession sharedInstance] setActive:NO error:nil];
        return true;
    #else
        return true;
    #endif
    
    return false;
}

void PlatformAudioFocus::setFocusChangeCallback(const std::function<void(AudioFocusState)>& callback) {
    _focusCallback = callback;
    
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        // 设置JNI回调
        JniMethodInfo methodInfo;
        bool hasMethod = JniHelper::getStaticMethodInfo(methodInfo,
            "org/cocos2dx/cpp/AppActivity", "setAudioFocusCallback", "(Z)V");
        if (hasMethod) {
            // 这里需要传递回调函数给Java层
            // 简化实现,实际项目需要更复杂的JNI通信
        }
    #endif
}

void PlatformAudioFocus::platformSpecificInit() {
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        // Android特定初始化代码
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // iOS特定初始化代码
    #endif
}

void PlatformAudioFocus::platformSpecificCleanup() {
    abandonAudioFocus();
}

6.6 场景集成示例

Classes/scenes/AudioChannelDemoScene.h
#ifndef __AUDIO_CHANNEL_DEMO_SCENE_H__
#define __AUDIO_CHANNEL_DEMO_SCENE_H__

#include "cocos2d.h"
#include "audio/AudioManager.h"

NS_CC_BEGIN

class AudioChannelDemoScene : public Scene {
public:
    static Scene* createScene();
    virtual bool init() override;
    CREATE_FUNC(AudioChannelDemoScene);
    
private:
    void createUI();
    void onPlayBGMClicked(Ref* sender);
    void onPlaySFXClicked(Ref* sender);
    void onPlayAmbientClicked(Ref* sender);
    void onPauseAllClicked(Ref* sender);
    void onResumeAllClicked(Ref* sender);
    void onStopAllClicked(Ref* sender);
    void onFocusTestClicked(Ref* sender);
    void updateDebugInfo(float dt);
    
    Layer* _uiLayer;
    Label* _debugLabel;
    std::vector<int> _activeInstances;
};

NS_CC_END

#endif // __AUDIO_CHANNEL_DEMO_SCENE_H__
Classes/scenes/AudioChannelDemoScene.cpp
#include "AudioChannelDemoScene.h"

USING_NS_CC;

Scene* AudioChannelDemoScene::createScene() {
    auto scene = AudioChannelDemoScene::create();
    return scene;
}

bool AudioChannelDemoScene::init() {
    if (!Scene::init()) {
        return false;
    }
    
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    _uiLayer = Layer::create();
    this->addChild(_uiLayer);
    
    // 标题
    auto title = Label::createWithTTF("Audio Channel Management Demo", "fonts/arial.ttf", 36);
    title->setPosition(Vec2(origin.x + visibleSize.width / 2,
                            origin.y + visibleSize.height * 0.95));
    title->setColor(Color3B::WHITE);
    _uiLayer->addChild(title);
    
    // 调试信息标签
    _debugLabel = Label::createWithTTF("Initializing...", "fonts/arial.ttf", 20);
    _debugLabel->setPosition(Vec2(origin.x + visibleSize.width / 2,
                                  origin.y + visibleSize.height * 0.85));
    _debugLabel->setColor(Color3B::YELLOW);
    _uiLayer->addChild(_debugLabel);
    
    createUI();
    
    // 注册测试音频资源
    auto audioManager = AudioManager::getInstance();
    
    AudioResource bgmResource;
    bgmResource.resourceId = "demo_bgm";
    bgmResource.filePath = "audio/bgm/demo_background.mp3";
    bgmResource.channelType = AudioChannelType::BGM;
    bgmResource.priority = AudioPriority::BACKGROUND;
    bgmResource.baseVolume = 0.7f;
    bgmResource.streamable = true;
    bgmResource.estimatedDurationMs = 60000; // 1分钟
    audioManager->registerAudioResource(bgmResource);
    
    AudioResource sfxResource;
    sfxResource.resourceId = "demo_sfx";
    sfxResource.filePath = "audio/sfx/demo_effect.wav";
    sfxResource.channelType = AudioChannelType::SFX;
    sfxResource.priority = AudioPriority::NORMAL;
    sfxResource.baseVolume = 0.8f;
    sfxResource.estimatedDurationMs = 500;
    audioManager->registerAudioResource(sfxResource);
    
    AudioResource ambientResource;
    ambientResource.resourceId = "demo_ambient";
    ambientResource.filePath = "audio/ambient/demo_wind.ogg";
    ambientResource.channelType = AudioChannelType::AMBIENT;
    ambientResource.priority = AudioPriority::LOW;
    ambientResource.baseVolume = 0.5f;
    ambientResource.loop = true;
    ambientResource.estimatedDurationMs = 30000;
    audioManager->registerAudioResource(ambientResource);
    
    // 启动调试信息更新
    this->schedule(CC_SCHEDULE_SELECTOR(AudioChannelDemoScene::updateDebugInfo), 0.5f);
    
    return true;
}

void AudioChannelDemoScene::createUI() {
    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();
    
    Menu* menu = Menu::create();
    menu->setPosition(Vec2::ZERO);
    _uiLayer->addChild(menu);
    
    // BGM控制按钮
    auto bgmBtn = MenuItemLabel::create(
        Label::createWithTTF("Play BGM", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onPlayBGMClicked, this)
    );
    bgmBtn->setPosition(Vec2(origin.x + visibleSize.width / 2,
                             origin.y + visibleSize.height * 0.75));
    menu->addChild(bgmBtn);
    
    // SFX控制按钮
    auto sfxBtn = MenuItemLabel::create(
        Label::createWithTTF("Play SFX", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onPlaySFXClicked, this)
    );
    sfxBtn->setPosition(Vec2(origin.x + visibleSize.width / 2,
                             origin.y + visibleSize.height * 0.65));
    menu->addChild(sfxBtn);
    
    // 环境音控制按钮
    auto ambientBtn = MenuItemLabel::create(
        Label::createWithTTF("Play Ambient", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onPlayAmbientClicked, this)
    );
    ambientBtn->setPosition(Vec2(origin.x + visibleSize.width / 2,
                                 origin.y + visibleSize.height * 0.55));
    menu->addChild(ambientBtn);
    
    // 控制按钮组
    auto pauseBtn = MenuItemLabel::create(
        Label::createWithTTF("Pause All", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onPauseAllClicked, this)
    );
    pauseBtn->setPosition(Vec2(origin.x + visibleSize.width / 4,
                               origin.y + visibleSize.height * 0.4));
    menu->addChild(pauseBtn);
    
    auto resumeBtn = MenuItemLabel::create(
        Label::createWithTTF("Resume All", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onResumeAllClicked, this)
    );
    resumeBtn->setPosition(Vec2(origin.x + visibleSize.width / 2,
                                origin.y + visibleSize.height * 0.4));
    menu->addChild(resumeBtn);
    
    auto stopBtn = MenuItemLabel::create(
        Label::createWithTTF("Stop All", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onStopAllClicked, this)
    );
    stopBtn->setPosition(Vec2(origin.x + visibleSize.width * 3/4,
                              origin.y + visibleSize.height * 0.4));
    menu->addChild(stopBtn);
    
    // 焦点测试按钮
    auto focusBtn = MenuItemLabel::create(
        Label::createWithTTF("Test Audio Focus", "fonts/arial.ttf", 24),
        CC_CALLBACK_1(AudioChannelDemoScene::onFocusTestClicked, this)
    );
    focusBtn->setPosition(Vec2(origin.x + visibleSize.width / 2,
                               origin.y + visibleSize.height * 0.25));
    menu->addChild(focusBtn);
}

void AudioChannelDemoScene::onPlayBGMClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    int instanceId = audioManager->playAudio("demo_bgm", AudioChannelType::BGM, 0.7f, true);
    
    if (instanceId > 0) {
        _activeInstances.push_back(instanceId);
        _debugLabel->setString(StringUtils::format("Started BGM (Instance: %d)", instanceId));
        CCLOG("Demo: Started BGM with instance %d", instanceId);
    } else {
        _debugLabel->setString("Failed to start BGM");
    }
}

void AudioChannelDemoScene::onPlaySFXClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    int instanceId = audioManager->playAudio("demo_sfx", AudioChannelType::SFX, 0.8f, false);
    
    if (instanceId > 0) {
        _activeInstances.push_back(instanceId);
        _debugLabel->setString(StringUtils::format("Started SFX (Instance: %d)", instanceId));
        CCLOG("Demo: Started SFX with instance %d", instanceId);
    } else {
        _debugLabel->setString("Failed to start SFX");
    }
}

void AudioChannelDemoScene::onPlayAmbientClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    int instanceId = audioManager->playAudio("demo_ambient", AudioChannelType::AMBIENT, 0.5f, true);
    
    if (instanceId > 0) {
        _activeInstances.push_back(instanceId);
        _debugLabel->setString(StringUtils::format("Started Ambient (Instance: %d)", instanceId));
        CCLOG("Demo: Started Ambient with instance %d", instanceId);
    } else {
        _debugLabel->setString("Failed to start Ambient");
    }
}

void AudioChannelDemoScene::onPauseAllClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    audioManager->pauseAllAudio();
    _debugLabel->setString("All audio paused");
    CCLOG("Demo: All audio paused");
}

void AudioChannelDemoScene::onResumeAllClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    audioManager->resumeAllAudio();
    _debugLabel->setString("All audio resumed");
    CCLOG("Demo: All audio resumed");
}

void AudioChannelDemoScene::onStopAllClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    audioManager->stopAllAudio();
    _activeInstances.clear();
    _debugLabel->setString("All audio stopped");
    CCLOG("Demo: All audio stopped");
}

void AudioChannelDemoScene::onFocusTestClicked(Ref* sender) {
    auto audioManager = AudioManager::getInstance();
    auto focusState = audioManager->getFocusState();
    
    std::string focusText;
    switch (focusState) {
        case AudioFocusState::FOCUS_GAINED:
            focusText = "Focus: Gained";
            break;
        case AudioFocusState::FOCUS_LOST:
            focusText = "Focus: Lost";
            break;
        case AudioFocusState::FOCUS_LOST_TRANSIENT:
            focusText = "Focus: Lost Transient";
            break;
        case AudioFocusState::FOCUS_LOST_TRANSIENT_CAN_DUCK:
            focusText = "Focus: Lost Transient (Can Duck)";
            break;
        default:
            focusText = "Focus: Unknown";
            break;
    }
    
    _debugLabel->setString("Audio Focus Test: " + focusText);
    CCLOG("Demo: Audio focus state - %s", focusText.c_str());
}

void AudioChannelDemoScene::updateDebugInfo(float dt) {
    auto audioManager = AudioManager::getInstance();
    auto stats = audioManager->getStats();
    auto focusState = audioManager->getFocusState();
    
    std::string debugInfo = StringUtils::format(
        "Focus: %d | Channels: %d | Active Instances: %d | Master Vol: %.2f",
        (int)focusState,
        stats.totalChannels,
        stats.totalActiveInstances,
        audioManager->getMasterVolume()
    );
    
    // 添加频道详细信息
    for (const auto& channelStat : stats.channelStats) {
        debugInfo += StringUtils::format("\n  %d: %d/%d (%.2f)",
            (int)channelStat.first,
            channelStat.second.activeCount,
            channelStat.second.totalCapacity,
            channelStat.second.averageVolume);
    }
    
    _debugLabel->setString(debugInfo);
}

6.7 AppDelegate集成

Classes/AppDelegate.cpp (补充)
#include "AppDelegate.h"
#include "scenes/AudioChannelDemoScene.h"
#include "audio/AudioManager.h"
#include "platforms/PlatformAudioFocus.h"

// ... 其他代码不变 ...

bool AppDelegate::applicationDidFinishLaunching() {
    // ... 之前的初始化代码 ...
    
    // 初始化音频管理器
    auto audioManager = AudioManager::getInstance();
    
    // 设置主音量
    audioManager->setMasterVolume(0.8f);
    
    // 创建设备特定频道配置
    #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
        // Android平台优化
        audioManager->setChannelVolume(AudioChannelType::SFX, 0.7f); // 降低SFX音量避免破音
    #elif (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
        // iOS平台优化
        audioManager->setChannelVolume(AudioChannelType::BGM, 0.9f); // 提升BGM质量
    #endif
    
    // 创建并显示演示场景
    auto scene = AudioChannelDemoScene::createScene();
    director->runWithScene(scene);
    
    return true;
}

void AppDelegate::applicationDidEnterBackground() {
    CCLOG("AppDelegate: Entering background");
    
    // 暂停所有音频
    auto audioManager = AudioManager::getInstance();
    audioManager->pauseAllAudio();
    
    // 放弃音频焦点
    auto platformFocus = PlatformAudioFocus::getInstance();
    if (platformFocus) {
        platformFocus->abandonAudioFocus();
    }
}

void AppDelegate::applicationWillEnterForeground() {
    CCLOG("AppDelegate: Entering foreground");
    
    // 请求音频焦点
    auto platformFocus = PlatformAudioFocus::getInstance();
    if (platformFocus) {
        platformFocus->requestAudioFocus();
    }
    
    // 恢复音频(根据焦点状态决定)
    auto audioManager = AudioManager::getInstance();
    if (audioManager->getFocusState() == AudioFocusState::FOCUS_GAINED) {
        audioManager->resumeAllAudio();
    }
}

7. 运行结果

7.1 预期效果

  • 应用启动后显示音频频道管理演示界面
  • 点击不同按钮可以播放BGM、SFX、环境音,并在调试信息区显示实例ID
  • 暂停/恢复/停止按钮可以正确控制所有音频状态
  • 音频焦点状态实时显示在调试信息区
  • 各频道统计信息(活跃实例数、容量使用率)正确更新
  • 应用切入后台时自动暂停音频,返回前台时恢复(根据焦点状态)

7.2 控制台输出示例

AudioManager: Initializing...
AudioManager: Created 5 default channels
AudioManager: Registered resource demo_bgm
AudioManager: Initialization complete
Demo: Started BGM with instance 1
Demo: Started SFX with instance 2
AudioFocusManager: Focus lost - 3
AudioFocusManager: Applied strategy for channel 1 (BGM): volume to 0.3
AudioFocusManager: Applied strategy for channel 2 (SFX): pause all
Focus: 3 | Channels: 5 | Active Instances: 2 | Master Vol: 0.80
  BGM: 1/1 (0.21)
  SFX: 0/16 (0.00)
  AMBIENT: 0/4 (0.00)
  VOICE: 0/2 (0.00)
  UI: 0/8 (0.00)

8. 测试步骤

8.1 功能测试

  1. 频道独立性测试
    // 测试用例
    void testChannelIndependence() {
        auto audioManager = AudioManager::getInstance();
    
        // 播放BGM
        int bgmId = audioManager->playAudio("demo_bgm", AudioChannelType::BGM);
        CC_ASSERT(bgmId > 0);
    
        // 播放SFX(不应影响BGM)
        int sfxId = audioManager->playAudio("demo_sfx", AudioChannelType::SFX);
        CC_ASSERT(sfxId > 0);
    
        // 暂停SFX频道不应影响BGM
        audioManager->pauseAudioByChannel(AudioChannelType::SFX);
        CC_ASSERT(audioManager->isPlaying(bgmId));
    }
  2. 焦点响应测试
    • 模拟音频焦点丢失,验证各频道是否正确响应
    • 测试焦点恢复后音频是否正确恢复
  3. 优先级管理测试
    • 超过频道容量时,验证低优先级音频被正确替换
    • 测试优先级动态调整功能

8.2 性能测试

  1. 内存占用测试
    • 监控长时间播放多音频时的内存使用情况
    • 测试频道容量限制是否有效防止内存溢出
  2. CPU占用测试
    • 测量音频更新循环的CPU使用率
    • 测试大量并发音频播放时的性能表现
  3. 响应延迟测试
    • 测量从调用播放到实际听到声音的时间
    • 测试暂停/恢复操作的响应速度

8.3 自动化测试脚本

#!/bin/bash
# 音频频道管理自动化测试脚本

echo "开始音频频道管理测试..."

# 构建测试版本
./build_test.sh

# 安装并启动应用
adb install -r build/android/bin/AudioChannelDemo-debug.apk
adb shell am start -n com.yourcompany.audiochannel/.AppActivity
sleep 5

# 测试音频播放
echo "测试音频播放功能..."
adb shell input tap 512 576  # 点击Play BGM
sleep 2
adb shell input tap 512 480  # 点击Play SFX
sleep 1

# 测试暂停/恢复
echo "测试暂停恢复功能..."
adb shell input tap 256 288  # 点击Pause All
sleep 1
adb shell input tap 512 288  # 点击Resume All
sleep 1

# 测试焦点变化(模拟来电)
echo "测试音频焦点处理..."
# 发送广播模拟电话接入
adb shell am broadcast -a android.intent.action.PHONE_STATE --es state "ringing"
sleep 3
# 挂断电话
adb shell am broadcast -a android.intent.action.PHONE_STATE --es state "idle"
sleep 2

# 检查应用是否崩溃
if adb shell ps | grep -q "com.yourcompany.audiochannel"; then
    echo "音频频道管理测试通过!"
else
    echo "测试失败:应用崩溃!"
    exit 1
fi

9. 部署场景

9.1 平台特定配置

Android平台
  1. AndroidManifest.xml​ 添加音频权限:
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
    <uses-permission android:name="android.permission.INTERNET" /> <!-- 如果需要网络音频 -->
  2. AppActivity.java​ 实现音频焦点回调:
    public class AppActivity extends Cocos2dxActivity {
        private static AudioManager audioManager;
        private static AppActivity instance;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            instance = this;
            audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        }
    
        public static boolean hasAudioFocus() {
            if (audioManager != null) {
                return audioManager.isMusicActive();
            }
            return false;
        }
    
        public static boolean requestAudioFocus() {
            if (audioManager != null) {
                AudioManager.OnAudioFocusChangeListener listener = 
                    new AudioManager.OnAudioFocusChangeListener() {
                        @Override
                        public void onAudioFocusChange(int focusChange) {
                            // 通知C++层
                            nativeOnAudioFocusChange(focusChange);
                        }
                    };
                int result = audioManager.requestAudioFocus(listener, 
                    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
                return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
            }
            return false;
        }
    
        public static void abandonAudioFocus() {
            if (audioManager != null) {
                audioManager.abandonAudioFocus(null);
            }
        }
    
        private static native void nativeOnAudioFocusChange(int focusChange);
    }
iOS平台
  1. Info.plist​ 添加音频配置:
    <key>UIBackgroundModes</key>
    <array>
        <string>audio</string>
    </array>
    <key>NSMicrophoneUsageDescription</key>
    <string>This app needs microphone access for audio recording.</string>
  2. AppController.mm​ 配置音频会话:
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 配置音频会话
        AVAudioSession *audioSession = [AVAudioSession sharedInstance];
        NSError *error = nil;
        [audioSession setCategory:AVAudioSessionCategoryPlayback 
                         withOptions:AVAudioSessionCategoryOptionMixWithOthers | 
                                    AVAudioSessionCategoryOptionDuckOthers 
                               error:&error];
        if (error) {
            NSLog(@"Audio session configuration error: %@", error);
        }
    
        [audioSession setActive:YES error:&error];
        if (error) {
            NSLog(@"Audio session activation error: %@", error);
        }
    
        return YES;
    }

9.2 部署优化策略

  1. 资源分级加载
    // 根据设备性能动态调整频道容量
    void configureChannelsByDevice() {
        auto audioManager = AudioManager::getInstance();
    
        // 检测设备性能
        float memoryMB = getDeviceMemoryMB();
        int cpuCores = getDeviceCPUCores();
    
        if (memoryMB < 1000) { // 低端设备
            audioManager->setChannelVolume(AudioChannelType::SFX, 0.6f);
            // 减少SFX频道容量
        } else if (cpuCores >= 8) { // 高端设备
            // 增加频道容量,提升音质
        }
    }
  2. 动态资源卸载
    // 内存压力大时卸载不活跃资源
    void handleMemoryPressure() {
        auto audioManager = AudioManager::getInstance();
        auto stats = audioManager->getStats();
    
        if (stats.memoryUsageMB > MEMORY_THRESHOLD_MB) {
            // 停止低优先级频道
            audioManager->stopAudioByChannel(AudioChannelType::AMBIENT);
            audioManager->stopAudioByChannel(AudioChannelType::UI);
        }
    }

10. 疑难解答

10.1 常见问题及解决方案

问题1:Android平台音频焦点监听失效
  • 原因:JNI回调未正确注册或Java层未实现回调方法
  • 解决:检查JNI方法签名,确保Java层的onAudioFocusChange正确调用native方法
问题2:iOS平台后台播放被中断
  • 原因:音频会话配置不正确或未启用后台模式
  • 解决:在Info.plist中配置UIBackgroundModes,设置正确的音频会话类别
问题3:频道容量限制导致重要音频被丢弃
  • 原因:优先级管理逻辑错误或优先级设置不当
  • 解决:检查优先级比较逻辑,确保关键音频(如游戏结束音效)设置为CRITICAL优先级
问题4:暂停后恢复音频位置不准确
  • 原因:SimpleAudioEngine不支持精确的播放位置控制
  • 解决:使用支持位置控制的音频库(如OpenSL ES、AVAudioPlayer),或接受当前限制

10.2 调试技巧

// 音频系统调试模式
#define AUDIO_DEBUG_MODE 1

#if AUDIO_DEBUG_MODE
#define AUDIO_DEBUG_LOG(format, ...) \
    CCLOG("[AUDIO_DEBUG] " format, ##__VA_ARGS__)

class AudioSystemDebugger {
public:
    static void dumpChannelStates() {
        auto audioManager = AudioManager::getInstance();
        auto stats = audioManager->getStats();
        
        AUDIO_DEBUG_LOG("=== Audio System Status ===");
        AUDIO_DEBUG_LOG("Master Volume: %.2f", audioManager->getMasterVolume());
        AUDIO_DEBUG_LOG("Focus State: %d", (int)audioManager->getFocusState());
        AUDIO_DEBUG_LOG("Total Channels: %d", stats.totalChannels);
        AUDIO_DEBUG_LOG("Total Active Instances: %d", stats.totalActiveInstances);
        
        for (const auto& channelStat : stats.channelStats) {
            AUDIO_DEBUG_LOG("Channel %d: %d/%d active, avg vol: %.2f", 
                (int)channelStat.first,
                channelStat.second.activeCount,
                channelStat.second.totalCapacity,
                channelStat.second.averageVolume);
        }
    }
    
    static void monitorAudioEvents() {
        // 在实际项目中,可以hook音频播放/停止事件进行监控
        AUDIO_DEBUG_LOG("Monitoring audio events...");
    }
};
#else
#define AUDIO_DEBUG_LOG(...)
#endif

11. 未来展望与技术趋势

11.1 技术发展趋势

  1. AI驱动的音频管理:使用机器学习预测用户行为,提前加载/卸载音频资源
  2. 空间音频普及:基于头部追踪的3D音频定位成为标配
  3. 超低延迟音频:5G和边缘计算推动实时音频交互延迟降至毫秒级
  4. 自适应音频质量:根据网络状况和设备性能动态调整音频质量
  5. 跨设备音频同步:多设备间的无缝音频体验

11.2 新兴挑战

  • 隐私保护:音频焦点管理涉及的权限和隐私问题日益严格
  • 能耗优化:移动设备对音频播放的功耗要求越来越高
  • 标准化缺失:跨平台音频管理缺乏统一标准
  • 实时性要求:云游戏等新兴场景对音频延迟提出极致要求

12. 总结

本文全面阐述了Cocos2d-x中音频暂停/恢复与频道管理的完整解决方案,从技术背景到实践实现,提供了企业级的音频管理系统。核心贡献包括:
  1. 多频道架构设计:实现BGM、SFX、环境音、语音等独立频道管理
  2. 精细化状态控制:支持实例级别的播放、暂停、恢复、停止操作
  3. 智能焦点管理:跨平台音频焦点协调与响应策略
  4. 优先级调度系统:基于重要性的音频资源分配和冲突解决
  5. 完整工具链:包含管理器、频道、焦点、平台适配器等全套组件
该方案有效解决了Cocos2d-x原生音频系统的局限性,提供了生产环境可用的音频管理能力。通过合理的频道划分和状态管理,能够显著提升游戏的音频体验和资源利用效率。
在实际部署中,开发者需要根据目标平台特性进行适配优化,特别关注音频焦点管理和后台播放策略。随着游戏音频技术的发展,这套基础架构也为未来的功能扩展(如空间音频、AI音频管理)奠定了坚实基础。
音频系统的稳定性和性能直接影响用户体验,投入精力构建健壮的音频管理框架是高质量游戏开发的必要保障。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。