Page MenuHomeFreeBSD

sound examples: Add mmap example
Needs ReviewPublic

Authored by meka_tilda.center on Nov 14 2025, 12:39 AM.
Tags
None
Referenced Files
Unknown Object (File)
Mon, Dec 15, 1:42 PM
Unknown Object (File)
Sat, Dec 13, 3:55 AM
Unknown Object (File)
Fri, Dec 12, 1:39 AM
Unknown Object (File)
Sat, Dec 6, 12:23 PM
Unknown Object (File)
Thu, Dec 4, 12:25 AM
Unknown Object (File)
Wed, Dec 3, 12:41 AM
Unknown Object (File)
Tue, Dec 2, 4:17 PM
Unknown Object (File)
Mon, Dec 1, 12:05 PM
Subscribers

Details

Summary

This example combines mmap and usleep to syncronize playback and record

Diff Detail

Repository
rG FreeBSD src repository
Lint
Lint Skipped
Unit
Tests Skipped

Event Timeline

meka_tilda.center created this revision.
meka_tilda.center set the repository for this revision to rG FreeBSD src repository.

This is an initial quick review, as I haven't had a chance to properly test it yet.

Both configs in main() open /dev/dsp, so there is no need two have two configs and duplicate the logic twice. You can have one config but with mode = O_RDWR | O_EXCL | O_NONBLOCK, and call mmap() with PROT_READ | PROT_WRITE on the same FD. Or am I missing something?

share/examples/sound/mmap.c
40 ↗(On Diff #166400)
42 ↗(On Diff #166400)
47–49 ↗(On Diff #166400)
53 ↗(On Diff #166400)
54–56 ↗(On Diff #166400)

The Makefile needs to be updated to install mmap.c.

I also did some tests and there is a lot of distortion.

share/examples/sound/mmap.c
4–8 ↗(On Diff #166400)

That's the strange thing. If I run it 20-30 times, I would get roughly half of the times distorted sound, half of the times normal sound. Also, I tried having one config and two events for read and write, but I couldn't produce any sound like that. Anyway, let me fix this example to conform to the comments and lets go from there.

Remove extra functions as they are very simple

I forgot to edit Makefile. Fixed now.

That's the strange thing. If I run it 20-30 times, I would get roughly half of the times distorted sound, half of the times normal sound. Also, I tried having one config and two events for read and write, but I couldn't produce any sound like that.

I think this is probably a timing issue. I haven't looked into it yet but my guess it that we are not reading/writing exactly when we should.

share/examples/sound/mmap.c
70 ↗(On Diff #166783)

Since we have a mmap field in struct config, wouldn't it make sense to do that in oss_init()?

Let me try to compensate the drift by reading SNDCTL_DSP_GETIPTR and SNDCTL_DSP_GETOPTR and experiment with that for a bit.

Map or allocate buffer in oss_init. For some reason with this patch probability of producing proper sound is way higher. I still have to add GETIPTR/GETOPTR checks and usleep based on the position.

share/examples/sound/mmap.c
30 ↗(On Diff #166857)

This could go in oss.h, and we could remove it from other examples files as well. Again, best in a separate patch.

share/examples/sound/oss.h
230 ↗(On Diff #166857)

We don't check for NULL here. You can do that in a separate patch though, it's not related to mmap.

meka_tilda.center marked an inline comment as done.

Remove needless include

I think this needs to be rebased since we committed the oss.h changes.

Are you working on the timing issues? I haven't really looked into those.

I am looking into the code in general and I want to figure out how to display information so it makes more sense, not just dump printf.

I think I'll try my luck with http://manuals.opensound.com/developer/mmap_test.c.html first and fit it into our example and once the usleep version works, see what are the values of ptr compared to the ones in kqueue scenario. My thinking is "if I can't fix it, move to simpler example", which I'm hoping the official docs are. Anyway, I didn't give up on this code, I just need to approach it from the different angle, for now.

The usleep based mmap example based on official OSS example. There are three big things about it right now that make it not-so-perfect:

  1. It works for cases where in and out config have matching numbers for buffer (channels, fragments, ...)
  2. Calculating duration for usleep would be more optimal. Asking the kernel to fetch playback pointer every 1ms is a big overhead.
  3. It still has glitches sometimes, but the probability of that happening is way lower than previous examples.

I added -v which will print ci.ptr so it is a bit easier to see where the playback pointer is at. Once the example works, I'll remove this option.

Lowering usleep time and checking if ci.ptr is zero made all the glitches in the sound go away. I do need to run it for quite a few times to validate that, but in the worst case I significantly lowered the probability of gibberish on the output. Next, I need to calculate the usleep time based on ci.ptr.

meka_tilda.center edited the summary of this revision. (Show Details)

Check if input and output config are the same by checking number of bytes and number of input/output channels. I had cases where input and output channel numbers are different, as well as difference in buffer_info.bytes when the number of input and output channels is the same. I'm afraid that handling all possible cases would make this example complicated, so I chose to write the code that insists that input and output configs are the same.

Calculate the amount of microseconds to sleep, but keep it in the range between 400 and 800 microseconds. Position is not updated continuously, but at some frequency. That means that value we get from ioctl can not be trusted to be accurate, which is the reason to limit the maximum duration of the sleep. The minimal duration of sleep is put in place so the code doesn't call ioctl too often.

I left the verbose output but it is off by default. If verbose is off, program will inform the user of existence of -v option. If it's OK, I'd leave it there as I used it to debug the timing issues and it might prove valuable to some future developer if they are looking into why this example doesn't work for them.

Reverting to a version which just waits for 0.4ms between iterations. I ran this program for ~100 times on my desktop and it didn't produce any glitches. Should I remove verbose or leave it as it is?

How do we arrive at 0.4ms sleep though? What is the logic behind it? Is it just because it seems to work on your setup, or is there some calculation you used to arrive at it?