Python和C++音调音符规划和算法

🎯要点

  1. 🎯音符表征和代码实现:🖊半音位置和索引之间的转换 | 🖊全音阶音调表征 | 🖊全音阶缓存 | 🖊全音阶音高表征。🎯音程表征和代码实现:🖊音程质量 | 🖊计算开始和结束音高。🎯情态和音调和代码实现:🖊创建情态:全音阶调式、五声音阶、八音调态、全音调态、布鲁斯调式 | 🖊构建模态 | 🖊音调表征 | 🖊生成基于音高的音阶。🎯音符时长代码实现:🖊时间线转换算法 | 🖊节拍时间转换算法 | 🖊全音符时间表征 | 🖊位置和时长算子 | 🖊节拍位置 | 🖊拍号和节奏 | 🖊时间转换算法。🎯音符及其连音代码实现:🖊梁和连音定义 | 🖊反转旋律的算法。🎯和弦代码实现:🖊文本规范构建和弦 | 🖊三级和弦 | 🖊次和弦 | 🖊四和弦 。🎯乐器模型代码实现:🖊演奏记号列表。🎯乐谱表征和代码实现:🖊声音中的嵌套音符 | 🖊节奏、节拍和调号序列 | 🖊基于时间的音符搜索方式:红黑树方式、音程树方式 | 🖊事件和观察者模式更新音程树。🎯MIDI 表征和代码实现:🖊MIDI消息定义 | 🖊乐谱和MIDI文件转换算法 | 🖊音符转换为MIDI文件。🎯音调约束规划 | 🎯音调移位变换 | 🎯旋律反转变换和重塑 | 🎯和声转录 | 🎯逆转旋律。
  2. 🎯音乐合成:🖊节拍自定义:🖊使用底鼓、军鼓和踩镲 | 🖊旋律创建:🖊贝多芬《致爱丽丝》混音版 | 🖊和弦创建:🖊观察音色、和声和时间的细微变化 | 🖊音阶、调和旋律构建 | 🖊全音阶和弦 | 🖊傅里叶变换创建音效 | 🖊 基于音符的音效 | 🖊歌曲编辑。
  3. 🎯Python数字声音合成 | 🎯C++数字声音合成

🍇C++多抽头混响音效

混响与延迟类似,都为声音添加了回声。 不过,它的不同之处在于,延迟会添加一个回声(即使该回声会重复出现,每次都会更安静),而混响会在声音中添加多个回声。 基本上,混响使声音听起来像是在教堂、深洞或小浴室中演奏的。 延迟只会让事情听起来有点回声。

该技术非常简单。 您有一个样本缓冲区来保存最后 N 个样本,然后当您处理传入样本时,您添加来自延迟缓冲区的多个样本,每个样本乘以不同的音量(幅度)并将其添加到传入样本中以获得输出样本。 然后,您还将该传出样本放入循环缓冲区中当前索引处的混响缓冲区中。

这是一些关于如何实现它的伪代码:


reverbBuffer[reverbSamples] = 0;
  
reverbIndex= 0;
  
for (i = 0; i < numSamples; ++i)
{

  outSample[i] = inSample[i];
  for (j = 0; j < numTaps; ++j)
    outSample[i] += reverbBuffer[reverbIndex - taps[j].tapTime] * taps[j].feedbackMultiplier; 
  

  reverbBuffer[reverbIndex] = outSample[i];
  

  reverbIndex++;
  if (reverbIndex>= reverbSamples)
    reverbIndex= 0;
}

在下面的示例代码以及上面的混响处理示例中,以下是所使用的拍子的时间和幅度。幅度以 dB 和幅度的形式给出,因此您可以看到您更喜欢的那个。
 Time (ms)  d B  Amplitude  79 − 25 0.0562 130 − 23 0.0707 230 − 15 0.1778 340 − 23 0.0707 470 − 17 0.1412 532 − 21 0.0891 662 − 13 0.2238 \begin{array}{lll} \text { Time (ms) } & dB & \text { Amplitude } \\ 79 & -25 & 0.0562 \\ 130 & -23 & 0.0707 \\ 230 & -15 & 0.1778 \\ 340 & -23 & 0.0707 \\ 470 & -17 & 0.1412 \\ 532 & -21 & 0.0891 \\ 662 & -13 & 0.2238 \end{array}  Time (ms) 79130230340470532662dB25231523172113 Amplitude 0.05620.07070.17780.07070.14120.08910.2238
