cycfi / q

C++ Library for Audio Digital Signal Processing
MIT License
1.12k stars 146 forks source link

frequency to note conversion questions #70

Closed SeanTolstoyevski closed 1 month ago

SeanTolstoyevski commented 1 month ago

Hi everyone.

I have a c++ code where I listen to the sound coming from the microphone and get the frequency of the sound. I don't expect it to work perfectly, but I want to find the musical note of the sound coming out of my guitar. I used miniaudio and q for this. I use Miniaudio for audio io operations. I will share the code with you.

My questions are:

code

#include <chrono>
#include <csignal>
#include <cstdint>
#include <format>
#include <iostream>
#include <thread>
#include <string>

#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio/miniaudio.h"
#include "q/pitch/pitch_detector.hpp"
#include "q/support/literals.hpp"

namespace q = cycfi::q;
using namespace q::literals;

bool running = false;

std::vector<float> audio_buffer;

void printFreq(const ma_device *dev, void *pUserData)
{
    if (audio_buffer.size() < dev->sampleRate * 3) // 3 second buffer
    {
        return;
    }
    auto pitch_detector = static_cast<q::pitch_detector *>(pUserData);
    bool ok{};
    for (const auto &v : audio_buffer)
    {
        if (! running)
        {
            return;
        }
        ok = pitch_detector->operator()(v);
    }    // val loop
    auto per = pitch_detector->periodicity();
    if (ok && per > 0.90)
    {
        auto freq = pitch_detector->get_frequency();
        if (freq > 0)
        {
            std::cout << std::format("Frequency: {}, per: {}\n", freq, per);
        }

        audio_buffer.clear();
        pitch_detector->reset();
    }    // freq ok if
}

void data_callback(ma_device *pDevice, void *pOutput, const void *pInput,
                                     ma_uint32 frameCount)
{
    const auto inputBuffer = static_cast<const float *>(pInput);

    if (! running)
    {
        return;
    }
    audio_buffer.insert(audio_buffer.end(), inputBuffer,
                                            inputBuffer + frameCount);
    printFreq(pDevice, pDevice->pUserData);
}

void signalHandler(int signum)
{
    std::cout << "Interrupt signal (" << signum << ") received.\n";

    running = false;    // stop the main loop
}

int main()
{
    q::pitch_detector pitch_detector{q::frequency(50), q::frequency(400), 1,
                                                                     -45_dB};

    ma_device_config device_config =
        ma_device_config_init(ma_device_type_capture);
    device_config.dataCallback = data_callback;
    device_config.capture.format = ma_format_f32;
    // device_config.sampleRate = 44100;
    device_config.capture.channels = 1;
    device_config.pUserData = static_cast<void *>(&pitch_detector);

    ma_device device;
    device.pUserData = static_cast<void *>(&pitch_detector);
    auto result = ma_device_init(NULL, &device_config, &device);
    if (result != MA_SUCCESS)
    {
        std::cerr << std::format("Failed to create miniaudio device: {}\n",
                                                         ma_result_description(result));
        return EXIT_FAILURE;
    }

    result = ma_device_start(&device);
    if (result != MA_SUCCESS)
    {
        std::cerr << std::format("Failed to start miniaudio device: {}\n",
                                                         ma_result_description(result));
        return EXIT_FAILURE;
    }
    std::cout << std::format("device: {}, sample rate: {}\n",
                                                     device.capture.name, device.sampleRate);

    running = true;
    std::signal(SIGINT, signalHandler);
    std::signal(SIGTERM, signalHandler);
    std::signal(SIGBREAK, signalHandler);
    std::signal(SIGABRT, signalHandler);

    while (running)
    {
        // no block for audio io because running with another thread
        std::this_thread::sleep_for(std::chrono::milliseconds(70));
    }

    std::cout << "done\n";
    ma_device_stop(&device);
    ma_device_uninit(&device);
    return EXIT_SUCCESS;
}

Although I don't think it's right to explain this here, I think it might be useful to share. There is currently no computer application that visually impaired musicians can use to tune their instruments. It's available for iOS and it money (using a mobile device isn't always practical). This is both a learning adventure for me and when something good happens, I want to publish it as a usable, accessible application with a gui toolset like wxWidgets. That's why your help is important :) .

djowel commented 1 month ago
  • How can I convert the frequency values ​​in range 0-1 returned by the get_frequency member function of the pitch_detector class into normal pitch values?

get_frequency does not return values ​​in range 0-1. It returns the actual frequency. Have you tried running the pitch_detector test? Here's the relevant code:

https://github.com/cycfi/q/blob/develop/test/pitch_detector.cpp#L63-L76

djowel commented 1 month ago

To convert frequency to pitch, you can use this: https://cycfi.github.io/q/q/v1.0_beta/reference/units/pitch.html

SeanTolstoyevski commented 1 month ago

hi @djowel

Thanks very much for your answer.

I set the sps parameter for pitch_detector incorrectly. When I replaced this to the sample rate of the input device, I started to get correct results. As far as I understand from the test codes, this means sample rate. Am I thinking right?

djowel commented 1 month ago

hi @djowel

Thanks very much for your answer.

I set the sps parameter for pitch_detector incorrectly. When I replaced this to the sample rate of the input device, I started to get correct results. As far as I understand from the test codes, this means sample rate. Am I thinking right?

That is correct.

djowel commented 1 month ago

If this issue has been resolved, please close. Thanks.