comparison runner/FeatureExtractionManager.cpp @ 288:fd4a6183482f kapoor_1202

Merge from branch piper-nopiper. The next release will be based on this.
author Chris Cannam
date Fri, 02 Dec 2016 10:04:44 +0000
parents 5ba121f3889e
children 0e866ef12d87
comparison
equal deleted inserted replaced
259:9e7d180be225 288:fd4a6183482f
21 #include <vamp-hostsdk/PluginInputDomainAdapter.h> 21 #include <vamp-hostsdk/PluginInputDomainAdapter.h>
22 #include <vamp-hostsdk/PluginSummarisingAdapter.h> 22 #include <vamp-hostsdk/PluginSummarisingAdapter.h>
23 #include <vamp-hostsdk/PluginWrapper.h> 23 #include <vamp-hostsdk/PluginWrapper.h>
24 #include <vamp-hostsdk/PluginLoader.h> 24 #include <vamp-hostsdk/PluginLoader.h>
25 25
26 #include "base/Debug.h"
26 #include "base/Exceptions.h" 27 #include "base/Exceptions.h"
27 28
28 #include <iostream> 29 #include <iostream>
29 30
30 using namespace std; 31 using namespace std;
49 50
50 #include <QTextStream> 51 #include <QTextStream>
51 #include <QFile> 52 #include <QFile>
52 #include <QFileInfo> 53 #include <QFileInfo>
53 54
54 FeatureExtractionManager::FeatureExtractionManager() : 55 FeatureExtractionManager::FeatureExtractionManager(bool verbose) :
56 m_verbose(verbose),
55 m_summariesOnly(false), 57 m_summariesOnly(false),
56 // We can read using an arbitrary fixed block size -- 58 // We can read using an arbitrary fixed block size --
57 // PluginBufferingAdapter handles this for us. But while this 59 // PluginBufferingAdapter handles this for us. But while this
58 // doesn't affect the step and block size actually passed to the 60 // doesn't affect the step and block size actually passed to the
59 // plugin, it does affect the overall time range of the audio 61 // plugin, it does affect the overall time range of the audio
71 { 73 {
72 } 74 }
73 75
74 FeatureExtractionManager::~FeatureExtractionManager() 76 FeatureExtractionManager::~FeatureExtractionManager()
75 { 77 {
78 SVDEBUG << "FeatureExtractionManager::~FeatureExtractionManager: cleaning up"
79 << endl;
80
76 for (PluginMap::iterator pi = m_plugins.begin(); 81 for (PluginMap::iterator pi = m_plugins.begin();
77 pi != m_plugins.end(); ++pi) { 82 pi != m_plugins.end(); ++pi) {
78 delete pi->first; 83 delete pi->first;
79 } 84 }
80 foreach (AudioFileReader *r, m_readyReaders) { 85 foreach (AudioFileReader *r, m_readyReaders) {
81 delete r; 86 delete r;
82 } 87 }
88
89 SVDEBUG << "FeatureExtractionManager::~FeatureExtractionManager: done" << endl;
83 } 90 }
84 91
85 void FeatureExtractionManager::setChannels(int channels) 92 void FeatureExtractionManager::setChannels(int channels)
86 { 93 {
87 m_channels = channels; 94 m_channels = channels;
117 const PluginSummarisingAdapter::SegmentBoundaries &boundaries) 124 const PluginSummarisingAdapter::SegmentBoundaries &boundaries)
118 { 125 {
119 for (SummaryNameSet::const_iterator i = names.begin(); 126 for (SummaryNameSet::const_iterator i = names.begin();
120 i != names.end(); ++i) { 127 i != names.end(); ++i) {
121 if (getSummaryType(*i) == PluginSummarisingAdapter::UnknownSummaryType) { 128 if (getSummaryType(*i) == PluginSummarisingAdapter::UnknownSummaryType) {
122 cerr << "ERROR: Unknown summary type \"" << *i << "\"" << endl; 129 SVCERR << "ERROR: Unknown summary type \"" << *i << "\"" << endl;
123 return false; 130 return false;
124 } 131 }
125 } 132 }
126 m_summaries = names; 133 m_summaries = names;
127 m_boundaries = boundaries; 134 m_boundaries = boundaries;
154 return PluginInputDomainAdapter::BlackmanHarrisWindow; 161 return PluginInputDomainAdapter::BlackmanHarrisWindow;
155 case GaussianWindow: 162 case GaussianWindow:
156 case ParzenWindow: 163 case ParzenWindow:
157 // Not supported in Vamp SDK, fall through 164 // Not supported in Vamp SDK, fall through
158 default: 165 default:
159 cerr << "ERROR: Unknown or unsupported window type \"" << t << "\", using Hann (\"" << HanningWindow << "\")" << endl; 166 SVCERR << "ERROR: Unknown or unsupported window type \"" << t << "\", using Hann (\"" << HanningWindow << "\")" << endl;
160 return PluginInputDomainAdapter::HanningWindow; 167 return PluginInputDomainAdapter::HanningWindow;
161 } 168 }
162 } 169 }
163 170
164 bool FeatureExtractionManager::addFeatureExtractor 171 bool FeatureExtractionManager::addFeatureExtractor
166 { 173 {
167 //!!! exceptions rather than return values? 174 //!!! exceptions rather than return values?
168 175
169 if (transform.getSampleRate() == 0) { 176 if (transform.getSampleRate() == 0) {
170 if (m_sampleRate == 0) { 177 if (m_sampleRate == 0) {
171 cerr << "NOTE: Transform does not specify a sample rate, using default rate of " << m_defaultSampleRate << endl; 178 SVCERR << "NOTE: Transform does not specify a sample rate, using default rate of " << m_defaultSampleRate << endl;
172 transform.setSampleRate(m_defaultSampleRate); 179 transform.setSampleRate(m_defaultSampleRate);
173 m_sampleRate = m_defaultSampleRate; 180 m_sampleRate = m_defaultSampleRate;
174 } else { 181 } else {
175 cerr << "NOTE: Transform does not specify a sample rate, using previous transform's rate of " << m_sampleRate << endl; 182 SVCERR << "NOTE: Transform does not specify a sample rate, using previous transform's rate of " << m_sampleRate << endl;
176 transform.setSampleRate(m_sampleRate); 183 transform.setSampleRate(m_sampleRate);
177 } 184 }
178 } 185 }
179 186
180 if (m_sampleRate == 0) { 187 if (m_sampleRate == 0) {
181 m_sampleRate = transform.getSampleRate(); 188 m_sampleRate = transform.getSampleRate();
182 } 189 }
183 190
184 if (transform.getSampleRate() != m_sampleRate) { 191 if (transform.getSampleRate() != m_sampleRate) {
185 cerr << "WARNING: Transform sample rate " << transform.getSampleRate() << " does not match previously specified transform rate of " << m_sampleRate << " -- only a single rate is supported for each run" << endl; 192 SVCERR << "WARNING: Transform sample rate " << transform.getSampleRate() << " does not match previously specified transform rate of " << m_sampleRate << " -- only a single rate is supported for each run" << endl;
186 cerr << "WARNING: Using previous rate of " << m_sampleRate << " for this transform as well" << endl; 193 SVCERR << "WARNING: Using previous rate of " << m_sampleRate << " for this transform as well" << endl;
187 transform.setSampleRate(m_sampleRate); 194 transform.setSampleRate(m_sampleRate);
188 } 195 }
189 196
190 Plugin *plugin = 0; 197 Plugin *plugin = 0;
191 198
206 i != m_transformPluginMap.end(); ++i) { 213 i != m_transformPluginMap.end(); ++i) {
207 Transform test = i->first; 214 Transform test = i->first;
208 test.setOutput(transform.getOutput()); 215 test.setOutput(transform.getOutput());
209 test.setSummaryType(transform.getSummaryType()); 216 test.setSummaryType(transform.getSummaryType());
210 if (transform == test) { 217 if (transform == test) {
211 cerr << "NOTE: Already have transform identical to this one (for \"" 218 SVCERR << "NOTE: Already have transform identical to this one (for \""
212 << transform.getIdentifier().toStdString() 219 << transform.getIdentifier().toStdString()
213 << "\") in every detail except output identifier and/or " 220 << "\") in every detail except output identifier and/or "
214 << "summary type; sharing its plugin instance" << endl; 221 << "summary type; sharing its plugin instance" << endl;
215 plugin = i->second; 222 plugin = i->second;
216 if (transform.getSummaryType() != Transform::NoSummary && 223 if (transform.getSummaryType() != Transform::NoSummary &&
229 PluginBase *pb = tf->instantiatePluginFor(transform); 236 PluginBase *pb = tf->instantiatePluginFor(transform);
230 plugin = tf->downcastVampPlugin(pb); 237 plugin = tf->downcastVampPlugin(pb);
231 if (!plugin) { 238 if (!plugin) {
232 //!!! todo: handle non-Vamp plugins too, or make the main --list 239 //!!! todo: handle non-Vamp plugins too, or make the main --list
233 // option print out only Vamp transforms 240 // option print out only Vamp transforms
234 cerr << "ERROR: Failed to load plugin for transform \"" 241 SVCERR << "ERROR: Failed to load plugin for transform \""
235 << transform.getIdentifier().toStdString() << "\"" << endl; 242 << transform.getIdentifier().toStdString() << "\"" << endl;
236 if (pb) { 243 if (pb) {
237 cerr << "NOTE: (A plugin was loaded, but apparently not a Vamp plugin)" << endl; 244 SVCERR << "NOTE: (A plugin was loaded, but apparently not a Vamp plugin)" << endl;
238 } 245 }
239 delete pb; 246 delete pb;
240 return false; 247 return false;
241 } 248 }
242 249
292 adapter->setSummarySegmentBoundaries(m_boundaries); 299 adapter->setSummarySegmentBoundaries(m_boundaries);
293 plugin = adapter; 300 plugin = adapter;
294 } 301 }
295 302
296 if (!plugin->initialise(m_channels, m_blockSize, m_blockSize)) { 303 if (!plugin->initialise(m_channels, m_blockSize, m_blockSize)) {
297 cerr << "ERROR: Plugin initialise (channels = " << m_channels << ", stepSize = " << m_blockSize << ", blockSize = " << m_blockSize << ") failed." << endl; 304 SVCERR << "ERROR: Plugin initialise (channels = " << m_channels << ", stepSize = " << m_blockSize << ", blockSize = " << m_blockSize << ") failed." << endl;
298 delete plugin; 305 delete plugin;
299 return false; 306 return false;
300 } 307 }
301 308
302 // cerr << "Initialised plugin" << endl; 309 SVDEBUG << "Initialised plugin" << endl;
303 310
304 size_t actualStepSize = 0; 311 size_t actualStepSize = 0;
305 size_t actualBlockSize = 0; 312 size_t actualBlockSize = 0;
306 pba->getActualStepAndBlockSizes(actualStepSize, actualBlockSize); 313 pba->getActualStepAndBlockSizes(actualStepSize, actualBlockSize);
307 transform.setStepSize(actualStepSize); 314 transform.setStepSize(actualStepSize);
308 transform.setBlockSize(actualBlockSize); 315 transform.setBlockSize(actualBlockSize);
309 316
310 Plugin::OutputList outputs = plugin->getOutputDescriptors(); 317 Plugin::OutputList outputs = plugin->getOutputDescriptors();
311 for (int i = 0; i < (int)outputs.size(); ++i) { 318 for (int i = 0; i < (int)outputs.size(); ++i) {
312 319
313 // cerr << "Newly initialised plugin output " << i << " has bin count " << outputs[i].binCount << endl; 320 SVDEBUG << "Newly initialised plugin output " << i << " has bin count " << outputs[i].binCount << endl;
314 321
315 m_pluginOutputs[plugin][outputs[i].identifier] = outputs[i]; 322 m_pluginOutputs[plugin][outputs[i].identifier] = outputs[i];
316 m_pluginOutputIndices[outputs[i].identifier] = i; 323 m_pluginOutputIndices[outputs[i].identifier] = i;
317 } 324 }
318 325
319 cerr << "NOTE: Loaded and initialised plugin for transform \"" 326 SVCERR << "NOTE: Loaded and initialised plugin for transform \""
320 << transform.getIdentifier().toStdString() 327 << transform.getIdentifier().toStdString()
321 << "\" with plugin step size " << actualStepSize 328 << "\" with plugin step size " << actualStepSize
322 << " and block size " << actualBlockSize 329 << " and block size " << actualBlockSize
323 << " (adapter step and block size " << m_blockSize << ")" 330 << " (adapter step and block size " << m_blockSize << ")"
324 << endl; 331 << endl;
325 332
326 // cerr << "NOTE: That transform is: " << transform.toXmlString() << endl; 333 SVDEBUG << "NOTE: That transform is: " << transform.toXmlString() << endl;
327 334
328 if (pida) { 335 if (pida) {
329 cerr << "NOTE: PluginInputDomainAdapter timestamp adjustment is " 336 SVCERR << "NOTE: PluginInputDomainAdapter timestamp adjustment is "
330
331 << pida->getTimestampAdjustment() << endl; 337 << pida->getTimestampAdjustment() << endl;
332 } 338 }
333 339
334 } else { 340 } else {
335 341
356 } 362 }
357 363
358 if (transform.getPluginVersion() != "") { 364 if (transform.getPluginVersion() != "") {
359 if (QString("%1").arg(plugin->getPluginVersion()) 365 if (QString("%1").arg(plugin->getPluginVersion())
360 != transform.getPluginVersion()) { 366 != transform.getPluginVersion()) {
361 cerr << "ERROR: Transform specifies version " 367 SVCERR << "ERROR: Transform specifies version "
362 << transform.getPluginVersion() 368 << transform.getPluginVersion()
363 << " of plugin \"" << plugin->getIdentifier() 369 << " of plugin \"" << plugin->getIdentifier()
364 << "\", but installed plugin is version " 370 << "\", but installed plugin is version "
365 << plugin->getPluginVersion() 371 << plugin->getPluginVersion()
366 << endl; 372 << endl;
373 (plugin->getOutputDescriptors()[0].identifier.c_str()); 379 (plugin->getOutputDescriptors()[0].identifier.c_str());
374 } else { 380 } else {
375 if (m_pluginOutputs[plugin].find 381 if (m_pluginOutputs[plugin].find
376 (transform.getOutput().toLocal8Bit().data()) == 382 (transform.getOutput().toLocal8Bit().data()) ==
377 m_pluginOutputs[plugin].end()) { 383 m_pluginOutputs[plugin].end()) {
378 cerr << "ERROR: Transform requests nonexistent plugin output \"" 384 SVCERR << "ERROR: Transform requests nonexistent plugin output \""
379 << transform.getOutput() 385 << transform.getOutput()
380 << "\"" << endl; 386 << "\"" << endl;
381 return false; 387 return false;
382 } 388 }
383 } 389 }
384 390
385 m_transformPluginMap[transform] = plugin; 391 m_transformPluginMap[transform] = plugin;
386 392
387 // cerr << "NOTE: Assigned plugin " << plugin << " for transform: " << transform.toXmlString() << endl; 393 SVDEBUG << "NOTE: Assigned plugin " << plugin << " for transform: " << transform.toXmlString() << endl;
388 394
389 if (!(originalTransform == transform)) { 395 if (!(originalTransform == transform)) {
390 m_transformPluginMap[originalTransform] = plugin; 396 m_transformPluginMap[originalTransform] = plugin;
391 // cerr << "NOTE: Also assigned plugin " << plugin << " for original transform: " << originalTransform.toXmlString() << endl; 397 SVDEBUG << "NOTE: Also assigned plugin " << plugin << " for original transform: " << originalTransform.toXmlString() << endl;
392 } 398 }
393 399
394 } else { 400 } else {
395 401
396 plugin = m_transformPluginMap[transform]; 402 plugin = m_transformPluginMap[transform];
410 { 416 {
411 TransformFactory *tf = TransformFactory::getInstance(); 417 TransformFactory *tf = TransformFactory::getInstance();
412 418
413 if (m_sampleRate == 0) { 419 if (m_sampleRate == 0) {
414 if (m_defaultSampleRate == 0) { 420 if (m_defaultSampleRate == 0) {
415 cerr << "ERROR: Default transform requested, but no default sample rate available" << endl; 421 SVCERR << "ERROR: Default transform requested, but no default sample rate available" << endl;
416 return false; 422 return false;
417 } else { 423 } else {
418 cerr << "NOTE: Using default sample rate of " << m_defaultSampleRate << " for default transform" << endl; 424 SVCERR << "NOTE: Using default sample rate of " << m_defaultSampleRate << " for default transform" << endl;
419 m_sampleRate = m_defaultSampleRate; 425 m_sampleRate = m_defaultSampleRate;
420 } 426 }
421 } 427 }
422 428
423 Transform transform = tf->getDefaultTransformFor(transformId, m_sampleRate); 429 Transform transform = tf->getDefaultTransformFor(transformId, m_sampleRate);
424 430
425 bool result = addFeatureExtractor(transform, writers); 431 bool result = addFeatureExtractor(transform, writers);
426 if (!result) { 432 if (!result) {
427 if (transform.getType() == Transform::UnknownType) { 433 if (transform.getType() == Transform::UnknownType) {
428 cerr << "(Maybe mixed up filename with transform, or --transform with --default?)" << endl; 434 SVCERR << "(Maybe mixed up filename with transform, or --transform with --default?)" << endl;
429 } 435 }
430 } 436 }
431 return result; 437 return result;
432 } 438 }
433 439
452 // We don't actually need to open this here yet, we just hoist 458 // We don't actually need to open this here yet, we just hoist
453 // it to the top for error reporting purposes 459 // it to the top for error reporting purposes
454 QFile file(transformFile); 460 QFile file(transformFile);
455 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 461 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
456 // Error case 1. File does not exist or cannot be opened 462 // Error case 1. File does not exist or cannot be opened
457 cerr << "ERROR: Failed to open transform file \"" << transformFile 463 SVCERR << "ERROR: Failed to open transform file \"" << transformFile
458 << "\" for reading" << endl; 464 << "\" for reading" << endl;
459 return false; 465 return false;
460 } 466 }
461 } 467 }
462 468
482 488
483 RDFTransformFactory factory 489 RDFTransformFactory factory
484 (QUrl::fromLocalFile(QFileInfo(transformFile).absoluteFilePath()) 490 (QUrl::fromLocalFile(QFileInfo(transformFile).absoluteFilePath())
485 .toString()); 491 .toString());
486 ProgressPrinter printer("Parsing transforms RDF file"); 492 ProgressPrinter printer("Parsing transforms RDF file");
487 std::vector<Transform> transforms = factory.getTransforms(&printer); 493 std::vector<Transform> transforms = factory.getTransforms
494 (m_verbose ? &printer : 0);
488 495
489 if (factory.isOK()) { 496 if (factory.isOK()) {
490 if (transforms.empty()) { 497 if (transforms.empty()) {
491 cerr << "ERROR: Transform file \"" << transformFile 498 SVCERR << "ERROR: Transform file \"" << transformFile
492 << "\" is valid RDF but defines no transforms" << endl; 499 << "\" is valid RDF but defines no transforms" << endl;
493 return false; 500 return false;
494 } else { 501 } else {
495 bool success = true; 502 bool success = true;
496 for (int i = 0; i < (int)transforms.size(); ++i) { 503 for (int i = 0; i < (int)transforms.size(); ++i) {
500 } 507 }
501 return success; 508 return success;
502 } 509 }
503 } else { // !factory.isOK() 510 } else { // !factory.isOK()
504 if (factory.isRDF()) { 511 if (factory.isRDF()) {
505 cerr << "ERROR: Invalid transform RDF file \"" << transformFile 512 SVCERR << "ERROR: Invalid transform RDF file \"" << transformFile
506 << "\": " << factory.getErrorString() << endl; 513 << "\": " << factory.getErrorString() << endl;
507 return false; 514 return false;
508 } 515 }
509 516
510 // the not-RDF case: fall through without reporting an 517 // the not-RDF case: fall through without reporting an
516 523
517 if (tryXml) { 524 if (tryXml) {
518 525
519 QFile file(transformFile); 526 QFile file(transformFile);
520 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { 527 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
521 cerr << "ERROR: Failed to open transform file \"" 528 SVCERR << "ERROR: Failed to open transform file \""
522 << transformFile.toStdString() << "\" for reading" << endl; 529 << transformFile.toStdString() << "\" for reading" << endl;
523 return false; 530 return false;
524 } 531 }
525 532
526 QTextStream *qts = new QTextStream(&file); 533 QTextStream *qts = new QTextStream(&file);
532 xmlError = transform.getErrorString(); 539 xmlError = transform.getErrorString();
533 540
534 if (xmlError == "") { 541 if (xmlError == "") {
535 542
536 if (transform.getIdentifier() == "") { 543 if (transform.getIdentifier() == "") {
537 cerr << "ERROR: Transform file \"" << transformFile 544 SVCERR << "ERROR: Transform file \"" << transformFile
538 << "\" is valid XML but defines no transform" << endl; 545 << "\" is valid XML but defines no transform" << endl;
539 return false; 546 return false;
540 } 547 }
541 548
542 return addFeatureExtractor(transform, writers); 549 return addFeatureExtractor(transform, writers);
543 } 550 }
544 } 551 }
545 552
546 cerr << "ERROR: Transform file \"" << transformFile 553 SVCERR << "ERROR: Transform file \"" << transformFile
547 << "\" could not be parsed" << endl; 554 << "\" could not be parsed" << endl;
548 if (rdfError != "") { 555 if (rdfError != "") {
549 cerr << "ERROR: RDF parser reported: " << rdfError << endl; 556 SVCERR << "ERROR: RDF parser reported: " << rdfError << endl;
550 } 557 }
551 if (xmlError != "") { 558 if (xmlError != "") {
552 cerr << "ERROR: XML parser reported: " << xmlError << endl; 559 SVCERR << "ERROR: XML parser reported: " << xmlError << endl;
553 } 560 }
554 561
555 return false; 562 return false;
556 } 563 }
557 564
558 void FeatureExtractionManager::addSource(QString audioSource, bool willMultiplex) 565 void FeatureExtractionManager::addSource(QString audioSource, bool willMultiplex)
559 { 566 {
560 std::cerr << "Have audio source: \"" << audioSource.toStdString() << "\"" << std::endl; 567 SVCERR << "Have audio source: \"" << audioSource.toStdString() << "\"" << endl;
561 568
562 // We don't actually do anything with it here, unless it's the 569 // We don't actually do anything with it here, unless it's the
563 // first audio source and we need it to establish default channel 570 // first audio source and we need it to establish default channel
564 // count and sample rate 571 // count and sample rate
565 572
566 if (m_channels == 0 || m_defaultSampleRate == 0) { 573 if (m_channels == 0 || m_defaultSampleRate == 0) {
567 574
568 ProgressPrinter retrievalProgress("Retrieving first input file to determine default rate and channel count..."); 575 ProgressPrinter retrievalProgress("Retrieving first input file to determine default rate and channel count...");
569 576
570 FileSource source(audioSource, &retrievalProgress); 577 FileSource source(audioSource, m_verbose ? &retrievalProgress : 0);
571 if (!source.isAvailable()) { 578 if (!source.isAvailable()) {
572 cerr << "ERROR: File or URL \"" << audioSource.toStdString() 579 SVCERR << "ERROR: File or URL \"" << audioSource.toStdString()
573 << "\" could not be located"; 580 << "\" could not be located";
574 if (source.getErrorString() != "") { 581 if (source.getErrorString() != "") {
575 cerr << ": " << source.getErrorString(); 582 SVCERR << ": " << source.getErrorString();
576 } 583 }
577 cerr << endl; 584 SVCERR << endl;
578 throw FileNotFound(audioSource); 585 throw FileNotFound(audioSource);
579 } 586 }
580 587
581 source.waitForData(); 588 source.waitForData();
582 589
583 // Open to determine validity, channel count, sample rate only 590 // Open to determine validity, channel count, sample rate only
584 // (then close, and open again later with actual desired rate &c) 591 // (then close, and open again later with actual desired rate &c)
585 592
593 AudioFileReaderFactory::Parameters params;
594 params.normalisation = (m_normalise ?
595 AudioFileReaderFactory::Normalisation::Peak :
596 AudioFileReaderFactory::Normalisation::None);
597
586 AudioFileReader *reader = 598 AudioFileReader *reader =
587 AudioFileReaderFactory::createReader(source, 0, 599 AudioFileReaderFactory::createReader
588 m_normalise, 600 (source, params, m_verbose ? &retrievalProgress : 0);
589 &retrievalProgress);
590 601
591 if (!reader) { 602 if (!reader) {
592 throw FailedToOpenFile(audioSource); 603 throw FailedToOpenFile(audioSource);
593 } 604 }
594 605
595 retrievalProgress.done(); 606 if (m_verbose) retrievalProgress.done();
596 607
597 cerr << "File or URL \"" << audioSource.toStdString() << "\" opened successfully" << endl; 608 SVCERR << "File or URL \"" << audioSource.toStdString() << "\" opened successfully" << endl;
598 609
599 if (!willMultiplex) { 610 if (!willMultiplex) {
600 if (m_channels == 0) { 611 if (m_channels == 0) {
601 m_channels = reader->getChannelCount(); 612 m_channels = reader->getChannelCount();
602 cerr << "Taking default channel count of " 613 SVCERR << "Taking default channel count of "
603 << reader->getChannelCount() << " from audio file" << endl; 614 << reader->getChannelCount() << " from audio file" << endl;
604 } 615 }
605 } 616 }
606 617
607 if (m_defaultSampleRate == 0) { 618 if (m_defaultSampleRate == 0) {
608 m_defaultSampleRate = reader->getNativeRate(); 619 m_defaultSampleRate = reader->getNativeRate();
609 cerr << "Taking default sample rate of " 620 SVCERR << "Taking default sample rate of "
610 << reader->getNativeRate() << "Hz from audio file" << endl; 621 << reader->getNativeRate() << "Hz from audio file" << endl;
611 cerr << "(Note: Default may be overridden by transforms)" << endl; 622 SVCERR << "(Note: Default may be overridden by transforms)" << endl;
612 } 623 }
613 624
614 m_readyReaders[audioSource] = reader; 625 m_readyReaders[audioSource] = reader;
615 } 626 }
616 627
617 if (willMultiplex) { 628 if (willMultiplex) {
618 ++m_channels; // channel count is simply number of sources 629 ++m_channels; // channel count is simply number of sources
619 cerr << "Multiplexing, incremented target channel count to " 630 SVCERR << "Multiplexing, incremented target channel count to "
620 << m_channels << endl; 631 << m_channels << endl;
621 } 632 }
622 } 633 }
623 634
624 void FeatureExtractionManager::extractFeatures(QString audioSource) 635 void FeatureExtractionManager::extractFeatures(QString audioSource)
678 // can't use this; open it again 689 // can't use this; open it again
679 delete reader; 690 delete reader;
680 reader = 0; 691 reader = 0;
681 } 692 }
682 } 693 }
694
683 if (!reader) { 695 if (!reader) {
684 ProgressPrinter retrievalProgress("Retrieving audio data..."); 696 ProgressPrinter retrievalProgress("Retrieving audio data...");
685 FileSource fs(source, &retrievalProgress); 697 FileSource fs(source, m_verbose ? &retrievalProgress : 0);
686 fs.waitForData(); 698 fs.waitForData();
687 reader = AudioFileReaderFactory::createReader(fs, m_sampleRate, 699
688 m_normalise, 700 AudioFileReaderFactory::Parameters params;
689 &retrievalProgress); 701 params.targetRate = m_sampleRate;
690 retrievalProgress.done(); 702 params.normalisation = (m_normalise ?
691 } 703 AudioFileReaderFactory::Normalisation::Peak :
704 AudioFileReaderFactory::Normalisation::None);
705
706 reader = AudioFileReaderFactory::createReader
707 (fs, params, m_verbose ? &retrievalProgress : 0);
708 if (m_verbose) retrievalProgress.done();
709 }
710
692 if (!reader) { 711 if (!reader) {
693 throw FailedToOpenFile(source); 712 throw FailedToOpenFile(source);
694 } 713 }
695 if (reader->getChannelCount() != m_channels || 714 if (reader->getChannelCount() != m_channels ||
696 reader->getNativeRate() != m_sampleRate) { 715 reader->getNativeRate() != m_sampleRate) {
697 cerr << "NOTE: File will be mixed or resampled for processing, to: " 716 SVCERR << "NOTE: File will be mixed or resampled for processing, to: "
698 << m_channels << "ch at " 717 << m_channels << "ch at "
699 << m_sampleRate << "Hz" << endl; 718 << m_sampleRate << "Hz" << endl;
700 } 719 }
701 return reader; 720 return reader;
702 } 721 }
705 FeatureExtractionManager::extractFeaturesFor(AudioFileReader *reader, 724 FeatureExtractionManager::extractFeaturesFor(AudioFileReader *reader,
706 QString audioSource) 725 QString audioSource)
707 { 726 {
708 // Note: This also deletes reader 727 // Note: This also deletes reader
709 728
710 cerr << "Audio file \"" << audioSource.toStdString() << "\": " 729 SVCERR << "Audio file \"" << audioSource.toStdString() << "\": "
711 << reader->getChannelCount() << "ch at " 730 << reader->getChannelCount() << "ch at "
712 << reader->getNativeRate() << "Hz" << endl; 731 << reader->getNativeRate() << "Hz" << endl;
713 732
714 // allocate audio buffers 733 // allocate audio buffers
715 float **data = new float *[m_channels]; 734 float **data = new float *[m_channels];
735 }; 754 };
736 LifespanMgr lifemgr(reader, m_channels, data); 755 LifespanMgr lifemgr(reader, m_channels, data);
737 756
738 size_t frameCount = reader->getFrameCount(); 757 size_t frameCount = reader->getFrameCount();
739 758
740 // cerr << "file has " << frameCount << " frames" << endl; 759 SVDEBUG << "FeatureExtractionManager: file has " << frameCount << " frames" << endl;
741 760
742 int earliestStartFrame = 0; 761 int earliestStartFrame = 0;
743 int latestEndFrame = frameCount; 762 int latestEndFrame = frameCount;
744 bool haveExtents = false; 763 bool haveExtents = false;
745 764
746 foreach (Plugin *plugin, m_orderedPlugins) { 765 foreach (Plugin *plugin, m_orderedPlugins) {
747 766
748 PluginMap::iterator pi = m_plugins.find(plugin); 767 PluginMap::iterator pi = m_plugins.find(plugin);
749 768
750 // std::cerr << "Calling reset on " << plugin << std::endl; 769 SVDEBUG << "FeatureExtractionManager: Calling reset on " << plugin << endl;
751 plugin->reset(); 770 plugin->reset();
752 771
753 for (TransformWriterMap::iterator ti = pi->second.begin(); 772 for (TransformWriterMap::iterator ti = pi->second.begin();
754 ti != pi->second.end(); ++ti) { 773 ti != pi->second.end(); ++ti) {
755 774
767 earliestStartFrame = startFrame; 786 earliestStartFrame = startFrame;
768 } 787 }
769 if (!haveExtents || startFrame + duration > latestEndFrame) { 788 if (!haveExtents || startFrame + duration > latestEndFrame) {
770 latestEndFrame = startFrame + duration; 789 latestEndFrame = startFrame + duration;
771 } 790 }
791
772 /* 792 /*
773 cerr << "startFrame for transform " << startFrame << endl; 793 SVDEBUG << "startFrame for transform " << startFrame << endl;
774 cerr << "duration for transform " << duration << endl; 794 SVDEBUG << "duration for transform " << duration << endl;
775 cerr << "earliestStartFrame becomes " << earliestStartFrame << endl; 795 SVDEBUG << "earliestStartFrame becomes " << earliestStartFrame << endl;
776 cerr << "latestEndFrame becomes " << latestEndFrame << endl; 796 SVDEBUG << "latestEndFrame becomes " << latestEndFrame << endl;
777 */ 797 */
778 haveExtents = true; 798 haveExtents = true;
779 799
780 string outputId = transform.getOutput().toStdString(); 800 string outputId = transform.getOutput().toStdString();
781 if (m_pluginOutputs[plugin].find(outputId) == 801 if (m_pluginOutputs[plugin].find(outputId) ==
782 m_pluginOutputs[plugin].end()) { 802 m_pluginOutputs[plugin].end()) {
783 // We shouldn't actually reach this point: 803 // We shouldn't actually reach this point:
784 // addFeatureExtractor tests whether the output exists 804 // addFeatureExtractor tests whether the output exists
785 cerr << "ERROR: Nonexistent plugin output \"" << outputId << "\" requested for transform \"" 805 SVCERR << "ERROR: Nonexistent plugin output \"" << outputId << "\" requested for transform \""
786 << transform.getIdentifier().toStdString() << "\", ignoring this transform" 806 << transform.getIdentifier().toStdString() << "\", ignoring this transform"
787 << endl; 807 << endl;
788 /* 808
789 cerr << "Known outputs for all plugins are as follows:" << endl; 809 SVDEBUG << "Known outputs for all plugins are as follows:" << endl;
790 for (PluginOutputMap::const_iterator k = m_pluginOutputs.begin(); 810 for (PluginOutputMap::const_iterator k = m_pluginOutputs.begin();
791 k != m_pluginOutputs.end(); ++k) { 811 k != m_pluginOutputs.end(); ++k) {
792 cerr << "Plugin " << k->first << ": "; 812 SVDEBUG << "Plugin " << k->first << ": ";
793 if (k->second.empty()) { 813 if (k->second.empty()) {
794 cerr << "(none)"; 814 SVDEBUG << "(none)";
795 } 815 }
796 for (OutputMap::const_iterator i = k->second.begin(); 816 for (OutputMap::const_iterator i = k->second.begin();
797 i != k->second.end(); ++i) { 817 i != k->second.end(); ++i) {
798 cerr << "\"" << i->first << "\" "; 818 SVDEBUG << "\"" << i->first << "\" ";
799 } 819 }
800 cerr << endl; 820 SVDEBUG << endl;
801 } 821 }
802 */
803 } 822 }
804 } 823 }
805 } 824 }
806 825
807 int startFrame = earliestStartFrame; 826 int startFrame = earliestStartFrame;
833 for (int i = startFrame; i < endFrame; i += m_blockSize) { 852 for (int i = startFrame; i < endFrame; i += m_blockSize) {
834 853
835 //!!! inefficient, although much of the inefficiency may be 854 //!!! inefficient, although much of the inefficiency may be
836 // susceptible to compiler optimisation 855 // susceptible to compiler optimisation
837 856
838 SampleBlock frames = reader->getInterleavedFrames(i, m_blockSize); 857 auto frames = reader->getInterleavedFrames(i, m_blockSize);
839 858
840 // We have to do our own channel handling here; we can't just 859 // We have to do our own channel handling here; we can't just
841 // leave it to the plugin adapter because the same plugin 860 // leave it to the plugin adapter because the same plugin
842 // adapter may have to serve for input files with various 861 // adapter may have to serve for input files with various
843 // numbers of channels (so the adapter is simply configured 862 // numbers of channels (so the adapter is simply configured
909 } 928 }
910 } 929 }
911 930
912 int pp = progress; 931 int pp = progress;
913 progress = int(((i - startFrame) * 100.0) / (endFrame - startFrame) + 0.1); 932 progress = int(((i - startFrame) * 100.0) / (endFrame - startFrame) + 0.1);
914 if (progress > pp) extractionProgress.setProgress(progress); 933 if (progress > pp && m_verbose) extractionProgress.setProgress(progress);
915 } 934 }
916 935
917 // std::cerr << "FeatureExtractionManager: deleting audio file reader" << std::endl; 936 SVDEBUG << "FeatureExtractionManager: deleting audio file reader" << endl;
918 937
919 lifemgr.destroy(); // deletes reader, data 938 lifemgr.destroy(); // deletes reader, data
920 939
921 foreach (Plugin *plugin, m_orderedPlugins) { 940 foreach (Plugin *plugin, m_orderedPlugins) {
922 941
930 if (!m_summaries.empty()) { 949 if (!m_summaries.empty()) {
931 // Summaries requested on the command line, for all transforms 950 // Summaries requested on the command line, for all transforms
932 PluginSummarisingAdapter *adapter = 951 PluginSummarisingAdapter *adapter =
933 dynamic_cast<PluginSummarisingAdapter *>(plugin); 952 dynamic_cast<PluginSummarisingAdapter *>(plugin);
934 if (!adapter) { 953 if (!adapter) {
935 cerr << "WARNING: Summaries requested, but plugin is not a summarising adapter" << endl; 954 SVCERR << "WARNING: Summaries requested, but plugin is not a summarising adapter" << endl;
936 } else { 955 } else {
937 for (SummaryNameSet::const_iterator sni = m_summaries.begin(); 956 for (SummaryNameSet::const_iterator sni = m_summaries.begin();
938 sni != m_summaries.end(); ++sni) { 957 sni != m_summaries.end(); ++sni) {
939 featureSet.clear(); 958 featureSet.clear();
940 //!!! problem here -- we are requesting summaries 959 //!!! problem here -- we are requesting summaries
953 972
954 // Summaries specified in transform definitions themselves 973 // Summaries specified in transform definitions themselves
955 writeSummaries(audioSource, plugin); 974 writeSummaries(audioSource, plugin);
956 } 975 }
957 976
958 extractionProgress.done(); 977 if (m_verbose) extractionProgress.done();
959 978
960 finish(); 979 finish();
961 980
962 TempDirectory::getInstance()->cleanup(); 981 TempDirectory::getInstance()->cleanup();
963 } 982 }
971 for (TransformWriterMap::const_iterator ti = pi->second.begin(); 990 for (TransformWriterMap::const_iterator ti = pi->second.begin();
972 ti != pi->second.end(); ++ti) { 991 ti != pi->second.end(); ++ti) {
973 992
974 const Transform &transform = ti->first; 993 const Transform &transform = ti->first;
975 994
976 // cerr << "FeatureExtractionManager::writeSummaries: plugin is " << plugin 995 SVDEBUG << "FeatureExtractionManager::writeSummaries: plugin is " << plugin
977 // << ", found transform: " << transform.toXmlString() << endl; 996 << ", found transform: " << transform.toXmlString() << endl;
978 997
979 Transform::SummaryType summaryType = transform.getSummaryType(); 998 Transform::SummaryType summaryType = transform.getSummaryType();
980 PluginSummarisingAdapter::SummaryType pType = 999 PluginSummarisingAdapter::SummaryType pType =
981 (PluginSummarisingAdapter::SummaryType)summaryType; 1000 (PluginSummarisingAdapter::SummaryType)summaryType;
982 1001
983 if (transform.getSummaryType() == Transform::NoSummary) { 1002 if (transform.getSummaryType() == Transform::NoSummary) {
984 // cerr << "(no summary, continuing)" << endl; 1003 SVDEBUG << "FeatureExtractionManager::writeSummaries: no summary for this transform" << endl;
985 continue; 1004 continue;
986 } 1005 }
987 1006
988 PluginSummarisingAdapter *adapter = 1007 PluginSummarisingAdapter *adapter =
989 dynamic_cast<PluginSummarisingAdapter *>(plugin); 1008 dynamic_cast<PluginSummarisingAdapter *>(plugin);
990 if (!adapter) { 1009 if (!adapter) {
991 cerr << "FeatureExtractionManager::writeSummaries: INTERNAL ERROR: Summary requested for transform, but plugin is not a summarising adapter" << endl; 1010 SVCERR << "FeatureExtractionManager::writeSummaries: INTERNAL ERROR: Summary requested for transform, but plugin is not a summarising adapter" << endl;
992 continue; 1011 continue;
993 } 1012 }
994 1013
995 Plugin::FeatureSet featureSet = adapter->getSummaryForAllOutputs 1014 Plugin::FeatureSet featureSet = adapter->getSummaryForAllOutputs
996 (pType, PluginSummarisingAdapter::ContinuousTimeAverage); 1015 (pType, PluginSummarisingAdapter::ContinuousTimeAverage);
997 1016
998 // cerr << "summary type " << int(pType) << " for transform:" << endl << transform.toXmlString().toStdString()<< endl << "... feature set with " << featureSet.size() << " elts" << endl; 1017 SVDEBUG << "summary type " << int(pType) << " for transform:" << endl << transform.toXmlString().toStdString()<< endl << "... feature set with " << featureSet.size() << " elts" << endl;
999 1018
1000 writeFeatures(audioSource, plugin, featureSet, summaryType); 1019 writeFeatures(audioSource, plugin, featureSet, summaryType);
1001 } 1020 }
1002 } 1021 }
1003 1022
1016 ti != pi->second.end(); ++ti) { 1035 ti != pi->second.end(); ++ti) {
1017 1036
1018 const Transform &transform = ti->first; 1037 const Transform &transform = ti->first;
1019 const vector<FeatureWriter *> &writers = ti->second; 1038 const vector<FeatureWriter *> &writers = ti->second;
1020 1039
1021 // cerr << "writeFeatures: plugin " << plugin << " has transform: " << transform.toXmlString() << endl; 1040 // SVDEBUG << "writeFeatures: plugin " << plugin << " has transform: " << transform.toXmlString() << endl;
1022 1041
1023 if (transform.getSummaryType() == Transform::NoSummary && 1042 if (transform.getSummaryType() == Transform::NoSummary &&
1024 !m_summaries.empty()) { 1043 !m_summaries.empty()) {
1025 // cerr << "transform has no summary, but summaries requested on command line, so going for it anyway" << endl; 1044 SVDEBUG << "writeFeatures: transform has no summary, but summaries requested on command line, so going for it anyway" << endl;
1026 } else if (transform.getSummaryType() != summaryType) { 1045 } else if (transform.getSummaryType() != summaryType) {
1027 // Either we're not writing a summary and the transform 1046 // Either we're not writing a summary and the transform
1028 // has one, or we're writing a summary but the transform 1047 // has one, or we're writing a summary but the transform
1029 // has none or a different one; either way, skip it 1048 // has none or a different one; either way, skip it
1030 // cerr << "summary type differs from passed-in one " << summaryType << endl; 1049 SVDEBUG << "writeFeatures: transform summary type " << transform.getSummaryType() << " differs from passed-in one " << summaryType << ", skipping" << endl;
1031 continue; 1050 continue;
1032 } 1051 }
1033 1052
1034 string outputId = transform.getOutput().toStdString(); 1053 string outputId = transform.getOutput().toStdString();
1035 1054
1043 1062
1044 int outputIndex = m_pluginOutputIndices[outputId]; 1063 int outputIndex = m_pluginOutputIndices[outputId];
1045 Plugin::FeatureSet::const_iterator fsi = features.find(outputIndex); 1064 Plugin::FeatureSet::const_iterator fsi = features.find(outputIndex);
1046 if (fsi == features.end()) continue; 1065 if (fsi == features.end()) continue;
1047 1066
1048 // cerr << "this transform has " << writers.size() << " writer(s)" << endl; 1067 // SVDEBUG << "this transform has " << writers.size() << " writer(s)" << endl;
1049 1068
1050 for (int j = 0; j < (int)writers.size(); ++j) { 1069 for (int j = 0; j < (int)writers.size(); ++j) {
1051 writers[j]->write 1070 writers[j]->write
1052 (audioSource, transform, desc, fsi->second, 1071 (audioSource, transform, desc, fsi->second,
1053 Transform::summaryTypeToString(summaryType).toStdString()); 1072 Transform::summaryTypeToString(summaryType).toStdString());