通过更多的努力,您可能会想出一些更好的抽头值来使混响听起来更好。下面的示例代码加载 in.wav,使用上面提到的 Tap 对其进行处理,并将其写出为 out.wav。 与往常一样,波形加载代码对于某些波形格式存在一些问题。

#define _CRT_SECURE_NO_WARNINGS
 
#include <array>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <cmath>
#include <vector>
 
#define _USE_MATH_DEFINES
#include <math.h>

template <typename T, typename PHANTOM_TYPE>
struct SNumeric
{
public:
    explicit SNumeric(const T &value) : m_value(value) { }
    SNumeric() : m_value() { }
    inline T& Value() { return m_value; }
    inline const T& Value() const { return m_value; }
 
    typedef SNumeric<T, PHANTOM_TYPE> TType;
    typedef T TInnerType;
 
    // Math Operations
    TType operator+ (const TType &b) const
    {
        return TType(this->Value() + b.Value());
    }
 
    TType operator- (const TType &b) const
    {
        return TType(this->Value() - b.Value());
    }
 
    TType operator* (const TType &b) const
    {
        return TType(this->Value() * b.Value());
    }
 
    TType operator/ (const TType &b) const
    {
        return TType(this->Value() / b.Value());
    }
 
    TType& operator+= (const TType &b)
    {
        Value() += b.Value();
        return *this;
    }
 
    TType& operator-= (const TType &b)
    {
        Value() -= b.Value();
        return *this;
    }
 
    TType& operator*= (const TType &b)
    {
        Value() *= b.Value();
        return *this;
    }
 
    TType& operator/= (const TType &b)
    {
        Value() /= b.Value();
        return *this;
    }
 
    TType& operator++ ()
    {
        Value()++;
        return *this;
    }
 
    TType& operator-- ()
    {
        Value()--;
        return *this;
    }
 
    // Extended Math Operations
    template <typename T>
    T Divide(const TType &b)
    {
        return ((T)this->Value()) / ((T)b.Value());
    }
 
    // Logic Operations
    bool operator< (const TType &b) const {
        return this->Value() < b.Value();
    }
    bool operator<= (const TType &b) const {
        return this->Value() <= b.Value();
    }
    bool operator> (const TType &b) const {
        return this->Value() > b.Value();
    }
    bool operator>= (const TType &b) const {
        return this->Value() >= b.Value();
    }
    bool operator== (const TType &b) const {
        return this->Value() == b.Value();
    }
    bool operator!= (const TType &b) const {
        return this->Value() != b.Value();
    }
 
private:
    T m_value;
};
 
 
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef int16_t int16;
typedef int32_t int32;
 
typedef SNumeric<float, struct S__Frequency>      TFrequency;
typedef SNumeric<uint32, struct S__TimeMs>        TTimeMs;
typedef SNumeric<uint32, struct S__Samples>       TSamples;
typedef SNumeric<float, struct S__FractSamples>   TFractionalSamples;
typedef SNumeric<float, struct S__Decibels>       TDecibels;
typedef SNumeric<float, struct S__Amplitude>      TAmplitude;
typedef SNumeric<float, struct S__Phase>          TPhase;
 
 
static const float c_pi = (float)M_PI;
static const float c_twoPi = c_pi * 2.0f;
 
//=====================================================================================
// Structs
//=====================================================================================
 
struct SSoundSettings
{
    TSamples        m_sampleRate;
    TTimeMs         m_lengthMs;
    TSamples        m_currentSample;
};
 
 
struct SReverbTap
{
    TSamples    m_timeOffset;
    TAmplitude  m_feedback;
};
 
