Chris@0: Chris@0: #include "Azi.h" Chris@0: Chris@1: #include Chris@3: #include Chris@7: #include Chris@24: #include Chris@25: #include Chris@1: Chris@1: using std::vector; Chris@7: using std::complex; Chris@3: using std::cerr; Chris@3: using std::endl; Chris@0: Chris@0: Azi::Azi(float inputSampleRate) : Chris@1: Plugin(inputSampleRate), Chris@14: m_width(128) Chris@0: { Chris@0: } Chris@0: Chris@0: Azi::~Azi() Chris@0: { Chris@0: } Chris@0: Chris@0: string Chris@0: Azi::getIdentifier() const Chris@0: { Chris@0: return "azi"; Chris@0: } Chris@0: Chris@0: string Chris@0: Azi::getName() const Chris@0: { Chris@0: return "Stereo Plan"; Chris@0: } Chris@0: Chris@0: string Chris@0: Azi::getDescription() const Chris@0: { Chris@29: return "Return a stereo plan decomposition of the audio. The returned feature grid covers the stereo plan from left-channel-only (first bin) to right-channel-only (last bin), with each value indicating what proportion of signal energy is found at that point on the plan at that moment. The input should consist of two channels containing left and right channel signals."; Chris@0: } Chris@0: Chris@0: string Chris@0: Azi::getMaker() const Chris@0: { Chris@26: return "Chris Cannam"; Chris@0: } Chris@0: Chris@0: int Chris@0: Azi::getPluginVersion() const Chris@0: { Chris@0: // Increment this each time you release a version that behaves Chris@0: // differently from the previous one Chris@0: return 1; Chris@0: } Chris@0: Chris@0: string Chris@0: Azi::getCopyright() const Chris@0: { Chris@29: return "Freely redistributable (BSD license)"; Chris@0: } Chris@0: Chris@0: Azi::InputDomain Chris@0: Azi::getInputDomain() const Chris@0: { Chris@10: return FrequencyDomain; Chris@0: } Chris@0: Chris@0: size_t Chris@0: Azi::getPreferredBlockSize() const Chris@0: { Chris@12: return 8192; Chris@0: } Chris@0: Chris@0: size_t Chris@0: Azi::getPreferredStepSize() const Chris@0: { Chris@12: return 256; Chris@0: } Chris@0: Chris@0: size_t Chris@0: Azi::getMinChannelCount() const Chris@0: { Chris@25: return 1; // pointless, but supported Chris@0: } Chris@0: Chris@0: size_t Chris@0: Azi::getMaxChannelCount() const Chris@0: { Chris@1: return 2; Chris@0: } Chris@0: Chris@0: Azi::ParameterList Chris@0: Azi::getParameterDescriptors() const Chris@0: { Chris@0: ParameterList list; Chris@0: Chris@0: // If the plugin has no adjustable parameters, return an empty Chris@0: // list here (and there's no need to provide implementations of Chris@0: // getParameter and setParameter in that case either). Chris@0: Chris@0: // Note that it is your responsibility to make sure the parameters Chris@0: // start off having their default values (e.g. in the constructor Chris@0: // above). The host needs to know the default value so it can do Chris@0: // things like provide a "reset to default" function, but it will Chris@0: // not explicitly set your parameters to their defaults for you if Chris@0: // they have not changed in the mean time. Chris@0: Chris@0: return list; Chris@0: } Chris@0: Chris@0: float Chris@25: Azi::getParameter(string) const Chris@0: { Chris@0: return 0; Chris@0: } Chris@0: Chris@0: void Chris@25: Azi::setParameter(string, float) Chris@0: { Chris@0: } Chris@0: Chris@0: Azi::ProgramList Chris@0: Azi::getPrograms() const Chris@0: { Chris@0: ProgramList list; Chris@0: Chris@0: // If you have no programs, return an empty list (or simply don't Chris@0: // implement this function or getCurrentProgram/selectProgram) Chris@0: Chris@0: return list; Chris@0: } Chris@0: Chris@0: string Chris@0: Azi::getCurrentProgram() const Chris@0: { Chris@0: return ""; // no programs Chris@0: } Chris@0: Chris@0: void Chris@25: Azi::selectProgram(string) Chris@0: { Chris@0: } Chris@0: Chris@0: Azi::OutputList Chris@0: Azi::getOutputDescriptors() const Chris@0: { Chris@0: OutputList list; Chris@0: Chris@0: // See OutputDescriptor documentation for the possibilities here. Chris@0: // Every plugin must have at least one output. Chris@0: Chris@0: OutputDescriptor d; Chris@1: d.identifier = "plan"; Chris@1: d.name = "Plan"; Chris@0: d.description = ""; Chris@0: d.unit = ""; Chris@0: d.hasFixedBinCount = true; Chris@16: d.binCount = m_width * 2 + 3; // include a 1-bin "margin" at top and bottom Chris@0: d.hasKnownExtents = false; Chris@0: d.isQuantized = false; Chris@0: d.sampleType = OutputDescriptor::OneSamplePerStep; Chris@0: d.hasDuration = false; Chris@27: Chris@27: char buf[100]; Chris@28: for (int i = 0; i < int(d.binCount); ++i) { Chris@27: if (i == 0) { Chris@27: d.binNames.push_back("Left"); Chris@28: } else if (i + 1 == int(d.binCount)) { Chris@27: d.binNames.push_back("Right"); Chris@27: } else if (i == m_width + 1) { Chris@27: d.binNames.push_back("Centre"); Chris@27: } else { Chris@27: int p = int(round(double(i - m_width - 1) / Chris@27: double(m_width) * 100.0)); Chris@27: if (p > 0) { Chris@27: sprintf(buf, "R %03d", p); Chris@27: } else { Chris@27: sprintf(buf, "L %03d", -p); Chris@27: } Chris@27: d.binNames.push_back(buf); Chris@27: } Chris@27: } Chris@27: Chris@0: list.push_back(d); Chris@0: Chris@0: return list; Chris@0: } Chris@0: Chris@0: bool Chris@25: Azi::initialise(size_t channels, size_t, size_t blockSize) Chris@0: { Chris@0: if (channels < getMinChannelCount() || Chris@0: channels > getMaxChannelCount()) return false; Chris@0: Chris@25: if (blockSize > INT_MAX) return false; Chris@25: Chris@25: m_channels = int(channels); Chris@25: m_blockSize = int(blockSize); Chris@0: Chris@0: return true; Chris@0: } Chris@0: Chris@0: void Chris@0: Azi::reset() Chris@0: { Chris@0: // Clear buffers, reset stored values, etc Chris@0: } Chris@0: Chris@1: float Chris@3: Azi::rms(const vector &buffer) Chris@1: { Chris@1: float sum = 0; Chris@3: for (int i = 0; i < int(buffer.size()); ++i) { Chris@1: sum += buffer[i] * buffer[i]; Chris@1: } Chris@25: return sqrtf(sum / float(buffer.size())); Chris@1: } Chris@1: Chris@0: Azi::FeatureSet Chris@25: Azi::process(const float *const *inputBuffers, Vamp::RealTime) Chris@0: { Chris@8: vector left, right; Chris@13: Chris@13: int n = int(m_blockSize/2 + 1); Chris@13: Chris@14: vector plan(m_width * 2 + 3, 0.f); Chris@14: Chris@14: const float *inleft = inputBuffers[0]; Chris@25: const float *inright = (m_channels == 2 ? inputBuffers[1] : inleft); Chris@13: Chris@13: for (int i = 0; i < n; ++i) { Chris@13: Chris@13: int ri = i*2, ii = i*2 + 1; Chris@13: Chris@14: float lmag = sqrtf(inleft[ri] * inleft[ri] + inleft[ii] * inleft[ii]); Chris@14: float rmag = sqrtf(inright[ri] * inright[ri] + inright[ii] * inright[ii]); Chris@13: Chris@13: // lmag = 0.0, rmag = 1.0 -> min cancelled is at +1.0 Chris@13: // lmag = 1.0, rmag = 0.0 -> at -1.0 [val at 0.0 = 1.0] Chris@13: // lmag = 0.5, rmag = 0.2 -> at -0.6 [val at 0.0 = 0.3] Chris@13: // lmag = 0.5, rmag = 1.0 -> at +0.5 [val at 0.0 = 0.5] Chris@13: // lmag = 1.0, rmag = 1.0 -> at +0.0 Chris@13: Chris@13: // if lmag == rmag -> 0.0 Chris@13: // else if lmag > rmag -> negative Chris@13: // else -> positive Chris@13: Chris@13: // val at 0.0 = larger - smaller Chris@13: // mag of null = 1.0 - (smaller / larger) Chris@13: Chris@13: float larger = std::max(lmag, rmag); Chris@13: float smaller = std::min(lmag, rmag); Chris@13: Chris@14: float pan = 0.0; Chris@13: Chris@13: if (larger > smaller) { Chris@25: float abspan = 1.f - (smaller / larger); Chris@14: if (lmag > rmag) pan = -abspan; Chris@14: else pan = abspan; Chris@13: } Chris@13: Chris@14: float leftGain = 1.f, rightGain = 1.f; Chris@14: if (pan > 0.f) leftGain *= 1.f - pan; Chris@14: if (pan < 0.f) rightGain *= pan + 1.f; Chris@13: Chris@25: float wid = float(m_width); Chris@25: float pos = -pan * wid + wid; Chris@14: float mag = leftGain * lmag + rightGain * rmag; Chris@13: Chris@14: float ipos = floorf(pos); Chris@14: Chris@14: plan[int(ipos) + 1] += mag * (1.f - (pos - ipos)); Chris@14: plan[int(ipos) + 2] += mag * (pos - ipos); Chris@3: } Chris@1: Chris@2: FeatureSet fs; Chris@2: Feature f; Chris@7: f.values = plan; Chris@1: Chris@1: fs[0].push_back(f); Chris@1: Chris@1: return fs; Chris@0: } Chris@0: Chris@0: Azi::FeatureSet Chris@0: Azi::getRemainingFeatures() Chris@0: { Chris@0: return FeatureSet(); Chris@0: } Chris@0: