# 非流式音频处理工具使用指南

## 概述

`audioNonStream` 模块提供了一套完整的工具函数，用于处理非流式接口返回的音频数据。支持多种数据格式（Base64、Blob、ArrayBuffer、File）和灵活的字段路径提取。

## 主要特性

- ✅ 支持多种数据格式：Base64、Blob、ArrayBuffer、File
- ✅ 灵活的字段路径提取：支持嵌套路径（如 `data.audio` 或 `result.voice.content`）
- ✅ 自动类型检测：无需手动指定数据类型
- ✅ 自动播放功能：可选的音频自动播放
- ✅ 批量处理：支持批量处理多个音频响应
- ✅ 完整的回调系统：onAudioData、onError、onComplete
- ✅ TypeScript 支持：完整的类型定义
- ✅ 调试模式：可选的详细日志输出

## 安装

```bash
npm install mstf-kit
```

## 基本使用

### 1. 处理嵌套字段的 Base64 数据

```typescript
import { processNonStreamAudio } from 'mstf-kit';

// 服务器返回格式：
// {
//   "msg": "success",
//   "code": 200,
//   "data": {
//     "audio": "UklGRiQAAABXQVZFZm10..." // Base64音频数据
//   }
// }

const response = await fetch('/api/audio');
const data = await response.json();

const result = await processNonStreamAudio(data, {
  audioField: 'data.audio',  // 指定音频数据的路径
  dataType: 'base64',        // 指定数据类型
  mimeType: 'audio/wav',     // 指定MIME类型
  autoPlay: true             // 自动播放
});

console.log('音频URL:', result.url);
console.log('音频大小:', result.size);
console.log('音频类型:', result.mimeType);

// 使用音频URL
const audioElement = document.querySelector('audio');
audioElement.src = result.url;
```

### 2. 处理直接字段的 Base64 数据

```typescript
// 服务器返回格式：
// {
//   "msg": "success",
//   "audio": "UklGRiQAAABXQVZFZm10..." // Base64音频数据
// }

const result = await processNonStreamAudio(response, {
  audioField: 'audio',  // 直接指定字段名
  autoPlay: true
});
```

### 3. 自动检测数据类型

```typescript
// 不指定 dataType，工具会自动检测
const result = await processNonStreamAudio(response, {
  audioField: 'data.audio'
  // dataType 会自动检测为 'base64'
});
```

### 4. 处理 ArrayBuffer 响应

```typescript
import axios from 'axios';

// 使用 axios 获取 ArrayBuffer
const response = await axios.get('/api/audio', {
  responseType: 'arraybuffer'
});

const result = await processNonStreamAudio(response.data, {
  dataType: 'arraybuffer',
  mimeType: 'audio/mp3'
});
```

### 5. 处理 Blob 响应

```typescript
// 使用 fetch 获取 Blob
const response = await fetch('/api/audio');
const blob = await response.blob();

const result = await processNonStreamAudio(blob, {
  dataType: 'blob'
});
```

### 6. 处理 File 对象

```typescript
// 从文件上传获取 File 对象
const fileInput = document.querySelector('input[type="file"]');
const file = fileInput.files[0];

const result = await processNonStreamAudio(file, {
  dataType: 'file',
  mimeType: 'audio/mp3'
});
```

## 高级用法

### 使用回调函数

```typescript
const result = await processNonStreamAudio(response, {
  audioField: 'data.audio',
  dataType: 'base64',
  
  // 音频数据回调
  onAudioData: (blob) => {
    console.log('收到音频数据:', blob.size, '字节');
    // 可以在这里更新UI，显示音频信息
  },
  
  // 错误回调
  onError: (error) => {
    console.error('处理音频失败:', error);
    // 显示错误提示
    alert('音频加载失败，请重试');
  },
  
  // 完成回调
  onComplete: (blob) => {
    console.log('处理完成');
    if (blob) {
      console.log('成功获取音频，大小:', blob.size);
    }
  }
});
```

### 启用调试模式

```typescript
const result = await processNonStreamAudio(response, {
  audioField: 'data.audio',
  debug: true  // 启用详细日志输出
});

// 控制台会输出详细的处理过程：
// [NonStreamAudio] 开始处理非流式音频响应 {...}
// [NonStreamAudio] 按字段路径提取音频数据: data.audio
// [NonStreamAudio] 从response.data中提取数据
// [NonStreamAudio] 提取的音频数据类型: string
// [NonStreamAudio] 自动检测的数据类型: base64
// [NonStreamAudio] 将Base64数据转换为Blob
// [NonStreamAudio] 成功创建Blob: { size: 12345, type: 'audio/mpeg' }
// ...
```

### 批量处理多个音频

