【毕业设计】基于程序化生成和音频检测的生态仿真与3D内容生成系统----音频检测算法设计

news2025/1/12 22:05:03

(2条消息) 【开发日志】2022.09.02 ZENO----Audio----Beat detection algorithm----Combine Wav&Mp3_minimp3 和 ffmpeg_EndlessDaydream的博客-CSDN博客https://blog.csdn.net/Angelloveyatou/article/details/126670613

4 音频检测算法设计

4.1 节拍检测算法

4.1.1 节拍检测算法

要实现节拍检测算法,我们首先需要计算所选频率范围内的声音能量。我们可以通过使用FFT分析并将所选范围内频率箱的平方幅度相加来做到这一点。然后,我们计算当前播放位置之前一段时间(例如,几秒钟)的平均能量。

获得平均能量后,将其与所选频率范围内声音的当前能量进行比较。如果两种能量之间的差异超过一定的阈值,我们可以得出结论,有一个节拍。可以调整阈值以控制节拍检测的灵敏度。

为了实时实现算法,我们需要维护先前能量值的缓冲区,并在每次计算能量时更新它。我们可以使用圆形缓冲区来存储能量值,并使用指针来跟踪缓冲区中的当前位置。

节拍检测算法并不完美,可能会错过某些节拍或检测误报。但是,它可以很好地近似歌曲的节奏,并可用于同步视觉效果或触发游戏或交互式应用程序中的事件。

节拍检测算法是用于分析音频信号以确定其节奏或节拍的算法。以下是一些常见的节拍检测算法:

1.自相关函数法:该方法通过计算信号的自相关函数来检测节拍。信号的自相关函数将显示信号与其自身在时间上的延迟之间的相关性。当信号具有重复的模式时,自相关函数将具有明显的峰值,这些峰值对应于信号的节拍。

2.峰值检测法:该方法通过寻找信号中的峰值来检测节拍。通常,这些峰值与信号的强度或能量相关。在一段时间内检测到的峰值数量将与该时间段内的节拍数量相匹配。

3.快速傅里叶变换法:该方法通过对信号进行快速傅里叶变换(FFT)来检测节拍。 FFT将信号转换为频率域,其中可以检测到频率和强度。可以根据频率域中的能量峰值来确定信号的节拍。

4.基于模型的方法:该方法使用基于时间的模型来检测节拍。模型将信号表示为一系列时序事件,并使用模型来识别节拍模式。

这些算法可以单独或组合使用,以获得更准确的节拍检测结果。

本系统使用基于声能的简单统计模型计算。

4.1.2 基于声能的简单统计模型检测节拍

本系统是基于声能的简单统计模型实现简单的节拍检测算法,基本思想是利用音频数据的能量变化来检测节奏。计算当前播放前几秒钟声音的平均能量,并将其与声音的当前能量进行比较,如果能量差超过某个阈值,可以说有一个节拍。

使用 1024 个样本的窗口大小和 44100 Hz 的采样率,我们需要一个 44100/1024 = 43 个元素的缓冲区来存储 1 秒的历史记录。此样本的值可以从FFT分析中获得。

我们将分析集中在频谱的第一小节中,这样做的原因是检查声音的较低频率以捕捉电池的踢鼓和军鼓的使用,电池是跟踪歌曲节奏的最常用乐器之一。在我们的实验中,我们将采用 60hz-130hz 的低音范围,我们将在其中找到底鼓,以及中低音 301hz-750hz,在那里可以找到军鼓声音。中低音包含大多数乐器的低次谐波,通常被视为低音存在范围。

因此,我们需要获取此范围内的声音信息,并获取FFT结果的相应元素。要获得FFT结果中每个元素的频率,我们只需要计算频率分割(44100/1024 = 43)并将其乘以数据数组的索引。所以第一个组件存储范围 0-43Hz 的结果,第二个组件存储 43-86Hz,第三个 86-129Hz 的结果......

算法

假设 k 和 k+n 是实际处理范围的极限,FFT[i] 是 i 位置的频率幅度。我们可以计算范围的当前能量为

 我们需要将此值与接下来的 42 个样本一起存储,以获得 1 秒的历史记录 (H)。

 

现在可以使用此历史记录计算波段的平均值 

 

通常,超过平均值加其一半的值是检测节拍的良好阈值。但是我们可以使用历史值的方差来调整这个因子。在像硬摇滚或摇滚乐这样非常嘈杂的音乐中,节拍检测变得有点狡猾,因此我们需要降低更高方差值的阈值。