class CMultitapReverb
{
public:
    CMultitapReverb(const std::vector<SReverbTap>& taps)
        : m_sampleIndex(0)
    {

        m_taps = taps;
        TSamples largestTimeOffset(0);
        std::for_each(m_taps.begin(), m_taps.end(),
            [&largestTimeOffset](const SReverbTap& tap)
            {
                if (tap.m_timeOffset > largestTimeOffset)
                    largestTimeOffset = tap.m_timeOffset;
            }
        );

        if (largestTimeOffset.Value() == 0)
            return;
 
        m_samples.resize(largestTimeOffset.Value()+1);
        std::fill(m_samples.begin(), m_samples.end(), TAmplitude(0.0f));
    }
 
    TAmplitude ProcessSample(TAmplitude sample)
    {

        if (m_samples.size() == 0)
            return sample;

        TAmplitude outSample = sample;
        std::for_each(m_taps.begin(), m_taps.end(),
            [&outSample, this](const SReverbTap& tap)
            {
                size_t tapSampleIndex;
                if (tap.m_timeOffset.Value() > m_sampleIndex)
                    tapSampleIndex = m_samples.size() - 1 - (tap.m_timeOffset.Value() - m_sampleIndex);
                else
                    tapSampleIndex = m_sampleIndex - tap.m_timeOffset.Value();
 
                outSample += m_samples[tapSampleIndex] * tap.m_feedback;
            }
        );
 
        m_samples[m_sampleIndex] = outSample;
 
        m_sampleIndex++;
        if (m_sampleIndex >= m_samples.size())
            m_sampleIndex = 0;
 
        return outSample;
    }
 
private:
    std::vector<SReverbTap> m_taps;
    std::vector<TAmplitude> m_samples;
    size_t                  m_sampleIndex;
};
 
inline TDecibels AmplitudeToDB(TAmplitude volume)
{
    return TDecibels(log10(volume.Value()));
}
 
inline TAmplitude DBToAmplitude(TDecibels dB)
{
    return TAmplitude(pow(10.0f, dB.Value() / 20.0f));
}
 
TSamples SecondsToSamples(const SSoundSettings &s, float seconds)
{
    return TSamples((int)(seconds * (float)s.m_sampleRate.Value()));
}
 
TSamples MilliSecondsToSamples(const SSoundSettings &s, float milliseconds)
{
    return SecondsToSamples(s, milliseconds / 1000.0f);
}
 
TTimeMs SecondsToMilliseconds(float seconds)
{
    return TTimeMs((uint32)(seconds * 1000.0f));
}
 
TFrequency Frequency(float octave, float note)
{

    return TFrequency((float)(440 * pow(2.0, ((double)((octave - 4) * 12 + note)) / 12.0)));
}
 
template <typename T>
T AmplitudeToAudioSample(const TAmplitude& in)
{
    const T c_min = std::numeric_limits<T>::min();
    const T c_max = std::numeric_limits<T>::max();
    const float c_minFloat = (float)c_min;
    const float c_maxFloat = (float)c_max;
 
    float ret = in.Value() * c_maxFloat;
 
    if (ret < c_minFloat)
        return c_min;
 
    if (ret > c_maxFloat)
        return c_max;
 
    return (T)ret;
}
 
TAmplitude GetLerpedAudioSample(const std::vector<TAmplitude>& samples, TFractionalSamples& index)
{
    uint32 a = (uint32)floor(index.Value());
    uint32 b = a + 1;
    float fract = index.Value() - floor(index.Value());
 
    float ampA = 0.0f;
    if (a >= 0 && a < samples.size())
        ampA = samples[a].Value();
 
    float ampB = 0.0f;
    if (b >= 0 && b < samples.size())
        ampB = samples[b].Value();
 
    return TAmplitude(fract * ampB + (1.0f - fract) * ampA);
}
 
