由于读出的编码不一样,所以unsigned时的声音和使用A-law的声音有很大差别。不知道如何才能将我的unsigned转换成A-law的.pcm使用的8位的.谢谢!

解决方案 »

  1.   

    不清楚 
    WAVE文件格式剖析
    来自:中国电脑教育报    WAVE文件作为多媒体中使用的声波文件格式之一,它是以RIFF格式为标准的。RIFF是英文Resource Interchange File Format的缩写,每个WAVE文件的头四个字节便是“RIFF”。WAVE文件由文件头和数据体两大部分组成。其中文件头又分为RIFF/WAV文件标识段和声音数据格式说明段两部分。WAVE文件各部分内容及格式见附表。
           常见的声音文件主要有两种,分别对应于单声道(11.025KHz采样率、8Bit的采样值)和双声道(44.1KHz采样率、16Bit的采样值)。采样率是指:声音信号在“模→数”转换过程中单位时间内采样的次数。采样值是指每一次采样周期内声音模拟信号的积分值。
           对于单声道声音文件,采样数据为八位的短整数(short int 00H-FFH);而对于双声道立体声声音文件,每次采样数据为一个16位的整数(int),高八位和低八位分别代表左右两个声道。
         WAVE文件数据块包含以脉冲编码调制(PCM)格式表示的样本。WAVE文件是由样本组织而成的。在单声道WAVE文件中,声道0代表左声道,声道1代表右声道。在多声道WAVE文件中,样本是交替出现的。 
         
           WAVE文件格式说明表  
         
           偏移地址 字节数 数据类型 内 容 
           
         文件头
         00H 4 char "RIFF"标志 
         04H 4 long int 文件长度 
         08H 4 char "WAVE"标志 
         0CH 4 char "fmt"标志 
         10H 4   过渡字节(不定) 
         14H 2 int 格式类别(10H为PCM形式的声音数据) 
         16H 2 int 通道数,单声道为1,双声道为2 
         18H 2 int 采样率(每秒样本数),表示每个通道的播放速度, 
         1CH 4 long int 波形音频数据传送速率,其值为通道数×每秒数据位数×每样本的数据位数/8。播放软件利用此值可以估计缓冲区的大小。 
         20H 2 int 数据块的调整数(按字节算的),其值为通道数×每样本的数据位值/8。播放软件需要一次处理多个该值大小的字节数据,以便将其值用于缓冲区的调整。 
         22H 2   每样本的数据位数,表示每个声道中各个样本的数据位数。如果有多个声道,对每个声道而言,样本大小都一样。 
         24H 4 char 数据标记符"data" 
         28H 4 long int 语音数据的长度 
         
           PCM数据的存放方式: 
         
         样本1 样本2 
         8位单声道 0声道 0声道 
         8位立体声 0声道(左) 1声道(右) 0声道(左) 1声道(右) 
         16位单声道 0声道低字节 0声道高字节 0声道低字节 0声道高字节 
         16位立体声 0声道(左)低字节 0声道(左)高字节 1声道(右)低字节 1声道(右)高字节 
         
         
         
         
          WAVE文件的每个样本值包含在一个整数i中,i的长度为容纳指定样本长度所需的最小字节数。首先存储低有效字节,表示样本幅度的位放在i的高有效位上,剩下的位置为0,这样8位和16位的PCM波形样本的数据格式如下所示。  
         
         样本大小 数据格式 最大值 最小值 
         8位PCM unsigned int 225 0 
         16位PCM int 32767 -32767 
      

  2.   

    VC调用ACM音频压缩编程接口的方法 
    王琰 
      
    音 频 和 视 频 数 据 是 大 多 数 多 媒 体 应 用 程 序 向 用 户 提 供 信 息 的 主 要 方 式, 这 些 数 据 一 般 具 有 较 高 的 采 样 速 率, 如 果 不 经 过 压 缩 的 话, 保 存 它 们 需 要 消 耗 大 量 的 存 贮 空 间, 在 网 络 上 进 行 传 输 的 效 率 也 很 低, 因 此 音 频 视 频 数 字 压 缩 编 码 在 多 媒 体 技 术 中 占 有 很 重 要 的 地 位。 就 音 频 数 据 而 言, 目 前 常 用 的 压 缩 方 法 有 很 多 种, 不 同 的 方 法 具 有 不 同 的 压 缩 比 和 还 原 音 质, 编 码 的 格 式 和 算 法 也 各 不 相 同, 其 中 某 些 压 缩 算 法 相 当 复 杂, 普 通 程 序 不 可 能 去 实 现 其 编 解 码 算 法。 所 幸 的 是, 与Windows 3.x 相 比,Windows 95/NT 4.0 为 多 媒 体 应 用 程 序 提 供 了 更 强 的 支 持, 引 入 了ACM(Audio Compression Manager, 音 频 压 缩 管 理 器) 和VCM(Video Compression Manager, 视 频 压 缩 管 理 器), 它 们 负 责 管 理 系 统 中 所 有 音 频 和 视 频 编 解 码 器(Coder-Decoder, 简 称CODEC, 是 实 现 音 频 视 频 数 据 编 解 码 的 驱 动 程 序), 应 用 程 序 可 以 通 过ACM 或VCM 提 供 的 编 程 接 口 调 用 这 些 系 统 中 现 成 的 编 解 码 器 来 实 现 音 频 或 视 频 数 据 的 压 缩 和 解 压 缩。95/NT 4.0 系 统 自 带 的 音 频CODECs 支 持 一 些 早 期 的 音 频 数 据 压 缩 标 准, 如ADPCM 等,Internet Explorer 4.0 等 应 用 程 序 包 含 的 音 频CODECs 支 持 一 些 比 较 新 的 压 缩 标 准, 如MPEG Layer 3 等。 在 控 制 面 板 的 多 媒 体 组 件 中 选 择“ 高 级”, 打 开“ 音 频 压 缩 的 编 码 解 码 器”, 就 可 列 出 系 统 中 安 装 的 所 有 音 频CODECs。 本 文 所 要 介 绍 的 就 是ACM 音 频 压 缩 接 口 的 编 程 方 法, 所 用 编 程 工 具 为VC++ 5.0。 
    获 取CODECs 的 信 息
    ---- ACM 的API 函 数 定 义 在 头 文 件msacm.h 中, 除 此 之 外, 对ACM 编 程 还 必 须 包 含 头 文 件mmsystem.h,mmreg.h, 这 两 个 头 文 件 定 义 了 多 媒 体 编 程 中 最 基 本 的 常 量 和 数 据 结 构。 为 了 避 免 有 些 高 版 本ACM 才 提 供 的 函 数 和 功 能 在 较 低 版 本 的ACM 中 上 不 可 用, 程 序 中 应 调 用acmGetVersion 函 数 查 询 用 户 机 器 中ACM 的 版 本 信 息。 
    ---- 前 面 提 到, 在 控 制 面 板 中 可 以 查 看 系 统 中CODECs 的 信 息, 而 在 应 用 程 序 中 也 常 常 需 要 知 道 某 种 音 频CODECs 是 否 存 在, 并 获 取 其 编 解 码 参 数 等 信 息, 这 一 点 可 以 通 过 调 用 下 面 两 个 函 数 来 实 现。 ---- MMRESULT mmr=acmMetrics(NULL, ACM_METRIC_COUNT_CODECS, &dwCodecs); ---- mmr = acmDriverEnum(CodecsEnumProc, 0, 0); ---- acmMetrics() 函 数 可 以 获 取 许 多ACM 对 象 的 有 用 信 息, 例 如 向 其 中 传 递ACM_METRIC_COUNT_CODECS 可 以 查 询 系 统 中 安 装 的 音 频CODECs 总 数。 函 数acmDriverEnum() 的 功 能 是 枚 举 所 有 的 音 频CODECs, 在acmDriverEnum() 的 参 数 中 指 定 回 调 函 数CodecsEnumProc() 可 以 进 一 步 查 询 每 个CODEC 的 信 息。Windows 编 程 中 经 常 要 用 到 回 调 函 数, 下 面 是 枚 举 音 频CODECs 的 一 个 回 调 函 数 的 示 例。 BOOL CALLBACK CodecsEnumProc(HACMDRIVERID hadid, DWORD dwInstance, DWORD fdwSupport) {DWORD dwSize = 0;if (fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) printf("多格式转换\n");ACMDRIVERDETAILS add;acmdd.cbStruct = sizeof(acmdd);MMRESULT mmr = acmDriverDetails(hadid, &acmdd, 0);if (mmr)  error_msg(mmr);else {printf(" 全称:  %s\n", acmdd.szLongName);printf(" 描述:   %s\n", acmdd.szFeatures);}HACMDRIVER had = NULL;mmr = acmDriverOpen(&had, hadid, 0); //打开驱动程序if (mmr)  error_msg(mmr);else {mmr = acmMetrics(had, ACM_METRIC_MAX_SIZE_FORMAT, &dwSize);WAVEFORMATEX* pwf = (WAVEFORMATEX*) malloc(dwSize);memset(pwf, 0, dwSize);pwf->cbSize = LOWORD(dwSize) - sizeof(WAVEFORMATEX);pwf->wFormatTag = WAVE_FORMAT_UNKNOWN;ACMFORMATDETAILS fd;memset(&fd, 0, sizeof(fd));fd.cbStruct = sizeof(fd);  fd.pwfx = pwf;  fd.cbwfx = dwSize;fd.dwFormatTag = WAVE_FORMAT_UNKNOWN;mmr = acmFormatEnum(had, &fd, FormatEnumProc, 0, 0);  if (mmr)  error_msg(mmr);free(pwf);acmDriverClose(had, 0);}return TRUE; }
    ---- CodecsEnumProc() 共 有 三 个 参 数。 第 一 个 参 数 是 驱 动 程 序 的ID 值; 第 二 个 参 数 是 实 例 数 据, 本 文 例 子 中 未 使 用; 第 三 个 参 数 描 述 该 驱 动 程 序 所 支 持 的 功 能, 它 由 一 组 标 识 进 行 或 运 算 构 成, 例 如, 如 果 设 置 了 标 识ACMDRIVERDETAILS_SUPPORTF_CODEC, 则 说 明 该 驱 动 程 序 可 以 将 一 种 编 码 格 式 的 音 频 信 号 转 换 成 另 一 种 编 码 格 式。 通 过acmDriverDetails() 函 数 可 以 获 得 对 该 驱 动 程 序 进 一 步 的 信 息, 如CODEC 的 名 称、 简 单 描 述 等。 以 上 信 息 实 际 上 是 由ACM 收 集, 并 保 存 在ACM 内 部, 所 以 查 询 以 上 信 息 时 并 未 真 正 将 驱 动 程 序 加 载 至 内 存。 而 要 获 得 每 一 种 驱 动 程 序 支 持 的 音 频 格 式 信 息, 则 必 须 将 驱 动 程 序 加 载 至 内 存, 这 是 通 过acmDriverOpen() 完 成 的, 在 退 出CodecsEnumProc() 前, 还 要 用acmDriverClose() 来 关 闭 已 打 开 的 驱 动 程 序。 在 使 用 音 频 格 式 枚 举 函 数 前, 需 要 先 分 配 一 块 缓 冲 区 存 置 格 式 信 息, 缓 冲 区 的 大 小 可 通 过 调 用acmMetrics() 查 询ACM_METRIC_MAX_SIZE_FORMAT 获 得, 格 式 信 息 中 的 音 频 格 式 标 识 设 为WAVE_FORMAT_UNKNOWN。 在 音 频 格 式 枚 举 中 同 样 使 用 了 回 调 函 数, 此 回 调 函 数 只 是 列 出 了 该 音 频 格 式 的 名 称 和 标 识 值。 BOOL CALLBACK FormatEnumProc(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd, DWORD dwInstance, DWORD fdwSupport) {printf("%4.4lXH, %s\n", pafd- >dwFormatTag, pafd- >szFormat);return TRUE; }
      

  3.   


    ---- 上 面 介 绍 了 浏 览 系 统 中 所 有 音 频CODECs 及 每 种CODEC 所 支 持 的 音 频 格 式 的 方 法, 某 些 典 型 的 应 用 程 序 可 能 需 要 列 出 系 统 中 所 有 可 以 选 用 的CODECs, 并 由 用 户 来 选 择 使 用 哪 一 种CODEC 进 行 压 缩, 此 时 就 需 要 利 用 上 面 的 编 程 方 法 来 获 取CODECs 的 信 息。 音 频 数 据 的 压 缩
    ---- 下 面 说 明 使 用 某 一CODEC 实 现 音 频 压 缩 的 过 程, 读 者 朋 友 只 需 稍 加 改 动 就 可 编 写 出 相 应 的 解 压 程 序。 假 设 源 信 号 为8K 采 样、16bits PCM 编 码、 单 声 道、 长 度 为1 秒 的 音 频 信 号。 驱 动 程 序 采 用Windows 95 自 带 的TrueSpeech 音 频CODEC, 它 能 实 现 大 约10:1 的 压 缩。 在 此 例 中,TrueSpeech CODEC 支 持 从 源 音 频 格 式 到 目 标 格 式 的 转 换, 而 在 实 际 应 用 中, 可 能 某 种CODEC 不 支 持 直 接 将 源 音 频 格 式 转 换 成 目 标 格 式, 这 时 可 以 采 取 两 步 转 换 法, 即 先 将 源 格 式 转 换 成 一 种 中 间 格 式, 再 将 此 中 间 格 式 转 换 成 目 标 格 式, 因 为 线 性PCM 编 码 最 为 简 单, 且 为 绝 大 多 数CODEC 所 支 持, 所 以 一 般 中 间 格 式 都 选 为 线 性PCM 格 式 的 一 种。 
    ---- 在 进 行 压 缩 之 前 首 先 需 要 确 定TrueSpeech 驱 动 程 序 的ID 值。 为 此 需 要 用 到acmDriverEnum() 函 数, 对 枚 举 到 的 每 一 个 驱 动 程 序, 由acmDriverEnum() 指 定 的 回 调 函 数 将 检 查 其 支 持 的 所 有 音 频 格 式, 若 其 中 包 括wFormatTag 值 为WAVE_FORMAT_DSPGROUP_TRUESPEECH 的 音 频 格 式, 则 此 驱 动 程 序 就 是 要 寻 找 的TrueSpeech CODEC, 它 所 支 持 的 第 一 种WAVE_FORMAT_DSPGROUP_TRUESPEECH 音 频 格 式 即 为 目 标 音 频 压 缩 格 式。 查 询 所 需 的CODEC 及 其 支 持 的 音 频 格 式 的 方 法 见 前 一 小 节 的 介 绍。 ---- 根 据 查 询 的 结 果, 设hadID 为TrueSpeech CODEC 的ID 值,pwfDrv 为 指 向 目 标WAVEFORMATEX 结 构 的 指 针, 接 下 来 利 用 获 得 的ID 值 打 开 相 应 的 驱 动 程 序。 HACMDRIVER had = NULL;mmr = acmDriverOpen(&had, hadID, 0);if(mmr) {  printf(" 打开驱动程序失败\n"); exit(1);  }
    ---- 压 缩 和 解 压 缩 一 样, 都 是 将 音 频 信 号 从 一 种 音 频 格 式 转 换 成 另 一 种 格 式, 要 完 成 这 一 过 程, 首 先 要 打 开 转 换 流。 在 用acmStreamOpen 打 开 转 换 流 时, 我 们 指 定 了ACM_STREAMOPENF_NONREALTIME 标 志, 它 表 示 转 换 无 需 实 时 进 行。 因 为 很 多 压 缩 算 法 的 计 算 量 是 相 当 大 的, 实 时 完 成 几 乎 是 不 可 能 的, 例 如 在 本 例 中, 如 果 不 指 定 此 标 志,TrueSpeech CODEC 就 会 返 回“ 无 法 完 成” 的 错 误。 HACMSTREAM hstr = NULL;DWORD dwSrcBytes = dwSrcSamples * wfSrc.wBitsPerSample / 8;mmr = acmStreamOpen(&hstr,had, //驱动程序句柄pwfSrc, //指向源音频格式的指针pwfDrv, //指向目标音频格式的指针NULL, //无过滤器NULL, //无回调函数0,ACM_STREAMOPENF_NONREALTIME);
    ---- 在 真 正 进 行 转 换 之 前, 还 必 须 准 备 转 换 流 的 信 息 头。 下 面 一 段 代 码 中, 先 利 用 源 数 据 的 大 小 以 及 目 标 格 式 的 平 均 数 据 率 估 算 目 标 数 据 的 缓 存 区 大 小, 然 后 调 用acmStreamPrepareHeader 为 转 换 准 备 信 息 头。 ---- DWORD dwDstBytes=pwfDrv->nAvgBytesPerSec*dwSrcSamples/wfSrc.nSamplesPerSec; ---- dwDstBytes = dwDstBytes*3/2; // 计 算 压 缩 后 音 频 数 据 大 小, 并 依 此 适 当 增 加 输 出 缓 冲 区 的 大 小。 BYTE* pDstData = new BYTE [dwDstBytes];ACMSTREAMHEADER shdr;memset(&strhdr, 0, sizeof(shdr));shdr.cbStruct = sizeof(shdr);shdr.pbSrc = pSrcData;  //源音频数据区shdr.cbSrcLength = dwSrcBytes;shdr.pbDst = pDstData; //压缩后音频数据缓冲区shdr.cbDstLength = dwDstBytes;mmr = acmStreamPrepareHeader(hstr, &shdr, 0); 
    ---- 语 音 数 据 真 正 的 压 缩 过 程 是 由 函 数acmStreamConvert() 完 成 的。 在 调 用acmStreamConvert() 时 可 以 指 定 回 调 函 数, 以 便 在 转 换 过 程 中 显 示 进 度 信 息 等。 在 本 例 中, 未 指 定 回 调 函 数, 只 是 简 单 地 等 待 压 缩 的 结 束。 ---- mmr = acmStreamConvert(hstr, &shdr, 0); ---- 数 据 压 缩 完 毕 后, 应 用 程 序 就 可 以 把 缓 冲 区 中 的 数 据 写 入 目 标 文 件 中。 ---- 最 后, 必 须 关 闭 转 换 流 和 驱 动 程 序。 mmr = acmStreamClose(hstr, 0);mmr = acmDriverClose(had, 0);
    ---- 本 文 介 绍 了 利 用ACM 获 取 音 频CODEC 的 信 息 以 及 实 现 音 频 压 缩 的 一 般 方 法 和 过 程, 对ACM 编 程 感 兴 趣 的 读 者 可 以 进 一 步 参 考VC++ 5 的 联 机 帮 助 中 关 于ACM 的 信 息。 
      

  4.   


    yintongshun(踏雪有痕) 谢谢你给我这么好的资料。