Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Microphone capure and play  (Read 3134 times)

0 Members and 1 Guest are viewing this topic.

kingtik

  • Newbie
  • *
  • Posts: 1
    • View Profile
Microphone capure and play
« on: January 16, 2018, 11:15:14 pm »
Hi,
I want to record audio from microphone and play it while its recorded so that i can hear myself in real time (small delay is not a problem). What i came up with is something like this:
 sf::SoundBufferRecorder recorder;
    auto success = recorder.setDevice(avaibleDevices[0]);

    std::cout <<"recorder set device: " << success << std::endl;
    McirophoneStream stream;
    recorder.start();
    while(1){
         recorder.start();
        const sf::SoundBuffer& buffer = recorder.getBuffer();
         sf::sleep(sf::seconds(0.01f));
         recorder.stop();
        stream.load(buffer);

                stream.play();

    }
 

The problem is that the output sound is incomplete and with a litle echo effect.
I tried to get rid of the sleep functions but that resulted in failures
Unsupported number of channels (0)
Failed to play audio stream: sound parameters have not been initialized (call initialize() first)

At the end of the day my goal is to record sound, play it on speakers and maybe add some effects in between. Is there a way to achieve that?

Hiura

  • SFML Team
  • Hero Member
  • *****
  • Posts: 4321
    • View Profile
    • Email
Re: Microphone capure and play
« Reply #1 on: January 17, 2018, 03:59:51 pm »
You got me curious... so I've implemented something relatively basic POC.

The first thing I changed in your code was the need to stop and restart the recording as this might produce glitches and might as well be slow (I haven't measured though!).

The code below is complete, so you can try it as-is. It is based not on `SoundBuffer` or `SoundBufferRecorder` but on the abstract `SoundRecorder` for the input and the abstract `SoundStream` for the output. It is basically a concurrent consumer-producer problem so I've used atomic/mutex/lock/condition variables to solve it (should be C++11 compatible).

It was hacked quite quickly so I don't guarantee it's bug-free, but it should be a good start, I believe, for more complex processing of the captured samples.

#include <SFML/Audio.hpp>
#include <SFML/Graphics.hpp>

#include <atomic>
#include <cassert>
#include <mutex>
#include <queue>


// Useful to hold onto the memory when converting it into a Chunk.
struct Samples {
    Samples(sf::Int16 const* ss, std::size_t count) {
        samples.reserve(count);
        std::copy_n(ss, count, std::back_inserter(samples));
    }

    Samples() {}

    std::vector<sf::Int16> samples;
};


class PlaybackRecorder : private sf::SoundRecorder, private sf::SoundStream {
public: /** API **/

    // Initialise capturing input & setup output
    void start() {
        sf::SoundRecorder::start();

        sf::SoundStream::initialize(sf::SoundRecorder::getChannelCount(), sf::SoundRecorder::getSampleRate());
        sf::SoundStream::play();
    }

    // Stop both recording & playback
    void stop() {
        sf::SoundRecorder::stop();
        sf::SoundStream::stop();
    }

    bool isRunning() { return isRecording; }


    ~PlaybackRecorder() {
        stop();
    }


protected: /** OVERRIDING SoundRecorder **/

    bool onProcessSamples(sf::Int16 const* samples, std::size_t sampleCount) override {
        {
            std::lock_guard<std::mutex> lock(mutex);
            data.emplace(samples, sampleCount);
        }
        cv.notify_one();
        return true; // continue capture
    }

    bool onStart() override {
        isRecording = true;
        return true;
    }

    void onStop() override {
        isRecording = false;
        cv.notify_one();
    }


protected: /** OVERRIDING SoundStream **/

    bool onGetData(Chunk& chunk) override {
        // Wait until either:
        //  a) the recording was stopped
        //  b) new data is available
        std::unique_lock<std::mutex> lock(mutex);
        cv.wait(lock, [this]{ return !isRecording || !data.empty(); });

        // Lock was acquired, examine which case we're into:
        if (!isRecording) return false; // stop playing.
        else {
            assert(!data.empty());

            playingSamples.samples = std::move(data.front().samples);
            data.pop();
            chunk.sampleCount = playingSamples.samples.size();
            chunk.samples = playingSamples.samples.data();
            return true;
        }
    }

    void onSeek(sf::Time) override { /* Not supported, silently does nothing. */ }

private:
    std::atomic<bool> isRecording{false};
    std::mutex mutex; // protects `data`
    std::condition_variable cv; // notify consumer thread of new samples
    std::queue<Samples> data; // samples come in from the recorder, and popped by the output stream
    Samples playingSamples; // used by the output stream.
};

int main(int, char const**)
{
    sf::RenderWindow window(sf::VideoMode(800, 600), "SFML PlayBack");

    if (!sf::SoundRecorder::isAvailable()) {
        return EXIT_FAILURE;
    }

    PlaybackRecorder input;
    input.start();

    while (window.isOpen())
    {
        sf::Event event;
        while (window.pollEvent(event))
        {
            if (event.type == sf::Event::Closed) {
                window.close();
            }

            if (event.type == sf::Event::KeyPressed) {
                if (input.isRunning()) input.stop();
                else input.start();
            }
        }

        window.clear(input.isRunning() ? sf::Color::White : sf::Color::Black);
        window.display();
    }

    return EXIT_SUCCESS;
}
 
SFML / OS X developer