Page 1 of 1
Pump audio data from file into JACK
Posted: Sat Mar 26, 2016 8:44 am
by f00bar
I have a problem in how to play waveform data from a high-latency device (think disk file, network socket). I cannot call any blocking function from the realtime thread, so I cannot fetch data directly. I guess some kind of FIFO should work. The writing to the FIFO must block when the FIFO is full, but must not when testing for emptyness. The JACK ringbuffer does not block at all, so the file I/O thread will then need to busywait. What is the canonical solution to this problem.
Recording works easier, because I can let the realtime thread signal the I/O thread when a new data block is ready. Reversing that mechanism would not work, because
1) this requires blocking the realtime thread
2) the I/O thread is to fast, so it needs to be paused anyways.
Re: Pump audio data from file into JACK
Posted: Sat Mar 26, 2016 11:55 am
by raboof
f00bar wrote:I have a problem in how to play waveform data from a high-latency device (think disk file, network socket). I cannot call any blocking function from the realtime thread, so I cannot fetch data directly. I guess some kind of FIFO should work. The writing to the FIFO must block when the FIFO is full, but must not when testing for emptyness. The JACK ringbuffer does not block at all, so the file I/O thread will then need to busywait. What is the canonical solution to this problem.
Good question, looking forward to the answers.
Not really answering your question, but of course you could simply read the whole waveform into memory before starting playback.
That's certainly easy, but of course might not work in your situation - it might introduce unacceptable latency while loading the waveform, or use an unacceptable amount of memory.
Re: Pump audio data from file into JACK
Posted: Sat Mar 26, 2016 12:07 pm
by f00bar
raboof wrote:f00bar wrote:I have a problem in how to play waveform data from a high-latency device (think disk file, network socket). I cannot call any blocking function from the realtime thread, so I cannot fetch data directly. I guess some kind of FIFO should work. The writing to the FIFO must block when the FIFO is full, but must not when testing for emptyness. The JACK ringbuffer does not block at all, so the file I/O thread will then need to busywait. What is the canonical solution to this problem.
Good question, looking forward to the answers.
Not really answering your question, but of course you could simply read the whole waveform into memory before starting playback.
That's certainly easy, but of course might not work in your situation - it might introduce unacceptable latency while loading the waveform, or use an unacceptable amount of memory.
Dump it into memory first works for small amounts of data. That is what Hydrogen does, and I used that approach in Anja. But now I want to play longer files and then I need a streaming solution.
Re: Pump audio data from file into JACK
Posted: Sat Mar 26, 2016 12:18 pm
by tramp
Using 2 fixed size buffers, big enough, and switch them between disk and realtime thread.
To say, read data in the first buffer, hand it over to the rt-thread. As soon the rt-thread starts to read the buffer, signal the disk thread to fill the other buffer. Switch them, when the rt-thread have reach the end of the first buffer, . . .
Here is how I use it for record, but the same approve, just in turn, could work for play.
https://github.com/brummer10/screcord.l ... record1.cc
Re: Pump audio data from file into JACK
Posted: Sun Mar 27, 2016 10:37 am
by f00bar
tramp wrote:Using 2 fixed size buffers, big enough, and switch them between disk and realtime thread.
To say, read data in the first buffer, hand it over to the rt-thread. As soon the rt-thread starts to read the buffer, signal the disk thread to fill the other buffer. Switch them, when the rt-thread have reach the end of the first buffer, . . .
Here is how I use it for record, but the same approve, just in turn, could work for play.
https://github.com/brummer10/screcord.l ... record1.cc
The problem is how to determine the current buffer. I have
Code: Select all
void Jasmine::Impl::writeByChannel(const float* data,unsigned int n_frames
,unsigned int n_channels_in,unsigned int channel_out_first)
{
auto buffer=m_buffers_in[0].get();
buffer->readyWait();
printf("Writing to %p\n",buffer);
buffer->writeByChannel(data,n_frames,n_channels_in,channel_out_first);
}
inline int Jasmine::Impl::dataProcess(jack_nframes_t N) noexcept
{
// Write data to output port
{
auto buffer_in_front=m_buffers_in[1].get();
auto ports_out_begin=m_ports_out.data();
auto ports_out_end=ports_out_begin + m_ports_out.size();
auto ch=0;
while( ports_out_begin!=ports_out_end )
{
float* buffer_out=static_cast<float*>
(jack_port_get_buffer(*ports_out_begin,N));
buffer_in_front->read(buffer_out,N,ch);
++ch;
++ports_out_begin;
}
buffer_in_front->frameOffsetAdvance(N);
if(buffer_in_front->done())
{
std::swap(m_buffers_in[0],m_buffers_in[1]);
m_buffers_in[1].get()->readySet();
}
}
return 0;
}
When the I/O thread calls `writeByChannel` the second time, the buffer is still the same, since the RT-thread has not yet switched over. So the output is
Code: Select all
Writing to 0x21eaef0
Writing to 0x21eaef0
Writing to 0x21eae60
Writing to 0x21eae60
Writing to 0x21eaef0
Writing to 0x21eae60
rather than
Code: Select all
Writing to 0x21eaef0
Writing to 0x21eae60
Writing to 0x21eaef0
Writing to 0x21eae60
Writing to 0x21eaef0
Writing to 0x21eae60
Also the two first writes must go without any wait, otherwise it wont fill the buffer in time, introducing a periodic popping sound.
Re: Pump audio data from file into JACK
Posted: Sun Mar 27, 2016 4:37 pm
by tramp
What I mean is, switch the buffer and signal the disc thread to fill the second buffer, when the rt-thread start to read the first buffer, not when it's finished. Choose the buffer size big enough to have a couple of jack-frame sizes in it, to give the disc thread more time to fill the buffer.
When you switch the buffer in the rt-thread, you could assume, that it is ready, as the disc thread fill a big chunk of data faster (at once) then the jack thread needs to play them.
This introduced a latency (playback wont start immediately when you press start), but, for playback files, that shouldn't matter.
Re: Pump audio data from file into JACK
Posted: Mon Mar 28, 2016 12:39 pm
by f00bar
tramp wrote:What I mean is, switch the buffer and signal the disc thread to fill the second buffer, when the rt-thread start to read the first buffer, not when it's finished. Choose the buffer size big enough to have a couple of jack-frame sizes in it, to give the disc thread more time to fill the buffer.
When you switch the buffer in the rt-thread, you could assume, that it is ready, as the disc thread fill a big chunk of data faster (at once) then the jack thread needs to play them.
This introduced a latency (playback wont start immediately when you press start), but, for playback files, that shouldn't matter.
Swapping and signaling before data outut seems to work