音视频开发—FFmpeg 音频重采样详解

音频重采样(audio resampling)是指改变音频信号的采样率的过程。采样率(sample rate)是指每秒钟采集的音频样本数,通常以赫兹(Hz)或每秒样本数(samples per second)表示。例如,CD音频的标准采样率是44.1kHz,表示每秒钟采集44,100个样本。

文章目录

    • 什么时候会用到重采样
    • 重采样的技术
    • 使用FFmpeg命令行对MP3文件进行重采样
      • 参数说明
      • 更详细的命令
    • FFmpeg 对MP3文件重采样代码实现
      • 流程图
      • 关键步骤:
        • 对MP3文件进行解码
        • 初始化重采样参数
        • 初始化编码器
        • 音频帧转换
      • 完整代码
      • 效果展示

什么时候会用到重采样

  1. 设备兼容性
    不同的音频设备可能支持不同的采样率。例如,某些音频接口或播放设备可能只支持特定的采样率。在这种情况下,需要将音频文件重采样到兼容的采样率。

  2. 节省存储空间
    高采样率的音频文件占用更多的存储空间。如果需要节省存储空间或带宽,可以将音频文件重采样到较低的采样率。

  3. 音频质量调整
    有时候需要在高质量和低质量之间进行平衡。例如,音频工程师可能会将录音采样率设置得很高,以捕捉更多的细节,但在混音或发行时,可能会选择稍低的采样率以便于处理和分发。

  4. 多媒体项目
    在多媒体项目中,不同来源的音频文件可能具有不同的采样率。为了保持项目的一致性,通常需要对音频文件进行重采样,使它们具有相同的采样率。

  5. 音频处理和分析
    某些音频处理和分析工具对输入的采样率有特定的要求。在使用这些工具之前,可能需要对音频进行重采样。

重采样的技术

重采样涉及插值算法,例如:

  1. 线性插值
    是最简单的一种插值方法,但可能会引入较多的失真。

  2. 多项式插值(如三次样条插值)
    能提供更高的精度,但计算复杂度较高。

  3. 窗函数插值(如Lanczos窗)
    常用于高质量音频重采样,能够有效减少伪影和失真。

通过这些算法,重采样过程可以在不同的采样率之间平滑过渡,尽量保持原音频信号的质量。

使用FFmpeg命令行对MP3文件进行重采样

一个名为input.mp3的MP3文件,并且你想将其采样率更改为48kHz(48000Hz),可以使用以下命令:

ffmpeg -i input.mp3 -ar 48000 output.mp3

参数说明

  • -i input.mp3:指定输入文件。
  • -ar 48000:指定新的采样率,这里是48000Hz。
  • output.mp3:指定输出文件名。

更详细的命令

如果你还想保留原始的比特率(bitrate)和声道数(channels),可以使用更多参数:

ffmpeg -i input.mp3 -ar 48000 -ab 192k -ac 2 output.mp3
  • -ab 192k:指定音频比特率为192kbps。
  • -ac 2:指定音频通道数为2(立体声)。

FFmpeg 对MP3文件重采样代码实现

流程图

在这里插入图片描述

关键步骤:

对MP3文件进行解码

具体流程如下:

在这里插入图片描述

打开输入文件: 调用 avformat_open_input 函数打开输入文件,并获得格式上下文 fmt_ctx

读取流信息: 调用 avformat_find_stream_info 函数读取文件的流信息,以便后续处理。

查找最佳音频流: 调用 av_find_best_stream 函数查找输入文件中的最佳音频流,并返回其索引 audio_stream_index 和对应的解码器 codec

分配解码器上下文: 调用 avcodec_alloc_context3 函数为找到的解码器分配解码器上下文 codec_ctx

将流的参数复制到解码器上下文: 调用 avcodec_parameters_to_context 函数将输入文件中音频流的参数复制到解码器上下文中。

打开解码器: 调用 avcodec_open2 函数打开解码器,准备解码音频数据。

代码实现:

int initialize_decoder(const char *input_filename, AVFormatContext **fmt_ctx, AVCodecContext **codec_ctx, int *audio_stream_index)
{
    AVCodec *codec = NULL;

    if (avformat_open_input(fmt_ctx, input_filename, NULL, NULL) < 0)
    {
        fprintf(stderr, "Could not open input file\n");
        return -1;
    }

    if (avformat_find_stream_info(*fmt_ctx, NULL) < 0)
    {
        fprintf(stderr, "Could not find stream information\n");
        return -1;
    }

    *audio_stream_index = av_find_best_stream(*fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
    if (*audio_stream_index < 0)
    {
        fprintf(stderr, "Could not find audio stream in input file\n");
        return -1;
    }

    *codec_ctx = avcodec_alloc_context3(codec);
    if (!(*codec_ctx))
    {
        fprintf(stderr, "Could not allocate audio codec context\n");
        return -1;
    }

    if (avcodec_parameters_to_context(*codec_ctx, (*fmt_ctx)->streams[*audio_stream_index]->codecpar) < 0)
    {
        fprintf(stderr, "Could not copy codec parameters to codec context\n");
        return -1;
    }

    if (avcodec_open2(*codec_ctx, codec, NULL) < 0)
    {
        fprintf(stderr, "Could not open codec\n");
        return -1;
    }


    return 0; // 成功
}
初始化重采样参数

具体流程如下

在这里插入图片描述

代码实现

int initialize_resampler(SwrContext **swr_ctx, AVCodecContext *codec_ctx, AVFrame **swr_frame)
{
    *swr_ctx = swr_alloc_set_opts(NULL,                      // ctx
                                  AV_CH_LAYOUT_STEREO,       // 输出的channel 的布局
                                  AV_SAMPLE_FMT_FLTP,        // 输出的采样格式
                                  48000,                     // 输出的采样率
                                  codec_ctx->channel_layout, // 输入的channel布局
                                  codec_ctx->sample_fmt,     // 输入的采样格式
                                  codec_ctx->sample_rate,    // 输入的采样率
                                  0, NULL);
    if (!(*swr_ctx))
    {
        fprintf(stderr, "Could not allocate resampler context\n");
        return -1;
    }
    if (swr_init(*swr_ctx) < 0)
    {
        fprintf(stderr, "Could not initialize the resampling context\n");
        swr_free(swr_ctx);
        return -1;
    }
    *swr_frame = av_frame_alloc();
    if (!(*swr_frame))
    {
        fprintf(stderr, "Could not allocate resampled frame\n");
        swr_free(swr_ctx);
        return -1;
    }

    return 0; // 成功
}
初始化编码器

大体流程如下:在这里插入图片描述

设置输出文件格式和路径: 调用 av_guess_format 函数猜测输出文件的格式,并根据输出文件名设置格式。

分配输出格式的上下文: 调用 avformat_alloc_output_context2 函数为输出文件分配格式上下文。

创建输出流: 调用 avformat_new_stream 函数为输出文件创建一个新的音频流。

查找编码器: 调用 avcodec_find_encoder 函数查找MP3编码器。

创建编码器上下文: 调用 avcodec_alloc_context3 函数为编码器分配上下文。

设置编码器参数: 设置编码器的比特率、采样格式、采样率和声道布局等参数。

打开编码器: 调用 avcodec_open2 函数打开编码器。

给输出流设置编码器参数: 调用 avcodec_parameters_from_context 函数将编码器的参数复制到输出流。

打开输出文件: 如果输出格式不支持直接输出到文件,则调用 avio_open 函数打开输出文件。

写入文件头部: 调用 avformat_write_header 函数写入输出文件的头部信息。

初始化输出数据包: 调用 av_packet_alloc 函数初始化一个数据包,用于存储编码后的音频数据。

代码实现

int initialize_encoder(const char *output_filename, AVFormatContext **out_fmt_ctx, AVCodecContext **enc_ctx, AVStream **out_stream,AVPacket **outpack)
{
    AVOutputFormat *out_fmt = NULL;
    AVCodec *encoder = NULL;
    AVCodecContext *encoder_ctx = NULL;
    AVStream *stream = NULL;
    // 设置输出文件的格式与路径
    out_fmt = av_guess_format(NULL, output_filename, NULL);
    if (!out_fmt)
    {
        fprintf(stderr, "could not guess file format\n");
        return -1;
    }
    // 打开输出格式的上下文
    if (avformat_alloc_output_context2(out_fmt_ctx, out_fmt, NULL, output_filename) < 0)
    {
        fprintf(stderr, "could not create output context\n");
        return -1;
    }
    // 创建输出流
    stream = avformat_new_stream(*out_fmt_ctx, NULL);
    if (!stream)
    {
        fprintf(stderr, "could not create output stream\n");
        return -1;
    }
    // 查找编码器
    encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);
    if (!encoder)
    {
        fprintf(stderr, "Codec not found\n");
        return -1;
    }
    // 创建编码器上下文
    encoder_ctx = avcodec_alloc_context3(encoder);
    if (!encoder_ctx)
    {
        fprintf(stderr, "Could not allocate audio codec context\n");
        return -1;
    }

    // 设置编码器参数
    encoder_ctx->bit_rate = 192000;                    // 比特率为192kbps
    encoder_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;      // 采样格式
    encoder_ctx->sample_rate = 48000;                  // 采样率
    encoder_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // 双声道布局
    encoder_ctx->channels = 2;

    // 打开编码器
    if (avcodec_open2(encoder_ctx, encoder, NULL) < 0)
    {
        fprintf(stderr, "Could not open codec\n");
        avcodec_free_context(&encoder_ctx);
        return -1;
    }
    // 给输出流设置编码器
    if (avcodec_parameters_from_context(stream->codecpar, encoder_ctx) < 0)
    {
        fprintf(stderr, "Failed to copy encoder parameters to output stream\n");
        avcodec_free_context(&encoder_ctx);
        return -1;
    }
    // 打开输出文件
    if (!((*out_fmt_ctx)->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&(*out_fmt_ctx)->pb, output_filename, AVIO_FLAG_WRITE) < 0)
        {
            fprintf(stderr, "Could not open output file\n");
            return -1;
        }
    }
    // 写入文件头部
    if (avformat_write_header((*out_fmt_ctx), NULL) < 0)
    {
        fprintf(stderr, "Error occurred when opening output file\n");
        return -1;
    }
    *enc_ctx = encoder_ctx;
    *out_stream = stream;
    //初始化输出的数据包
    *outpack = av_packet_alloc();
    return 0; // 成功
}
音频帧转换

需要转换的帧要与编码器的参数注意对应,否则编码器无法编码

重点是swr_convert_frame

swr_convert_frame 是 FFmpeg 中用于将一个音频帧从一种格式转换为另一种格式的函数。它使用 SwrContext(重采样上下文)来执行转换,包括采样率、通道布局和采样格式的转换。

函数定义

int swr_convert_frame(SwrContext *swr_ctx, AVFrame *out_frame, const AVFrame *in_frame);

参数说明

  • swr_ctx:指向 SwrContext 的指针,包含重采样所需的上下文信息。这个上下文在之前需要通过 swr_alloc_set_optsswr_init 等函数进行初始化。
  • out_frame:指向目标 AVFrame 的指针,存储转换后的音频数据。这个 AVFrame 需要预先分配并设置好其参数,例如采样率、通道布局和采样格式等。
  • in_frame:指向源 AVFrame 的指针,包含需要转换的音频数据。

返回值

函数返回一个整数,成功时返回0,失败时返回负数。

使用步骤

  1. 初始化 SwrContext: 使用 swr_alloc_set_optsswr_init 函数初始化重采样上下文,设置输入和输出的采样率、通道布局和采样格式等。
  2. 分配并设置 AVFrame: 分配并设置输入和输出 AVFrame,并确保 out_frame 的参数与 SwrContext 的输出参数一致。
  3. 调用 swr_convert_frame: 调用 swr_convert_frame 函数进行音频数据的转换。
  4. 处理转换后的音频数据: 转换完成后,可以使用 out_frame 中的数据进行后续处理。
 					// 确保有足够的缓冲区来容纳转换后的数据
                    swr_frame->channel_layout = AV_CH_LAYOUT_STEREO;
                    swr_frame->format = AV_SAMPLE_FMT_FLTP;
                    swr_frame->sample_rate = 48000;
                    swr_frame->nb_samples = encodec_ctx->frame_size;  //与编码器帧大小保持一致

                    // 分配缓冲区
                    if (av_frame_get_buffer(swr_frame, 0) < 0)
                    {
                        fprintf(stderr, "Could not allocate output frame samples\n");
                        av_frame_free(&swr_frame);
                        return -1;
                    }

                    // 执行重采样
                    if (swr_convert_frame(swr_ctx, swr_frame, frame) < 0)
                    {
                        fprintf(stderr, "Error while converting\n");
                        av_frame_free(&swr_frame);
                        return -1;
                    }

                    // 将重采样后的帧发送给编码器
                    if (avcodec_send_frame(encodec_ctx, swr_frame) == 0)
                    {
                        while (avcodec_receive_packet(encodec_ctx, out_packet) == 0)
                        {
                            // 正确设置数据包中的流索引
                            out_packet->stream_index = out_stream->index;

                            // 调整时间戳,使其基于输出流的时间基
                            av_packet_rescale_ts(out_packet, encodec_ctx->time_base, out_stream->time_base);

                            // 写入一个编码的数据包到输出文件
                            if (av_interleaved_write_frame(out_fmt_ctx, out_packet) < 0)
                            {
                                fprintf(stderr, "Error while writing output packet\n");
                                break;
                            }
                        }
                    }
                    av_frame_unref(swr_frame); // 清理帧数据以便重用

