Airplay 2 Receiver written in go

Go Play 2

This is a work in progress Airplay 2 Speaker implementation largely inspired by airplay2-receiver

Status

Can

  • be paired manually on IOS 14.x with a Homepod Mini
  • Play AAC 44100 with buffered audio
  • Play/Pause/Stop/Seek
  • PTP supported and sync with homepod mini

Next Step

  • Manage Homekit (break at POST /configure Endpoint)
  • Play ALAC
  • Need to be hardened

Multi Room accuracy

PTP (Precision Time protocol) is implemented, but it does not (yet) use NIC hardware timestamps. Therefore, the accuracy is around 1ms of offset between clocks.

How to build

  • Clone the repository

  • Get Dependencies (-d means no direct build)

go get -d 
  • Build dependency (go-fdkacc), and the program
make 

Dependencies

You need to have binutils, portaudio-dev, gcc and go runtime installed to build this program

Raspberry Pi

TODO

Docker image

You can build the image to test Linux build and that the service run properly

  • Build the image
docker build -t albanseurat/goplay2:latest .
  • Run the container
docker run -p 7000:7000 -it albanseurat/goplay2:latest

Acknowledgments

  • Docker build is intended to test building the program on Linux platform
  • Bonjour/mDns implementation needs to be changed to allow exposing airplay service outside docker container

Run

goplay2 by default run only on the ipv4 interface (because this issue on ipv6 parsing)

Parameters

delay (ms) is subtracted from the local "clock"
Ex: It takes around 60ms on my mac to launch the audio stream at the Anchor Time

i (interface) used to listen (by default eth0)

n (name) used as accessory name (Bonjour)

alsa (alsa port name) to replace default pcm port (default : pcm.default)

