C中的程序,它读取声音数据并检查给定数据是不是满足wav文件的先决条件

Posted

技术标签:

【中文标题】C中的程序,它读取声音数据并检查给定数据是不是满足wav文件的先决条件【英文标题】:program in C which reads data of sound and checks if the given data fulfills the prerequisites of a wav fileC中的程序,它读取声音数据并检查给定数据是否满足wav文件的先决条件 【发布时间】:2021-04-15 16:00:14 【问题描述】:

项目的需求是用C语言编写一个程序,根据wav格式/模板从输入中读取只有getchar的声音数据。 程序必须检查给定的数据是否正确。当检测到第一个错误时,会出现一条适当的消息并且程序停止。禁止使用数组、函数、指针和数学库。它还会检查我们是否数据不足或文件大小错误。

波形文件格式:http://tiny.systems/software/soundProgrammer/WavFormatDocs.pdfhttp://www.topherlee.com/software/pcm-tut-wavformat.html

示例我认为这段代码用于读取数字:

while ( (ch = getchar()) != '\n') 

    if (ch >= '0' && ch <= '9')
    
        bytes_sec = 10 * bytes_sec  + (ch - '0');
    

fprintf(stderr, "bytes/sec:%d\n", bytes_sec);

但这不正确,因为我们希望将数字保存为一个字节。

对于字符,我认为这段代码:

flag = 0;
fores = 0;

while ( ( (ch=getchar()) !='\n') && (flag==0)  ) 
    fores = fores + 1;
    if  (fores == 1) 
        if (ch != 'R')
        
            flag = 1;
        
    
    else if (fores == 2) 
        if (ch != 'I')
            
                flag = 1;
            
    
    else if (fores == 3) 
        if (ch!='F') 
                flag = 1;
            
    
    else 
        if ((fores != 4) || (ch != 'F')) 
            flag = 1;
        
    


if (flag == 1) 
    fprintf(stderr, "Error! \"RIFF\" not found\n");
    return 0;

我也不明白数据是以哪种形式(二进制、十六进制、十进制)给出的(我知道 wav 格式的数据是二进制的,但我仍然不明白究竟给出了什么数据以及如何 -形式以及是否作为单件或单独提供)。 最后,我们无法在终端中输入与可打印字符不对应的数据这一事实真的让我感到困惑。

假设我们有一个合法的 wav 文件(十六进制)的内容:

0000000 52 49 46 46 85 00 00 00 57 41 56 45 66 6d 74 20
0000020 10 00 00 00 01 00 01 00 44 ac 00 00 88 58 01 00
0000040 02 00 10 00 64 61 74 61 58 00 00 00 00 00 d4 07
0000060 a1 0f 5e 17 04 1f 8a 26 ea 2d 1c 35 18 3c d7 42
0000100 54 49 86 4f 69 55 f6 5a 27 60 f8 64 63 69 64 6d
0000120 f7 70 18 74 c5 76 fa 78 b6 7a f6 7b b9 7c ff 7c
0000140 c8 7c 12 7c e0 7a 33 79 0b 77 6c 74 58 71 d1 6d
0000160 dd 69 7e 65 b8 60 92 5b 0f 56 36 50 0c 4a 97 43
0000200 df 3c ea 35 45 78 74 72 61 44 61 74 61
0000215

我想将其从十六进制转换为十进制,然后使用记事本创建数据文件以使用命令fopen(fname, "rb"),但随后我将不得不使用被禁止的指针。 所以我仍然不明白程序如何在输入时获取价格/值。

同样在@AndreasWenzel 的建议下,我为 little-endian 提出了这段代码(注意:while 中的条件不正确,选择它是为了便于在 C 编译器中检查):

#include <stdio.h>

int ch, sample_rate, help, fores, sum, i;
int main()

    fores = 0;
    sample_rate = 0;
    
    while ( (ch = getchar()) != '\n' )
    
            help = (ch - '0');
            fprintf(stderr, "help:%d\n", help);
            if (help == 1)
            
                sum = 1;
                for (i=1 ; i<=fores ; i++)
                
                    sum = sum*2;
                
                sample_rate = sample_rate + (sum);
                fprintf(stderr, "sample:%d\n", sample_rate);
            
            fores = fores + 1;
    
fprintf(stderr, "sample rate:%d\n", sample_rate);

如果我们输入 10100100(二进制)=164(十进制),它将打印 37(十进制)。对吗?

【问题讨论】:

