首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >解析WAV文件并导出PCM数据

解析WAV文件并导出PCM数据
EN

Code Review用户
提问于 2019-06-10 14:34:59
回答 1查看 1.1K关注 0票数 7

我是个新手,我写了一些代码,可以获取WAV文件,并将原始数据(仅为PCM数据)导出到*.raw文件。但我对这个结构很困惑。代码是作为一个过程(LISP样式)编写的,并且没有函数。我能得到一些如何重组代码的建议吗?

main.c

代码语言:javascript
复制
#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
#include "wave.h"

byte buf[5]; // 4 char string buffer
char filename[200]; //
FILE *fp;
FILE *pcm_data;

int main(int argc, char *argv[])
{
    strcat(filename, argv[1]);
    struct riff_chunk riff = {"RIFF", 0, "WAVE"};
    struct fmt_chunk fmt = {"fmt "};
    struct data_chunk data = {"data"};

    if ((fp = fopen(argv[1], "rb"))==NULL) {
        printf("Can't open the file. Exit.\n");
        return 1;
    }

    // Reading RIFF section
    if (fread(&riff.id, sizeof(byte), 12, fp)!= 12)  {
        printf("Can't read RIFF chunk or EOF is met. Exit.\n");
        return 1;
    } else {
        memccpy(buf, riff.id, '\0', 4);
        if (strcmp(buf, "RIFF")!=0) {
            printf("File format is not RIFF. Exit.\n");
            return 1;
        }
        memccpy(buf, riff.type, '\0', 4);
        if (strcmp(buf, "WAVE")!=0) {
            printf("File format is not WAVE. Exit.\n");
            return 1;
        }
    };

    // Reading fmt.id and fmt.size
    if (fread(&fmt, sizeof(byte), 8, fp)!=8) {
        printf("Can't read fmt chunk or EOF is met. Exit.\n");
        return 1;
    } else {
        memccpy(buf, fmt.id, '\0', 4);
        if (strcmp(buf, "fmt ")!=0) {
            printf("File have no fmt chunk. Exit.\n");
            return 1;
        }
    }

    // Reading fmt Sample Format Info
    if (fread(&fmt.compression, sizeof(byte), fmt.size, fp) != fmt.size) {
        printf("Can't read Sample Format Info in fmt chunk or EOF is met. Exit.\n");
        return 1;
    }

    printf("Compression: %d\n", fmt.compression);
    printf("Channels: %d\n", fmt.chanels);
    printf("Sample Rate: %d\n", fmt.sample_rate);
    printf("Bit Rate: %d\n", fmt.bit_per_sample);

    // Reading data/some chunk
    if (fread(&data, sizeof(byte), 8, fp)!=8) {
        printf("Error of reading data chunk. Exit.\n");
        return 1;
    } else {
        while (memccpy(buf, data.id, '\0', 4), strcmp(buf, "data")!=0) {
            fseek(fp, data.size, 1); // перемещаем указатель файла на конец чанка (его размер)
            fread(&data, sizeof(byte), 8, fp);
        }
    }

    // Reading PCM
    byte *dump = (byte*)malloc(data.size);
    if (dump == NULL) {
        printf("Allocation memory error");
        return 1;
    }

    if (fmt.compression == 1) {
        fmt.number_of_blocks = data.size / fmt.block_align;
        if ((fread(dump, fmt.block_align, fmt.number_of_blocks, fp))!=fmt.number_of_blocks) {  
            printf("Readin PCM data error.\n");
            return 1;
        } else {
            strcat(filename, ".raw");
            if ((pcm_data = fopen(filename, "wb"))==NULL) {
                printf("Can't open the PCM file for write. Exit.\n");
                return 1;
            }

            if(fwrite(dump, fmt.block_align, fmt.number_of_blocks, pcm_data)!=fmt.number_of_blocks) {
                printf("Can't write PCM file. Exit.\n");
                return 1;
            }
            printf("------------\nDone. PCM data writing in PCM file. Exit.\n");
        }

    } else {
        printf("Compression type is not PCM. Exit.\n");
        return 1;
    }
    free(dump);
    fclose(fp);
    fclose(pcm_data);
    return 0;
}

wave.h

