You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

iOS平台从Hex/Binary原始数据生成音频文件方案咨询(BLE项目)

我刚好做过类似的BLE音频流项目,给你梳理下具体的实现步骤——核心是先把Hex数据转成原始音频二进制,再封装成WAV/MP3文件。先从最直接的WAV开始讲,因为它对原始PCM的兼容性最好:

第一步:明确硬件输出的音频参数(关键前提)

首先你必须从硬件那边确认几个核心参数,没有这些你生成的音频大概率是杂音或者播放异常:

  • 采样率(比如44100Hz、16000Hz,常见语音场景用16kHz)
  • 位深(比如16bit、8bit,16bit是主流)
  • 声道数(单声道/立体声,BLE音频一般用单声道)
  • 音频格式(是PCM原始脉冲编码,还是已经压缩过的MP3帧?如果是PCM就按下面的步骤处理;如果硬件已经发MP3帧,那Hex转二进制后直接拼接写入文件即可)
第二步:Hex字符串转二进制数据

iOS端拿到的HEX格式字符串(比如"00FF1234..."),首先要转成Data类型(Swift)或者NSData(Objective-C)。这里给你个Swift的实现示例:

func hexStringToData(_ hexString: String) -> Data? {
    // 先清理字符串,去掉空格并统一大写
    let cleanedString = hexString.replacingOccurrences(of: " ", with: "").uppercased()
    var data = Data(capacity: cleanedString.count / 2)
    var index = cleanedString.startIndex
    
    while index < cleanedString.endIndex {
        let nextIndex = cleanedString.index(index, offsetBy: 2)
        // 每两个Hex字符转成一个字节
        guard let byte = UInt8(cleanedString[index..<nextIndex], radix: 16) else {
            return nil
        }
        data.append(byte)
        index = nextIndex
    }
    return data
}
第三步:封装成WAV文件(最直接的方式)

WAV文件本质是PCM原始数据 + 文件头,文件头里包含了我们第一步确认的所有参数。下面是Swift的封装示例,默认参数是语音场景常用的16kHz采样率、16bit位深、单声道:

func pcmDataToWav(data: Data, sampleRate: Int = 16000, bitDepth: Int = 16, channels: Int = 1) -> Data? {
    let sampleCount = data.count / (bitDepth / 8)
    let byteRate = sampleRate * channels * (bitDepth / 8)
    let blockAlign = channels * (bitDepth / 8)
    
    var wavHeader = Data()
    
    // 构造RIFF chunk
    wavHeader.append(Data("RIFF".utf8))
    let riffSize = 4 + 24 + 8 + data.count // 固定计算逻辑
    wavHeader.append(riffSize.littleEndianData)
    
    // 构造WAVE chunk
    wavHeader.append(Data("WAVE".utf8))
    
    // 构造fmt子chunk
    wavHeader.append(Data("fmt ".utf8))
    let fmtSize: UInt32 = 16 // PCM格式固定为16
    wavHeader.append(fmtSize.littleEndianData)
    let audioFormat: UInt16 = 1 // 1代表PCM原始格式
    wavHeader.append(audioFormat.littleEndianData)
    wavHeader.append(UInt16(channels).littleEndianData)
    wavHeader.append(UInt32(sampleRate).littleEndianData)
    wavHeader.append(UInt32(byteRate).littleEndianData)
    wavHeader.append(UInt16(blockAlign).littleEndianData)
    wavHeader.append(UInt16(bitDepth).littleEndianData)
    
    // 构造data子chunk
    wavHeader.append(Data("data".utf8))
    wavHeader.append(UInt32(data.count).littleEndianData)
    
    // 拼接文件头和PCM原始数据
    wavHeader.append(data)
    return wavHeader
}

// 辅助扩展:将数值转成小端字节序的Data(WAV要求小端)
extension UInt32 {
    var littleEndianData: Data {
        var value = self.littleEndian
        return Data(bytes: &value, count: MemoryLayout<UInt32>.size)
    }
}

extension UInt16 {
    var littleEndianData: Data {
        var value = self.littleEndian
        return Data(bytes: &value, count: MemoryLayout<UInt16>.size)
    }
}

调用示例:

// 假设receivedHexString是从BLE收到的Hex字符串
if let hexData = hexStringToData(receivedHexString),
   let wavData = pcmDataToWav(data: hexData) {
    let documentsDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    let wavURL = documentsDir.appendingPathComponent("recorded.wav")
    do {
        try wavData.write(to: wavURL)
        print("WAV文件生成成功:\(wavURL)")
    } catch {
        print("写入文件失败:\(error)")
    }
}
第四步:转成MP3文件(如果需要压缩格式)

如果要生成MP3,因为MP3是有损压缩格式,不能直接像WAV那样拼头,需要用编码器处理。iOS上可以集成LAME编码器(可以通过CocoaPods引入lame-ios),核心步骤是把PCM数据喂给编码器,得到MP3二进制后写入文件。简单示例:

func pcmToMp3(pcmData: Data, sampleRate: Int = 16000, channels: Int = 1) -> Data? {
    let lame = lame_init()
    lame_set_in_samplerate(lame, Int32(sampleRate))
    lame_set_num_channels(lame, Int32(channels))
    lame_set_mode(lame, channels == 1 ? MPEG_mode.MONO : MPEG_mode.STEREO)
    lame_set_VBR(lame, vbr_default)
    lame_init_params(lame)
    
    let pcmBuffer = Array(pcmData)
    // 预分配MP3缓冲区,大小按经验值设置
    var mp3Buffer = [UInt8](repeating: 0, count: pcmBuffer.count / 2 + 7200)
    let mp3Bytes = lame_encode_buffer_interleaved(lame, pcmBuffer, Int32(pcmBuffer.count / (2 * channels)), &mp3Buffer, Int32(mp3Buffer.count))
    
    if mp3Bytes < 0 {
        lame_close(lame)
        return nil
    }
    
    let finalMp3Data = Data(mp3Buffer[0..<Int(mp3Bytes)])
    lame_close(lame)
    return finalMp3Data
}
额外注意事项
  • 一定要和硬件对齐参数!采样率、位深哪怕错一个,生成的音频要么是杂音要么播放速度异常。
  • BLE传输可能丢包,建议在硬件和iOS端加帧校验(比如序号、CRC校验和),避免生成的音频出现断帧。
  • 如果硬件发送的已经是MP3帧,那直接把Hex转成二进制后拼接写入文件即可,不需要再编码。

内容的提问来源于stack exchange,提问作者Sagar Thummar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:45:33