./goplay2 -alsa pcm.default -i en0 -n aiwa
Owner
Alban Seurat
Everything should be made as simple as possible, but no simpler.
Alban Seurat
Comments
  • ALSA output

    ALSA output

    I see this thing is hard coded for pcm.default for the ALSA output. It would be great to have a command line option for the ALSA output. A cmd line option for the listen port would also be great. I have a few 7.1 ch sound cards in my server and have an elaborate ALSA config that busts them up into several outputs and then I run several instances of shairport-sync and each one is on it's own port and set to output to different ALSA outputs. I would love to be able to do this with AirPlay 2.

  • Dockerfile broken

    Dockerfile broken

    Hey Alban

    Thanks a lot for this project!!!

    Seems like the provided Dockerfile currently does not build (no makefile).

    Happy to send a PR for a working one though.

  • Thanks for making this a reality! Requests: Multiple instances, Audio output routing, Volume Control

    Thanks for making this a reality! Requests: Multiple instances, Audio output routing, Volume Control

    Extremely excited about this initiative. Thanks so much for all the work you're putting into this. Tested it out on OSX and got it working no problem.

    My use case is that I'd love to be able to run several airplay receivers at the same time with different names and then route audio to from each to different virtual devices. I could do the routing in a separate program like Loopback but would need to be able to direct the output of goplay to a specific output device (ex. "audio capture 1").

    Let me know if this is possible. I think the features needed would be:

    1. Running goplay on different ports so you can run multiple instances (command line param?)
    2. Pointing audio output at a specific output device
    3. Unrelated but I noticed that changing the volume doesn't do anything. Is this something that could be supported so you can still control volume from the sending device?

    Random question out of curiosity, why do all apps work except Spotify?

    Thanks again and happy to help test.

  • Multi-architecture, runnable Docker image

    Multi-architecture, runnable Docker image

    Fix #18

    Title says all.

    This is a dockerfile allowing one to cross-compile goplay2 and produce a usable, runnable multi-architecture image.

    The invocation command is a bit of a mouthful though that could be made simpler.

    The main "limitation" right now is that it expects pulseaudio to be running on the host or in another docker image (I really did not feel like cramming in pulseaudio here).

    I'm running this in docker on a RPI 3.

  • Build fails on Mac OS

    Build fails on Mac OS

    arya@Aryas-Mac-mini goplay2 % neofetch
                        'c.          [email protected]
                     ,xNMM.          -------------------------
                   .OMMMMo           OS: macOS 11.6 20G165 arm64
                   OMMM0,            Host: Macmini9,1
         .;loddo:' loolloddol;.      Kernel: 20.6.0
       cKMMMMMMMMMMNWMMMMMMMMMM0:    Uptime: 1 hour, 18 mins
     .KMMMMMMMMMMMMMMMMMMMMMMMWd.    Packages: 126 (brew)
     XMMMMMMMMMMMMMMMMMMMMMMMX.      Shell: zsh 5.8
    ;MMMMMMMMMMMMMMMMMMMMMMMM:       Resolution: 1920x1080
    :MMMMMMMMMMMMMMMMMMMMMMMM:       DE: Aqua
    .MMMMMMMMMMMMMMMMMMMMMMMMX.      WM: Quartz Compositor
     kMMMMMMMMMMMMMMMMMMMMMMMMWd.    WM Theme: Blue (Dark)
     .XMMMMMMMMMMMMMMMMMMMMMMMMMMk   Terminal: iTerm2
      .XMMMMMMMMMMMMMMMMMMMMMMMMK.   Terminal Font: Monaco 12
        kMMMMMMMMMMMMMMMMMMMMMMd     CPU: Apple M1
         ;KMMMMMMMWXXWMMMMMMMk.      GPU: Apple M1
           .cooc,.    .,coo:.        Memory: 1381MiB / 8192MiB
    
    
    
    
    arya@Aryas-Mac-mini goplay2 % brew install portaudio fdk-aac go
    Updating Homebrew...
    Warning: portaudio 19.7.0 is already installed and up-to-date.
    To reinstall 19.7.0, run:
      brew reinstall portaudio
    Warning: fdk-aac 2.0.2 is already installed and up-to-date.
    To reinstall 2.0.2, run:
      brew reinstall fdk-aac
    Warning: go 1.17.2 is already installed and up-to-date.
    To reinstall 1.17.2, run:
      brew reinstall go
    
    arya@Aryas-Mac-mini goplay2 % go build
    # goplay2/codec
    codec/aac.go:32:10: fatal error: 'aacdecoder_lib.h' file not found
    #include "aacdecoder_lib.h"
             ^~~~~~~~~~~~~~~~~~
    1 error generated.
    
  • SIGSEGV when switching songs fast

    SIGSEGV when switching songs fast

    When switching song fast I get a segfault. After looking at Filter on audio/ring.go I didn't see anything wrong (I'm not that experienced in Go, tbh).

    I'm running features/audio-sync commit d110dd6

    If you need any other info I'll gladly provide everything needed.

    Have a nice day!

    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4216b9d]
    
    goroutine 10 [running]:
    container/list.(*Element).Next(...)
    	/usr/local/Cellar/go/1.16.6/libexec/src/container/list/list.go:32
    goplay2/audio.(*Ring).Filter(0xc000216000, 0xc000285ea0)
    	/Users/eliseomartelli/Projects/goplay2/audio/ring.go:155 +0x13d
    goplay2/audio.(*Player).skipUntil(0xc000012a50, 0x0, 0xb6cb71)
    	/Users/eliseomartelli/Projects/goplay2/audio/player.go:123 +0x105
    goplay2/audio.(*Player).Run(0xc000012a50)
    	/Users/eliseomartelli/Projects/goplay2/audio/player.go:110 +0x485
    main.main.func1(0xc000012a50, 0xc00001cfa0)
    	/Users/eliseomartelli/Projects/goplay2/main.go:71 +0x2b
    created by main.main
    	/Users/eliseomartelli/Projects/goplay2/main.go:70 +0x97d
    
  • Issues with CFLAGS and LDFLAGS

    Issues with CFLAGS and LDFLAGS

    Hey @AlbanSeurat

    This https://github.com/openairplay/goplay2/blob/main/codec/aac.go#L27-L30 is problematic.

    • on linux, the use of a static version of libfdk is forced
    • on mac & linux, the path information (-I, -L) may or may not work / make sense, override proper system values to something undesirable

    Both points are problematic for cross-compilation and for systems that do not ship a static version of libfdk - typically Debian Buster in the Dockerfile already requires live-patching the source (https://github.com/openairplay/goplay2/pull/19/files#diff-dd2c0eb6ea5cfc6c4bd4eac30934e2d5746747af48fef6da689e85b752f39557R9)

    Suggestion would be to leave the responsibility of picking these to the build system / command (eg: "-extldflags -static(-pie)") for people who would really want to be specific about these.

    For most normal people, things will just continue building by default, and this should make life easier for advanced users (looking for cross-compilation, or more control over static vs. dynamic).

    Let me know your thoughts and if you agree I can send a small PR to change that.

  • Conf dir

    Conf dir

    Fix #20

    New flag -c allows one to point to a base directory where configuration will live.

    If unspecified, pwd will be used, which should match the previous behavior.

    This is just a quick suggestion at this point, but I thought I would send it over early for feedback / modifications.

  • build failure on Raspberry Pi B+

    build failure on Raspberry Pi B+

    Architecture: armv7l

    pi@raspBplus-api:~/Development/goplay2 $ cat /etc/issue Raspbian GNU/Linux 10 \n \l

    pi@raspBplus-api:~/Development/goplay2 $ go version go version go1.16.6 linux/arm

    pi@raspBplus-api:~/Development/goplay2 $ ./goplay2 -i eth0 -n pitunes

    2021/08/04 23:38:56 pitunes/config.json is not valid - at new file will be created at program exit
    2021/08/04 23:38:56 Starting goplay for device Pi: 24e650f4-3d3f-4832-ad39-6a1c3bff81a6, guid: 24e650f4-3d3f-4832-ad39-6a1c3bff81a6, Psi: 24e650f4-3d3f-4832-ad39-6a1c3bff81a6
    2021/08/04 23:38:56 dial unix /run/user/1000/pulse/native: connect: no such file or directory
    2021/08/04 23:38:56 Warning : impossible to store config file pitunes/config.json
    panic: dial unix /run/user/1000/pulse/native: connect: no such file or directory
    
    goroutine 1 [running]:
    log.Panic(0x293deb0, 0x1, 0x1)
    	/usr/local/go/src/log/log.go:354 +0x84
    goplay2/codec.NewStream(0x2f8f00, 0x64)
    	/home/pi/Development/goplay2/codec/pulseaudio.go:45 +0x80
    goplay2/audio.NewPlayer(0x289a828, 0x28980c0, 0x147ae)
    	/home/pi/Development/goplay2/audio/player.go:32 +0x40
    main.main()
    	/home/pi/Development/goplay2/main.go:71 +0x950
    

    What am I missing?

  • Stopped working with iOS version 15.1

    Stopped working with iOS version 15.1

    arya@Aryas-Mac-mini goplay2 % ./goplay2 -n Airplay2 -i en0
    2021/10/27 13:10:07 Airplay2/config.json is not valid - at new file will be created at program exit
    2021/10/27 13:10:07 Starting goplay for device Pi: bf832c73-77c2-47b5-9212-544192fb2a17, guid: bf832c73-77c2-47b5-9212-544192fb2a17, Psi: bf832c73-77c2-47b5-9212-544192fb2a17
    2021/10/27 13:11:29 request received : GET /info body 70
    2021/10/27 13:11:29 response sent : body 374
    2021/10/27 13:11:29 request received : POST /pair-verify body 37
    2021/10/27 13:11:29 response sent : body 140
    2021/10/27 13:11:29 request received : POST /pair-setup body 6
    2021/10/27 13:11:29 response sent : body 409
    2021/10/27 13:11:29 request received : POST /pair-setup body 457
    2021/10/27 13:11:29 response sent : body 69
    2021/10/27 13:11:29 request received : POST /pair-setup body 159
    2021/10/27 13:11:29 response sent : body 143
    2021/10/27 13:11:29 request received : POST /pair-verify body 37
    2021/10/27 13:11:29 server.go:82: Error:Error handling RSTP request Invalid internal pairing verify step Waiting
    
  • Don't run.

    Don't run.

    Hello, this is my issue pi@raspberrypi:/home/raye/goplay2 $ ./goplay2 -sink default:b1

    2021/08/22 19:15:04 goplay/config.json is not valid - at new file will be created at program exit 2021/08/22 19:15:04 Starting goplay for device Pi: ca7e2ab8-ff55-49a2-b6ae-84385eb0323e, guid: ca7e2ab8-ff55-49a2-b6ae-84385eb0323e, Psi: ca7e2ab8-ff55-49a2-b6ae-84385eb0323e 2021/08/22 19:15:04 dial unix /run/user/1000/pulse/native: connect: connection refused 2021/08/22 19:15:04 Warning : impossible to store config file goplay/config.json panic: dial unix /run/user/1000/pulse/native: connect: connection refused

    goroutine 1 [running]: log.Panic(0xd3feb0, 0x1, 0x1) /usr/local/go/src/log/log.go:354 +0x84 goplay2/codec.NewStream(0x2875b8, 0x64) /home/raye/goplay2/codec/pulseaudio.go:45 +0x80 goplay2/audio.NewPlayer(0xc9e438, 0xc94150, 0x147ae) /home/raye/goplay2/audio/player.go:32 +0x40 main.main() /home/raye/goplay2/main.go:71 +0x950

    What it's wrong ? Thanks

  • fix: remove difference between linux and macOS paths

    fix: remove difference between linux and macOS paths

    Since brew symlinks fdk-aac under /usr/local/include/ it isn't necessary to split the include for Linux and macOS systems as long as the user has properly configured brew on their system.

    For macOS you need to be sure that you have /usr/local/include in your path.

    ➜  ~ tree /usr/local/include/fdk-aac
    /usr/local/include/fdk-aac
    ├── FDK_audio.h
    ├── aacdecoder_lib.h
    ├── aacenc_lib.h
    ├── genericStds.h
    ├── machine_type.h
    └── syslib_channelMapDescr.h
    
    0 directories, 6 files
    

    Inside bashrc/zshrc:

    export PATH="/usr/local/include:$PATH"
    
  • Unable to Pair on iOS 15 beta 6

    Unable to Pair on iOS 15 beta 6

    It seems like something has changed in the pairing mechanism for the latest iOS beta. On attempted casting the following is logged with an error Unable to connect:

    2021/08/24 18:50:45 request received : GET /info body 70
    2021/08/24 18:50:45 response sent : body 374
    2021/08/24 18:50:45 request received : POST /pair-verify body 37
    2021/08/24 18:50:45 response sent : body 140
    2021/08/24 18:50:45 request received : POST /pair-setup body 6
    2021/08/24 18:50:45 response sent : body 409
    2021/08/24 18:50:45 request received : POST /pair-setup body 457
    2021/08/24 18:50:45 response sent : body 69
    2021/08/24 18:50:45 request received : POST /pair-setup body 159
    2021/08/24 18:50:45 response sent : body 143
    2021/08/24 18:50:45 request received : POST /pair-verify body 37
    2021/08/24 18:50:45 server.go:82: Error:Error handling RSTP request Invalid internal pairing verify step Waiting
    

    When paired through the home app there are no errors. However the following is in the log:

    2021/08/24 18:57:20 request received : POST /configure body 148
    2021/08/24 18:57:20 response sent : body 237
    2021/08/24 18:57:20 request received : TEARDOWN rtsp://192.168.1.73/7350123517057450268 body 42
    2021/08/24 18:57:20 response sent : body 0
    2021/08/24 18:57:20 server.go:37: Error:Event error : EOF
    2021/08/24 18:57:20 server.go:76: Error:Error parsing RSTP request EOF 
    

    And the same error when trying to cast.

    Casting works on iOS 14.7.1 and macOS devices.

  • Error after several minutes

    Error after several minutes

    Hello, after 45min - 1 hours I have a lot of error Capture d’écran 2021-08-23 à 00 23 59 I have take testing on divers Apple Device. I think this issue can come when divers Apple devices is connected in same time AirPlay.

    Sorry for bad english :)

  • "Error parsing RSTP request close tcp4" when playing to two GoPlay2 devices at once

    I have GoPlay2 setup on two Raspberry Pi's right now (a 3 and a 4). Both are on the same network and I have no trouble playing to each one individually. However, when I try to play to both at once from an iPhone, the following error is thrown and they both disconnect:

    2021/08/20 14:52:29 server.go:37: Error:Event error : EOF
    2021/08/20 14:52:29 server.go:76: Error:Error parsing RSTP request close tcp4 192.168.1.67:7000->192.168.1.64:52259: use of closed network connection
    2021/08/20 14:52:29 server.go:76: Error:Error parsing RSTP request close tcp4 192.168.1.67:7000->192.168.1.64:52259: use of closed network connection
    2021/08/20 14:52:29 server.go:37: Error:Event error : EOF
    

    In the above error, .67 is the Pi throwing the error and .64 is the phone playing the music.

    Also of possible significance, when I attempted to add each device to the Home app, I saw the following error in the log (though they were both added to the app just fine):

    2021/08/20 14:51:27 server.go:37: Error:Event error : EOF
    2021/08/20 14:51:27 server.go:76: Error:Error parsing RSTP request EOF
    
  • Feature request: ability to have configuration in a user specified directory

    Feature request: ability to have configuration in a user specified directory

    Right now, the config is always stored under $(pwd)/$name

    Having the flexibility to point to a completely different base directory where to store config would be nice and particularly useful in the context of hardened docker images where typically only a specific volume is rw while the rest of the image is read-only.

    I'll send a PR as a suggestion to allow this.