完整代码

extern "C"
{
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libswresample/swresample.h>
}
#include <string>
#include <iostream>
using namespace std;

int initialize_encoder(const char *output_filename, AVFormatContext **out_fmt_ctx, AVCodecContext **enc_ctx, AVStream **out_stream,AVPacket **outpack)
{
    AVOutputFormat *out_fmt = NULL;
    AVCodec *encoder = NULL;
    AVCodecContext *encoder_ctx = NULL;
    AVStream *stream = NULL;

    // 设置输出文件的格式与路径
    out_fmt = av_guess_format(NULL, output_filename, NULL);
    if (!out_fmt)
    {
        fprintf(stderr, "could not guess file format\n");
        return -1;
    }

    // 打开输出格式的上下文
    if (avformat_alloc_output_context2(out_fmt_ctx, out_fmt, NULL, output_filename) < 0)
    {
        fprintf(stderr, "could not create output context\n");
        return -1;
    }

    // 创建输出流
    stream = avformat_new_stream(*out_fmt_ctx, NULL);
    if (!stream)
    {
        fprintf(stderr, "could not create output stream\n");
        return -1;
    }

    // 查找编码器
    encoder = avcodec_find_encoder(AV_CODEC_ID_MP3);
    if (!encoder)
    {
        fprintf(stderr, "Codec not found\n");
        return -1;
    }

    // 创建编码器上下文
    encoder_ctx = avcodec_alloc_context3(encoder);
    if (!encoder_ctx)
    {
        fprintf(stderr, "Could not allocate audio codec context\n");
        return -1;
    }

    // 设置编码器参数
    encoder_ctx->bit_rate = 192000;                    // 比特率为192kbps
    encoder_ctx->sample_fmt = AV_SAMPLE_FMT_FLTP;      // 采样格式
    encoder_ctx->sample_rate = 48000;                  // 采样率
    encoder_ctx->channel_layout = AV_CH_LAYOUT_STEREO; // 双声道布局
    encoder_ctx->channels = 2;

    // 打开编码器
    if (avcodec_open2(encoder_ctx, encoder, NULL) < 0)
    {
        fprintf(stderr, "Could not open codec\n");
        avcodec_free_context(&encoder_ctx);
        return -1;
    }

    // 给输出流设置编码器
    if (avcodec_parameters_from_context(stream->codecpar, encoder_ctx) < 0)
    {
        fprintf(stderr, "Failed to copy encoder parameters to output stream\n");
        avcodec_free_context(&encoder_ctx);
        return -1;
    }

    // 打开输出文件
    if (!((*out_fmt_ctx)->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open(&(*out_fmt_ctx)->pb, output_filename, AVIO_FLAG_WRITE) < 0)
        {
            fprintf(stderr, "Could not open output file\n");
            return -1;
        }
    }

    // 写入文件头部
    if (avformat_write_header((*out_fmt_ctx), NULL) < 0)
    {
        fprintf(stderr, "Error occurred when opening output file\n");
        return -1;
    }

    *enc_ctx = encoder_ctx;
    *out_stream = stream;
    //初始化输出的数据包
    *outpack = av_packet_alloc();
    return 0; // 成功
}