void NormalizeSamples(std::vector<TAmplitude>& samples, TAmplitude maxAmplitude)
{

    if (samples.size() == 0)
        return;
 
    TAmplitude largestAbsVal = TAmplitude(abs(samples.front().Value()));
    std::for_each(samples.begin() + 1, samples.end(), [&largestAbsVal](const TAmplitude &a)
    {
        TAmplitude absVal = TAmplitude(abs(a.Value()));
        if (absVal > largestAbsVal)
            largestAbsVal = absVal;
    }
    );
 

    largestAbsVal /= maxAmplitude;
    if (largestAbsVal <= TAmplitude(0.0f))
        return;
 
    std::for_each(samples.begin(), samples.end(), [&largestAbsVal](TAmplitude &a)
    {
        a /= largestAbsVal;
 
        if (a >= TAmplitude(1.0f))
        {
            int ijkl = 0;
        }
    }
    );
}
 
void ResampleData(std::vector<TAmplitude>& samples, int srcSampleRate, int destSampleRate)
{

    if (srcSampleRate == destSampleRate)
        return;
 
    float fResampleRatio = (float)destSampleRate / (float)srcSampleRate;
 
    int nNewDataNumSamples = (int)((float)samples.size() * fResampleRatio);
 
    std::vector<TAmplitude> newSamples;
    newSamples.resize(nNewDataNumSamples);
 
    for (int nIndex = 0; nIndex < nNewDataNumSamples; ++nIndex)
        newSamples[nIndex] = GetLerpedAudioSample(samples, TFractionalSamples((float)nIndex / fResampleRatio));
 

    std::swap(samples, newSamples);
}
 
void ChangeNumChannels(std::vector<TAmplitude>& samples, int nSrcChannels, int nDestChannels)
{

    if (nSrcChannels == nDestChannels ||
        nSrcChannels < 1 || nSrcChannels > 2 ||
        nDestChannels < 1 || nDestChannels > 2)
    {
        return;
    }
 
    if (nDestChannels == 2)
    {
        std::vector<TAmplitude> newSamples;
        newSamples.resize(samples.size() * 2);
        for (size_t index = 0; index < samples.size(); ++index)
        {
            newSamples[index * 2] = samples[index];
            newSamples[index * 2 + 1] = samples[index];
        }
 
        std::swap(samples, newSamples);
    }

    else
    {
        std::vector<TAmplitude> newSamples;
        newSamples.resize(samples.size() / 2);
        for (size_t index = 0; index < samples.size() / 2; ++index)
            newSamples[index] = samples[index * 2] + samples[index * 2 + 1];
 
        std::swap(samples, newSamples);
    }
}
 
float PCMToFloat(unsigned char *pPCMData, int nNumBytes)
{
    switch (nNumBytes)
    {
    case 1:
    {
        uint8 data = pPCMData[0];
        return (float)data / 255.0f;
    }
    case 2:
    {
        int16 data = pPCMData[1] << 8 | pPCMData[0];
        return ((float)data) / ((float)0x00007fff);
    }
    case 3:
    {
        int32 data = pPCMData[2] << 16 | pPCMData[1] << 8 | pPCMData[0];
        return ((float)data) / ((float)0x007fffff);
    }
    case 4:
    {
        int32 data = pPCMData[3] << 24 | pPCMData[2] << 16 | pPCMData[1] << 8 | pPCMData[0];
        return ((float)data) / ((float)0x7fffffff);
    }
    default:
    {
        return 0.0f;
    }
    }
}
 
struct SMinimalWaveFileHeader
{

    unsigned char m_szChunkID[4];      //0
    uint32        m_nChunkSize;        //4
    unsigned char m_szFormat[4];       //8

    unsigned char m_szSubChunk1ID[4];  //12
    uint32        m_nSubChunk1Size;    //16
    uint16        m_nAudioFormat;      //18
    uint16        m_nNumChannels;      //20
    uint32        m_nSampleRate;       //24
    uint32        m_nByteRate;         //28
    uint16        m_nBlockAlign;       //30
    uint16        m_nBitsPerSample;    //32
 