【参考方案1】:

请参阅this link,了解整数在以二进制形式存储时如何在内存中表示。请注意,对于此任务,您不必阅读有关如何在内存中表示有符号整数(您只需要了解无符号整数),也不必阅读有关如何在内存中表示浮点数的信息。

在 RIFF 文件(例如 .wav 文件)中,大多数数据不是存储在 ASCII 中,而是以二进制形式存储。 RIFF 文件中只有极少数内容以 ASCII 格式存储。例如,(子)块头每个都包含一个4字节的ID,例如RIFFfmt ,以ASCII存储。但是数字几乎永远不会以字符 '0''9' 的 ASCII 编码存储,正如您的代码所假设的那样。

读取二进制文件时,while 条件

(ch=getchar()) !='\n'

没有意义。这样的表达式只对以行分隔的文本输入有意义。在二进制文件中,值'\n',即换行符的ASCII编码值,没有特殊含义。它只是一个字节值,与其他任何值一样,可能会巧合地出现在二进制文件中,例如在数字的二进制表示中。

因此,为了检查RIFF ChunkID,您的while 循环应该始终从文件中准确读取4 个字节。在找到'\n' 之前,它不应继续阅读,就像现在一样。

为了读取后续的 4 字节长的 ChunkSize 值,您应该再次读取正好 4 字节。您还应该有一个uint_least32_t 类型的变量,它是一个无符号整数,并且保证长度至少为32 位。这确保了变量的大小足以存储 ChunkSize。然后,您可以使用getchar 一次读取一个字节,然后使用各个字节的值计算整数。请参阅上面有关如何在计算机内存中表示整数的链接,以了解如何从单个字节值计算更大的数字。按照these rules on homework questions,我不会提供这个问题的解决方案,除非你特别要求。在此之前,我只会提供以下提示:

您必须考虑到该号码存储在little-endian byte ordering。 使用getchar 读取时,应将结果存储在intunsigned char,而不是signed charchar(在大多数平台上签名)。否则,如果字节的值大于 127,则变量的值将为负值。这样的负值更难处理。

请注意,如果您使用#include &lt;windows.h&gt;(并且您在具有此标头的平台上),则可以使用typedef DWORD 而不是uint_least32_t。如果您使用uint_least32_t,则必须使用#include &lt;stdint.h&gt;


编辑:既然您已经自己解决了问题,我将提供自己的替代解决方案。但是,该任务禁止使用数组、指针和用户定义的函数。另一方面,在我看来,所有尊重这些限制的问题解决方案都将是混乱的,并且包含大量不必要的代码重复。因此,我在我的解决方案中使用了它们,它不遵守这些限制:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <limits.h>

//This will print an error message if you attempt to compile this 
//on a platform on which there are not 8 bits per byte.
#if CHAR_BIT != 8
#error "This program assumes 8 bits per byte."
#endif

void VerifyLabel( const char *label );
uint_fast32_t ReadNumber( int num_bytes, const char *fieldname );

int main( void )

    uint_fast32_t temp;

    VerifyLabel( "RIFF" );

    ReadNumber( 4, "ChunkSize" );

    VerifyLabel( "WAVE" );

    VerifyLabel( "fmt " );

    temp =
        ReadNumber( 4, "Subchunk1Size" );

    if ( temp != 16 )
    
        fprintf( stderr, "Error: Expected Subchunk1Size to be 16!\n" );
        exit( EXIT_FAILURE );
    

    temp =
        ReadNumber( 2, "AudioFormat" );

    if ( temp != 1 )
    
        fprintf( stderr, "Error: Expected AudioFormat to be 1 (PCM)!\n" );
        exit( EXIT_FAILURE );
    

    temp =
        ReadNumber( 2, "NumChannels" );

    if ( temp != 1 && temp != 2 )
    
        fprintf( stderr, "Error: Expected NumChannels to be 1 (mono) or 2 (stereo)!\n" );
        exit( EXIT_FAILURE );
    

    ReadNumber( 4, "SampleRate" );

    ReadNumber( 4, "ByteRate" );

    ReadNumber( 2, "BlockAlign" );

    temp =
        ReadNumber( 2, "BitePerSample" );

    if ( temp != 8 && temp != 16 )
    
        fprintf( stderr, "Error: Expected BitsPerSample to be 8 or 16!\n" );
        exit( EXIT_FAILURE );
    

    VerifyLabel( "data" );

    ReadNumber( 4, "Subchunk2Size" );

    return 0;