int initialize_decoder(const char *input_filename, AVFormatContext **fmt_ctx, AVCodecContext **codec_ctx, int *audio_stream_index)
{
    AVCodec *codec = NULL;

    if (avformat_open_input(fmt_ctx, input_filename, NULL, NULL) < 0)
    {
        fprintf(stderr, "Could not open input file\n");
        return -1;
    }

    if (avformat_find_stream_info(*fmt_ctx, NULL) < 0)
    {
        fprintf(stderr, "Could not find stream information\n");
        return -1;
    }

    *audio_stream_index = av_find_best_stream(*fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);
    if (*audio_stream_index < 0)
    {
        fprintf(stderr, "Could not find audio stream in input file\n");
        return -1;
    }

    *codec_ctx = avcodec_alloc_context3(codec);
    if (!(*codec_ctx))
    {
        fprintf(stderr, "Could not allocate audio codec context\n");
        return -1;
    }

    if (avcodec_parameters_to_context(*codec_ctx, (*fmt_ctx)->streams[*audio_stream_index]->codecpar) < 0)
    {
        fprintf(stderr, "Could not copy codec parameters to codec context\n");
        return -1;
    }

    if (avcodec_open2(*codec_ctx, codec, NULL) < 0)
    {
        fprintf(stderr, "Could not open codec\n");
        return -1;
    }


    return 0; // 成功
}

int initialize_resampler(SwrContext **swr_ctx, AVCodecContext *codec_ctx, AVFrame **swr_frame)
{
    *swr_ctx = swr_alloc_set_opts(NULL,                      // ctx
                                  AV_CH_LAYOUT_STEREO,       // 输出的channel 的布局
                                  AV_SAMPLE_FMT_FLTP,        // 输出的采样格式
                                  48000,                     // 输出的采样率
                                  codec_ctx->channel_layout, // 输入的channel布局
                                  codec_ctx->sample_fmt,     // 输入的采样格式
                                  codec_ctx->sample_rate,    // 输入的采样率
                                  0, NULL);
    if (!(*swr_ctx))
    {
        fprintf(stderr, "Could not allocate resampler context\n");
        return -1;
    }

    if (swr_init(*swr_ctx) < 0)
    {
        fprintf(stderr, "Could not initialize the resampling context\n");
        swr_free(swr_ctx);
        return -1;
    }

    *swr_frame = av_frame_alloc();
    if (!(*swr_frame))
    {
        fprintf(stderr, "Could not allocate resampled frame\n");
        swr_free(swr_ctx);
        return -1;
    }

    return 0; // 成功
}