    unsigned char m_szSubChunk2ID[4];  //36
    uint32        m_nSubChunk2Size;    //40
 

};
 

template <typename T>
bool WriteWaveFile(const char *fileName, const std::vector<TAmplitude> &samples, const SSoundSettings &sound)
{
    //open the file if we can
    FILE *file = fopen(fileName, "w+b");
    if (!file)
        return false;
 
    //calculate bits per sample and the data size
    const int32 bitsPerSample = sizeof(T) * 8;
    const int dataSize = samples.size() * sizeof(T);
 
    SMinimalWaveFileHeader waveHeader;
 
    //fill out the main chunk
    memcpy(waveHeader.m_szChunkID, "RIFF", 4);
    waveHeader.m_nChunkSize = dataSize + 36;
    memcpy(waveHeader.m_szFormat, "WAVE", 4);
 
    //fill out sub chunk 1 "fmt "
    memcpy(waveHeader.m_szSubChunk1ID, "fmt ", 4);
    waveHeader.m_nSubChunk1Size = 16;
    waveHeader.m_nAudioFormat = 1;
    waveHeader.m_nNumChannels = 1;
    waveHeader.m_nSampleRate = sound.m_sampleRate.Value();
    waveHeader.m_nByteRate = sound.m_sampleRate.Value() * 1 * bitsPerSample / 8;
    waveHeader.m_nBlockAlign = 1 * bitsPerSample / 8;
    waveHeader.m_nBitsPerSample = bitsPerSample;
 
    //fill out sub chunk 2 "data"
    memcpy(waveHeader.m_szSubChunk2ID, "data", 4);
    waveHeader.m_nSubChunk2Size = dataSize;
 
    //write the header
    fwrite(&waveHeader, sizeof(SMinimalWaveFileHeader), 1, file);
 
    //write the wave data itself, converting it from float to the type specified
    std::vector<T> outSamples;
    outSamples.resize(samples.size());
    for (size_t index = 0; index < samples.size(); ++index)
        outSamples[index] = AmplitudeToAudioSample<T>(samples[index]);
    fwrite(&outSamples[0], dataSize, 1, file);
 
    //close the file and return success
    fclose(file);
    return true;
}
 

