 |
|
Page 1 of 1
|
[ 16 posts ] |
|
Improving the ALSA backend
Improving the ALSA backend
Author |
Message |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
 Improving the ALSA backend
I have an openSUSE 10.3 x86-64, Core2Duo E6420, ASUS P5B system with two sound cards: a Sound Blaster Live! and a AD1988 integrated codec. ALSA drivers are "1.0.16.20080601" and alsa-lib is "1.0.16.hg20080510" (your learn to love the openSUSE Build Service  ).
With the ALSA backend in bsnes 0.032a the SBLive! crashes and AD1988 has distorsioned sound.
I think the crashes are because
should be since length is in frames and no in samples. Also adding a to the end of the init() function to see exactly how the sound card has been configured. The SB Live! is configured this way: The AD1988 is configured this way: So even if the code asked for a 90us latency, what we have is a 2ms latency for the SB Live! and a 43ms latency for AD1988. Now, was 90us really intended? It's very low, isn't? Since I had problems to make the SB Live! work with a 2ms latency without buffer underruns I have changed "device.latency = 90;" for "device.latency = 90000;". If 90us was really intented (what OSS archieves with policy = 4?) say me. New configuration is: - SB Live!: Exactly 90ms latency, with 4 periods. - AD1988 ~85ms latency, with ~4 periods. At the end, I have found that this alternative code works fine for both SB Live! and AD1988, speed regulation included.  |  |  |  | Code: #include <alsa/asoundlib.h>
#include <ruby/ruby.h>
namespace ruby {
#include "alsa.h"
class pAudioALSA { public: AudioALSA &self;
struct { snd_pcm_t *handle; snd_pcm_format_t format; int channels; const char *name; unsigned latency; } device;
struct { uint32_t *data; unsigned length; } buffer;
struct { unsigned frequency; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; } settings;
bool cap(Audio::Setting setting) { if(setting == Audio::Frequency) return true; return false; }
uintptr_t get(Audio::Setting setting) { if(setting == Audio::Frequency) return settings.frequency; return false; }
bool set(Audio::Setting setting, uintptr_t param) { if(setting == Audio::Frequency) { settings.frequency = param; if(device.handle) { term(); init(); } return true; } return false; }
void sample(uint16_t left, uint16_t right) { if(!device.handle) return;
buffer.data[buffer.length++] = left + (right << 16); if(buffer.length < settings.period_size) return; uint32_t *buffer_ptr = buffer.data; do { snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); if(written < 0) { snd_pcm_recover(device.handle, written, 1); } else if(written < buffer.length) { //only some samples written buffer.length -= written; buffer_ptr += written; } else { //all samples written buffer.length = 0; } } while(buffer.length != 0); }
bool init() { if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { //failed to initialize term(); return false; }
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED, device.channels, settings.frequency, 1, device.latency) < 0) { //failed to set device parameters term(); return false; }
if(snd_pcm_get_params(device.handle, &settings.buffer_size, &settings.period_size) < 0) { //I suppose ALSA always uses 4 periods/buffer, and I expect buffer_size to give me the exact latency requested... better than nothing settings.period_size = device.latency*1e-6*settings.frequency/4; }
buffer.data = new uint32_t[settings.period_size];
return true; }
void term() { if(device.handle) { snd_pcm_drain(device.handle); snd_pcm_close(device.handle); device.handle = 0; }
if(buffer.data) { delete[] buffer.data; buffer.data = 0; } }
pAudioALSA(AudioALSA &self_) : self(self_) { device.handle = 0; device.format = SND_PCM_FORMAT_S16_LE; device.channels = 2; device.name = "default"; device.latency = 100000;
buffer.data = 0; buffer.length = 0;
settings.frequency = 32000; }
~pAudioALSA() { term(); } };
bool AudioALSA::cap(Setting setting) { return p.cap(setting); } uintptr_t AudioALSA::get(Setting setting) { return p.get(setting); } bool AudioALSA::set(Setting setting, uintptr_t param) { return p.set(setting, param); } void AudioALSA::sample(uint16_t left, uint16_t right) { p.sample(left, right); } bool AudioALSA::init() { return p.init(); } void AudioALSA::term() { p.term(); } AudioALSA::AudioALSA() : p(*new pAudioALSA(*this)) {} AudioALSA::~AudioALSA() { delete &p; }
} //namespace ruby |  |  |  |  |
Patch:  |  |  |  | Code: --- src/lib/ruby/audio/alsa.cpp +++ src/lib/ruby/audio/alsa.cpp @@ -19,13 +19,14 @@ } device; struct { - uint16_t *data; + uint32_t *data; unsigned length; - unsigned size; } buffer; struct { unsigned frequency; + snd_pcm_uframes_t buffer_size; + snd_pcm_uframes_t period_size; } settings; bool cap(Audio::Setting setting) { @@ -53,30 +54,26 @@ void sample(uint16_t left, uint16_t right) { if(!device.handle) return; - buffer.data[buffer.length++] = left; - buffer.data[buffer.length++] = right; - if(buffer.length + 2 < buffer.size) return; //will crash in some cases if not stopped two before - - snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer.data, buffer.length); - if(written < 0) { - snd_pcm_recover(device.handle, written, 1); - //no samples written, drop one sample to prevent possible emulation stall - buffer.length -= 2; - memmove(buffer.data, buffer.data + 2, buffer.length * sizeof(uint16_t)); - } else if(written < buffer.length) { - //only some samples written - buffer.length -= written; - memmove(buffer.data, buffer.data + written, buffer.length * sizeof(uint16_t)); - } else { - //all samples written - buffer.length = 0; - } + buffer.data[buffer.length++] = left + (right << 16); + if(buffer.length < settings.period_size) return; + uint32_t *buffer_ptr = buffer.data; + do { + snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); + if(written < 0) { + snd_pcm_recover(device.handle, written, 1); + } else if(written < buffer.length) { + //only some samples written + buffer.length -= written; + buffer_ptr += written; + } else { + //all samples written + buffer.length = 0; + } + } while(buffer.length != 0); } bool init() { - buffer.data = new uint16_t[buffer.size]; - - if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) { + if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { //failed to initialize term(); return false; @@ -89,6 +86,13 @@ return false; } + if(snd_pcm_get_params(device.handle, &settings.buffer_size, &settings.period_size) < 0) { + //I suppose ALSA always uses 4 periods/buffer, and I expect buffer_size to give me the exact latency requested... better than nothing + settings.period_size = device.latency*1e-6*settings.frequency/4; + } + + buffer.data = new uint32_t[settings.period_size]; + return true; } @@ -110,13 +114,12 @@ device.format = SND_PCM_FORMAT_S16_LE; device.channels = 2; device.name = "default"; - device.latency = 90; + device.latency = 100000; buffer.data = 0; buffer.length = 0; - buffer.size = device.latency * 32; - settings.frequency = 22050; + settings.frequency = 32000; } ~pAudioALSA() { |  |  |  |  |
Also would be good to add an option to select the device. With SB Live! I have no problems using HQ2x and so, but since the AD1988 doesn't makes hardware mixing the CPU usage is too high when using it with HQ2x. Using "plughw" insted of "default" the CPU usage with AD1988 is lower (even if I loss sound in the rest of the system).
If someone else also has problems try this alternative and say me...
Edit: Sorry for the error in the code. Corrected.
Edit2: Forget the blocking thing.
Edit3: Changed latency value to 100ms and readded application audio buffer to reduce CPU usage.
At this point I think the code is so good like it can be. Application buffer size and latency are the only two numbers that could be tweaked. So I open the poll.
Last edited by RedDwarf on Sun Jun 01, 2008 11:54 am, edited 10 times in total.
|
Sat May 31, 2008 3:36 pm |
|
 |
grinvader
ZSNES Shake Shake Prinny
Joined: Wed Jul 28, 2004 4:15 pm Posts: 5616 Location: PAL50, dood !
|
90ms is a whoppy 5.4 frames. Have fun getting completely off-sync sound effects.
_________________ 皆黙って俺について来い!!
Pantheon: Gideon Zhi | CaitSith2 | Nach | kode54
|
Sat May 31, 2008 7:42 pm |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
I just selected it in case 90us was a "us" vs "ms" mistake and 90ms was selected for a good motive. So which one is the "best" value?
100ms in 6 NTSC frames and 5 PAL frames, too much/noticable? 50 ms is 3 NTSC frames and 2,5 PAL frames... 2,5 would be also a problem (frames vs fields, I don't really understand how the sync is done)? Less than 16,666 ms starts to be very CPU intensive... and apparently some soundcards can't have so little buffer (driver problem?).
|
Sat May 31, 2008 9:07 pm |
|
 |
Nach
ZSNES Developer
Joined: Tue Jul 27, 2004 10:54 pm Posts: 3902 Location: Solar powered park bench
|
I'll look this code over later, and test it out, thank you.
_________________ May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it. _____________ Insane Coding
|
Mon Jun 02, 2008 12:19 am |
|
 |
byuu
|
Well, I was impatient so I tested it out myself. I have Intel HDA, and this patch helped tremendously. Speed regulation works fine now and latency is very low.
Only problem I saw was that setting "emulation speed" to 75% spits out errors onto the console about invalid format parameters or somesuch. Probably because 24000hz isn't a standard sampling rate. Hopefully there's a way around that besides using 22050hz, as all the other drivers can pull it off.
Still also need to add support for disabling speed regulation entirely (Audio::Synchronize -> false.)
I really appreciate the patches to ALSA and libao, thanks again.
|
Mon Jun 02, 2008 8:18 am |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|

It also happens to me with the AD1988 (also hda-intel). It is weird, the '1' from means
I will look into it, but looks like an alsa-lib problem (that probably would affect *any* card that doesn't has native support for 24KHz). snd_pcm_set_params() is a new wrapper funcion to make the init process easier, if libao/openal hadn't the problem is because they use the old functions.
To fix it with old/actual alsa versions snd_pcm_set_params() would need to be changed for the five or six functions needed previously to set the sound card... it can be copied&pasted from libao or openal. With some look, if I find the exact problem and report it to alsa devs, ALSA 1.0.17 will work with the code like it is right now.
Edit: Problem reported to ALSA devs and alternative init() function that allows 24KHz with hda-intel and actual alsa version:
 |  |  |  | Code: bool init() { if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { //failed to initialize term(); return false; }
snd_pcm_hw_params_t *hwparams; snd_pcm_sw_params_t *swparams; unsigned rate = settings.frequency; unsigned buffer_time = device.latency; unsigned int period_time = device.latency/4;
snd_pcm_hw_params_alloca(&hwparams); if(snd_pcm_hw_params_any(device.handle, hwparams) < 0) { term(); return false; }
if(snd_pcm_hw_params_set_access(device.handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 || snd_pcm_hw_params_set_format(device.handle, hwparams, device.format) < 0 || snd_pcm_hw_params_set_channels(device.handle, hwparams, device.channels) < 0 || snd_pcm_hw_params_set_rate_near(device.handle, hwparams, &rate, 0) < 0 || snd_pcm_hw_params_set_period_time_near(device.handle, hwparams, &period_time, 0) < 0 || snd_pcm_hw_params_set_buffer_time_near(device.handle, hwparams, &buffer_time, 0) < 0 ) { term(); return false; } if(snd_pcm_hw_params(device.handle, hwparams) < 0) { term(); return false; }
if(snd_pcm_get_params(device.handle, &settings.buffer_size, &settings.period_size) < 0) { term(); return false; }
snd_pcm_sw_params_alloca(&swparams); if(snd_pcm_sw_params_current(device.handle, swparams) < 0) { term(); return false; } if(snd_pcm_sw_params_set_start_threshold(device.handle, swparams, (settings.buffer_size / settings.period_size) * settings.period_size) < 0) { term(); return false; } if(snd_pcm_sw_params(device.handle, swparams) < 0) { term(); return false; }
buffer.data = new uint32_t[settings.period_size];
return true; } |  |  |  |  |
Note that snd_pcm_set_params() has a more complex logic than this code... and I suppose that complexity isn't there only to add bugs 
|
Mon Jun 02, 2008 2:55 pm |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
The one that voted for the original code could give some info?
Using the snd_pcm_dump_setup() code from the first post and posting the output could help.
|
Tue Jun 03, 2008 9:00 pm |
|
 |
Deathlike2
ZSNES Developer
Joined: Tue Dec 28, 2004 6:47 am Posts: 6747
|
Even though this isn't a relatively scientific poll, but if the discrepency between the codes exist, then it makes one wonder how inconsistant the behavior is for ALSA, not necessarily of the hardware being run...
_________________ Continuing FF4 Research...
|
Thu Jun 05, 2008 6:56 pm |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
I have a problem understanding the "Uncapped" mode. At this mode the play rate is set at 32KHz, the same that at 100%. But if the emulator goes at a speed of 150%/90fps I get 48000 samples each second.
If I receive 48000 samples each second but only play 32000 samples each second... each second I acummulate 16000 samples!!! So what I get at the end is a sound that is behind the rest of the emulator, and at some point the buffer where the 16000samples/second are acummulated is fulled and I get a segmentation fault.
What I'm failing to understand?
|
Thu Jun 05, 2008 11:30 pm |
|
 |
byuu
|
I've been meaning to ask others something about this anyway, so I've responded to you and asked that question here:
http://board.zsnes.com/phpBB2/viewtopic ... 641#170641
Oh, and it's kind of pointless to accept votes without posts explaining the results. I'd ignore the two "no" votes until they respond.
|
Fri Jun 06, 2008 12:27 am |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|

I wasn't waiting for samples to fully play, but I was saving them to play them later.
So what I must do is overwrite samples that hadn't time to play? I though making this I would obtain very cracky sound, but seems to work with the same sound quality than the openal code.
Here is the patch over the first one:
 |  |  |  | Code: --- src/lib/ruby/audio/alsa.cpp +++ src/lib/ruby/audio/alsa.cpp @@ -24,22 +24,29 @@ } buffer; struct { + bool synchronize; unsigned frequency; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; } settings; bool cap(Audio::Setting setting) { + if(setting == Audio::Synchronize) return true; if(setting == Audio::Frequency) return true; return false; } uintptr_t get(Audio::Setting setting) { + if(setting == Audio::Synchronize) return settings.synchronize; if(setting == Audio::Frequency) return settings.frequency; return false; } bool set(Audio::Setting setting, uintptr_t param) { + if(setting == Audio::Synchronize) { + settings.synchronize = param; + return true; + } if(setting == Audio::Frequency) { settings.frequency = param; if(device.handle) { @@ -57,6 +64,17 @@ buffer.data[buffer.length++] = left + (right << 16); if(buffer.length < settings.period_size) return; + if(!settings.synchronize) { + snd_pcm_sframes_t delay; + snd_pcm_delay(device.handle, &delay); + if(delay < 0) + snd_pcm_prepare(device.handle); + else if(delay > settings.buffer_size - settings.period_size) { + buffer.length = 0; + return; + } + } + uint32_t *buffer_ptr = buffer.data; do { snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); @@ -120,6 +138,7 @@ buffer.data = 0; buffer.length = 0; + settings.synchronize = true; settings.frequency = 32000; } |  |  |  |  |
Edit: I should made my test in the crappy hda-intel card and not in the SB Live!. Add a "snd_pcm_avail_update(device.handle);" just before "snd_pcm_delay(device.handle, &delay)" if you get no sound with uncapped speed.
|
Fri Jun 06, 2008 5:53 pm |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
In case anyone is interested in getting exactly 100ms latency (that probably isn't so exact anyway, but...).
My hda-intel AD1988 gets those 682 periods instead of the 720 periods because of dmix. Since some time ago dmix is automatically used if the sound card doesn't does hardware mixing. And by default it uses a 1024 period size. Since the card is 48KHz, at 32KHz (bsnes default speed) you get a 32/48*1024=682 period.
At least in openSUSE that value can be changed at /usr/share/alsa/pcm/dmix.conf. At the period_size section change "default 1024" by "default <whatever>".
Note that I'm not saying you should. Probably you shouldn't. It's just FYI.
You should only need to change it if you REALLY NEED very low latencies... and after playing a little with the 100ms latency I don't see the need for less. For sure not less than 43ms.
Also, with the next "glitch-free" PulseAudio dmix will dissapear and you will have latencies so low like you want. So all this will not be relevant at all.
|
Mon Jun 09, 2008 7:36 pm |
|
 |
Nach
ZSNES Developer
Joined: Tue Jul 27, 2004 10:54 pm Posts: 3902 Location: Solar powered park bench
|

Hi.
Sorry I wasn't around for a while, been busy.
I'd really like to hear for those two who voted in favor of the original ALSA as to why. RedDwarf's seems to better than what I initially wrote, except I noticed for certain situations where there where these little freezes in game which were very annoying, on some of the machines I tested.
Here's what I have now:
 |  |  |  | Code: #include <alsa/asoundlib.h>
#include <ruby/ruby.h>
namespace ruby {
#include "alsa.h"
class pAudioALSA { public: AudioALSA &self;
struct { snd_pcm_t *handle; snd_pcm_format_t format; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; int channels; const char *name; unsigned latency; } device;
struct { uint32_t *data; unsigned length; } buffer;
struct { bool synchronize; unsigned frequency; } settings;
bool cap(Audio::Setting setting) { if(setting == Audio::Synchronize) return true; if(setting == Audio::Frequency) return true; return false; }
uintptr_t get(Audio::Setting setting) { if(setting == Audio::Synchronize) return settings.synchronize; if(setting == Audio::Frequency) return settings.frequency; return false; }
bool set(Audio::Setting setting, uintptr_t param) { if(setting == Audio::Synchronize) { settings.synchronize = param; return true; } if(setting == Audio::Frequency) { settings.frequency = param; if(device.handle) { term(); init(); } return true; } return false; }
void sample(uint16_t left, uint16_t right) { if(!device.handle) return;
buffer.data[buffer.length++] = left + (right << 16); if(buffer.length < device.period_size) return;
if(!settings.synchronize) { snd_pcm_avail_update(device.handle); snd_pcm_sframes_t delay; snd_pcm_delay(device.handle, &delay); if(delay < 0) { snd_pcm_prepare(device.handle); } else if(delay > device.buffer_size - device.period_size) { buffer.length = 0; return; } }
uint32_t *buffer_ptr = buffer.data; int i = 3;
while ((buffer.length > 0) && i--) { snd_pcm_sframes_t written = snd_pcm_writei(device.handle, buffer_ptr, buffer.length); if(written < 0) { //no samples written snd_pcm_recover(device.handle, written, 1); } else if(written <= buffer.length) { buffer.length -= written; buffer_ptr += written; } } if(i < 0) { if (buffer.data == buffer_ptr) { --buffer.length; ++buffer_ptr; } memmove(buffer.data, buffer_ptr, buffer.length * sizeof(uint32_t)); } }
bool init() { if(snd_pcm_open(&device.handle, device.name, SND_PCM_STREAM_PLAYBACK, 0) < 0) { //failed to initialize term(); return false; }
if(snd_pcm_set_params(device.handle, device.format, SND_PCM_ACCESS_RW_INTERLEAVED, device.channels, settings.frequency, 1, device.latency) < 0) { //failed to set device parameters term(); return false; }
if(snd_pcm_get_params(device.handle, &device.buffer_size, &device.period_size) < 0) { device.period_size = device.latency * 1e-6 * settings.frequency / 4; term(); return false; }
buffer.data = new uint32_t[device.period_size]; return true; }
void term() { if(device.handle) { snd_pcm_drain(device.handle); snd_pcm_close(device.handle); device.handle = 0; }
if(buffer.data) { delete[] buffer.data; buffer.data = 0; } }
pAudioALSA(AudioALSA &self_) : self(self_) { device.handle = 0; device.format = SND_PCM_FORMAT_S16_LE; device.channels = 2; device.name = "default"; device.latency = 60000;
buffer.data = 0; buffer.length = 0;
settings.synchronize = false; settings.frequency = 22050; }
~pAudioALSA() { term(); } };
bool AudioALSA::cap(Setting setting) { return p.cap(setting); } uintptr_t AudioALSA::get(Setting setting) { return p.get(setting); } bool AudioALSA::set(Setting setting, uintptr_t param) { return p.set(setting, param); } void AudioALSA::sample(uint16_t left, uint16_t right) { p.sample(left, right); } bool AudioALSA::init() { return p.init(); } void AudioALSA::term() { p.term(); } AudioALSA::AudioALSA() : p(*new pAudioALSA(*this)) {} AudioALSA::~AudioALSA() { delete &p; }
} //namespace ruby
|  |  |  |  |
This I find is in every way better than what was originally in bsnes, thanks RedDwarf.
Improvements here seem to work well on the half dozen machines I tested with various sound cards and CPU power. Good sound, not too much CPU, not too high latency (2/5 less than code above), no freezes. On really fast machines when playing at max speed, there's a bit of audio dropped, but I think that's acceptable.
There remains the question of how to handle ALSA for non Linux UNIX systems. Should we make a different Makefile param, or should I modify the code to dynamically use ALSA (so no need for -lasound), and #ifdef __linux__ the file?
BTW RedDrawf, dmix is not automatically used. I recently installed Linux on a machine here on a card which lacks hardware mixing, and I can only use one sound program at a time, even though they are all ALSA.
_________________ May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it. _____________ Insane Coding
Last edited by Nach on Wed Jul 02, 2008 4:51 pm, edited 1 time in total.
|
Fri Jun 27, 2008 12:36 am |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
Documentation says it should  Not sure how this is archieved. I think it has to do with config files, but... I don't read LISP
They are at 1.0.17rc3. If it's just a config fix with some luck you could have it for 1.0.17 if you report it now.
About the new code. "device.latency = 600000;"? There isn't an extra zero?
|
Sat Jun 28, 2008 4:47 pm |
|
 |
Nach
ZSNES Developer
Joined: Tue Jul 27, 2004 10:54 pm Posts: 3902 Location: Solar powered park bench
|
What else is new? If I actually care. Even if I did have dmix, it only works for applications which use the ALSA API which is worthless. Most of the sound card drivers also suck, and the API is a disaster, they should just drop the project altogether.
Ah yes, thanks for the catch. I was thinking before posting the code to make it a bit cleaner and remove some zeros and change 1e-6 to 1e-3, but then I changed my mind, and put back one too many zeros. I'll go correct that in my post above.
_________________ May 9 2007 - NSRT 3.4, now with lots of hashing and even more accurate information! Go download it. _____________ Insane Coding
|
Wed Jul 02, 2008 4:50 pm |
|
 |
RedDwarf
Rookie
Joined: Thu Jan 27, 2005 7:28 pm Posts: 37
|
 Stupid patch over 0.039
When I used snd_pcm_delay() the snd_pcm_avail_update() documentation wrongly specified it "Use it only for mmap access".
The thing is snd_pcm_delay() is wrongly used here. snd_pcm_delay() can include extra latencies (network latency when using PulseAudio, an extra buffer with USB audio, etc.). snd_pcm_avail_update() gives the value is really interestig here: how much space is available/empty to write.
Not really important, but...
|
Sat Jan 24, 2009 10:30 am |
|
|
|
Page 1 of 1
|
[ 16 posts ] |
|
Who is online |
Users browsing this forum: No registered users and 1 guest |
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum
|
|
 |
|