tmp
[blender.git] / extern / audaspace / bindings / python / PyHandle.cpp
1 /*******************************************************************************
2  * Copyright 2009-2016 Jörg Müller
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16
17 #include "PyHandle.h"
18
19 #include "devices/IHandle.h"
20 #include "devices/I3DHandle.h"
21 #include "Exception.h"
22
23 #include <memory>
24
25 #include <structmember.h>
26
27 using namespace aud;
28
29 extern PyObject* AUDError;
30 static const char* device_not_3d_error = "Device is not a 3D device!";
31
32 static void
33 Handle_dealloc(Handle* self)
34 {
35         if(self->handle)
36                 delete reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle);
37         Py_TYPE(self)->tp_free((PyObject *)self);
38 }
39
40 PyDoc_STRVAR(M_aud_Handle_pause_doc,
41                          "pause()\n\n"
42                          "Pauses playback.\n\n"
43                          ":return: Whether the action succeeded.\n"
44                          ":rtype: bool");
45
46 static PyObject *
47 Handle_pause(Handle* self)
48 {
49         try
50         {
51                 return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->pause());
52         }
53         catch(Exception& e)
54         {
55                 PyErr_SetString(AUDError, e.what());
56                 return nullptr;
57         }
58 }
59
60 PyDoc_STRVAR(M_aud_Handle_resume_doc,
61                          "resume()\n\n"
62                          "Resumes playback.\n\n"
63                          ":return: Whether the action succeeded.\n"
64                          ":rtype: bool");
65
66 static PyObject *
67 Handle_resume(Handle* self)
68 {
69         try
70         {
71                 return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->resume());
72         }
73         catch(Exception& e)
74         {
75                 PyErr_SetString(AUDError, e.what());
76                 return nullptr;
77         }
78 }
79
80 PyDoc_STRVAR(M_aud_Handle_stop_doc,
81                          "stop()\n\n"
82                          "Stops playback.\n\n"
83                          ":return: Whether the action succeeded.\n"
84                          ":rtype: bool\n\n"
85                          ".. note:: This makes the handle invalid.");
86
87 static PyObject *
88 Handle_stop(Handle* self)
89 {
90         try
91         {
92                 return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->stop());
93         }
94         catch(Exception& e)
95         {
96                 PyErr_SetString(AUDError, e.what());
97                 return nullptr;
98         }
99 }
100
101 static PyMethodDef Handle_methods[] = {
102         {"pause", (PyCFunction)Handle_pause, METH_NOARGS,
103          M_aud_Handle_pause_doc
104         },
105         {"resume", (PyCFunction)Handle_resume, METH_NOARGS,
106          M_aud_Handle_resume_doc
107         },
108         {"stop", (PyCFunction)Handle_stop, METH_NOARGS,
109          M_aud_Handle_stop_doc
110         },
111         {nullptr}  /* Sentinel */
112 };
113
114 PyDoc_STRVAR(M_aud_Handle_attenuation_doc,
115                          "This factor is used for distance based attenuation of the "
116                          "source.\n\n"
117                          ".. seealso:: :attr:`Device.distance_model`");
118
119 static PyObject *
120 Handle_get_attenuation(Handle* self, void* nothing)
121 {
122         try
123         {
124                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
125                 if(handle)
126                 {
127                         return Py_BuildValue("f", handle->getAttenuation());
128                 }
129                 else
130                 {
131                         PyErr_SetString(AUDError, device_not_3d_error);
132                         return nullptr;
133                 }
134         }
135         catch(Exception& e)
136         {
137                 PyErr_SetString(AUDError, e.what());
138                 return nullptr;
139         }
140 }
141
142 static int
143 Handle_set_attenuation(Handle* self, PyObject* args, void* nothing)
144 {
145         float factor;
146
147         if(!PyArg_Parse(args, "f:attenuation", &factor))
148                 return -1;
149
150         try
151         {
152                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
153                 if(handle)
154                 {
155                         if(handle->setAttenuation(factor))
156                                 return 0;
157                         PyErr_SetString(AUDError, "Couldn't set the attenuation!");
158                 }
159                 else
160                         PyErr_SetString(AUDError, device_not_3d_error);
161         }
162         catch(Exception& e)
163         {
164                 PyErr_SetString(AUDError, e.what());
165         }
166
167         return -1;
168 }
169
170 PyDoc_STRVAR(M_aud_Handle_cone_angle_inner_doc,
171                          "The opening angle of the inner cone of the source. If the cone "
172                          "values of a source are set there are two (audible) cones with "
173                          "the apex at the :attr:`location` of the source and with infinite "
174                          "height, heading in the direction of the source's "
175                          ":attr:`orientation`.\n"
176                          "In the inner cone the volume is normal. Outside the outer cone "
177                          "the volume will be :attr:`cone_volume_outer` and in the area "
178                          "between the volume will be interpolated linearly.");
179
180 static PyObject *
181 Handle_get_cone_angle_inner(Handle* self, void* nothing)
182 {
183         try
184         {
185                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
186                 if(handle)
187                 {
188                         return Py_BuildValue("f", handle->getConeAngleInner());
189                 }
190                 else
191                 {
192                         PyErr_SetString(AUDError, device_not_3d_error);
193                         return nullptr;
194                 }
195         }
196         catch(Exception& e)
197         {
198                 PyErr_SetString(AUDError, e.what());
199                 return nullptr;
200         }
201 }
202
203 static int
204 Handle_set_cone_angle_inner(Handle* self, PyObject* args, void* nothing)
205 {
206         float angle;
207
208         if(!PyArg_Parse(args, "f:cone_angle_inner", &angle))
209                 return -1;
210
211         try
212         {
213                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
214                 if(handle)
215                 {
216                         if(handle->setConeAngleInner(angle))
217                                 return 0;
218                         PyErr_SetString(AUDError, "Couldn't set the cone inner angle!");
219                 }
220                 else
221                         PyErr_SetString(AUDError, device_not_3d_error);
222         }
223         catch(Exception& e)
224         {
225                 PyErr_SetString(AUDError, e.what());
226         }
227
228         return -1;
229 }
230
231 PyDoc_STRVAR(M_aud_Handle_cone_angle_outer_doc,
232                          "The opening angle of the outer cone of the source.\n\n"
233                          ".. seealso:: :attr:`cone_angle_inner`");
234
235 static PyObject *
236 Handle_get_cone_angle_outer(Handle* self, void* nothing)
237 {
238         try
239         {
240                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
241                 if(handle)
242                 {
243                         return Py_BuildValue("f", handle->getConeAngleOuter());
244                 }
245                 else
246                 {
247                         PyErr_SetString(AUDError, device_not_3d_error);
248                         return nullptr;
249                 }
250         }
251         catch(Exception& e)
252         {
253                 PyErr_SetString(AUDError, e.what());
254                 return nullptr;
255         }
256 }
257
258 static int
259 Handle_set_cone_angle_outer(Handle* self, PyObject* args, void* nothing)
260 {
261         float angle;
262
263         if(!PyArg_Parse(args, "f:cone_angle_outer", &angle))
264                 return -1;
265
266         try
267         {
268                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
269                 if(handle)
270                 {
271                         if(handle->setConeAngleOuter(angle))
272                                 return 0;
273                         PyErr_SetString(AUDError, "Couldn't set the cone outer angle!");
274                 }
275                 else
276                         PyErr_SetString(AUDError, device_not_3d_error);
277         }
278         catch(Exception& e)
279         {
280                 PyErr_SetString(AUDError, e.what());
281         }
282
283         return -1;
284 }
285
286 PyDoc_STRVAR(M_aud_Handle_cone_volume_outer_doc,
287                          "The volume outside the outer cone of the source.\n\n"
288                          ".. seealso:: :attr:`cone_angle_inner`");
289
290 static PyObject *
291 Handle_get_cone_volume_outer(Handle* self, void* nothing)
292 {
293         try
294         {
295                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
296                 if(handle)
297                 {
298                         return Py_BuildValue("f", handle->getConeVolumeOuter());
299                 }
300                 else
301                 {
302                         PyErr_SetString(AUDError, device_not_3d_error);
303                         return nullptr;
304                 }
305         }
306         catch(Exception& e)
307         {
308                 PyErr_SetString(AUDError, e.what());
309                 return nullptr;
310         }
311 }
312
313 static int
314 Handle_set_cone_volume_outer(Handle* self, PyObject* args, void* nothing)
315 {
316         float volume;
317
318         if(!PyArg_Parse(args, "f:cone_volume_outer", &volume))
319                 return -1;
320
321         try
322         {
323                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
324                 if(handle)
325                 {
326                         if(handle->setConeVolumeOuter(volume))
327                                 return 0;
328                         PyErr_SetString(AUDError, "Couldn't set the cone outer volume!");
329                 }
330                 else
331                         PyErr_SetString(AUDError, device_not_3d_error);
332         }
333         catch(Exception& e)
334         {
335                 PyErr_SetString(AUDError, e.what());
336         }
337
338         return -1;
339 }
340
341 PyDoc_STRVAR(M_aud_Handle_distance_maximum_doc,
342                          "The maximum distance of the source.\n"
343                          "If the listener is further away the source volume will be 0.\n\n"
344                          ".. seealso:: :attr:`Device.distance_model`");
345
346 static PyObject *
347 Handle_get_distance_maximum(Handle* self, void* nothing)
348 {
349         try
350         {
351                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
352                 if(handle)
353                 {
354                         return Py_BuildValue("f", handle->getDistanceMaximum());
355                 }
356                 else
357                 {
358                         PyErr_SetString(AUDError, device_not_3d_error);
359                         return nullptr;
360                 }
361         }
362         catch(Exception& e)
363         {
364                 PyErr_SetString(AUDError, e.what());
365                 return nullptr;
366         }
367 }
368
369 static int
370 Handle_set_distance_maximum(Handle* self, PyObject* args, void* nothing)
371 {
372         float distance;
373
374         if(!PyArg_Parse(args, "f:distance_maximum", &distance))
375                 return -1;
376
377         try
378         {
379                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
380                 if(handle)
381                 {
382                         if(handle->setDistanceMaximum(distance))
383                                 return 0;
384                         PyErr_SetString(AUDError, "Couldn't set the maximum distance!");
385                 }
386                 else
387                         PyErr_SetString(AUDError, device_not_3d_error);
388         }
389         catch(Exception& e)
390         {
391                 PyErr_SetString(AUDError, e.what());
392         }
393
394         return -1;
395 }
396
397 PyDoc_STRVAR(M_aud_Handle_distance_reference_doc,
398                          "The reference distance of the source.\n"
399                          "At this distance the volume will be exactly :attr:`volume`.\n\n"
400                          ".. seealso:: :attr:`Device.distance_model`");
401
402 static PyObject *
403 Handle_get_distance_reference(Handle* self, void* nothing)
404 {
405         try
406         {
407                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
408                 if(handle)
409                 {
410                         return Py_BuildValue("f", handle->getDistanceReference());
411                 }
412                 else
413                 {
414                         PyErr_SetString(AUDError, device_not_3d_error);
415                         return nullptr;
416                 }
417         }
418         catch(Exception& e)
419         {
420                 PyErr_SetString(AUDError, e.what());
421                 return nullptr;
422         }
423 }
424
425 static int
426 Handle_set_distance_reference(Handle* self, PyObject* args, void* nothing)
427 {
428         float distance;
429
430         if(!PyArg_Parse(args, "f:distance_reference", &distance))
431                 return -1;
432
433         try
434         {
435                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
436                 if(handle)
437                 {
438                         if(handle->setDistanceReference(distance))
439                                 return 0;
440                         PyErr_SetString(AUDError, "Couldn't set the reference distance!");
441                 }
442                 else
443                         PyErr_SetString(AUDError, device_not_3d_error);
444         }
445         catch(Exception& e)
446         {
447                 PyErr_SetString(AUDError, e.what());
448         }
449
450         return -1;
451 }
452
453 PyDoc_STRVAR(M_aud_Handle_keep_doc,
454                          "Whether the sound should be kept paused in the device when its "
455                          "end is reached.\n"
456                          "This can be used to seek the sound to some position and start "
457                          "playback again.\n\n"
458                          ".. warning:: If this is set to true and you forget stopping this "
459                          "equals a memory leak as the handle exists until the device is "
460                          "destroyed.");
461
462 static PyObject *
463 Handle_get_keep(Handle* self, void* nothing)
464 {
465         try
466         {
467                 return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getKeep());
468         }
469         catch(Exception& e)
470         {
471                 PyErr_SetString(AUDError, e.what());
472                 return nullptr;
473         }
474 }
475
476 static int
477 Handle_set_keep(Handle* self, PyObject* args, void* nothing)
478 {
479         if(!PyBool_Check(args))
480         {
481                 PyErr_SetString(PyExc_TypeError, "keep is not a boolean!");
482                 return -1;
483         }
484
485         bool keep = args == Py_True;
486
487         try
488         {
489                 if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setKeep(keep))
490                         return 0;
491                 PyErr_SetString(AUDError, "Couldn't set keep of the sound!");
492         }
493         catch(Exception& e)
494         {
495                 PyErr_SetString(AUDError, e.what());
496         }
497
498         return -1;
499 }
500
501 PyDoc_STRVAR(M_aud_Handle_location_doc,
502                          "The source's location in 3D space, a 3D tuple of floats.");
503
504 static PyObject *
505 Handle_get_location(Handle* self, void* nothing)
506 {
507         try
508         {
509                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
510                 if(handle)
511                 {
512                         Vector3 v = handle->getLocation();
513                         return Py_BuildValue("(fff)", v.x(), v.y(), v.z());
514                 }
515                 else
516                 {
517                         PyErr_SetString(AUDError, device_not_3d_error);
518                 }
519         }
520         catch(Exception& e)
521         {
522                 PyErr_SetString(AUDError, e.what());
523         }
524
525         return nullptr;
526 }
527
528 static int
529 Handle_set_location(Handle* self, PyObject* args, void* nothing)
530 {
531         float x, y, z;
532
533         if(!PyArg_Parse(args, "(fff):location", &x, &y, &z))
534                 return -1;
535
536         try
537         {
538                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
539                 if(handle)
540                 {
541                         Vector3 location(x, y, z);
542                         if(handle->setLocation(location))
543                                 return 0;
544                         PyErr_SetString(AUDError, "Location couldn't be set!");
545                 }
546                 else
547                         PyErr_SetString(AUDError, device_not_3d_error);
548         }
549         catch(Exception& e)
550         {
551                 PyErr_SetString(AUDError, e.what());
552         }
553
554         return -1;
555 }
556
557 PyDoc_STRVAR(M_aud_Handle_loop_count_doc,
558                          "The (remaining) loop count of the sound. A negative value indicates infinity.");
559
560 static PyObject *
561 Handle_get_loop_count(Handle* self, void* nothing)
562 {
563         try
564         {
565                 return Py_BuildValue("i", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getLoopCount());
566         }
567         catch(Exception& e)
568         {
569                 PyErr_SetString(AUDError, e.what());
570                 return nullptr;
571         }
572 }
573
574 static int
575 Handle_set_loop_count(Handle* self, PyObject* args, void* nothing)
576 {
577         int loops;
578
579         if(!PyArg_Parse(args, "i:loop_count", &loops))
580                 return -1;
581
582         try
583         {
584                 if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setLoopCount(loops))
585                         return 0;
586                 PyErr_SetString(AUDError, "Couldn't set the loop count!");
587         }
588         catch(Exception& e)
589         {
590                 PyErr_SetString(AUDError, e.what());
591         }
592
593         return -1;
594 }
595
596 PyDoc_STRVAR(M_aud_Handle_orientation_doc,
597                          "The source's orientation in 3D space as quaternion, a 4 float tuple.");
598
599 static PyObject *
600 Handle_get_orientation(Handle* self, void* nothing)
601 {
602         try
603         {
604                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
605                 if(handle)
606                 {
607                         Quaternion o = handle->getOrientation();
608                         return Py_BuildValue("(ffff)", o.w(), o.x(), o.y(), o.z());
609                 }
610                 else
611                 {
612                         PyErr_SetString(AUDError, device_not_3d_error);
613                 }
614         }
615         catch(Exception& e)
616         {
617                 PyErr_SetString(AUDError, e.what());
618         }
619
620         return nullptr;
621 }
622
623 static int
624 Handle_set_orientation(Handle* self, PyObject* args, void* nothing)
625 {
626         float w, x, y, z;
627
628         if(!PyArg_Parse(args, "(ffff):orientation", &w, &x, &y, &z))
629                 return -1;
630
631         try
632         {
633                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
634                 if(handle)
635                 {
636                         Quaternion orientation(w, x, y, z);
637                         if(handle->setOrientation(orientation))
638                                 return 0;
639                         PyErr_SetString(AUDError, "Couldn't set the orientation!");
640                 }
641                 else
642                         PyErr_SetString(AUDError, device_not_3d_error);
643         }
644         catch(Exception& e)
645         {
646                 PyErr_SetString(AUDError, e.what());
647         }
648
649         return -1;
650 }
651
652 PyDoc_STRVAR(M_aud_Handle_pitch_doc,
653                          "The pitch of the sound.");
654
655 static PyObject *
656 Handle_get_pitch(Handle* self, void* nothing)
657 {
658         try
659         {
660                 return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getPitch());
661         }
662         catch(Exception& e)
663         {
664                 PyErr_SetString(AUDError, e.what());
665                 return nullptr;
666         }
667 }
668
669 static int
670 Handle_set_pitch(Handle* self, PyObject* args, void* nothing)
671 {
672         float pitch;
673
674         if(!PyArg_Parse(args, "f:pitch", &pitch))
675                 return -1;
676
677         try
678         {
679                 if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setPitch(pitch))
680                         return 0;
681                 PyErr_SetString(AUDError, "Couldn't set the sound pitch!");
682         }
683         catch(Exception& e)
684         {
685                 PyErr_SetString(AUDError, e.what());
686         }
687
688         return -1;
689 }
690
691 PyDoc_STRVAR(M_aud_Handle_position_doc,
692                          "The playback position of the sound in seconds.");
693
694 static PyObject *
695 Handle_get_position(Handle* self, void* nothing)
696 {
697         try
698         {
699                 return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getPosition());
700         }
701         catch(Exception& e)
702         {
703                 PyErr_SetString(AUDError, e.what());
704                 return nullptr;
705         }
706 }
707
708 static int
709 Handle_set_position(Handle* self, PyObject* args, void* nothing)
710 {
711         float position;
712
713         if(!PyArg_Parse(args, "f:position", &position))
714                 return -1;
715
716         try
717         {
718                 if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->seek(position))
719                         return 0;
720                 PyErr_SetString(AUDError, "Couldn't seek the sound!");
721         }
722         catch(Exception& e)
723         {
724                 PyErr_SetString(AUDError, e.what());
725         }
726
727         return -1;
728 }
729
730 PyDoc_STRVAR(M_aud_Handle_relative_doc,
731                          "Whether the source's location, velocity and orientation is relative or absolute to the listener.");
732
733 static PyObject *
734 Handle_get_relative(Handle* self, void* nothing)
735 {
736         try
737         {
738                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
739                 if(handle)
740                 {
741                         return PyBool_FromLong((long)handle->isRelative());
742                 }
743                 else
744                 {
745                         PyErr_SetString(AUDError, device_not_3d_error);
746                 }
747         }
748         catch(Exception& e)
749         {
750                 PyErr_SetString(AUDError, e.what());
751         }
752
753         return nullptr;
754 }
755
756 static int
757 Handle_set_relative(Handle* self, PyObject* args, void* nothing)
758 {
759         if(!PyBool_Check(args))
760         {
761                 PyErr_SetString(PyExc_TypeError, "Value is not a boolean!");
762                 return -1;
763         }
764
765         bool relative = (args == Py_True);
766
767         try
768         {
769                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
770                 if(handle)
771                 {
772                         if(handle->setRelative(relative))
773                                 return 0;
774                         PyErr_SetString(AUDError, "Couldn't set the relativeness!");
775                 }
776                 else
777                         PyErr_SetString(AUDError, device_not_3d_error);
778         }
779         catch(Exception& e)
780         {
781                 PyErr_SetString(AUDError, e.what());
782         }
783
784         return -1;
785 }
786
787 PyDoc_STRVAR(M_aud_Handle_status_doc,
788                          "Whether the sound is playing, paused or stopped (=invalid).");
789
790 static PyObject *
791 Handle_get_status(Handle* self, void* nothing)
792 {
793         try
794         {
795                 return PyBool_FromLong((long)(*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getStatus());
796         }
797         catch(Exception& e)
798         {
799                 PyErr_SetString(AUDError, e.what());
800                 return nullptr;
801         }
802 }
803
804 PyDoc_STRVAR(M_aud_Handle_velocity_doc,
805                          "The source's velocity in 3D space, a 3D tuple of floats.");
806
807 static PyObject *
808 Handle_get_velocity(Handle* self, void* nothing)
809 {
810         try
811         {
812                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
813                 if(handle)
814                 {
815                         Vector3 v = handle->getVelocity();
816                         return Py_BuildValue("(fff)", v.x(), v.y(), v.z());
817                 }
818                 else
819                 {
820                         PyErr_SetString(AUDError, device_not_3d_error);
821                 }
822         }
823         catch(Exception& e)
824         {
825                 PyErr_SetString(AUDError, e.what());
826         }
827
828         return nullptr;
829 }
830
831 static int
832 Handle_set_velocity(Handle* self, PyObject* args, void* nothing)
833 {
834         float x, y, z;
835
836         if(!PyArg_Parse(args, "(fff):velocity", &x, &y, &z))
837                 return -1;
838
839         try
840         {
841                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
842                 if(handle)
843                 {
844                         Vector3 velocity(x, y, z);
845                         if(handle->setVelocity(velocity))
846                                 return 0;
847                         PyErr_SetString(AUDError, "Couldn't set the velocity!");
848                 }
849                 else
850                         PyErr_SetString(AUDError, device_not_3d_error);
851         }
852         catch(Exception& e)
853         {
854                 PyErr_SetString(AUDError, e.what());
855         }
856
857         return -1;
858 }
859
860 PyDoc_STRVAR(M_aud_Handle_volume_doc,
861                          "The volume of the sound.");
862
863 static PyObject *
864 Handle_get_volume(Handle* self, void* nothing)
865 {
866         try
867         {
868                 return Py_BuildValue("f", (*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->getVolume());
869         }
870         catch(Exception& e)
871         {
872                 PyErr_SetString(AUDError, e.what());
873                 return nullptr;
874         }
875 }
876
877 static int
878 Handle_set_volume(Handle* self, PyObject* args, void* nothing)
879 {
880         float volume;
881
882         if(!PyArg_Parse(args, "f:volume", &volume))
883                 return -1;
884
885         try
886         {
887                 if((*reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle))->setVolume(volume))
888                         return 0;
889                 PyErr_SetString(AUDError, "Couldn't set the sound volume!");
890         }
891         catch(Exception& e)
892         {
893                 PyErr_SetString(AUDError, e.what());
894         }
895
896         return -1;
897 }
898
899 PyDoc_STRVAR(M_aud_Handle_volume_maximum_doc,
900                          "The maximum volume of the source.\n\n"
901                          ".. seealso:: :attr:`Device.distance_model`");
902
903 static PyObject *
904 Handle_get_volume_maximum(Handle* self, void* nothing)
905 {
906         try
907         {
908                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
909                 if(handle)
910                 {
911                         return Py_BuildValue("f", handle->getVolumeMaximum());
912                 }
913                 else
914                 {
915                         PyErr_SetString(AUDError, device_not_3d_error);
916                         return nullptr;
917                 }
918         }
919         catch(Exception& e)
920         {
921                 PyErr_SetString(AUDError, e.what());
922                 return nullptr;
923         }
924 }
925
926 static int
927 Handle_set_volume_maximum(Handle* self, PyObject* args, void* nothing)
928 {
929         float volume;
930
931         if(!PyArg_Parse(args, "f:volume_maximum", &volume))
932                 return -1;
933
934         try
935         {
936                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
937                 if(handle)
938                 {
939                         if(handle->setVolumeMaximum(volume))
940                                 return 0;
941                         PyErr_SetString(AUDError, "Couldn't set the maximum volume!");
942                 }
943                 else
944                         PyErr_SetString(AUDError, device_not_3d_error);
945         }
946         catch(Exception& e)
947         {
948                 PyErr_SetString(AUDError, e.what());
949         }
950
951         return -1;
952 }
953
954 PyDoc_STRVAR(M_aud_Handle_volume_minimum_doc,
955                          "The minimum volume of the source.\n\n"
956                          ".. seealso:: :attr:`Device.distance_model`");
957
958 static PyObject *
959 Handle_get_volume_minimum(Handle* self, void* nothing)
960 {
961         try
962         {
963                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
964                 if(handle)
965                 {
966                         return Py_BuildValue("f", handle->getVolumeMinimum());
967                 }
968                 else
969                 {
970                         PyErr_SetString(AUDError, device_not_3d_error);
971                         return nullptr;
972                 }
973         }
974         catch(Exception& e)
975         {
976                 PyErr_SetString(AUDError, e.what());
977                 return nullptr;
978         }
979 }
980
981 static int
982 Handle_set_volume_minimum(Handle* self, PyObject* args, void* nothing)
983 {
984         float volume;
985
986         if(!PyArg_Parse(args, "f:volume_minimum", &volume))
987                 return -1;
988
989         try
990         {
991                 I3DHandle* handle = dynamic_cast<I3DHandle*>(reinterpret_cast<std::shared_ptr<IHandle>*>(self->handle)->get());
992                 if(handle)
993                 {
994                         if(handle->setVolumeMinimum(volume))
995                                 return 0;
996                         PyErr_SetString(AUDError, "Couldn't set the minimum volume!");
997                 }
998                 else
999                         PyErr_SetString(AUDError, device_not_3d_error);
1000         }
1001         catch(Exception& e)
1002         {
1003                 PyErr_SetString(AUDError, e.what());
1004         }
1005
1006         return -1;
1007 }
1008
1009 static PyGetSetDef Handle_properties[] = {
1010         {(char*)"attenuation", (getter)Handle_get_attenuation, (setter)Handle_set_attenuation,
1011          M_aud_Handle_attenuation_doc, nullptr },
1012         {(char*)"cone_angle_inner", (getter)Handle_get_cone_angle_inner, (setter)Handle_set_cone_angle_inner,
1013          M_aud_Handle_cone_angle_inner_doc, nullptr },
1014         {(char*)"cone_angle_outer", (getter)Handle_get_cone_angle_outer, (setter)Handle_set_cone_angle_outer,
1015          M_aud_Handle_cone_angle_outer_doc, nullptr },
1016         {(char*)"cone_volume_outer", (getter)Handle_get_cone_volume_outer, (setter)Handle_set_cone_volume_outer,
1017          M_aud_Handle_cone_volume_outer_doc, nullptr },
1018         {(char*)"distance_maximum", (getter)Handle_get_distance_maximum, (setter)Handle_set_distance_maximum,
1019          M_aud_Handle_distance_maximum_doc, nullptr },
1020         {(char*)"distance_reference", (getter)Handle_get_distance_reference, (setter)Handle_set_distance_reference,
1021          M_aud_Handle_distance_reference_doc, nullptr },
1022         {(char*)"keep", (getter)Handle_get_keep, (setter)Handle_set_keep,
1023          M_aud_Handle_keep_doc, nullptr },
1024         {(char*)"location", (getter)Handle_get_location, (setter)Handle_set_location,
1025          M_aud_Handle_location_doc, nullptr },
1026         {(char*)"loop_count", (getter)Handle_get_loop_count, (setter)Handle_set_loop_count,
1027          M_aud_Handle_loop_count_doc, nullptr },
1028         {(char*)"orientation", (getter)Handle_get_orientation, (setter)Handle_set_orientation,
1029          M_aud_Handle_orientation_doc, nullptr },
1030         {(char*)"pitch", (getter)Handle_get_pitch, (setter)Handle_set_pitch,
1031          M_aud_Handle_pitch_doc, nullptr },
1032         {(char*)"position", (getter)Handle_get_position, (setter)Handle_set_position,
1033          M_aud_Handle_position_doc, nullptr },
1034         {(char*)"relative", (getter)Handle_get_relative, (setter)Handle_set_relative,
1035          M_aud_Handle_relative_doc, nullptr },
1036         {(char*)"status", (getter)Handle_get_status, nullptr,
1037          M_aud_Handle_status_doc, nullptr },
1038         {(char*)"velocity", (getter)Handle_get_velocity, (setter)Handle_set_velocity,
1039          M_aud_Handle_velocity_doc, nullptr },
1040         {(char*)"volume", (getter)Handle_get_volume, (setter)Handle_set_volume,
1041          M_aud_Handle_volume_doc, nullptr },
1042         {(char*)"volume_maximum", (getter)Handle_get_volume_maximum, (setter)Handle_set_volume_maximum,
1043          M_aud_Handle_volume_maximum_doc, nullptr },
1044         {(char*)"volume_minimum", (getter)Handle_get_volume_minimum, (setter)Handle_set_volume_minimum,
1045          M_aud_Handle_volume_minimum_doc, nullptr },
1046         {nullptr}  /* Sentinel */
1047 };
1048
1049 PyDoc_STRVAR(M_aud_Handle_doc,
1050                          "Handle objects are playback handles that can be used to control "
1051                          "playback of a sound. If a sound is played back multiple times "
1052                          "then there are as many handles.");
1053
1054 static PyTypeObject HandleType = {
1055         PyVarObject_HEAD_INIT(nullptr, 0)
1056         "aud.Handle",              /* tp_name */
1057         sizeof(Handle),            /* tp_basicsize */
1058         0,                         /* tp_itemsize */
1059         (destructor)Handle_dealloc,/* tp_dealloc */
1060         0,                         /* tp_print */
1061         0,                         /* tp_getattr */
1062         0,                         /* tp_setattr */
1063         0,                         /* tp_reserved */
1064         0,                         /* tp_repr */
1065         0,                         /* tp_as_number */
1066         0,                         /* tp_as_sequence */
1067         0,                         /* tp_as_mapping */
1068         0,                         /* tp_hash  */
1069         0,                         /* tp_call */
1070         0,                         /* tp_str */
1071         0,                         /* tp_getattro */
1072         0,                         /* tp_setattro */
1073         0,                         /* tp_as_buffer */
1074         Py_TPFLAGS_DEFAULT,        /* tp_flags */
1075         M_aud_Handle_doc,          /* tp_doc */
1076         0,                                 /* tp_traverse */
1077         0,                                 /* tp_clear */
1078         0,                                 /* tp_richcompare */
1079         0,                                 /* tp_weaklistoffset */
1080         0,                                 /* tp_iter */
1081         0,                                 /* tp_iternext */
1082         Handle_methods,            /* tp_methods */
1083         0,                         /* tp_members */
1084         Handle_properties,         /* tp_getset */
1085         0,                         /* tp_base */
1086         0,                         /* tp_dict */
1087         0,                         /* tp_descr_get */
1088         0,                         /* tp_descr_set */
1089         0,                         /* tp_dictoffset */
1090         0,                         /* tp_init */
1091         0,                         /* tp_alloc */
1092         0,                         /* tp_new */
1093 };
1094
1095
1096 AUD_API PyObject* Handle_empty()
1097 {
1098         return HandleType.tp_alloc(&HandleType, 0);
1099 }
1100
1101
1102 AUD_API Handle*checkHandle(PyObject* handle)
1103 {
1104         if(!PyObject_TypeCheck(handle, &HandleType))
1105         {
1106                 PyErr_SetString(PyExc_TypeError, "Object is not of type Handle!");
1107                 return nullptr;
1108         }
1109
1110         return (Handle*)handle;
1111 }
1112
1113
1114 bool initializeHandle()
1115 {
1116         return PyType_Ready(&HandleType) >= 0;
1117 }
1118
1119
1120 void addHandleToModule(PyObject* module)
1121 {
1122         Py_INCREF(&HandleType);
1123         PyModule_AddObject(module, "Handle", (PyObject *)&HandleType);
1124 }
1125
1126