7 audio_init_prototype
= CFUNCTYPE(py_object
, c_void_p
, c_int
, c_int
, c_int
)
8 audio_set_volume_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
, c_float
)
9 audio_process_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
, c_void_p
, c_int
)
10 audio_flush_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
)
11 audio_destroy_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
)
13 class RaopNativeCallbacks(Structure
):
14 _fields_
= [("cls", py_object
),
15 ("audio_init", audio_init_prototype
),
16 ("audio_set_volume", audio_set_volume_prototype
),
17 ("audio_process", audio_process_prototype
),
18 ("audio_flush", audio_flush_prototype
),
19 ("audio_destroy", audio_destroy_prototype
)]
21 def LoadShairplay(path
):
22 if sys
.maxsize
< 2**32:
23 libname
= "shairplay32"
25 libname
= "shairplay64"
27 if platform
.system() == "Windows":
28 libname
= libname
+ ".dll"
29 elif platform
.system() == "Darwin":
30 libname
= "lib" + libname
+ ".dylib"
32 libname
= "lib" + libname
+ ".so"
35 fullpath
= os
.path
.join(path
, libname
)
36 libshairplay
= cdll
.LoadLibrary(fullpath
)
38 raise RuntimeError("Couldn't load shairplay library " + libname
)
40 # Initialize dnssd related functions
41 libshairplay
.dnssd_init
.restype
= c_void_p
42 libshairplay
.dnssd_init
.argtypes
= [POINTER(c_char
), c_int
, POINTER(c_int
)]
43 libshairplay
.dnssd_register_raop
.restype
= c_int
44 libshairplay
.dnssd_register_raop
.argtypes
= [c_void_p
, c_char_p
, c_ushort
]
45 libshairplay
.dnssd_register_airplay
.restype
= c_int
46 libshairplay
.dnssd_register_airplay
.argtypes
= [c_void_p
, c_char_p
, c_ushort
]
47 libshairplay
.dnssd_unregister_raop
.restype
= None
48 libshairplay
.dnssd_unregister_raop
.argtypes
= [c_void_p
]
49 libshairplay
.dnssd_unregister_airplay
.restype
= None
50 libshairplay
.dnssd_unregister_airplay
.argtypes
= [c_void_p
]
51 libshairplay
.dnssd_destroy
.restype
= None
52 libshairplay
.dnssd_destroy
.argtypes
= [c_void_p
]
54 # Initialize raop related functions
55 libshairplay
.raop_init
.restype
= c_void_p
56 libshairplay
.raop_init
.argtypes
= [POINTER(RaopNativeCallbacks
), c_char_p
]
57 libshairplay
.raop_start
.restype
= c_int
58 libshairplay
.raop_start
.argtypes
= [c_void_p
, POINTER(c_ushort
), POINTER(c_char
), c_int
]
59 libshairplay
.raop_stop
.restype
= None
60 libshairplay
.raop_stop
.argtypes
= [c_void_p
]
61 libshairplay
.raop_destroy
.restype
= None
62 libshairplay
.raop_destroy
.argtypes
= [c_void_p
]
67 -----BEGIN RSA PRIVATE KEY-----
68 MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt
69 wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U
70 wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf
71 /+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/
72 UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW
73 BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa
74 LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5
75 NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm
76 lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz
77 aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu
78 a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM
79 oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z
80 oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+
81 k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL
82 AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA
83 cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf
84 54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov
85 17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc
86 1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI
87 LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ
88 2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=
89 -----END RSA PRIVATE KEY-----
93 def audio_init(self
, bits
, channels
, samplerate
):
94 raise NotImplementedError()
96 def audio_set_volume(self
, session
, volume
):
99 def audio_process(self
, session
, buffer):
100 raise NotImplementedError()
102 def audio_flush(self
, session
):
105 def audio_destroy(self
, session
):
109 def audio_init_cb(self
, cls
, bits
, channels
, samplerate
):
110 session
= self
.callbacks
.audio_init(bits
, channels
, samplerate
)
111 return py_object(session
)
113 def audio_set_volume_cb(self
, cls
, sessionptr
, volume
):
114 session
= cast(sessionptr
, py_object
).value
115 self
.callbacks
.audio_set_volume(session
, volume
)
117 def audio_process_cb(self
, cls
, sessionptr
, buffer, buflen
):
118 session
= cast(sessionptr
, py_object
).value
119 strbuffer
= string_at(buffer, buflen
)
120 self
.callbacks
.audio_process(session
, strbuffer
)
122 def audio_flush_cb(self
, cls
, sessionptr
):
123 session
= cast(sessionptr
, py_object
).value
124 self
.callbacks
.audio_flush(session
)
126 def audio_destroy_cb(self
, cls
, sessionptr
):
127 session
= cast(sessionptr
, py_object
).value
128 self
.callbacks
.audio_destroy(session
)
131 def __init__(self
, libshairplay
, callbacks
):
132 self
.libshairplay
= libshairplay
133 self
.callbacks
= callbacks
136 # We need to hold a reference to native_callbacks
137 self
.native_callbacks
= RaopNativeCallbacks()
138 self
.native_callbacks
.audio_init
= audio_init_prototype(self
.audio_init_cb
)
139 self
.native_callbacks
.audio_set_volume
= audio_set_volume_prototype(self
.audio_set_volume_cb
)
140 self
.native_callbacks
.audio_process
= audio_process_prototype(self
.audio_process_cb
)
141 self
.native_callbacks
.audio_flush
= audio_flush_prototype(self
.audio_flush_cb
)
142 self
.native_callbacks
.audio_destroy
= audio_destroy_prototype(self
.audio_destroy_cb
)
144 # Initialize the raop instance with our callbacks
145 self
.instance
= self
.libshairplay
.raop_init(pointer(self
.native_callbacks
), RSA_KEY
)
146 if self
.instance
== None:
147 raise RuntimeError("Initializing library failed")
150 if self
.instance
!= None:
151 self
.libshairplay
.raop_destroy(self
.instance
)
154 def start(self
, port
, hwaddrstr
):
155 port
= c_ushort(port
)
156 hwaddr
= create_string_buffer(hwaddrstr
, len(hwaddrstr
))
158 ret
= self
.libshairplay
.raop_start(self
.instance
, pointer(port
), hwaddr
, c_int(len(hwaddr
)))
160 raise RuntimeError("Starting RAOP instance failed")
164 self
.libshairplay
.raop_stop(self
.instance
)
167 def __init__(self
, libshairplay
):
168 self
.libshairplay
= libshairplay
171 hwaddr
= (c_char
* 6)()
174 self
.instance
= self
.libshairplay
.dnssd_init(hwaddr
, c_int(len(hwaddr
)), pointer(error
))
175 if self
.instance
== None:
176 raise RuntimeError("Initializing library failed: " + str(error
.value
))
179 if self
.instance
!= None:
180 self
.libshairplay
.dnssd_destroy(self
.instance
)
183 def register_raop(self
, name
, port
):
184 self
.libshairplay
.dnssd_register_raop(self
.instance
, name
, c_ushort(port
))
186 def unregister_raop(self
):
187 self
.libshairplay
.dnssd_unregister_raop(self
.instance
)
189 def register_airplay(self
, name
, port
):
190 self
.libshairplay
.dnssd_register_airplay(self
.instance
, name
, c_ushort(port
))
192 def unregister_airplay(self
):
193 self
.libshairplay
.dnssd_unregister_airplay(self
.instance
)