dtmf_fsk音频数据测试

音频测试

1.dtmf调制成音频数据

  1. 输入任意按键值,输出是该按键对应的dtmf调制的音频信号,输出结果保存到文件中。
  2. 要求dtmf使用的基波响度为-12dB,采样率为16000Hz,生成的音频长度为100ms。

test.c

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define FILE_NAME_SIZE_MAX  50
#define SAMPLE_RATE         16000 
#define AMPLITUDE           32767 
#define PI                  3.14159265358979323846
#define AUDIO_TIME_SEC_LEN  (0.1)
#define AUDIO_LOUDNESS_DBFS (-12) 

typedef struct {
    char key;
    int freq1;
    int freq2;
} dtmf_st;

static const dtmf_st dtmf_table[] = {
    {'1', 697, 1209}, {'2', 697, 1336}, {'3', 697, 1477}, {'A', 697, 1633},
    {'4', 770, 1209}, {'5', 770, 1336}, {'6', 770, 1477}, {'B', 770, 1633},
    {'7', 852, 1209}, {'8', 852, 1336}, {'9', 852, 1477}, {'C', 852, 1633},
    {'#', 941, 1209}, {'0', 941, 1336}, {'D', 941, 1477}
};

static const int dtmf_table_size = sizeof(dtmf_table) / sizeof(dtmf_st);

int main(void) {
    char key = 0;
    short *audio_data = NULL;
    int num_samples = 0;
    int freq1 = -1, freq2 = -1;
    double dtmf_amp = 0;
    double time_in_sec = 0;
    double sin_wave1 = 0, sin_wave2 = 0;
    char filename[FILE_NAME_SIZE_MAX] = {0};
    FILE *fp = NULL;
    int i = 0;

    while(1) {
        printf("\nPlease input key:");
        if(scanf("%c", &key) != 1) {
            while(getchar() != '\n');
            printf("Input error! \nPlease input (0-9,A-D,#,*)\n");
            continue;
        }

        for(i=0;i<dtmf_table_size; i++) {
            if(key == dtmf_table[i].key) {
                break;
            }
        }
        if(i < dtmf_table_size) {
            freq1 = dtmf_table[i].freq1;
            freq2 = dtmf_table[i].freq2;
            // printf("%d %d\n",freq1,freq2);
            break;
        } 
        else {
            printf("Invalid key!\n");
        }
    }

    num_samples = SAMPLE_RATE * AUDIO_TIME_SEC_LEN;

    audio_data = malloc(sizeof(short) * num_samples);
    if(audio_data == NULL) {
        goto ERROR;
    }

    dtmf_amp = pow(10, AUDIO_LOUDNESS_DBFS/20.0) * AMPLITUDE;
    for (int i = 0; i < num_samples; i++) {
        time_in_sec = i / (double) SAMPLE_RATE;
        sin_wave1 = sin(2 * PI * freq1 * time_in_sec);
        sin_wave2 = sin(2 * PI * freq2 * time_in_sec);
        audio_data[i] = dtmf_amp * (sin_wave1 + sin_wave2);
    }

    sprintf(filename, "key_%c.raw", key);
    fp = fopen(filename, "wb");
    if (!fp) {
        perror("fopen");
        goto ERROR;
    }

    if(fwrite(audio_data, sizeof(short), num_samples, fp) < 0)
    {
        perror("fwrite");
        goto ERROR;
    }

ERROR:
    if (fp) {
        fclose(fp);
        fp = NULL;
    }
    if (audio_data) {
        free(audio_data);
        audio_data = NULL;
    }
    return 0;
}

sound forge打开

在这里插入图片描述

2.使用fsk调成音频数据

使用C语言编写一段程序,程序可以实现将输入的16进制数据使用fsk调制成音频数据。

1.输入为一个16进制数,输出为一个音频文件。例如输入0x55,对应的二进制为01010101,fsk调制后的频率组合为 低频-高频-低频-高频-低频-高频-低频-高频。

2.音频采样率为16000Hz。

3.fsk调制时使用的两个信号分别为1209Hz和697Hz。

4.fsk使用的调制信号的幅值为-12dB。

5.fsk调制时每个频率的信号长度为10ms。

6.两个不同频率的信号拼接时要注意保持相位上的连续性。

在linux系统中,数据保存为raw格式

test.c

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>
#include <stdint.h>
#include <stdbool.h>

#define AMPLITUDE             32767.0 
#define SAMPLE_RATE           (16000.0)                    
#define LOW_FREQ              697                         
#define HIGH_FREQ             1209                       
#define AUDIO_LOUDNESS_DBFS   (-12.0) 
#define SIGNAL_LENGTH         0.01                   
#define BIN_STR_SIZE_MAX      64
#define PI                    3.14159265358979323846            
#define OUTPUT_FILE           "output.raw"

struct wave {
    int freq;               
    double amplitude;       
};
static const struct wave waves[2] = {
        {LOW_FREQ, pow(10, AUDIO_LOUDNESS_DBFS / 20.0)*AMPLITUDE}, 
        {HIGH_FREQ, pow(10, AUDIO_LOUDNESS_DBFS / 20.0)*AMPLITUDE}
    };

static const char *hex_table[] = {"0000", "0001", "0010", "0011", "0100",
                                  "0101", "0110", "0111", "1000", "1001",
                                  "1010", "1011", "1100", "1101", "1110",
                                  "1111"};

void hex_to_bin(char *hex_string, char *bin_string) 
{
    int i = 0;
    char hex_digit = 0;
    for (i = 0; hex_string[i] != '\0'; i++) {
        hex_digit = hex_string[i];
        if (hex_digit >= '0' && hex_digit <= '9') {
            strcat(bin_string, hex_table[hex_digit - '0']);
        } else if (hex_digit >= 'A' && hex_digit <= 'F') {
            strcat(bin_string, hex_table[hex_digit - 'A' + 10]);
        } else if (hex_digit >= 'a' && hex_digit <= 'f') {
            strcat(bin_string, hex_table[hex_digit - 'a' + 10]);
        } else {
            printf("error invalid digit '%c'\n", hex_digit);
            goto ERROR;
        }
    }

    if (strlen(bin_string) == 0) {
        printf("error input is null\n");
        goto ERROR;
    }

    if (strlen(bin_string) % 4 != 0) {
        printf("error:invalid len %d\n", (int)strlen(bin_string));
        goto ERROR;
    }
ERROR:
    return ;
}


double generate_fsk_signal(int fi,int freq,int amplitude,int num_samples,short *audio_data) 
{
    static int pos = 0;
    double t = 0;
    for (int i = 0; i < num_samples; i++)
    {
        audio_data[pos++] = (short)(amplitude * sin(2 * PI * freq * t + fi));
        t += 1/SAMPLE_RATE;
    }
    return (2 * PI * freq * (t+1/SAMPLE_RATE)  + fi); //相位对齐
}

void generate_sin_wave(int n,char *bin_string,short *audio_data,int num_samples) 
{
    int len = strlen(bin_string);
    int i=0 ,j=0;
    double fi = 0;
    int index = 0;
    for(i=0;i<len;i++)
    {
        index = bin_string[i] - '0';
        if(index == 0) {
            fi = generate_fsk_signal(fi,waves[index].freq,waves[index].amplitude,num_samples,audio_data);
        } 
        else if(index == 1) {
            fi = generate_fsk_signal(fi,waves[index].freq,waves[index].amplitude,num_samples,audio_data);
        }
    }  
}



char *get_hex_str(char *str)
{
    int len = strlen(str);

    if(str == NULL) {
        goto ERROR;
    }

    if (len == 0 || len > BIN_STR_SIZE_MAX / 4) {
        goto ERROR;
    }

    if(strstr(str,"0x") || strstr(str,"0X") ) {
        str += 2;
    }

    for(int i=0; i<strlen(str); i++){
        if(!isxdigit(str[i])){
            printf("error: invalid hex_string\n");
            goto ERROR;
        }
    }

    return str;
ERROR:
    return NULL;
}

int main(int argc, char **argv) 
{
    int i = 0, j = 0;
    int index = 0;
    int num_samples = 0;
    FILE *fp = NULL;
    int signal_length = 0;
    char *hex_str = NULL;
    double last_fi = 0;
    char bin_string[BIN_STR_SIZE_MAX] = {0};
    short *audio_data = NULL;
    
    if (argc < 2) {
        printf("Usage: %s hex_string\n", argv[0]);
        goto ERROR;
    }

    hex_str = get_hex_str(argv[1]);
    if(hex_str == NULL){
        goto ERROR;
    }

    hex_to_bin(hex_str, bin_string);
    num_samples = SIGNAL_LENGTH * SAMPLE_RATE;
    signal_length = strlen(bin_string) * num_samples;
    audio_data = malloc(sizeof(short) * signal_length);
    memset(audio_data,0,sizeof(short) * signal_length);
  
    generate_sin_wave(signal_length,bin_string,audio_data,num_samples);

    fp = fopen(OUTPUT_FILE, "wb");
    if (fp == NULL) {
        perror("fopen");
        goto ERROR;
    }

    if(fwrite(audio_data, sizeof(short), signal_length, fp) < 0) {
        perror("fwrite");
        goto ERROR;
    }
ERROR:
    if (fp) {
        fclose(fp);
        fp = NULL;
    }
    if (audio_data) {
        free(audio_data);
        audio_data = NULL;
    }

    return 0;
}

在这里插入图片描述

相关知识:

dtmf:

在这里插入图片描述

响度:

响度常用的单位是dBSPL、dBFS、dBpa、dBm0。

dB计算:对于功率,dB = 10 * lg(A/B)。对于电压或电流,dB= 20 * lg(A/B)。此处A,B代表参与比较的功率值或者电流、电压值。

以dBfs举例说明:

dBfs = 20 * lg(x/32767)。

其中32767是16bit采样精度时最大的值,x取值0 ~ 32767。所以dBfs的最大值为当x取值为32767时的值,即0dBfs。

采样率:

采样率是指对模拟音频信号进行取样的每秒次数,通常以赫兹 (Hz) 为单位表示。当我们要将模拟音频信号转换成数字形式时(例如录制音频或将其从 CD 转换成数字格式),需要对其进行采样,即在时间轴上不断地对音频信号进行抽样,获取一系列数字值。采样率越高,每秒中采集到的样本数量就越多,这可以提供更精确的音频表示,但同时也会增加所需的存储空间和处理能力。

振幅:

振幅是指波形的最大偏移量,通常用单位分贝 (dB) 或电平(以电压为单位)来表示。在音频信号中,振幅描述了声音的响度或强度。例如,在一段声音信号中,振幅越高,则声音会听起来更响亮、更强烈,振幅越低,则声音会听起来更柔和、更安静。与采样率不同,振幅是代表音频信号能量的单一数值,其大小与声音响度直接相关。

正弦波:

y = A * sin(ωt + ϕ)

其中,A代表振幅,即波形的最大偏移量;

ω代表角频率,即单位时间内的变化角度;

t代表时间;

ϕ代表初相位,即初始状态下正弦波图像的左右平移位置

φ = arcsin(y/A) - wt

每秒中采集到的样本数量就越多,这可以提供更精确的音频表示,但同时也会增加所需的存储空间和处理能力。

振幅:

振幅是指波形的最大偏移量,通常用单位分贝 (dB) 或电平(以电压为单位)来表示。在音频信号中,振幅描述了声音的响度或强度。例如,在一段声音信号中,振幅越高,则声音会听起来更响亮、更强烈,振幅越低,则声音会听起来更柔和、更安静。与采样率不同,振幅是代表音频信号能量的单一数值,其大小与声音响度直接相关。

正弦波:

y = A * sin(ωt + ϕ)

其中,A代表振幅,即波形的最大偏移量;

ω代表角频率,即单位时间内的变化角度;

t代表时间;

ϕ代表初相位,即初始状态下正弦波图像的左右平移位置

φ = arcsin(y/A) - wt

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值