我们可以定义一条线(方差,阈值)方程来表示阈值和方差之间的关系。以 (0, 1.55) (0.02, 1.25) 作为这条线的两个点。

 

我们的 FFT 结果在 0..1 范围内,因此方差值也在 0..1 范围内。

最后检测到节拍,如果

 

输出1 ,反之输出0,从而生成01序列输出到下一个结点。

为了实现该算法,我定义了一些变量来存储历史数据、采样频率和窗口大小等信息,并编写一些辅助函数来计算平均值、方差和阈值等。另外,为了存储历史数据,我使用双端队列(deque)容器,以便在开头插入新元素并在末尾删除最旧的元素。

本系统检测节拍具体步骤如下:

1.将音频数据按窗口大小进行分割,并计算每个窗口内的平均能量。

2.从频谱中选取低音范围,如60hz-130hz的范围,来捕捉电池的踢鼓和军鼓的使用。

3.对于每个窗口内的数据,在低音范围内计算其FFT结果,并获取相应的频率幅度。

4.根据一定历史记录的范围,如1秒内的历史数据,计算当前时间点的能量值和历史数据的平均能量。

5.根据历史数据的方差调整平均能量值的阈值。

6.判断当前时间点的能量值是否超过阈值,并根据一定规则来检测节拍。

4.1.3 本系统中部分音频结点 