Genetic Algorithm written in go

This genetic algorithm is designed to minimise the problem specific code from a genetic algorithm. The three interfaces Gene, Initialiser, an Evaluato

Feb 15, 2022
Simple and expressive toolbox written in Go

ugo Simple and expressive toolbox written with love and care in Go. Deeply inspired by underscore.js and has the same syntax and behaviour Fully cover

Sep 27, 2022
A simplistic todo list manager written in Go
A simplistic todo list manager written in Go

Tasks Tasks is a simplistic Go webapp to manage tasks, I built this tool to manage tasks which I wanted to do, there are many good kanban style boards

Dec 10, 2022
A session manager for tmux written in Go
A session manager for tmux written in Go

Smug - tmux session manager Inspired by tmuxinator and tmuxp. Smug automates your tmux workflow. You can create a single configuration file, and Smug

Jan 6, 2023
An example client implementation written in GO to access the CyberVox platform API

About This is an example client implementation written in GO to access the CyberVox platform API.

Nov 7, 2022
💥 Fusion is a tiny stream processing library written in Go.

?? Fusion Fusion is a tiny stream processing library written in Go. See reactor for a stream processing tool built using fusion. Features Simple & lig

Jun 30, 2021
Simple 'UserKit' for Malware written in Go. Startup, Hidden Files, Critical Process and Registry Watcher

