andrewrk / libsoundio

C library for cross-platform real-time audio input and output
http://libsound.io/
MIT License
1.92k stars 229 forks source link

How to repeatedly emit a tone #219

Closed sevagh closed 4 years ago

sevagh commented 4 years ago

Hello,

I'm trying to adapt the sinewave example to fit an app where I repeatedly play a sine tone.

Right now, I have something like this:

class AudioEngine::AudioEngine() {
 // creates a member struct SoundIo *soundio
 // creates a member struct SoundIoDevice *device, default device
 // execute in background for (;;) soundio_wait_events(soundio)
}

class AudioEngine::play_tone(std::vector<double> &tone) {
    struct SoundIoOutStream *outstream = ...
    outstream->userdata = reinterpret_cast<void *>(&tone);
    ...
}

static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
  auto tone = reinterpret_cast<std::vector<double>*>(outstream->userdata);
 // create samples by indexing (*tone)[]

 while (frames_left > 0) {
 }
}

Now, this "works", but I don't ever destroy the outstream, and I can't figure out where to do that.

I tried passing an outstream destroy lambda with outstream->userdata, so that in write_callback, at the end, with no frames left, I can call it, but I believe it's the wrong time to delete the outstream, cause I get all sorts of errors:

sevagh:libjungle $ ./bin/libjungle_example
init audio engine
Using default output device: Built-in Audio Analog Stereo
Generating tone
init 100bpm tempo ticker
Starting periodic async executor with bpm: 100 period: 600000 us
press ctrl-c to exit
playing a tone with size: 2400
outstream->userdata pointer: 0x7fd2ab7fd740, done
tone size: 2400, done
Assertion 's' failed at pulse/stream.c:2271, function pa_stream_cork(). Aborting.

My other thought was to accumulate a vector of "outstreams to destroy", and call those on exit, but I'll be gathering hundreds and thousands of these eventually.

Could you advise on how I should be proceeding here?

Thanks.

sevagh commented 4 years ago

Frankly I don't even know if I should be creating outstreams repeatedly, or there's a way to "rewind" the current outstream - maybe with the underflow callback? http://libsound.io/doc-2.0.0/structSoundIoOutStream.html#a610870a5304701ec08d410243c2eb328

kritzikratzi commented 4 years ago

typically you only open the soundcard once. then you write something like a mixer/sample-player yourself. if it's idle (ie nothing is playing), you just zero-fill the buffers.

but you don't open/close the soundcard repeatetly.

sevagh commented 4 years ago

If I create a single outstream, and change my loop to always do

soundio_outstream_start(outstream)

I hear my tone once, and never again. How should I clear the buffers of an outstream?

sevagh commented 4 years ago

I'll link my unworking code soon - sorry, verbal descriptions of long C++ classes are very unhelpful.

sevagh commented 4 years ago

Start stream which is executed on a timer: https://github.com/sevagh/libjungle/blob/master/src/audio.cpp#L71

Event wait loop: https://github.com/sevagh/libjungle/blob/master/src/audio.cpp#L40

Write callback: https://github.com/sevagh/libjungle/blob/master/src/audio.cpp#L116

I create a sinewave (std::vector<double>) and try to iterate through it in the write_callback. However, I only hear one beep.

If I add a soundio_flush_events somewhere, I hear a few more beeps. I feel like I'm missing a piece - flushing, clearing buffers, etc., that I need to continually emit tones.

sevagh commented 4 years ago

I'm starting to figure things out with software_latency, etc. The documentation contains all the info I need but I had to roll my sleeves up, so to speak (vs. "For Dummies" markdown/quickstart documentation I'm used to).