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