GoUserKit Simple UserKit for Malware written in Go Features Makes Process Critical (NtSetInformationProcess) Hides Files Simple Add to Startup (HKCU R

Jan 3, 2023
jacobin - A more than minimal JVM written in Go and capable of running Java 11 bytecode.

This overview gives the background on this project, including its aspirations and the features that it supports. The remaining pages discuss the basics of JVM operation and, where applicable, how Jacobin implements the various steps, noting any items that would be of particular interest to JVM cognoscenti.

Dec 29, 2022
A wrapper for the Wandbox API, written in Golang!

GoWandBox A simple wrapper for the WandBox API, written in Golang! Documentation can be found at classpythonaddike.github.io/gowandbox/ Note: This wra

Sep 19, 2021
A simple typewriter written in go for KOBO e-readers
A simple typewriter written in go for KOBO e-readers

Kobowriter This small project aims to let you use your old KOBO e-reader (mine is a GLO HD) as a simple, distraction free typewriter. For years I thou

Dec 25, 2022
fccCoin Clone written in GoLang

fccCoin Description fccCoin Clone written in GoLang Actual Code for fccCoin written in Python

Oct 2, 2021
Conventional Commits parser written in Go

Conventional Commit Parser This is a parser for Conventional Commits go get -u github.com/release-lab/conventional-commit-parser package main import

Feb 4, 2022
Polarite is a Pastebin alternative made for simplicity written in Go.
 Polarite is a Pastebin alternative made for simplicity written in Go.

Polarite is a Pastebin alternative made for simplicity written in Go. Usage Web Interface Visit https://polarite.teknologiumum.com API Send a POST req

Dec 1, 2022
A boilerplate showing how to create a Pulumi component provider written in Go

xyz Pulumi Component Provider (Go) This repo is a boilerplate showing how to create a Pulumi component provider written in Go. You can search-replace

Mar 4, 2022
Person is a simple CRUD application written in go which exposes API endpoint to create the person.

Person Person is a simple CRUD application written in go which exposes API endpoint to create the person. Installation Install docker in your local sy

Oct 18, 2021
Chief Client Go is a cross platform Krunker client written in Go Lang

Chief Client Go Chief Client Go is a client for Mac and Linux written in GoLang Features Ad Blocker Option to use proxy Installation To install this c

Nov 6, 2021
An atomic counter that also tracks and calculates a rate written in Go.

An atomic counter that also tracks and calculates a rate written in Go.

Oct 27, 2021
Experimental Monika After Story persistent data loader written in Go

Go Persistent Loader This project is an experiment on loading/deserializing Monika After Story persistent (save) file into memory. Currently it contai

May 10, 2022
A modern programming language written in Golang.

MangoScript A modern programming language written in Golang. Here is what I want MangoScript to look like: struct Rectangle { width: number he

Nov 12, 2021