int main()
{


    AVFormatContext *fmt_ctx = NULL;
    AVOutputFormat *out_fmt = NULL;      // 输出格式
    AVFormatContext *out_fmt_ctx = NULL; // 输出格式上下文
    AVCodecContext *codec_ctx = NULL;    // 解码器上下文
    AVCodec *codec = NULL;               // 解码器
    AVCodec *encodec = NULL;             // 编码器
    AVCodecContext *encodec_ctx = NULL;  // 编码器上下文
    AVPacket *packet;
    AVFrame *frame;
    AVStream *out_stream = NULL; // 输出流
    AVPacket *out_packet;
    int audio_stream_index;
    SwrContext *swr_ctx;
    AVFrame *swr_frame;
    int ret;

    ret = initialize_encoder("output.mp3", &out_fmt_ctx, &encodec_ctx, &out_stream,&out_packet);
    if (ret != 0)
    {
        fprintf(stderr, "init encode failed\n");
        return -1;
    }
 

     // 初始化解码器
    ret = initialize_decoder("test.mp3", &fmt_ctx, &codec_ctx, &audio_stream_index);
    if (ret != 0)
    {
        fprintf(stderr, "init decode failed\n");
        return -1;
    }

    packet = av_packet_alloc(); // 初始化数据包
    frame = av_frame_alloc();
   
     // 初始化重采样上下文
    ret = initialize_resampler(&swr_ctx, codec_ctx, &swr_frame);
    if (ret != 0)
    {
        fprintf(stderr, "init resampler failed\n");
        return -1;
    }
   
    // 解码 ---- 重采样  -----编码
    while (av_read_frame(fmt_ctx, packet) >= 0)
    {
        if (packet->stream_index == audio_stream_index)
        {
            if (avcodec_send_packet(codec_ctx, packet) == 0)
            {
                while (avcodec_receive_frame(codec_ctx, frame) == 0)
                {
                    // 确保有足够的缓冲区来容纳转换后的数据
                    swr_frame->channel_layout = AV_CH_LAYOUT_STEREO;
                    swr_frame->format = AV_SAMPLE_FMT_FLTP;
                    swr_frame->sample_rate = 48000;
                    swr_frame->nb_samples = encodec_ctx->frame_size;  //与编码器帧大小保持一致

                    // 分配缓冲区
                    if (av_frame_get_buffer(swr_frame, 0) < 0)
                    {
                        fprintf(stderr, "Could not allocate output frame samples\n");
                        av_frame_free(&swr_frame);
                        return -1;
                    }

                    // 执行重采样
                    if (swr_convert_frame(swr_ctx, swr_frame, frame) < 0)
                    {
                        fprintf(stderr, "Error while converting\n");
                        av_frame_free(&swr_frame);
                        return -1;
                    }

                    // 将重采样后的帧发送给编码器
                    if (avcodec_send_frame(encodec_ctx, swr_frame) == 0)
                    {
                        while (avcodec_receive_packet(encodec_ctx, out_packet) == 0)
                        {
                            // 正确设置数据包中的流索引
                            out_packet->stream_index = out_stream->index;

                            // 调整时间戳,使其基于输出流的时间基
                            av_packet_rescale_ts(out_packet, encodec_ctx->time_base, out_stream->time_base);

                            // 写入一个编码的数据包到输出文件
                            if (av_interleaved_write_frame(out_fmt_ctx, out_packet) < 0)
                            {
                                fprintf(stderr, "Error while writing output packet\n");
                                break;
                            }
                        }
                    }
                    av_frame_unref(swr_frame); // 清理帧数据以便重用
                }
            }
        }
        av_packet_unref(packet);
    }


// 收尾工作
    // 写入文件尾部信息
    if (av_write_trailer(out_fmt_ctx) < 0)
    {
        fprintf(stderr, "Error writing trailer of the output file\n");
    }

    // 关闭输出文件和释放输出上下文
    if (!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        avio_closep(&out_fmt_ctx->pb);
    }
    avformat_free_context(out_fmt_ctx);
    // 其他资源清理
    av_packet_free(&out_packet);
    av_frame_free(&frame);
    av_frame_free(&swr_frame);
    av_packet_free(&packet);
    avcodec_close(codec_ctx);
    avcodec_close(encodec_ctx);
    avformat_close_input(&fmt_ctx);
    swr_free(&swr_ctx);

    return 0;
}

效果展示

ffmpeg 可以查看MP3文件的具体参数

命令:ffmpeg -i test.mp3

在这里插入图片描述

Stream #0:0

  • Stream:表示这是一个流。
  • #0:0:表示这是第一个输入文件中的第一个流(通常第一个流是视频流,第二个流是音频流,依此类推)。

Audio: mp3

  • Audio:表示这个流是一个音频流。
  • mp3:表示音频编码格式是 MP3(MPEG Audio Layer III)。

44100 Hz

  • 表示音频采样率为 44.1 kHz(44100 赫兹),即每秒钟采集 44100 次音频样本。

stereo

  • 表示音频是立体声(双声道),即音频信号包含两个独立的声道。

fltp

  • 表示音频采样格式是 float planar(浮点平面格式),即每个音频样本用浮点数表示,并且每个声道的数据存储在单独的平面中。