bool ReadWaveFile(const char *fileName, std::vector<TAmplitude>& samples, int32 sampleRate)
{
    //open the file if we can
    FILE *File = fopen(fileName, "rb");
    if (!File)
    {
        return false;
    }
 
    //read the main chunk ID and make sure it's "RIFF"
    char buffer[5];
    buffer[4] = 0;
    if (fread(buffer, 4, 1, File) != 1 || strcmp(buffer, "RIFF"))
    {
        fclose(File);
        return false;
    }
 
    //read the main chunk size
    uint32 nChunkSize;
    if (fread(&nChunkSize, 4, 1, File) != 1)
    {
        fclose(File);
        return false;
    }
 
    //read the format and make sure it's "WAVE"
    if (fread(buffer, 4, 1, File) != 1 || strcmp(buffer, "WAVE"))
    {
        fclose(File);
        return false;
    }
 
    long chunkPosFmt = -1;
    long chunkPosData = -1;
 
    while (chunkPosFmt == -1 || chunkPosData == -1)
    {
        //read a sub chunk id and a chunk size if we can
        if (fread(buffer, 4, 1, File) != 1 || fread(&nChunkSize, 4, 1, File) != 1)
        {
            fclose(File);
            return false;
        }
 
        //if we hit a fmt
        if (!strcmp(buffer, "fmt "))
        {
            chunkPosFmt = ftell(File) - 8;
        }
        //else if we hit a data
        else if (!strcmp(buffer, "data"))
        {
            chunkPosData = ftell(File) - 8;
        }
 
        //skip to the next chunk
        fseek(File, nChunkSize, SEEK_CUR);
    }
 
    //we'll use this handy struct to load in 
    SMinimalWaveFileHeader waveData;
 
    //load the fmt part if we can
    fseek(File, chunkPosFmt, SEEK_SET);
    if (fread(&waveData.m_szSubChunk1ID, 24, 1, File) != 1)
    {
        fclose(File);
        return false;
    }
 
    //load the data part if we can
    fseek(File, chunkPosData, SEEK_SET);
    if (fread(&waveData.m_szSubChunk2ID, 8, 1, File) != 1)
    {
        fclose(File);
        return false;
    }
 
    //verify a couple things about the file data
    if (waveData.m_nAudioFormat != 1 ||       //only pcm data
        waveData.m_nNumChannels < 1 ||        //must have a channel
        waveData.m_nNumChannels > 2 ||        //must not have more than 2
        waveData.m_nBitsPerSample > 32 ||     //32 bits per sample max
        waveData.m_nBitsPerSample % 8 != 0 || //must be a multiple of 8 bites
        waveData.m_nBlockAlign > 8)           //blocks must be 8 bytes or lower
    {
        fclose(File);
        return false;
    }
 
    //figure out how many samples and blocks there are total in the source data
    int nBytesPerBlock = waveData.m_nBlockAlign;
    int nNumBlocks = waveData.m_nSubChunk2Size / nBytesPerBlock;
    int nNumSourceSamples = nNumBlocks * waveData.m_nNumChannels;
 
    //allocate space for the source samples
    samples.resize(nNumSourceSamples);
 
    //maximum size of a block is 8 bytes.  4 bytes per samples, 2 channels
    unsigned char pBlockData[8];
    memset(pBlockData, 0, 8);
 
    //read in the source samples at whatever sample rate / number of channels it might be in
    int nBytesPerSample = nBytesPerBlock / waveData.m_nNumChannels;
    for (int nIndex = 0; nIndex < nNumSourceSamples; nIndex += waveData.m_nNumChannels)
    {
        //read in a block
        if (fread(pBlockData, waveData.m_nBlockAlign, 1, File) != 1)
        {
            fclose(File);
            return false;
        }
 
        //get the first sample
        samples[nIndex].Value() = PCMToFloat(pBlockData, nBytesPerSample);
 
        //get the second sample if there is one
        if (waveData.m_nNumChannels == 2)
        {
            samples[nIndex + 1].Value() = PCMToFloat(&pBlockData[nBytesPerSample], nBytesPerSample);
        }
    }
 
    //re-sample the sample rate up or down as needed
    ResampleData(samples, waveData.m_nSampleRate, sampleRate);
 
    //handle switching from mono to stereo or vice versa
    ChangeNumChannels(samples, waveData.m_nNumChannels, 1);
 
    return true;
}
 

参阅一:计算思维

参阅二:亚图跨际

相关推荐

  1. PythonC++音调音符规划算法

    2024-04-26 10:24:03       33 阅读
  2. Python录制绘制音频

    2024-04-26 10:24:03       47 阅读
  3. 如何区别单音节音节

    2024-04-26 10:24:03       39 阅读
  4. ThreeJs的音频位置音频

    2024-04-26 10:24:03       22 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-26 10:24:03       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-26 10:24:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-26 10:24:03       20 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-26 10:24:03       20 阅读

热门阅读

  1. 力扣795.区间子数组个数 | 树状数组解法

    2024-04-26 10:24:03       13 阅读
  2. 磨损对输送带安全的影响

    2024-04-26 10:24:03       33 阅读
  3. C#中的LINQ(Language-Integrated Query)

    2024-04-26 10:24:03       14 阅读
  4. 二叉树层次遍历

    2024-04-26 10:24:03       19 阅读
  5. 分布式与微服务区别?

    2024-04-26 10:24:03       11 阅读
  6. npm cnpm pnpm yarn 有什么区别? 哪个更好用呢?

    2024-04-26 10:24:03       15 阅读
  7. sklearn混淆矩阵的计算和seaborn可视化

    2024-04-26 10:24:03       19 阅读
  8. springboot+Vue实现分页

    2024-04-26 10:24:03       13 阅读
  9. 相交链表的判断(leetcode)

    2024-04-26 10:24:03       11 阅读