算法实现 

    struct AudioBeats : zeno::INode {
        std::deque<double> H;
        virtual void apply() override {
            auto wave = get_input<PrimitiveObject>("wave");
            float threshold = get_input<NumericObject>("threshold")->get<float>();
            auto start_time = get_input<NumericObject>("time")->get<float>();
            float sampleFrequency = wave->userData().get<zeno::NumericObject>("SampleRate")->get<float>();
            int start_index = int(sampleFrequency * start_time);
            int duration_count = 1024;
            auto fft = Aquila::FftFactory::getFft(duration_count);
            std::vector<double> samples;
            samples.resize(duration_count);
            for (auto i = 0; i < duration_count; i++) {
//                if (start_index + i >= wave->size()) {
//                    break;
//                }
                samples[i] = wave->attr<float>("value")[min((start_index + i), wave->size()-1)];
                
                //if (start_index + i >= wave->size()) {
                //    break;
                //}
                //samples[i] = wave->attr<float>("value")[start_index + i];
            }
            Aquila::SpectrumType spectrums = fft->fft(samples.data());

            {
                double E = 0;
                for (const auto& spectrum: spectrums) {
                    E += spectrum.real() * spectrum.real() + spectrum.imag() * spectrum.imag();
                }
                E /= duration_count;
                H.push_back(E);
            }

            while (H.size() > 43) {
                H.pop_front();
            }
            double avg_H = 0;
            for (const auto& E: H) {
                avg_H += E;
            }
            avg_H /= H.size();

            double var_H = 0;
            for (const auto& E: H) {
                var_H += (E - avg_H) * (E - avg_H);
            }
            var_H /= H.size();
            int beat = H.back() - threshold > (-15 * var_H + 1.55) * avg_H;
            set_output("beat", std::make_shared<NumericObject>(beat));
            set_output("var_H", std::make_shared<NumericObject>((float)var_H));


            auto output_H = std::make_shared<ListObject>();
            for (int i = 0; i < 43 - H.size(); i++) {
                output_H->arr.emplace_back(std::make_shared<NumericObject>((float)0));
            }
            for (const auto & h: H) {
                output_H->arr.emplace_back(std::make_shared<NumericObject>((float)h));
            }
            set_output("H", output_H);

            auto output_E = std::make_shared<ListObject>();
            for (const auto& spectrum: spectrums) {
                double e = spectrum.real() * spectrum.real() + spectrum.imag() * spectrum.imag();
                output_E->arr.emplace_back(std::make_shared<NumericObject>((float)e));
            }
            set_output("E", output_E);
        }
    };

    ZENDEFNODE(AudioBeats, {
        {
            "wave",
            {"float", "time", "0"},
            {"float", "threshold", "0.005"},
        },
        {
            "beat",
            "var_H",
            "H",
            "E",
        },
        {},
        {
            "audio"
        },
    });

    struct AudioEnergy : zeno::INode {
        double minE = std::numeric_limits<double>::max();
        double maxE = std::numeric_limits<double>::min();
        std::vector<double> init;
        virtual void apply() override {
            auto wave = get_input<PrimitiveObject>("wave");
            int duration_count = 1024;
            if (init.empty()) {
                auto fft = Aquila::FftFactory::getFft(duration_count);
                int clip_count = wave->size() / duration_count;
                init.reserve(clip_count);
                for (auto i = 0; i < clip_count; i++) {
                    std::vector<double> samples;
                    samples.resize(duration_count);
                    for (auto j = 0; j < duration_count; j++) {
                        samples[j] = wave->attr<float>("value")[min(duration_count * i + j, wave->size()-1)];
                    }
                    Aquila::SpectrumType spectrums = fft->fft(samples.data());
                    {
                        double E = 0;
                        for (const auto& spectrum: spectrums) {
                            E += spectrum.real() * spectrum.real() + spectrum.imag() * spectrum.imag();
                        }
                        E /= duration_count;
                        minE = min(minE, E);
                        maxE = max(maxE, E);
                        init.push_back(E);
                    }
                }
    //            for (auto i = 0; i < clip_count; i++) {
    //                init[i] = init[i] / maxE;
    //            }
            }

    //        auto vis = std::make_shared<PrimitiveObject>();
    //        vis->resize(init.size());
    //        auto &index = vis->add_attr<float>("index");
    //        auto &listE = vis->add_attr<float>("E");
    //        for (auto i = 0; i < init.size(); i++) {
    //            index[i] = i;
    //            listE[i] = init[i];
    //        }
    //        set_output("vis", vis);

            set_output("minE", std::make_shared<NumericObject>((float)minE));
            set_output("maxE", std::make_shared<NumericObject>((float)maxE));

            auto start_time = get_input2<float>("time");
            float sampleFrequency = wave->userData().get<zeno::NumericObject>("SampleRate")->get<float>();
            int start_index = int(sampleFrequency * start_time);
            auto fft = Aquila::FftFactory::getFft(duration_count);
            std::vector<double> samples;
            samples.resize(duration_count);
            for (auto i = 0; i < duration_count; i++) {
                samples[i] = wave->attr<float>("value")[min((start_index + i), wave->size()-1)];
            }
            Aquila::SpectrumType spectrums = fft->fft(samples.data());
            double E = 0;
            for (const auto& spectrum: spectrums) {
                E += spectrum.real() * spectrum.real() + spectrum.imag() * spectrum.imag();
            }
            E /= duration_count;
            set_output("E", std::make_shared<NumericObject>((float)E));
            double uniE = (E - minE) / (maxE - minE);
            set_output("uniE", std::make_shared<NumericObject>((float)uniE));
            start_index /= duration_count;
            start_index = min(start_index, init.size() - 1);
            std::vector<double> _queue;
            for (int i = max(start_index - 43, 0); i < start_index; i++) {
                _queue.push_back((init[i] - minE) / (maxE - minE));
            }
            if (_queue.size() > 0) {
                double avg_H = 0;
                for (const double & e: _queue) {
                    avg_H += e;
                }
                avg_H /= _queue.size();
                double var_H = 0;
                for (const double & e: _queue) {
                    var_H += (e - avg_H) * (e - avg_H);
                }
                var_H /= _queue.size();
                double std_H = sqrt(var_H);
    //            zeno::log_info("E: {}, avg_H: {}, std_H: {}, var_H: {}", uniE, avg_H, std_H, var_H);
                float threshold = get_input2<float>("threshold");
                int beat = uniE > avg_H + std_H * threshold;
                set_output("beat", std::make_shared<NumericObject>(beat));
            }
            else {
                set_output("beat", std::make_shared<NumericObject>(0));
            }
        }
    };
    ZENDEFNODE(AudioEnergy, {
        {
            "wave",
            {"float", "time", "0"},
            {"float", "threshold", "1"},
        },
        {
            "beat",
            "E",
            "uniE",
            "minE",
            "maxE",
//            "vis",
        },
        {},
        {
            "audio"
        },
    });

    struct AudioFFT : zeno::INode {
        virtual void apply() override {
            auto wave = get_input<PrimitiveObject>("wave");
            int duration_count = 1024;
            auto start_time = get_input2<float>("time");
            float sampleFrequency = wave->userData().get<zeno::NumericObject>("SampleRate")->get<float>();
            int start_index = int(sampleFrequency * start_time);
            std::vector<double> samples;
            samples.resize(duration_count+1);
            for (auto i = 0; i < duration_count+1; i++) {
                samples[i] = wave->attr<float>("value")[min((start_index + i), wave->size()-1)];
            }
            auto pre_emphasis = get_input2<int>("preEmphasis");
            if (pre_emphasis) {
                auto alpha = get_input2<float>("preEmphasisAlpha");
                for (auto i = 0; i < duration_count; i++) {
                    samples[i] = samples[i+1] - alpha * samples[i];
                }
            }
            samples.pop_back();
            auto hamming_window = get_input2<int>("hammingWindow");
            if (hamming_window) {
                for (auto i = 0; i < duration_count; i++) {
                    double i_value = 0.54 - 0.46 * std::cos(2.0 * M_PI * i / (duration_count - 1));
                    samples[i] = samples[i] * i_value;
                }
            }

            auto fft = Aquila::FftFactory::getFft(duration_count);
            Aquila::SpectrumType spectrums = fft->fft(samples.data());

            auto fft_prim = std::make_shared<PrimitiveObject>();
            fft_prim->resize(duration_count / 2 + 1);
            auto &freq = fft_prim->add_attr<float>("freq");
            auto &real = fft_prim->add_attr<float>("real");
            auto &image = fft_prim->add_attr<float>("image");
            auto &square = fft_prim->add_attr<float>("square");
            auto &power = fft_prim->add_attr<float>("power");
            for (std::size_t i = 0; i < fft_prim->verts.size(); ++i) {
                float r = spectrums[i].real();
                float im = spectrums[i].imag();
                freq[i] = float(i);
                real[i] = r;
                image[i] = im;
                float square_v = r * r + im * im;
                square[i] = square_v;
                power[i] = square_v / duration_count;
            }
            set_output("FFTPrim", fft_prim);
        }
    };
    ZENDEFNODE(AudioFFT, {
        {
            "wave",
            {"float", "time", "0"},
            {"bool", "preEmphasis", "0"},
            {"float", "preEmphasisAlpha", "0.97"},
            {"bool", "hammingWindow", "1"},
        },
        {
            "FFTPrim",
        },
        {},
        {
            "audio"
        },
    });
    struct MelFilter : zeno::INode {
        virtual void apply() override {
            auto fftPrim = get_input<PrimitiveObject>("FFTPrim");
            auto &power = fftPrim->attr<float>("power");
            auto sampleFreq = get_input2<float>("sampleFreq");
            auto rangePerFilter = get_input2<float>("rangePerFilter");
            float halfFreq = sampleFreq / 2;
            auto count = get_input2<int>("count");
            std::vector<float> hz_points;
            float mel_fh = 2595.0 * log10(1+halfFreq/700.0);
            for (int i = 0; i <= count + 1; i++) {
                float mel = mel_fh * i / (count + 1);
                float hz = 700.0 * (pow(10.0, mel / 2595.0) - 1);
                hz_points.push_back(hz);
            }
            std::vector<int> bin;
            for (const auto& hz: hz_points) {
                int index = (1024.0+1.0) * hz / sampleFreq;
                bin.push_back(index);
            }
            auto fbank = std::make_shared<PrimitiveObject>();
            fbank->resize(count);
            auto& fbank_v = fbank->add_attr<float>("fbank");
            for (auto i = 1; i <= count; i++) {
                int s = bin[i-1];
                int m = bin[i];
                int e = bin[i+1];
                s = (int) zaudio::lerp(m, s, rangePerFilter);
                e = (int) zaudio::lerp(m, e, rangePerFilter);
                float total = 0;
                for (auto i = s; i < m; i++) {
                    float cof = (float)(m - i) / (float)(m - s);
                    total += power[i] * cof;
                }
                for (auto i = m; i < e; i++) {
                    float cof = 1 - (float)(m - i) / (float)(e - m);
                    total += power[i] * cof;
                }
                if (total == 0) {
                    fbank_v[i-1] = std::numeric_limits<float>::min();
                }
                else {
                    fbank_v[i-1] = log(total);
                }
            }
            auto indexType = get_input2<std::string>("indexType");
            if (indexType == "index") {
                auto& index = fbank->add_attr<float>("i");
                for (auto i = 1; i <= count; i++) {
                    index[i-1] = (float)(i-1);
                };
            } else if (indexType == "indexdivcount") {
                auto& index = fbank->add_attr<float>("i");
                for (auto i = 1; i <= count; i++) {
                    index[i-1] = (float)(i-1) /count;
                };
            }
            set_output("FilterBank", fbank);
        }
    };
    ZENDEFNODE(MelFilter, {
        {
            "FFTPrim",
            {"int", "count", "15"},
            {"float", "sampleFreq", "44100"},
            {"float", "rangePerFilter", "1"},
            {"enum none index indexdivcount", "indexType", "index"},
        },
        {
            "FilterBank",
        },
        {},
        {
            "audio",
        },
    });
} // namespace zeno