```typescript
import { processBatchNonStreamAudio } from 'mstf-kit';

const responses = [
  { data: { audio: "base64data1..." } },
  { data: { audio: "base64data2..." } },
  { data: { audio: "base64data3..." } }
];

const results = await processBatchNonStreamAudio(responses, {
  audioField: 'data.audio',
  dataType: 'base64',
  mimeType: 'audio/wav'
});

// 遍历结果
results.forEach((result, index) => {
  console.log(`音频${index + 1}:`, {
    url: result.url,
    size: result.size,
    type: result.mimeType
  });
  
  // 创建播放列表
  const audioElement = document.createElement('audio');
  audioElement.src = result.url;
  audioElement.controls = true;
  document.body.appendChild(audioElement);
});
```

## 便捷函数

### 快速获取 Blob

```typescript
import { getAudioBlob } from 'mstf-kit';

// 快速提取音频 Blob，无需其他信息
const blob = await getAudioBlob(response, 'data.audio', 'base64');

// 使用 Blob
const url = URL.createObjectURL(blob);
audioElement.src = url;
```

### 快速创建音频 URL

```typescript
import { createAudioUrl } from 'mstf-kit';

// 直接获取可用的音频 URL
const url = await createAudioUrl(response, {
  audioField: 'data.audio',
  dataType: 'base64'
});

audioElement.src = url;
```

### 下载音频文件

```typescript
import { downloadAudio } from 'mstf-kit';

// 触发浏览器下载音频文件
await downloadAudio(response, 'my-audio.mp3', {
  audioField: 'data.audio',
  dataType: 'base64'
});
```

## 实际应用场景

### 场景1：TTS（文本转语音）服务

```typescript
import { processNonStreamAudio } from 'mstf-kit';

async function textToSpeech(text: string) {
  try {
    // 调用TTS API
    const response = await fetch('/api/tts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text })
    });
    
    const data = await response.json();
    
    // 处理返回的音频数据
    const result = await processNonStreamAudio(data, {
      audioField: 'data.audio',
      dataType: 'base64',
      mimeType: 'audio/mp3',
      autoPlay: true,
      onAudioData: (blob) => {
        console.log('TTS音频生成成功，大小:', blob.size);
      },
      onError: (error) => {
        console.error('TTS失败:', error);
        alert('语音合成失败，请重试');
      }
    });
    
    return result;
  } catch (error) {
    console.error('TTS请求失败:', error);
    throw error;
  }
}

// 使用
const button = document.querySelector('#speak-button');
button.addEventListener('click', async () => {
  const text = document.querySelector('#text-input').value;
  await textToSpeech(text);
});
```

### 场景2：音频文件上传预览

```typescript
import { processNonStreamAudio } from 'mstf-kit';

const fileInput = document.querySelector('#audio-upload');
const audioPreview = document.querySelector('#audio-preview');

fileInput.addEventListener('change', async (event) => {
  const file = event.target.files[0];
  
  if (!file) return;
  
  // 验证文件类型
  if (!file.type.startsWith('audio/')) {
    alert('请选择音频文件');
    return;
  }
  
  try {
    // 处理音频文件
    const result = await processNonStreamAudio(file, {
      dataType: 'file',
      onAudioData: (blob) => {
        console.log('音频文件信息:', {
          name: file.name,
          size: blob.size,
          type: blob.type
        });
      }
    });
    
    // 显示预览
    audioPreview.src = result.url;
    audioPreview.style.display = 'block';
    
  } catch (error) {
    console.error('处理音频文件失败:', error);
    alert('无法加载音频文件');
  }
});
```

### 场景3：多语言音频切换

```typescript
import { processBatchNonStreamAudio } from 'mstf-kit';

async function loadMultiLanguageAudio() {
  // 获取多语言音频数据
  const response = await fetch('/api/audio/multi-language');
  const data = await response.json();
  
  // 批量处理
  const results = await processBatchNonStreamAudio(data.languages, {
    audioField: 'audio',
    dataType: 'base64',
    mimeType: 'audio/mp3'
  });
  
  // 创建语言选择器
  const languageSelector = document.querySelector('#language-selector');
  const audioPlayer = document.querySelector('#audio-player');
  
  results.forEach((result, index) => {
    const language = data.languages[index];
    
    // 添加选项
    const option = document.createElement('option');
    option.value = result.url;
    option.textContent = language.name;
    languageSelector.appendChild(option);
  });
  
  // 切换语言
  languageSelector.addEventListener('change', (event) => {
    audioPlayer.src = event.target.value;
    audioPlayer.play();
  });
}

loadMultiLanguageAudio();
```

### 场景4：音频消息系统