320 kb/s

  • 表示音频比特率为 320 kbps(千比特每秒),即每秒钟有 320000 比特的数据传输速率。

经过重采样之后,再次查看参数
在这里插入图片描述

音频采样率改为了 48 kHz(48000 赫兹),并且正常播放

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/659836.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何理解和使用 this 关键字

this 关键字是许多编程语言中的一个核心概念&#xff0c;在面向对象编程&#xff08;OOP&#xff09;中尤为重要。在JavaScript、Java、C、C#等语言中&#xff0c;this 扮演着至关重要的角色。理解 this 的意义和用法&#xff0c;对于编写清晰、有效的代码至关重要。 什么是th…

OrangePi Kunpeng Pro体验——安装Hass与驱动SPI小屏幕

OrangePi Kunpeng Pro 是一款面向开发者和爱好者的高性能开发板。在本次测评中&#xff0c;主要将以前的一些代码在该开发板上实现&#xff0c;包括docker部署hass&#xff0c;引脚驱动SPI小屏幕。中间遇到了一些小小问题&#xff0c;但都成功了&#xff0c;一起来试试吧~ 一、…

wifi贴码推广哪家靠谱?

如今越来越多的人想轻资产创业&#xff0c;WIFI贴码是共享行业最无成本的创业项目了&#xff0c;而在选择厂商的时候&#xff0c;大家就想要知道哪家公司靠谱&#xff0c;更好、更便宜、可靠。那么wifi贴码推广哪家靠谱&#xff1f;别急&#xff0c;下面小编将带你一起了解。 目…

LLM - 模型下载与 git-lfs 安装

目录 一.引言 二.安装 git lfs 1.使用 apt-get 安装 2.使用 Brew 安装 3.LFS 验证 三.总结 一.引言 在 HuggingFace 上下载模型时提供一个 git clone 的指令&#xff0c;执行后可以下载对应模型的模型文件: 但是本机还没有 git lfs 命令: git: lfs is not a git comman…

CPU对代码执行效率的优化,CPU的缓存、指令重排序

目录 一、CPU对代码执行效率的优化 1. 指令流水线&#xff08;Instruction Pipelining&#xff09; 2. 超标量架构&#xff08;Superscalar Architecture&#xff09; 3. 动态指令重排序&#xff08;Dynamic Instruction Reordering&#xff09; 4. 分支预测&#xff08;…

文献解读-群体基因组第二期|《中国人群中PAX2新生突变的检测及表型分析:一项单中心研究》

关键词&#xff1a;应用遗传流行病学&#xff1b;群体测序&#xff1b;群体基因组&#xff1b;基因组变异检测&#xff1b; 文献简介 标题&#xff08;英文&#xff09;&#xff1a;Detection of De Novo PAX2 Variants and Phenotypes in Chinese Population: A Single-Cente…

下一代 CI/CD:利用 Tekton 和 ArgoCD 实现云原生自动化

一、回顾目标 背景&#xff1a; ​ 部门业务上云&#xff0c;之前服务采用传统的部署方式&#xff0c;这种方式简单&#xff0c;但是不能为应用程序定义资源使用边界&#xff0c;很难合理地分配计算资源&#xff0c;而且程序之间容易产生影响。随着互联网时代的到来&#xff…

【Chrono Engine学习总结】6-创建自定义场景-6.1-3D场景获取

由于Chrono的官方教程在一些细节方面解释的并不清楚&#xff0c;自己做了一些尝试&#xff0c;做学习总结。 Chrono可以导入自定义的三维模型&#xff0c;所以想自己搭建一个3D仿真环境。过程中遇到了一些问题&#xff0c;记录与整理。 1、3D环境的创建方法 Chrono的Irrlich…

2024年6月1日(星期六)骑行禹都甸

