MTG / essentia

C++ library for audio and music analysis, description and synthesis, including Python bindings
http://essentia.upf.edu
GNU Affero General Public License v3.0
2.85k stars 533 forks source link

In YamlOutput::pool::set(): Error when checking types. Expected: essentia::Pool, received: essentia::Pool #1281

Open nvssynthesis opened 2 years ago

nvssynthesis commented 2 years ago

This is my first github 'issue' post, so I hope I'm formatting it alright & including the right amount of information. I am writing a GUI application using essentia and juce. The program uses a few separate steps of loading in an audio file (truly just getting an absolute path to a .wav), passing that file path to a massive analysis function, and exporting the analysis in YAML format. I will be making use of a flag enum specific to these needs, so I put these functions in a struct with all static methods (nothing depends on any struct instance, no method is private, and it has no plain old data other than the flags enum). The function declarations in question are:

template<typename timing_t = unsigned int>
static std::unique_ptr<essentia::Pool> analyze(const std::string filename,
           const essentia::Real sampleRate = 44100.f,
           const timing_t frameSize = 512U,
           const timing_t hopSize = 2000U,
           const unsigned short aFlags = none)
static void exportAnalysis(const std::unique_ptr<essentia::Pool> &pool, const std::string outputFilename)

I believe I am doing standard essentia stuff in analyze() and exportAnalysis(), perhaps other than wrapping the pool in a unique_ptr. Maybe it is also suspect that analyze() calls essentia::shutdown() before returning the pool, and then exportAnalysis needs to re-call

essentia::init();
AlgorithmFactory& factory = AlgorithmFactory::instance();

so that it can then use

Algorithm* yamlOutput = AlgorithmFactory::create("YamlOutput",
                                                     "filename", outputFilename);
        yamlOutput->input("pool").set(*pool); // RUN TIME ERROR
        yamlOutput->compute();

However, once we arrive at yamlOutput->input("pool").set(*pool); I get the following error:

libc++abi.dylib: terminating with uncaught exception of type essentia::EssentiaException: In YamlOutput::pool::set(): Error when checking types. Expected: essentia::Pool, received: essentia::Pool

This error makes no sense to me, because it basically says that it is expecting the same type that it in fact did receive, and somehow this throws an exception. Also, when I inspect the contents of the pool I am trying to pass, it looks valid, containing all of the data from the prior analysis.

Any thoughts/suggestions?

nvssynthesis commented 2 years ago

update: the function throwing the exception is

inline void checkType(const std::type_info& received,
                        const std::type_info& expected) const 

in types.h. A closer look shows that for some reason, while the nameOfType() of both received and expected match (both are essentia::Pool), their addresses differ. Screenshot attached. What could cause this?

Screen Shot 2022-10-12 at 7 31 09 PM

nvssynthesis commented 2 years ago

Alright, a bit more detail and simplification. Regarding the following function:

template<typename timing_t = unsigned int> // int for frameSize & hopSize to be in samples, floating point to be in ms
void analyzeMini(const std::string filename = "/Users/nicholassolem/development/audio for analysis/smooth transition/sinish to saw 440hz.wav",
                 const essentia::Real sampleRate = 44100.f,
                 const timing_t frameSize = 512U,
                 const timing_t hopSize = 2000U,
               const unsigned short aFlags = 0){
        using namespace essentia::standard;
        essentia::init();
        AlgorithmFactory& factory = AlgorithmFactory::instance();
        // USE AudioLoader INSTEAD TO GET SAMPLE RATE AUTOMATICALLY AND ALSO ALLOW MULTICHANNEL
        Algorithm* audio    = factory.create("MonoLoader", "filename", filename, "sampleRate", sampleRate);
        Algorithm* fc       = factory.create("FrameCutter",
                                            "frameSize", frameSize,
                                    "hopSize", hopSize);
        Algorithm* w        = factory.create("Windowing",
                                    "type", "blackmanharris92", "size", frameSize);
        Algorithm* spec     = factory.create("Spectrum",
                                             "size", frameSize);
        Algorithm* entropy  = factory.create("Entropy");
//        std::unique_ptr<essentia::Pool> pool = std::make_unique<essentia::Pool>();
        Algorithm* poolAggr = factory.create("PoolAggregator");
        Algorithm* yamlOut = AlgorithmFactory::create("YamlOutput",
                                             "filename", "/Users/nicholassolem/development/audio for analysis/thingTest.yaml");
        // Audio -> FrameCutter
        essentia::Pool *p = new essentia::Pool /*= *pool*/;
        std::vector<essentia::Real> audioBuffer, frame;
        essentia::Real entropyVal;

        audio->output("audio").set(audioBuffer);
        fc->input("signal").set(audioBuffer);   // frame creator
        fc->output("frame").set(frame);
        w->input("frame").set(frame);
        w->output("frame").set(frame);
        spec->input("frame").set(frame);
        spec->output("spectrum").set(frame);
        entropy->input("array").set(frame);
        entropy->output("entropy").set(entropyVal);

        poolAggr->input("input").set(*p);
        poolAggr->output("output").set(*p);
        yamlOut->input("pool").set(*p);

        audio->compute();

        int m = 0;
        while (true) {
            fc->compute();
            if (!(frame.size())) {
                break;
            }

            w->compute();
            spec->compute();
            entropy->compute();
            p->add("lowlevel.entropy", entropyVal);
            m++;
        }

        yamlOut->compute();

        delete entropy;
        delete spec;
        delete w;
        delete fc;
        delete audio;
        essentia::shutdown();
//        return pool;
    }

When I have a command line program that runs this function, everything is fine. However, once I try to use this function in a JUCE GUI application, the above mentioned problem occurs. I simplified the structure even more than above, so that analyzeMini() just lives in the MainComponent class. I will link to my application soon so others can try to reproduce the problem. Is it possible to do a workaround by making a custom checkType that checks based on nameOfType rather than their addresses? The address mismatch of essentia::Pool is what trips it up.