void VerifyLabel( const char *label )

    for ( const char *p = label; *p != '\0'; p++ )
    
        int c;

        if ( (c = getchar()) == EOF )
        
            fprintf( stderr, "input error while verifying \"%s\" label!\n", label );
            exit( EXIT_FAILURE );
        

        if ( (uint_fast8_t)*p != c )
        
            fprintf( stderr, "did not find \"%s\" label in expected position!\n", label );
            exit( EXIT_FAILURE );
        
    

    fprintf( stderr, "\"%s\" label OK\n", label );


uint_fast32_t ReadNumber( int num_bytes, const char *fieldname )

    int c;

    uint_fast32_t sum = 0;

    if ( num_bytes < 1 || num_bytes > 4 )
    
        fprintf( stderr, "this function only supports reading between 1 and 4 bytes\n" );
        exit( EXIT_FAILURE );
    

    for ( int i = 0; i < num_bytes; i++ )
    
        if ( (c = getchar()) == EOF )
        
            fprintf( stderr, "input error while reading field \"%s\"!\n", fieldname );
            exit( EXIT_FAILURE );
        

        sum += (uint_fast8_t)c << 8 * i;
    

    //On most platforms, unsigned int is 32-bits. On those platforms, the following
    //line is equivalent to:
    //fprintf( stderr, "%s: %u\n", fieldname, sum );
    fprintf( stderr, "%s: %" PRIuFAST32 "\n", fieldname, sum );

    return sum;


这是我的程序的示例输出:

"RIFF" label OK
ChunkSize: 88236
"WAVE" label OK
"fmt " label OK
Subchunk1Size: 16
AudioFormat: 1
NumChannels: 1
SampleRate: 44100
ByteRate: 44100
BlockAlign: 1
BitePerSample: 8
"data" label OK
Subchunk2Size: 88200

【讨论】:

感谢您的宝贵时间和您为我提供的帮助!如果您想看一下ι已更新/编辑了原始帖子。如果您觉得某些东西更好或需要在我看来用代码解释是可以接受/欢迎的。我描述的部分只是整个项目的 1/7,所以用代码做一个小小的解释,我认为它不会造成任何伤害。随意做任何你认为更合适或更有成效的事情。 @W44:在您的更新中,您写道:“注意:while 中的条件不正确,选择它是为了便于在 ac 编译器中检查”——我不明白您的意思打算通过继续使用'\n' 来完成,因为正如我在回答中已经解释的那样,该值在二进制文件中是没有意义的。 @W44:help=(ch - '0'); 的意义何在?该表达式仅在评估以 ASCII 编码的数字时才有意义,其中每个字节有一个数字。但是,在 RIFF 文件中,大多数数字不以 ASCII 表示,而是以二进制形式存储。在二进制表示中,一个字节可以表示介于0255 之间的数字(在十六进制表示中是0x000xFF)。而4个字节,当一起用来存储一个数字时,可以表示一个介于04,294,967,295之间的数字。所有这些都在我发布的链接中进行了解释。 @W44:请注意,您发布的文件的十六进制视图是文本,而不是二进制。它显示了各个字节的值,因此这种十六进制视图非常适合显示二进制文件的内容。但是您不应该将二进制文件的这种十六进制文本表示与实际的二进制数据混淆。解析这样的十六进制文本表示与解析实际的二进制数据不同。 @W44:还要注意程序notepad.exe 无法创建二进制文件。它用于创建文本文件。为了创建二进制文件,您必须使用所谓的“十六进制编辑器”程序(或使用您自己的程序创建它们)。一些文本编辑器具有“二进制”或“十六进制”模式,允许您以二进制模式执行编辑,但大多数都没有(包括记事本)。

以上是关于C中的程序,它读取声音数据并检查给定数据是不是满足wav文件的先决条件的主要内容,如果未能解决你的问题,请参考以下文章

Python如何读取csv文件某一列的每一行数据,并判断该数值是不是满足条件?

如何编写读取一对坐标的代码并检查给定点是不是在一个圆内和一个矩形外?

在python中实施多线程以读取文件中的行,并检查该行是否与给定的字符串匹配[closed]

如下:为啥C语言读取文件中的数据并输出时有乱码出现?本来输入的是数字,然后从文件中读取后就变汉字

从文件或标准输入读取

c_cpp C程序读取字符串并检查它是否是回文,而不使用库函数。显示结果。