参考文献

BEAT DETECTION ALGORITHMS.doc (parallelcube.com)icon-default.png?t=N2N8https://www.parallelcube.com/web/wp-content/uploads/2018/03/BeatDetectionAlgorithms.pdf

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaSE——数组

这篇文章的面向读者为Java初级程序员&#xff0c;也就是刚刚接触到Java这门语言&#xff0c;里面描述的内容主要是数组相关的内容&#xff0c;讲解了最基础的一些数组扩容思路&#xff0c;数组赋值机制&#xff0c;什么是引用地址&#xff0c;什么是基础数据赋值。 Java该章节数…

JAVA入坑之线程

目录 一、&#xff1a;相关概念 1.1中央处理器(CPU&#xff0c;Central Processing Unit) 1.2程序、进程、线程 1.3线程与任务 二、线程的创建&#xff1a; 2.1继承Thread创建线程&#xff1a; 使用Thread子类创建线程的优缺点 2.2实现Runnable接口创建线程&#xff1a;…

【C++STL精讲】list的使用教程及其模拟实现

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f337;list是什么&#xff1f;&#x1f337;list如何使用&#xff1f;&#x1f337;list的模拟实现&#x1f33a;定义list类&#x1f33a;构造函数&#x1f33a;push_back&#x1f33a;pop_back &#x1f337;list迭…