代码语言:javascript
复制
typedef char                byte;  // 1 byte \ 8 bit 
typedef short int           word;  // 2 byte \ 16 bit
typedef unsigned int        dword; // 4 byte \ 32 bit

struct riff_chunk
{
    byte     id[4];             // 4b "RIFF" string  >|
    dword    size;              // 4b                 |-> 12 byte
    byte     type[4];           // 4b "WAVE" string  >|
};

struct fmt_chunk 
{
    byte     id[4];             // 4b "fmt" string
    dword    size;              // 4b _____
    word     compression;       // 2b
    word     chanels;           // 2b
    dword    sample_rate;       // 4b _____
    dword    byte_per_sec;      // 4b
    word     block_align;       // 2b
    word     bit_per_sample;    // 2b _____
    word     extra_format_size; // 2b _____
    byte*    extra_format_data; // 8b _____
    dword    number_of_blocks;  // 4b _____
};

struct data_chunk
{
    byte     id[4];             // 4 "data" string
    dword    size;              // 4
};

int dump(word*, dword*);
EN

回答 1

Code Review用户

回答已采纳

发布于 2019-06-11 09:04:11

除了消除不必要的全局变量(大多数可以移到函数作用域)之外,我不认为非常需要重新组织代码的结构。

有一些可移植性问题需要解决。我将从整数类型宽度的假设开始:

*

这些注释是假设,需要为您构建的每个平台进行验证。有一种便携的方法可以得到你想要的东西:

代码语言:javascript
复制
typedef uint8_t byte;
typedef uint16_t word;
typedef uint32_t dword;

或者只是在整个过程中使用标准的固定宽度类型--这比发明自己的术语更容易混淆。

注意,当使用printf()和系列时,我们需要使用正确的说明符。这是错误的:

printf(“压缩:%d\n",fmt.compression);printf(”通道:%d\n",fmt.chanels);printf(“采样率:%d\n",fmt.sample_rate);

对于最初的定义,我们应该对前两个使用%hu,对于最后两个应该使用%u。使用标准的固定宽度类型,我们可以获得可用于格式化的宏:

代码语言:javascript
复制
printf("Compression: %" PRIu16 "\n", fmt.compression);
printf("Channels: %" PRIu16 "\n", fmt.chanels);
printf("Sample Rate: %" PRIu32 "\n", fmt.sample_rate);
printf("Bit Rate: %" PRIu32 "\n", fmt.bit_per_sample);

以下是一个效率低下的问题:

代码语言:javascript
复制
    memccpy(buf, riff.id, '\0', 4);
    if (strcmp(buf, "RIFF")!=0) {

首先,memccpy不是标准C(尽管它是由POSIX定义的),因此可移植性略有降低。但是这里不需要复制到buf --只需使用strncmp()进行比较:

代码语言:javascript
复制
    if (strncmp("RIFF", riff.id, sizeof riff.id)) {

有很多硬编码的尺寸。例如:

if (fread(&riff.id,sizeof(字节),12,fp)= 12) {

我们编写sizeof (byte)是很奇怪的,尽管byte只是char的别名(因此大小为1 char),但是在我们可以更有意义地编写sizeof riff的地方使用12

代码语言:javascript
复制
if (fread(&riff, 1, sizeof riff, fp) != sizeof riff)  {

(我还将&riff.id更改为&riff,以表明我们是为整个结构编写的,而不仅仅是id成员。)

在分配时,不需要将返回的void指针转换为更具体的指针大小(也可能有轻微的损害)。使用非空指针的固有真实性也是惯用的,而不是显式地针对NULL进行测试:

代码语言:javascript
复制
byte *dump = malloc(data.size);
if (!dump) {

我很高兴您没有忘记这里的错误检查,并且在打开文件时--这是C语言编程的一个重要部分,这是正确的。一个小改进是:错误消息应该转到stderr,而不是stdout

代码语言:javascript
复制
        fprintf(stderr, "Memory allocation error\n");

顺便说一句,在编译时不要忘记,以确保结构是打包的,没有成员之间的任何填充。

增强建议:允许用户指定写入输出的位置(或将其发送到标准输出,并让他们使用shell重定向输出)。如果输入文件目前位于只读目录中,我们将失败。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/222026

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档