-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Syncs using PTP and pyaudio callback #1
base: master
Are you sure you want to change the base?
Conversation
Addresses the reconnect issue and 100% CPU consumption after disconnect
-AAC -ALAC -OPUS -PCM (may need tweaking) This commit programmatically constructs the appropriate QuickTime atom extradata chunk for ALAC to suit sample-size/rate/channels. It also dynamically determines the correct playback clock rates, instead of the static 44100.
which is a smidge faster than struct.
-increment_index -decrement_index -get_fullness Refactor find_seq from linear to binary search Bin = O(log n) vs linear O(n) - binary iterates max several times vs linear (up to) thousands
This commit adds a stable latency calculation which accounts for: -default playback device latency -pyaudio buffers and ensures that this receiver falls into synchronization with other playing devices. May need to pause/unpause play when a new device joins. Admittedly, the output is not phase locked, but PTP may help to take care of that. We actually *want* the play-head to be offset and ahead. This way, it synchronizes with other Airplay players (this is why Airplay clients stream a bunch of RTP in advance). In my tests with other Sonos endpoints - this latency calculation puts everything in sync to within a millisecond. Idea: dynamically announce output device latency into the plist sent to the sender.
This commit implements PTPv2 Slave in Python3. It will listen and slave to other Masters. There is no code to run as Master yet. Everything necessary for Airplayv2. Import this file and: if "timingProtocol" in plist: if plist['timingProtocol'] == 'PTP': print('PTP Startup') mac = int((ifen[ni.AF_LINK][0]["addr"]).replace(":", ""), 16) self.ptp_proc, self.ptp_link = PTP.spawn(mac) Bitshifting is an expensive operation in python. It's possible that for every new byte shifted to the left, a new object is created to hold the data. int.from_bytes appears to be the most efficient binary unpacking method (without requiring imports) - sometimes unpack is a smidge faster: -- data = bytes.fromhex('1b02005400000608000000000000000000000000'\ '010203040506000583bf017905fe00000000000000000000000000f8f8fe4'\ '36af8010203fffe0405060001a000080010010203fffe0405060202030405060005') -- def test3(): correctionNanoseconds = int.from_bytes(data[8:16], byteorder='big') print(timeit.timeit("test3()", globals=locals(), number=100000000)) >>> 26.746907015000033 -- import struct def test2(): correctionNanoseconds = struct.unpack(">Q", data[8:16])[0] print(timeit.timeit("test2()", globals=locals(), number=100000000) ) >>> 26.19966978500088 -- def test(): correctionNanoseconds = \ data[8] <<40|data[9] <<32|data[10]<<24| \ data[11]<<16|data[12]<< 8|data[13] print(timeit.timeit("test()", globals=locals(), number=100000000) ) >>> 43.50181922199772 --
Like! One potential issue: does everything still work the same/as well as before, in case, e.g., someone turns off PTP in the Flags? We're going to lint and clean up the code, (I will rebase my master also) and once those are in, you can rebase and tidy up the PR. 😄 Unless you want to rebase from the main repo? |
Yeah. I can apply changes in a clean master. This is a mess since I formatted with black |
Then I'm gonna PR against openairplay directly |
windows set volume by pid
b88bcc6
to
57e4a3b
Compare
5810460
to
445a3b3
Compare
6ab566c
to
ca4db5c
Compare
Hey there,
Though better create another thread to share my findings.
No noticeable audio skips now!!
This one I'm using the callback, this way we know more precisely when data is going to be played. It needs more love though
I applied a filter to the PTP, in my findings the PTP sync is quite ugly, i.e. it just jumps a few msec back and forth quite often which causes the stream to do skips.
The RTP buffer needs to be able to track back and forth, added a new
previous()
method to go back, however it might be interesting to get the frame we need directly instead of advancing / going back.Pause, seek, and track skip is not working well