利用STM32实现自平衡机器人功能与方法

将机器人整体开源&#xff0c;同时总结一下机器人搭建过程中遇到的坑和未来的改进方向。在分享的文件里包含了结构设计、程序控制、电路设计以及其他模块相关资料供大家参考。 第一&#xff1a;机器人原理分析 首先来看成品图&#xff1a; 如图所示&#xff0c;该机器人根据陀…

宝塔面板设置虚拟内存Swap降低的方法

宝塔面板可以通过设置Swap/虚拟内存的方式来降低内存使用率和负载&#xff0c;使用宝塔面板的Linux工具箱即可设置虚拟内存&#xff0c;新手站长来详细说下宝塔Linux面板设置Swap/虚拟内存的方法&#xff1a; 宝塔面板设置虚拟内存 设置虚拟内存是通过Linux工具箱&#xff0c…

4.0、Java继承与多态 - 抽象类与抽象方法

4.0、Java继承与多态 - 抽象类与抽象方法 先给大家举个例子 -> 创建一个父类 - 图形类&#xff1b;图形类中有一个计算面积的方法 calculateArea()&#xff1b; 创建三个子类 - 正方形、三角形、圆形 类&#xff1b; 由于我们图形类父类中未明确指明是什么图形&#xff0c…

内网域环境搭建学习

建立的关系就是这样&#xff0c;接下来就开始讲解遇到的困难 虚拟机中我们可以克隆来实现域的搭建 可能会出现这样的问题&#xff0c;原因是你的虚拟机没有关闭&#xff0c;所以才会导致这样的原因&#xff0c;解决方法 将虚拟机打开后&#xff0c;电源 -> 关闭客户机&…

MySQL学习笔记第二天

第03章 基本的SELECT语句 1.SQL概述 1.1 SQL背景知识 1946年&#xff0c;世界上第一台电脑诞生&#xff0c;如今&#xff0c;借由这台电脑发展起来的互联网已经自成江湖。在这几十年里&#xff0c;无数的技术、产业在这片江湖里沉浮&#xff0c;有的方兴未艾&#xff0c;有的…

reactxp搭建,start:windows运行不起来

1、官网 reactxp 2、VSCode和Visual Studio2019 安装VSCode Visual Studio 下载地址 先不用勾选工作负荷&#xff0c;直接安装 3、安装nvm 访问下载地址下载安装nvm&#xff1a; 百度云分享 官网直装链接 nvm的github发行界面下载nvm-setup.exe GitCode镜像下载nvm-setup…

ACL配置学习(附练习题)------ensp

