3 Copyright (C) 2012 Juho Vähä-Herttua
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
22 audio_init_prototype
= CFUNCTYPE(py_object
, c_void_p
, c_int
, c_int
, c_int
)
23 audio_set_volume_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
, c_float
)
24 audio_process_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
, c_void_p
, c_int
)
25 audio_flush_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
)
26 audio_destroy_prototype
= CFUNCTYPE(None, c_void_p
, c_void_p
)
28 class RaopNativeCallbacks(Structure
):
29 _fields_
= [("cls", py_object
),
30 ("audio_init", audio_init_prototype
),
31 ("audio_set_volume", audio_set_volume_prototype
),
32 ("audio_process", audio_process_prototype
),
33 ("audio_flush", audio_flush_prototype
),
34 ("audio_destroy", audio_destroy_prototype
)]
36 def LoadShairplay(path
):
37 if sys
.maxsize
< 2**32:
38 libname
= "shairplay32"
40 libname
= "shairplay64"
42 if platform
.system() == "Windows":
43 libname
= libname
+ ".dll"
44 elif platform
.system() == "Darwin":
45 libname
= "lib" + libname
+ ".dylib"
47 libname
= "lib" + libname
+ ".so"
50 fullpath
= os
.path
.join(path
, libname
)
51 libshairplay
= cdll
.LoadLibrary(fullpath
)
53 raise RuntimeError("Couldn't load shairplay library " + libname
)
55 # Initialize dnssd related functions
56 libshairplay
.dnssd_init
.restype
= c_void_p
57 libshairplay
.dnssd_init
.argtypes
= [POINTER(c_int
)]
58 libshairplay
.dnssd_register_raop
.restype
= c_int
59 libshairplay
.dnssd_register_raop
.argtypes
= [c_void_p
, c_char_p
, c_ushort
, POINTER(c_char
), c_int
]
60 libshairplay
.dnssd_register_airplay
.restype
= c_int
61 libshairplay
.dnssd_register_airplay
.argtypes
= [c_void_p
, c_char_p
, c_ushort
, POINTER(c_char
), c_int
]
62 libshairplay
.dnssd_unregister_raop
.restype
= None
63 libshairplay
.dnssd_unregister_raop
.argtypes
= [c_void_p
]
64 libshairplay
.dnssd_unregister_airplay
.restype
= None
65 libshairplay
.dnssd_unregister_airplay
.argtypes
= [c_void_p
]
66 libshairplay
.dnssd_destroy
.restype
= None
67 libshairplay
.dnssd_destroy
.argtypes
= [c_void_p
]
69 # Initialize raop related functions
70 libshairplay
.raop_init
.restype
= c_void_p
71 libshairplay
.raop_init
.argtypes
= [POINTER(RaopNativeCallbacks
), c_char_p
]
72 libshairplay
.raop_start
.restype
= c_int
73 libshairplay
.raop_start
.argtypes
= [c_void_p
, POINTER(c_ushort
), POINTER(c_char
), c_int
]
74 libshairplay
.raop_stop
.restype
= None
75 libshairplay
.raop_stop
.argtypes
= [c_void_p
]
76 libshairplay
.raop_destroy
.restype
= None
77 libshairplay
.raop_destroy
.argtypes
= [c_void_p
]
82 -----BEGIN RSA PRIVATE KEY-----
83 MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt
84 wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U
85 wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf
86 /+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/
87 UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW
88 BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa
89 LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5
90 NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm
91 lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz
92 aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu
93 a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM
94 oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z
95 oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+
96 k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL
97 AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA
98 cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf
99 54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov
100 17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc
101 1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI
102 LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ
103 2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=
104 -----END RSA PRIVATE KEY-----
108 def audio_init(self
, bits
, channels
, samplerate
):
109 raise NotImplementedError()
111 def audio_set_volume(self
, session
, volume
):
114 def audio_process(self
, session
, buffer):
115 raise NotImplementedError()
117 def audio_flush(self
, session
):
120 def audio_destroy(self
, session
):
124 def audio_init_cb(self
, cls
, bits
, channels
, samplerate
):
125 session
= self
.callbacks
.audio_init(bits
, channels
, samplerate
)
126 self
.sessions
.append(session
)
129 def audio_set_volume_cb(self
, cls
, sessionptr
, volume
):
130 session
= cast(sessionptr
, py_object
).value
131 self
.callbacks
.audio_set_volume(session
, volume
)
133 def audio_process_cb(self
, cls
, sessionptr
, buffer, buflen
):
134 session
= cast(sessionptr
, py_object
).value
135 strbuffer
= string_at(buffer, buflen
)
136 self
.callbacks
.audio_process(session
, strbuffer
)
138 def audio_flush_cb(self
, cls
, sessionptr
):
139 session
= cast(sessionptr
, py_object
).value
140 self
.callbacks
.audio_flush(session
)
142 def audio_destroy_cb(self
, cls
, sessionptr
):
143 session
= cast(sessionptr
, py_object
).value
144 self
.callbacks
.audio_destroy(session
)
145 if session
in self
.sessions
:
146 self
.sessions
.remove(session
)
149 def __init__(self
, libshairplay
, callbacks
):
150 self
.libshairplay
= libshairplay
151 self
.callbacks
= callbacks
155 # We need to hold a reference to native_callbacks
156 self
.native_callbacks
= RaopNativeCallbacks()
157 self
.native_callbacks
.audio_init
= audio_init_prototype(self
.audio_init_cb
)
158 self
.native_callbacks
.audio_set_volume
= audio_set_volume_prototype(self
.audio_set_volume_cb
)
159 self
.native_callbacks
.audio_process
= audio_process_prototype(self
.audio_process_cb
)
160 self
.native_callbacks
.audio_flush
= audio_flush_prototype(self
.audio_flush_cb
)
161 self
.native_callbacks
.audio_destroy
= audio_destroy_prototype(self
.audio_destroy_cb
)
163 # Initialize the raop instance with our callbacks
164 self
.instance
= self
.libshairplay
.raop_init(pointer(self
.native_callbacks
), RSA_KEY
)
165 if self
.instance
== None:
166 raise RuntimeError("Initializing library failed")
169 if self
.instance
!= None:
170 self
.libshairplay
.raop_destroy(self
.instance
)
173 def start(self
, port
, hwaddrstr
):
174 port
= c_ushort(port
)
175 hwaddr
= create_string_buffer(hwaddrstr
, len(hwaddrstr
))
177 ret
= self
.libshairplay
.raop_start(self
.instance
, pointer(port
), hwaddr
, c_int(len(hwaddr
)))
179 raise RuntimeError("Starting RAOP instance failed")
183 self
.libshairplay
.raop_stop(self
.instance
)
186 def __init__(self
, libshairplay
):
187 self
.libshairplay
= libshairplay
192 self
.instance
= self
.libshairplay
.dnssd_init(pointer(error
))
193 if self
.instance
== None:
194 raise RuntimeError("Initializing library failed: " + str(error
.value
))
197 if self
.instance
!= None:
198 self
.libshairplay
.dnssd_destroy(self
.instance
)
201 def register_raop(self
, name
, port
, hwaddrstr
):
202 hwaddr
= create_string_buffer(hwaddrstr
, len(hwaddrstr
))
203 self
.libshairplay
.dnssd_register_raop(self
.instance
, name
, c_ushort(port
), hwaddr
, len(hwaddr
))
205 def unregister_raop(self
):
206 self
.libshairplay
.dnssd_unregister_raop(self
.instance
)
208 def register_airplay(self
, name
, port
, hwaddrstr
):
209 hwaddr
= create_string_buffer(hwaddrstr
, len(hwaddrstr
))
210 self
.libshairplay
.dnssd_register_airplay(self
.instance
, name
, c_ushort(port
), hwaddr
, len(hwaddr
))
212 def unregister_airplay(self
):
213 self
.libshairplay
.dnssd_unregister_airplay(self
.instance
)