2024年6月1日 (星期六&#xff09;骑行禹都甸&#xff08;韭葱花&#xff09;&#xff0c;早8:30到9:00&#xff0c;昆明氧气厂门口集合&#xff0c;9:30准时出发【因迟到者&#xff0c;骑行速度快者&#xff0c;可自行追赶偶遇。】 偶遇地点:昆明氧气厂门口集合 &#xff0c;…

视频监控业务平台LntonCVS运用国标协议对接视频汇聚管理综合平台应用方案

为了实现“以信息化推动应急管理能力现代化”的目标&#xff0c;应急管理部提出了加速现代信息技术与应急管理业务深度融合的计划。这一计划是国家加强和改进应急管理工作的关键举措&#xff0c;也是满足日益严峻的应急管理形势和人民群众不断增长的公共安全需求的紧迫需求。 为…

12个好用的视频自动加字幕工具

多个提供视频字幕生成及翻译服务的工具和平台&#xff0c;涵盖了从自动识别到翻译、配音等多种功能&#xff0c;适合不同需求的用户选择使用。 网易见外: 支持视频智能字幕功能&#xff0c;转换正确率高&#xff0c;转换后的字幕可以直接导入Pr和会声会影等主流剪辑工具&#…

【Axure教程】拖动换位选择器

拖动换位选择器通常用于从一个列表中选择项目并将其移动到另一个列表中。用户可以通过拖动选项来实现选择和移动。这种交互方式在许多Web应用程序中很常见&#xff0c;特别是在需要对项目分组的情况下。 所以今天作者就教大家怎么在Axure用中继器制作一个拖动换位选择器的原型…

【Linux】初识Linux和Linux环境配置

1.什么是Linux操作系统 说到电脑系统 我想有大多数人会脱口而出&#xff1a;windows、mac 是的&#xff0c;这也是如今市场上主流的两种操作系统。 但是对于IT相关的人士来说&#xff0c;还有一种系统也是必须有姓名 那就是Linux Linux&#xff0c;Linux Is Not UniX 的…

Java_认识String类

在 C 语言中已经涉及到字符串了&#xff0c;但是在 C 语言中要表示字符串只能使用字符数组或者字符指针&#xff0c; 可以使用标准库提 供的字符串系列函数完成大部分操作&#xff0c;但是这种将数据和操作数据方法分离开 的方式不符合面相对象的思想&#xff0c;而字符串应用又…

VI 使用替换命令快速注释多行

使用替换命令快速注释多行&#xff1a; 按下 Esc 键确保你在普通模式下。输入 :起始行号,结束行号s/^/#/ 并按 Enter 键。 :起始行号 和 结束行号 分别是你要注释的起始行和结束行的行号。 关于正则 s/^/#/各个部分解释&#xff1a; s/: 这是vi编辑器中的替换命令的开头。s 表…

【Linux】centos7编写C语言程序,补充:使用yum安装软件包组

确保已安装gcc编译器 C语言程序&#xff0c;一般使用gcc进行编译&#xff0c;需确保已安装gcc。 若没有&#xff0c;可以使用yum安装gcc&#xff08;版本4.8.5&#xff09;&#xff0c;也可以使用SCL源安装gcc&#xff08;例如&#xff1a;版本9.3&#xff09;。 安装gcc&am…

htb_BoardLight

信息收集 nmap -sSVC 10.10.11.11开放80端口&#xff0c;将boardlight.htb写入/etc/hosts 同步进行子域名和目录扫描 子域名扫不到 这个目录扫描很奇怪哈&#xff0c;明明访问80端口有页面&#xff0c;就是扫不出来 直接浏览器访问80端口&#xff0c;四处看看&#xff0c;发…

Blazor入门-连接MySQL的简单例子:列出数据+简单查询

参考&#xff1a; ASP.NET Core 6.0 Blazor Server APP并使用MySQL数据库_blazor mysql-CSDN博客 https://blog.csdn.net/mzl87/article/details/129199352 本地环境&#xff1a;win10, visual studio 2022 community, mysql 8.0.33 (MySQL Community Server), net core 6.0 目…

DiffIR论文阅读笔记

ICCV2023的一篇用diffusion模型做Image Restoration的论文&#xff0c;一作是清华的教授&#xff0c;还在NIPS2023上一作发表了Hierarchical Integration Diffusion Model for Realistic Image Deblurring&#xff0c;作者里甚至有Luc Van Gool大佬。模型分三个部分&#xff0c…