从此文了解ACL配置&#xff0c;欢迎学习、指导。 目录 基本ACL配置举例 高级ACL配置举例 ACL配置练习题 定义 访问控制列表ACL&#xff08;Access Control List&#xff09;本质上是一种报文过滤器。 范围: OSI七层模型中的网络层、传输层的信息。 滤芯&#xff1a;五…

2023年4月实时获取地图边界数据方法,省市区县街道多级联动【附实时geoJson数据下载】

首先&#xff0c;来看下效果图 在线体验地址&#xff1a;https://geojson.hxkj.vip&#xff0c;并提供实时geoJson数据文件下载 可下载的数据包含省级geojson行政边界数据、市级geojson行政边界数据、区/县级geojson行政边界数据、省市区县街道行政编码四级联动数据&#xff0…

大型医院体检管理系统源码,PEIS体检系统源码 丰富的诊断模板,自动产生小结、综述和建议

PEIS体检管理系统源码 体检条码化管理&#xff0c;体检数据比对&#xff0c;丰富的诊断模板&#xff0c;自动产生小结、综述和建议。 文末获取联系 PEIS体检管理系统对医院体检中心进行系统化和规范化的管理&#xff0c;大大提高体检中心的综合管理水平、工作效率。系统从业务…

设计模式-结构型模式之享元模式

5. 享元模式 5.1. 模式动机 面向对象技术可以很好地解决一些灵活性或可扩展性问题&#xff0c;但在很多情况下需要在系统中增加类和对象的个数。当对象数量太多时&#xff0c;将导致运行代价过高&#xff0c;带来性能下降等问题。 享元模式正是为解决这一类问题而诞生的。享元模…

GoF代理模式

在java中代理模式的作用: 1.一个对象需要受到保护的时候&#xff0c;可以考虑使用代理对象取完成某个行为. 2.需要给某个对象的功能进行功能增强的时候&#xff0c;可以考虑找一个代理进行增强 3.A对象无法和B对象无法直接沟通&#xff0c;也可以使用代理模式解决 代理模式有三…

WPF mvvm框架Stylet使用教程-窗体交互用法

窗体操作 打开窗体 在stylet框架中&#xff0c;要打开一个窗口或者对话框&#xff0c;只需要直接使用窗口管理器 在要使用的ViewModel中注入IWindowManager&#xff0c;然后使用他的方法操作窗口。 ShowDialog(object viewModel)模态显示ShowWindow(object viewModel) 非模…

修改OPNET帮助文档的默认打开浏览器 给Edge浏览器配置IE Tab插件

我在使用 OPENT Modeler 软件时经常会用到帮助文档&#xff0c;但是其默认打开的是 IE 浏览器&#xff0c;想要其在 Edge 浏览器中打开&#xff0c;但是会出现网页无法打开的情况&#xff0c;这时需要给 Edge 浏览器安装一个 IE Tab 插件。 IE Tab 插件是专门针对浏览器而开发的…

vue3的介绍和两种创建方式(cli和vite)

目录 一、vue3的介绍 &#xff08;一&#xff09;vue3的简介 &#xff08;二&#xff09;vue3对比vue2带来的性能提升 二、vue3的两种创建方式 方式一&#xff1a;使用vue-cli创建&#xff08;推荐--全面&#xff09; 操作步骤 方式二&#xff1a;使用vite创建 操作步…

Spring是什么?关于Spring家族

初识Spring 什么是Spring&#xff1f; Spring是一个开源的Java企业级应用程序开发框架&#xff0c;由Rod Johnson于2003年创建&#xff0c;并在接下来的几年里得到了广泛的发展和应用。它提供了一系列面向对象的编程和配置模型&#xff0c;支持开发各种类型的应用程序&#x…

黑客网站攻击的主要手段

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 黑客与白帽子 有的童鞋觉得黑客和白帽子是同一回事儿&#xff0c;其实不然&#xff1b;而且&#xff0c;他们的工作方式与目标也有很大的差异。 黑客是指一群专门使用计算机…

9.2 变量的指针和指向变量的指针变量

9.2 变量的指针和指向变量的指针变量 一.指针变量的定义二.指针变量的引用三.整理至https://appd872nnyh9503.pc.xiaoe-tech.com/index的笔记 一.指针变量的定义 变量的指针 就是变量的地址。 我们可以定义一个指向 变量 的指针变量。 这种指针变量&#xff0c;我们在定义的时…