```typescript
import { processNonStreamAudio } from 'mstf-kit';

class AudioMessageSystem {
  private audioCache = new Map<string, string>();
  
  async loadAudioMessage(messageId: string) {
    // 检查缓存
    if (this.audioCache.has(messageId)) {
      return this.audioCache.get(messageId);
    }
    
    // 获取音频消息
    const response = await fetch(`/api/messages/${messageId}/audio`);
    const data = await response.json();
    
    // 处理音频
    const result = await processNonStreamAudio(data, {
      audioField: 'data.audio',
      dataType: 'base64',
      mimeType: 'audio/mp3',
      onError: (error) => {
        console.error(`加载音频消息 ${messageId} 失败:`, error);
      }
    });
    
    // 缓存URL
    this.audioCache.set(messageId, result.url);
    
    return result.url;
  }
  
  async playAudioMessage(messageId: string) {
    const url = await this.loadAudioMessage(messageId);
    
    const audio = new Audio(url);
    audio.play();
    
    return audio;
  }
  
  clearCache() {
    // 释放所有URL
    this.audioCache.forEach(url => {
      URL.revokeObjectURL(url);
    });
    this.audioCache.clear();
  }
}

// 使用
const audioSystem = new AudioMessageSystem();

document.querySelectorAll('.audio-message').forEach(element => {
  element.addEventListener('click', async () => {
    const messageId = element.dataset.messageId;
    await audioSystem.playAudioMessage(messageId);
  });
});
```

## API 参考

### processNonStreamAudio

处理非流式音频响应的主函数。

```typescript
function processNonStreamAudio(
  response: any,
  options?: NonStreamAudioOptions
): Promise<NonStreamAudioResult>
```

**参数：**

- `response`: 响应数据，可以是完整的响应对象、纯数据对象或直接的音频数据
- `options`: 处理选项

**返回：**

Promise<NonStreamAudioResult>，包含：
- `blob`: 音频Blob对象
- `url`: 音频URL（可用于audio标签的src）
- `size`: 音频大小（字节）
- `mimeType`: 音频MIME类型
- `audio`: 音频元素（如果启用了自动播放）

### NonStreamAudioOptions

```typescript
interface NonStreamAudioOptions {
  audioField?: string;           // 音频数据字段路径
  dataType?: AudioDataType;      // 数据类型
  mimeType?: string;             // MIME类型
  autoPlay?: boolean;            // 是否自动播放
  debug?: boolean;               // 是否启用调试日志
  onAudioData?: (blob: Blob) => void;     // 音频数据回调
  onError?: (error: Error) => void;       // 错误回调
  onComplete?: (blob?: Blob) => void;     // 完成回调
}
```

### AudioDataType

```typescript
type AudioDataType = 'base64' | 'blob' | 'arraybuffer' | 'file';
```

## 注意事项

1. **内存管理**：使用 `URL.createObjectURL()` 创建的 URL 需要手动释放。如果不再使用音频，请调用 `URL.revokeObjectURL(url)` 释放内存。

2. **自动播放限制**：现代浏览器对自动播放有限制，可能需要用户交互才能播放音频。

3. **CORS 问题**：如果音频来自不同域，确保服务器设置了正确的 CORS 头。

4. **数据大小**：Base64 编码会增加约 33% 的数据大小，对于大文件建议使用 Blob 或 ArrayBuffer。

5. **浏览器兼容性**：确保目标浏览器支持 Web Audio API 和 Blob API。

## 错误处理

```typescript
try {
  const result = await processNonStreamAudio(response, {
    audioField: 'data.audio',
    dataType: 'base64'
  });
  
  // 成功处理
  console.log('音频URL:', result.url);
  
} catch (error) {
  // 处理错误
  if (error.message.includes('无法从路径')) {
    console.error('字段路径错误，请检查 audioField 配置');
  } else if (error.message.includes('无法检测音频数据类型')) {
    console.error('数据类型检测失败，请手动指定 dataType');
  } else {
    console.error('未知错误:', error);
  }
}
```

## 性能优化建议

1. **缓存音频 URL**：对于重复使用的音频，缓存 URL 避免重复处理
2. **批量处理**：使用 `processBatchNonStreamAudio` 批量处理多个音频
3. **懒加载**：只在需要时才加载和处理音频数据
4. **预加载**：对于即将使用的音频，可以提前加载
5. **释放资源**：及时释放不再使用的 URL 和 Blob

## 总结

`audioNonStream` 模块提供了一套完整、灵活、易用的非流式音频处理解决方案。无论是简单的 Base64 数据还是复杂的嵌套响应格式，都能轻松处理。配合完整的 TypeScript 类型支持和丰富的回调系统，可以满足各种音频处理需求。
