comparison PyPlugin.cpp @ 8:3af6b5990ad8

more examples and some bug fixes
author fazekasgy
date Fri, 13 Jun 2008 16:50:00 +0000
parents a4c955e9a70b
children e9cf443b18f5
comparison
equal deleted inserted replaced
7:a4c955e9a70b 8:3af6b5990ad8
33 */ 33 */
34 34
35 35
36 36
37 /** 37 /**
38 * This Vamp plugin is a wrapper for Python Scripts. (vampy) 38 * This Vamp plugin is a wrapper for Python Scripts. (VamPy)
39 * Centre for Digital Music, Queen Mary, University of London. 39 * Centre for Digital Music, Queen Mary, University of London.
40 * Copyright 2008, George Fazekas. 40 * Copyright 2008, George Fazekas.
41 41
42 TODO: needs more complete error checking 42 TODO: needs more complete error checking
43 needs correct implementation of Python threading 43 needs correct implementation of Python threading
256 256
257 257
258 bool 258 bool
259 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) 259 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
260 { 260 {
261 //useful for debugging Python plugins
262 char method[]="initialise";
263 cerr << "[call] " << method << endl;
261 264
262 //placing Mutex before these calls causes deadlock 265 //placing Mutex before these calls causes deadlock
263 if (channels < getMinChannelCount() || 266 if (channels < getMinChannelCount() ||
264 channels > getMaxChannelCount()) return false; 267 channels > getMaxChannelCount()) return false;
265 268
266 m_inputDomain = getInputDomain(); 269 m_inputDomain = getInputDomain();
267 270
268 MutexLocker locker(&m_pythonInterpreterMutex); 271 MutexLocker locker(&m_pythonInterpreterMutex);
269 272
270 //useful for debugging Python plugins
271 char method[]="initialise";
272 cerr << "[call] " << method << endl;
273
274 initMaps(); 273 initMaps();
275 274
276 m_stepSize = stepSize; 275 m_stepSize = stepSize;
277 m_blockSize = blockSize; 276 m_blockSize = blockSize;
278 m_channels = channels; 277 m_channels = channels;
279 278
280 //quering process implementation type 279 //quering process implementation type
281 char legacyMethod[]="process"; 280 char legacyMethod[]="process";
282 char numpyMethod[]="processN"; 281 char numpyMethod[]="processN";
283 m_processType = 0; 282
284 283 if (PyObject_HasAttrString(m_pyInstance,legacyMethod) &
285 if (PyObject_HasAttrString(m_pyInstance,legacyMethod)) 284 m_processType == 0)
286 { 285 {
287 m_processType = legacyProcess; 286 m_processType = legacyProcess;
288 m_pyProcess = PyString_FromString(legacyMethod); 287 m_pyProcess = PyString_FromString(legacyMethod);
289 } 288 }
290 289
291 if (PyObject_HasAttrString(m_pyInstance,numpyMethod)) 290 if (PyObject_HasAttrString(m_pyInstance,numpyMethod) &
291 m_processType == 0)
292 { 292 {
293 m_processType = numpyProcess; 293 m_processType = numpyProcess;
294 m_pyProcess = PyString_FromString(numpyMethod); 294 m_pyProcess = PyString_FromString(numpyMethod);
295 } 295 }
296 296
313 PyObject *pyInputSampleRate = PyFloat_FromDouble((double)m_inputSampleRate); 313 PyObject *pyInputSampleRate = PyFloat_FromDouble((double)m_inputSampleRate);
314 314
315 //Call the method 315 //Call the method
316 PyObject *pyBool = 316 PyObject *pyBool =
317 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,pyInputSampleRate,NULL); 317 PyObject_CallMethodObjArgs(m_pyInstance,pyMethod,pyChannels,pyStepSize,pyBlockSize,pyInputSampleRate,NULL);
318 318
319 Py_DECREF(pyMethod); 319 Py_DECREF(pyMethod);
320 Py_DECREF(pyChannels); 320 Py_DECREF(pyChannels);
321 Py_DECREF(pyStepSize); 321 Py_DECREF(pyStepSize);
322 Py_DECREF(pyBlockSize); 322 Py_DECREF(pyBlockSize);
323 Py_DECREF(pyInputSampleRate); 323 Py_DECREF(pyInputSampleRate);
324 324
325 //Check return value 325 //Check return value
326 if (!PyBool_Check(pyBool)) { 326 if (PyErr_Occurred() || !PyBool_Check(pyBool)) {
327 PyErr_Print(); PyErr_Clear();
327 Py_CLEAR(pyBool); 328 Py_CLEAR(pyBool);
328 cerr << "ERROR: In Python plugin [" << m_class << "::" << method 329 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
329 << "] Expected Bool return value." << endl; 330 << "] Expected Bool return value." << endl;
330 return false; 331 return false;
331 } 332 }
413 { 414 {
414 MutexLocker locker(&m_pythonInterpreterMutex); 415 MutexLocker locker(&m_pythonInterpreterMutex);
415 416
416 char method[]="getPreferredStepSize"; 417 char method[]="getPreferredStepSize";
417 cerr << "[call] " << method << endl; 418 cerr << "[call] " << method << endl;
418 size_t rValue=0; //not set by default 419 size_t rValue=1024; //not set by default
419 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 420 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
420 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); 421 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
421 422
422 //Check return value 423 //Check return value
423 if (!PyInt_Check(pyInt)) { 424 if (!PyInt_Check(pyInt)) {
513 PyObject *pyDict, *pyKey, *pyValue; 514 PyObject *pyDict, *pyKey, *pyValue;
514 515
515 //Parse Output List 516 //Parse Output List
516 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { 517 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
517 518
518 //Get i-th Vamp output descriptor (Borrowed Reference) 519 //Get i-th VAMP output descriptor (Borrowed Reference)
519 pyDict = PyList_GET_ITEM(pyList,i); 520 pyDict = PyList_GET_ITEM(pyList,i);
520 521
521 //We only care about dictionaries holding output descriptors 522 //We only care about dictionaries holding output descriptors
522 if ( !PyDict_Check(pyDict) ) continue; 523 if ( !PyDict_Check(pyDict) ) continue;
523 524
528 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) 529 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
529 { 530 {
530 switch (outKeys[PyString_AsString(pyKey)]) 531 switch (outKeys[PyString_AsString(pyKey)])
531 { 532 {
532 case not_found : 533 case not_found :
533 cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; 534 cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
534 break; 535 break;
535 case identifier: 536 case identifier:
536 od.identifier = PyString_AsString(pyValue); 537 od.identifier = PyString_AsString(pyValue);
537 break; 538 break;
538 case name: 539 case name:
571 case sampleType: 572 case sampleType:
572 od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)]; 573 od.sampleType = (OutputDescriptor::SampleType) sampleKeys[PyString_AsString(pyValue)];
573 break; 574 break;
574 case sampleRate: 575 case sampleRate:
575 od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue); 576 od.sampleRate = (float) PyFloat_AS_DOUBLE(pyValue);
577 // od.sampleRate = m_inputSampleRate / m_stepSize;
578 cerr << od.sampleRate << endl;
576 break; 579 break;
577 default : 580 default :
578 cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; 581 cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
579 } 582 }
580 } // while dict 583 } // while dict
581 list.push_back(od); 584 list.push_back(od);
582 } // for list 585 } // for list
583 Py_CLEAR(pyList); 586 Py_CLEAR(pyList);
614 PyObject *pyDict, *pyKey, *pyValue; 617 PyObject *pyDict, *pyKey, *pyValue;
615 618
616 //Parse Output List 619 //Parse Output List
617 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) { 620 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyList); ++i) {
618 621
619 //Get i-th Vamp output descriptor (Borrowed Reference) 622 //Get i-th VAMP output descriptor (Borrowed Reference)
620 pyDict = PyList_GET_ITEM(pyList,i); 623 pyDict = PyList_GET_ITEM(pyList,i);
621 624
622 //We only care about dictionaries holding output descriptors 625 //We only care about dictionaries holding output descriptors
623 if ( !PyDict_Check(pyDict) ) continue; 626 if ( !PyDict_Check(pyDict) ) continue;
624 627
629 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue)) 632 while (PyDict_Next(pyDict, &pyPos, &pyKey, &pyValue))
630 { 633 {
631 switch (parmKeys[PyString_AsString(pyKey)]) 634 switch (parmKeys[PyString_AsString(pyKey)])
632 { 635 {
633 case not_found : 636 case not_found :
634 cerr << "Unknown key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; 637 cerr << "Unknown key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
635 break; 638 break;
636 case p::identifier: 639 case p::identifier:
637 pd.identifier = PyString_AsString(pyValue); 640 pd.identifier = PyString_AsString(pyValue);
638 break; 641 break;
639 case p::name: 642 case p::name:
656 break; 659 break;
657 case p::isQuantized: 660 case p::isQuantized:
658 pd.isQuantized = (bool) PyInt_AS_LONG(pyValue); 661 pd.isQuantized = (bool) PyInt_AS_LONG(pyValue);
659 break; 662 break;
660 default : 663 default :
661 cerr << "Invalid key in Vamp OutputDescriptor: " << PyString_AsString(pyKey) << endl; 664 cerr << "Invalid key in VAMP OutputDescriptor: " << PyString_AsString(pyKey) << endl;
662 } 665 }
663 } // while dict 666 } // while dict
664 list.push_back(pd); 667 list.push_back(pd);
665 } // for list 668 } // for list
666 Py_CLEAR(pyList); 669 Py_CLEAR(pyList);
748 #ifdef _DEBUG 751 #ifdef _DEBUG
749 cerr << "[call] process, frame:" << proccounter << endl; 752 cerr << "[call] process, frame:" << proccounter << endl;
750 proccounter++; 753 proccounter++;
751 #endif 754 #endif
752 755
753 if (m_blockSize == 0) { 756 if (m_blockSize == 0 || m_channels == 0) {
754 cerr << "ERROR: PyPlugin::process: " 757 cerr << "ERROR: PyPlugin::process: "
755 << "Plugin has not been initialised" << endl; 758 << "Plugin has not been initialised" << endl;
756 return FeatureSet(); 759 return FeatureSet();
757 } 760 }
758 761
762 return FeatureSet(); 765 return FeatureSet();
763 } 766 }
764 767
765 string method=PyString_AsString(m_pyProcess); 768 string method=PyString_AsString(m_pyProcess);
766 769
767
768
769 PyObject *pyOutputList = NULL; 770 PyObject *pyOutputList = NULL;
770 771
771 /*new numPy support*/ 772 /*new numPy support*/
772 if (m_processType == numpyProcess) { 773 if (m_processType == numpyProcess) {
773 774
774 //declare buffer object 775 //create a list of buffers
775 PyObject *pyBuffer; 776 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
776 777 for (size_t i=0; i < m_channels; ++i) {
777 //Expose memory using the Buffer Interface of C/API 778
778 //This will virtually pass a pointer only that can be 779 //Expose memory using the Buffer Interface of C/API
779 //recasted in Python code 780 //This will virtually pass a pointer which can be
780 pyBuffer = 781 //recasted in Python code as float or complex array
781 PyBuffer_FromMemory((void *) (float *) inputBuffers[0], 782 PyObject *pyBuffer = PyBuffer_FromMemory
782 (Py_ssize_t) sizeof(float) * m_blockSize); 783 ((void *) (float *) inputBuffers[i],
783 784 (Py_ssize_t) sizeof(float) * m_blockSize);
784 //Call python process (returns new reference) 785
785 pyOutputList = 786 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyBuffer);
786 PyObject_CallMethodObjArgs(m_pyInstance,m_pyProcess,pyBuffer,NULL); 787 }
788
789 //pass RealTime as frameCount
790 PyObject *pyLongSample = PyLong_FromLong (
791 Vamp::RealTime::realTime2Frame
792 (timestamp, (unsigned int) m_inputSampleRate));
793
794 //Call python process (returns new reference)
795 pyOutputList = PyObject_CallMethodObjArgs
796 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
797
798 Py_DECREF(pyChannelList);
799 Py_DECREF(pyLongSample);
787 800
788 } 801 }
789 802
790 if (m_processType == legacyProcess) { 803 if (m_processType == legacyProcess) {
791 804
792 //Declare new list object 805 //create a list of lists
793 PyObject *pyFloat, *pyList; 806 PyObject *pyChannelList = PyList_New((Py_ssize_t) m_channels);
794 pyList = PyList_New((Py_ssize_t) m_blockSize); 807 for (size_t i=0; i < m_channels; ++i) {
795 808
796 //Pack samples into a Python List Object 809 //Declare new list object
797 //pyFloat types will always be new references, 810 PyObject *pyFloat, *pyList;
798 //these will be discarded when the list is deallocated 811 pyList = PyList_New((Py_ssize_t) m_blockSize);
799 for (size_t i = 0; i < m_blockSize; ++i) { 812
800 pyFloat=PyFloat_FromDouble((double) inputBuffers[0][i]); 813 //Pack samples into a Python List Object
801 PyList_SET_ITEM(pyList, (Py_ssize_t) i, pyFloat); 814 //pyFloat types will always be new references,
802 } 815 //these will be discarded when the list is deallocated
803 816 for (size_t j = 0; j < m_blockSize; ++j) {
804 //Call python process (returns new reference) 817 pyFloat=PyFloat_FromDouble(
805 pyOutputList = 818 (double) inputBuffers[i][j]);
806 PyObject_CallMethodObjArgs(m_pyInstance,m_pyProcess,pyList,NULL); 819 PyList_SET_ITEM(pyList, (Py_ssize_t) j, pyFloat);
807 820 }
808 } 821 PyList_SET_ITEM(pyChannelList, (Py_ssize_t) i, pyList);
809 822 }
823
824 //pass RealTime as frameCount
825 PyObject *pyLongSample = PyLong_FromLong (
826 Vamp::RealTime::realTime2Frame
827 (timestamp, (unsigned int) m_inputSampleRate));
828
829 //Call python process (returns new reference)
830 pyOutputList = PyObject_CallMethodObjArgs
831 (m_pyInstance,m_pyProcess,pyChannelList,pyLongSample,NULL);
832
833 Py_DECREF(pyChannelList);
834 Py_DECREF(pyLongSample);
835
836 }
837
838 //return nothing
839 //Py_CLEAR(pyOutputList);
840 //return FeatureSet();
810 841
811 //Check return type 842 //Check return type
812 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) { 843 if (pyOutputList == NULL || !PyList_Check(pyOutputList) ) {
813 if (pyOutputList == NULL) { 844 if (pyOutputList == NULL) {
814 cerr << "ERROR: In Python plugin [" << m_class << "::" << method 845 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
816 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); } 847 if (PyErr_Occurred()) { PyErr_Print(); PyErr_Clear(); }
817 } else { 848 } else {
818 cerr << "ERROR: In Python plugin [" << m_class << "::" << method 849 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
819 << "] Expected List return type." << endl; 850 << "] Expected List return type." << endl;
820 } 851 }
821 //Py_CLEAR(pyMethod);
822 Py_CLEAR(pyOutputList); 852 Py_CLEAR(pyOutputList);
823 return FeatureSet(); 853 return FeatureSet();
824 } 854 }
825 855
826 //Py_DECREF(pyMethod);
827 // Py_DECREF(pyList); 856 // Py_DECREF(pyList);
828 // This appears to be tracked by the cyclic garbage collector 857 // This appears to be tracked by the cyclic garbage collector
829 // hence decrefing produces GC error 858 // hence decrefing produces GC error
830 #ifdef _DEBUG 859 #ifdef _DEBUG
831 cerr << "Process Returned Features" << endl; 860 cerr << "Process Returned Features" << endl;
862 { 891 {
863 emptyFeature = false; 892 emptyFeature = false;
864 switch (ffKeys[PyString_AsString(pyKey)]) 893 switch (ffKeys[PyString_AsString(pyKey)])
865 { 894 {
866 case not_found : 895 case not_found :
867 cerr << "Unknown key in Vamp FeatureSet: " 896 cerr << "Unknown key in VAMP FeatureSet: "
868 << PyString_AsString(pyKey) << endl; 897 << PyString_AsString(pyKey) << endl;
869 break; 898 break;
870 case hasTimestamp: 899 case hasTimestamp:
871 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); 900 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
872 break; 901 break;
873 case timeStamp: 902 case timeStamp:
874 feature.timestamp = timestamp + 903 feature.timestamp =
875 Vamp::RealTime::frame2RealTime 904 Vamp::RealTime::frame2RealTime(
876 ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); 905 PyLong_AsLong(pyValue),
906 (unsigned int) m_inputSampleRate );
907 #ifdef _DEBUG
908 cerr << "Timestamp: "
909 << (long)PyLong_AsLong(pyValue) << ", ->"
910 << feature.timestamp.toString() << endl;
911 #endif
877 break; 912 break;
878 case values: 913 case values:
879 feature.values = PyList_As_FloatVector(pyValue); 914 feature.values = PyList_As_FloatVector(pyValue);
880 break; 915 break;
881 case label: 916 case label:
882 feature.label = PyString_AsString(pyValue); 917 feature.label = PyString_AsString(pyValue);
883 break; 918 break;
884 default : 919 default :
885 cerr << "Invalid key in Vamp FeatureSet: " 920 cerr << "Invalid key in VAMP FeatureSet: "
886 << PyString_AsString(pyKey) << endl; 921 << PyString_AsString(pyKey) << endl;
887 } // switch 922 } // switch
888 923
889 } // while 924 } // while
890 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; 925 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
933 } 968 }
934 Py_DECREF(pyMethod); 969 Py_DECREF(pyMethod);
935 970
936 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; 971 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
937 FeatureSet returnFeatures; 972 FeatureSet returnFeatures;
938 973
974 //iterate through list of outputs
939 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) { 975 for (Py_ssize_t i = 0; i < PyList_GET_SIZE(pyOutputList); ++i) {
940 976
941 pyFeatureList = PyList_GET_ITEM(pyOutputList,i); 977 pyFeatureList = PyList_GET_ITEM(pyOutputList,i);
942 978
979 //iterate list of Features
943 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) { 980 for (Py_ssize_t j = 0; j < PyList_GET_SIZE(pyFeatureList); ++j) {
944 981 #ifdef _DEBUG
982 cerr << "feature: " << j << endl;
983 #endif
945 pyDict = PyList_GET_ITEM(pyFeatureList,j); 984 pyDict = PyList_GET_ITEM(pyFeatureList,j);
946 985
947 if ( !PyDict_Check(pyDict) ) continue; 986 if ( !PyDict_Check(pyDict) ) continue;
948 987
949 Py_ssize_t pyPos = NULL; 988 Py_ssize_t pyPos = NULL;
954 { 993 {
955 emptyFeature = false; 994 emptyFeature = false;
956 switch (ffKeys[PyString_AsString(pyKey)]) 995 switch (ffKeys[PyString_AsString(pyKey)])
957 { 996 {
958 case not_found : 997 case not_found :
959 cerr << "Unknown key in Vamp FeatureSet: " 998 cerr << "Unknown key in VAMP FeatureSet: "
960 << PyString_AsString(pyKey) << endl; 999 << PyString_AsString(pyKey) << endl;
961 break; 1000 break;
962 case hasTimestamp: 1001 case hasTimestamp:
963 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue); 1002 feature.hasTimestamp = (bool) PyInt_AS_LONG(pyValue);
964 break; 1003 break;
965 // TODO: clarify what to do here
966 case timeStamp: 1004 case timeStamp:
967 feature.timestamp = 1005 feature.timestamp =
968 Vamp::RealTime::frame2RealTime 1006 Vamp::RealTime::frame2RealTime(
969 ((size_t)PyInt_AS_LONG(pyValue), (size_t)m_inputSampleRate); 1007 PyLong_AsLong(pyValue),
1008 (unsigned int) m_inputSampleRate );
1009 #ifdef _DEBUG
1010 cerr << "Timestamp: "
1011 << (long)PyLong_AsLong(pyValue) << ", ->"
1012 << feature.timestamp.toString() << endl;
1013 #endif
970 break; 1014 break;
971 case values: 1015 case values:
972 feature.values = PyList_As_FloatVector(pyValue); 1016 feature.values = PyList_As_FloatVector(pyValue);
973 break; 1017 break;
974 case label: 1018 case label:
1060 1104
1061 for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) { 1105 for (Py_ssize_t k = 0; k < PyList_GET_SIZE(inputList); ++k) {
1062 //Get next list item (Borrowed Reference) 1106 //Get next list item (Borrowed Reference)
1063 pyFloat = PyList_GET_ITEM(inputList,k); 1107 pyFloat = PyList_GET_ITEM(inputList,k);
1064 ListElement = (float) PyFloat_AS_DOUBLE(pyFloat); 1108 ListElement = (float) PyFloat_AS_DOUBLE(pyFloat);
1109 #ifdef _DEBUG
1110 cerr << "value: " << ListElement << endl;
1111 #endif
1065 Output.push_back(ListElement); 1112 Output.push_back(ListElement);
1066 } 1113 }
1067 1114
1068 return Output; 1115 return Output;
1069 } 1116 }