//==============================================================================
double PitchDetector::detectPitch (float* samples, int numSamples) noexcept
{
    Array<double> pitches;
    pitches.ensureStorageAllocated (int (numSamples / numSamplesNeededForDetection));
    
    while (numSamples >= numSamplesNeededForDetection)
    {
        double pitch = detectPitchForBlock (samples, numSamplesNeededForDetection);//0.0;
        
        if (pitch > 0.0)
            pitches.add (pitch);
        
        numSamples -= numSamplesNeededForDetection;
        samples += numSamplesNeededForDetection;
    }
    
    if (pitches.size() == 1)
    {
        return pitches[0];
    }
    else if (pitches.size() > 1)
    {
        DefaultElementComparator<double> sorter;
        pitches.sort (sorter);
        
        const double stdDev = findStandardDeviation (pitches.getRawDataPointer(), pitches.size());
        const double medianSample = findMedian (pitches.getRawDataPointer(), pitches.size());
        const double lowerLimit = medianSample - stdDev;
        const double upperLimit = medianSample + stdDev;
        
        Array<double> correctedPitches;
        correctedPitches.ensureStorageAllocated (pitches.size());
        
        for (int i = 0; i < pitches.size(); ++i)
        {
            const double pitch = pitches.getUnchecked (i);
            
            if (pitch >= lowerLimit && pitch <= upperLimit)
                correctedPitches.add (pitch);
        }
        
        const double finalPitch = findMean (correctedPitches.getRawDataPointer(), correctedPitches.size());
        
        return finalPitch;
    }
    
    return 0.0;
}
Example #2
0
var::var (const StringArray& v)       : type (&VariantType_Array::instance)
{
    Array<var> strings;
    strings.ensureStorageAllocated (v.size());

    for (auto& i : v)
        strings.add (var (i));

    value.objectValue = new VariantType_Array::RefCountedArray (strings);
}
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
                                                 int /*midiChannel*/, int midiNoteNumber) const
{
    SynthesiserVoice* bottom = nullptr;
    SynthesiserVoice* top    = nullptr;

    // this is a list of voices we can steal, sorted by how long they've been running
    Array<SynthesiserVoice*> usableVoices;
    usableVoices.ensureStorageAllocated (voices.size());

    for (int i = 0; i < voices.size(); ++i)
    {
        SynthesiserVoice* const voice = voices.getUnchecked (i);

        if (voice->canPlaySound (soundToPlay))
        {
            VoiceAgeSorter sorter;
            usableVoices.addSorted (sorter, voice);

            const int note = voice->getCurrentlyPlayingNote();

            if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote())
                bottom = voice;

            if (top == nullptr || note > top->getCurrentlyPlayingNote())
                top = voice;
        }
    }

    jassert (bottom != nullptr && top != nullptr);

    // The oldest note that's playing with the target pitch playing is ideal..
    for (int i = 0; i < usableVoices.size(); ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
            return voice;
    }

    // ..otherwise, look for the oldest note that isn't the top or bottom note..
    for (int i = 0; i < usableVoices.size(); ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != bottom && voice != top)
            return voice;
    }

    // ..otherwise, there's only one or two voices to choose from - we'll return the top one..
    return top;
}
Example #4
0
    var clone (const var& original) const override
    {
        Array<var> arrayCopy;

        if (auto* array = toArray (original.value))
        {
            arrayCopy.ensureStorageAllocated (array->size());

            for (auto& i : *array)
                arrayCopy.add (i.clone());
        }

        return var (arrayCopy);
    }
    bool update (CodeDocument& codeDoc, int lineNum,
                 CodeDocument::Iterator& source,
                 CodeTokeniser* tokeniser, const int tabSpaces,
                 const CodeDocument::Position& selStart,
                 const CodeDocument::Position& selEnd)
    {
        Array <SyntaxToken> newTokens;
        newTokens.ensureStorageAllocated (8);

        if (tokeniser == nullptr)
        {
            const String line (codeDoc.getLine (lineNum));
            addToken (newTokens, line, line.length(), -1);
        }
        else if (lineNum < codeDoc.getNumLines())
        {
            const CodeDocument::Position pos (codeDoc, lineNum, 0);
            createTokens (pos.getPosition(), pos.getLineText(),
                          source, *tokeniser, newTokens);
        }

        replaceTabsWithSpaces (newTokens, tabSpaces);

        int newHighlightStart = 0;
        int newHighlightEnd = 0;

        if (selStart.getLineNumber() <= lineNum && selEnd.getLineNumber() >= lineNum)
        {
            const String line (codeDoc.getLine (lineNum));

            CodeDocument::Position lineStart (codeDoc, lineNum, 0), lineEnd (codeDoc, lineNum + 1, 0);
            newHighlightStart = indexToColumn (jmax (0, selStart.getPosition() - lineStart.getPosition()),
                                               line, tabSpaces);
            newHighlightEnd = indexToColumn (jmin (lineEnd.getPosition() - lineStart.getPosition(), selEnd.getPosition() - lineStart.getPosition()),
                                             line, tabSpaces);
        }

        if (newHighlightStart != highlightColumnStart || newHighlightEnd != highlightColumnEnd)
        {
            highlightColumnStart = newHighlightStart;
            highlightColumnEnd = newHighlightEnd;
        }
        else if (tokens == newTokens)
        {
            return false;
        }

        tokens.swapWith (newTokens);
        return true;
    }
    void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
    {
        const CharPointer_UTF16 utf16 (text.toUTF16());
        const size_t numChars = utf16.length();
        HeapBlock<int16> results (numChars + 1);
        results[numChars] = -1;
        float x = 0;

        if (GetGlyphIndices (dc, utf16, (int) numChars, reinterpret_cast <WORD*> (results.getData()),
                             GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR)
        {
            resultGlyphs.ensureStorageAllocated ((int) numChars);
            xOffsets.ensureStorageAllocated ((int) numChars + 1);

            for (size_t i = 0; i < numChars; ++i)
            {
                resultGlyphs.add (results[i]);
                xOffsets.add (x);
                x += getKerning (dc, results[i], results[i + 1]);
            }
        }

        xOffsets.add (x);
    }
void ComponentBuilder::updateChildComponents (Component& parent, const ValueTree& children)
{
    using namespace ComponentBuilderHelpers;

    const int numExistingChildComps = parent.getNumChildComponents();

    Array <Component*> componentsInOrder;
    componentsInOrder.ensureStorageAllocated (numExistingChildComps);

    {
        OwnedArray<Component> existingComponents;
        existingComponents.ensureStorageAllocated (numExistingChildComps);

        int i;
        for (i = 0; i < numExistingChildComps; ++i)
            existingComponents.add (parent.getChildComponent (i));

        const int newNumChildren = children.getNumChildren();
        for (i = 0; i < newNumChildren; ++i)
        {
            const ValueTree childState (children.getChild (i));

            ComponentBuilder::TypeHandler* const type = getHandlerForState (childState);
            jassert (type != nullptr);

            if (type != nullptr)
            {
                Component* c = findComponentWithID (existingComponents, getStateId (childState));

                if (c == nullptr)
                    c = createNewComponent (*type, childState, &parent);

                componentsInOrder.add (c);
            }
        }

        // (remaining unused items in existingComponents get deleted here as it goes out of scope)
    }

    // Make sure the z-order is correct..
    if (componentsInOrder.size() > 0)
    {
        componentsInOrder.getLast()->toFront (false);

        for (int i = componentsInOrder.size() - 1; --i >= 0;)
            componentsInOrder.getUnchecked(i)->toBehind (componentsInOrder.getUnchecked (i + 1));
    }
}
void AbstractParameterManager::changeListenerCallback (void* source) {
	filter->getCallbackLock().enter();
	// Record filter parameters to a temporary array then exit the lock
	Array <float> params;
	int numParams = filter->getNumParameters();
	params.ensureStorageAllocated(numParams);
	for (int i = 0; i < numParams; ++i) {
		// setUnchecked would be faster!
		params.add(filter->getParameter(i) );
	}
	filter->getCallbackLock().exit();
	for (int i = 0; i < numParams; ++i) {
		properties[i]->setValue(params[i]); 
	}
	sendChangeMessage(this);
}
//==============================================================================
void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumber,
                                                          const double time,
                                                          OwnedArray<MidiMessage>& dest)
{
    bool doneProg = false;
    bool donePitchWheel = false;
    Array<int> doneControllers;
    doneControllers.ensureStorageAllocated (32);

    for (int i = list.size(); --i >= 0;)
    {
        const MidiMessage& mm = list.getUnchecked(i)->message;

        if (mm.isForChannel (channelNumber) && mm.getTimeStamp() <= time)
        {
            if (mm.isProgramChange())
            {
                if (! doneProg)
                {
                    dest.add (new MidiMessage (mm, 0.0));
                    doneProg = true;
                }
            }
            else if (mm.isController())
            {
                if (! doneControllers.contains (mm.getControllerNumber()))
                {
                    dest.add (new MidiMessage (mm, 0.0));
                    doneControllers.add (mm.getControllerNumber());
                }
            }
            else if (mm.isPitchWheel())
            {
                if (! donePitchWheel)
                {
                    dest.add (new MidiMessage (mm, 0.0));
                    donePitchWheel = true;
                }
            }
        }
    }
}
Example #10
0
  void scanInputDevices ()
  {
#if 0
    StateType::WriteAccess state (m_state);

    // Make a copy of the currently connected devices.
    Array <InputImp*> disconnected;
    disconnected.ensureStorageAllocated (state->inputs.size ());
    for (int i = 0; i < state->inputs.size (); ++i)
    {
      InputImp* const device = state->inputs [i];
      if (device->getDeviceIndex () != -1)
        disconnected.add (device);
    }

    // Enumerate connected devices.
    StringArray const devices (juce::MidiInput::getDevices ());
    for (int deviceIndex = 0; deviceIndex < devices.size (); ++deviceIndex)
    {
      CompareDevices comp;
      String const deviceName (devices [deviceIndex]);

      // Remove this device from list of disconnected devices.
      {
        int const index = disconnected.indexOfSorted (comp, deviceName);
        if (index != -1)
          disconnected.remove (index);
      }

      // Find this device in our list.
      int const index = state->inputs.indexOfSorted (comp, deviceName);

      if (index != -1)
      {
        // Device already exists
        InputImp* const device = state->inputs [index];

        // Notify listeners with connected status.
        if (device->getDeviceIndex () == -1)
        {
          m_listeners.queue (&Listener::onMidiDevicesStatus, device, true);

          Logger::outputDebugString (device->getName () + ": connected");
        }

        device->setDeviceIndex (deviceIndex);
      }
      else
      {
        InputImp* const device = new InputImp (deviceName, deviceIndex);

        state->inputs.addSorted (comp, device);
      }
    }

    // Notify listeners with disconnected status.
    for (int i = 0; i < disconnected.size (); ++i)
    {
      InputImp* const device = disconnected [i];
      device->setDeviceIndex (-1);

      m_listeners.queue (&Listener::onMidiDevicesStatus, device, false);

      Logger::outputDebugString (device->getName () + ": disconnected");
    }
#else
    jassertfalse;
#endif
  }
Example #11
0
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
                                                 int /*midiChannel*/, int midiNoteNumber) const
{
    SynthesiserVoice* bottom = nullptr;
    SynthesiserVoice* top    = nullptr;

    // this is a list of voices we can steal, sorted by how long they've been running
    Array<SynthesiserVoice*> usableVoices;
    usableVoices.ensureStorageAllocated (voices.size());

    for (int i = 0; i < voices.size(); ++i)
    {
        SynthesiserVoice* const voice = voices.getUnchecked (i);

        if (voice->canPlaySound (soundToPlay))
        {
            VoiceAgeSorter sorter;
            usableVoices.addSorted (sorter, voice);

            const int note = voice->getCurrentlyPlayingNote();

            if (bottom == nullptr || note < bottom->getCurrentlyPlayingNote())
                bottom = voice;

            if (top == nullptr || note > top->getCurrentlyPlayingNote())
                top = voice;
        }
    }

    const int numUsableVoices = usableVoices.size();

    // The oldest note that's playing with the target pitch playing is ideal..
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
            return voice;
    }

    // Oldest voice that's isn't being held:
    // (this could be the top or bottom note if it had just been released.)
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (! (voice->isKeyDown() || voice->isSostenutoPedalDown()))
            return voice;
    }

    // Oldest voice that doesn't have a finger on it:
    // (this could be the top or bottom note if it had just been released.)
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (! voice->isKeyDown())
            return voice;
    }

    // At this point, all notes have fingers on them, so look for the oldest note
    // that isn't the top or bottom note..
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != bottom && voice != top)
            return voice;
    }

    // ..otherwise, there's only one or two voices to choose from - prefer to steal the highest one:
    jassert (top != nullptr || bottom != nullptr);
    return top != nullptr ? top : bottom;
}
    static void createStroke (const float thickness, const PathStrokeType::JointStyle jointStyle,
                              const PathStrokeType::EndCapStyle endStyle,
                              Path& destPath, const Path& source,
                              const AffineTransform& transform,
                              const float extraAccuracy, const Arrowhead* const arrowhead)
    {
        jassert (extraAccuracy > 0);

        if (thickness <= 0)
        {
            destPath.clear();
            return;
        }

        const Path* sourcePath = &source;
        Path temp;

        if (sourcePath == &destPath)
        {
            destPath.swapWithPath (temp);
            sourcePath = &temp;
        }
        else
        {
            destPath.clear();
        }

        destPath.setUsingNonZeroWinding (true);

        const float maxMiterExtensionSquared = 9.0f * thickness * thickness;
        const float width = 0.5f * thickness;

        // Iterate the path, creating a list of the
        // left/right-hand lines along either side of it...
        PathFlatteningIterator it (*sourcePath, transform, PathFlatteningIterator::defaultTolerance / extraAccuracy);

        Array <LineSection> subPath;
        subPath.ensureStorageAllocated (512);
        LineSection l;
        l.x1 = 0;
        l.y1 = 0;

        const float minSegmentLength = 0.0001f;

        while (it.next())
        {
            if (it.subPathIndex == 0)
            {
                if (subPath.size() > 0)
                {
                    addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead);
                    subPath.clearQuick();
                }

                l.x1 = it.x1;
                l.y1 = it.y1;
            }

            l.x2 = it.x2;
            l.y2 = it.y2;

            float dx = l.x2 - l.x1;
            float dy = l.y2 - l.y1;

            const float hypotSquared = dx*dx + dy*dy;

            if (it.closesSubPath || hypotSquared > minSegmentLength || it.isLastInSubpath())
            {
                const float len = std::sqrt (hypotSquared);

                if (len == 0)
                {
                    l.rx1 = l.rx2 = l.lx1 = l.lx2 = l.x1;
                    l.ry1 = l.ry2 = l.ly1 = l.ly2 = l.y1;
                }
                else
                {
                    const float offset = width / len;
                    dx *= offset;
                    dy *= offset;

                    l.rx2 = l.x1 - dy;
                    l.ry2 = l.y1 + dx;
                    l.lx1 = l.x1 + dy;
                    l.ly1 = l.y1 - dx;

                    l.lx2 = l.x2 + dy;
                    l.ly2 = l.y2 - dx;
                    l.rx1 = l.x2 - dy;
                    l.ry1 = l.y2 + dx;
                }

                subPath.add (l);

                if (it.closesSubPath)
                {
                    addSubPath (destPath, subPath, true, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead);
                    subPath.clearQuick();
                }
                else
                {
                    l.x1 = it.x2;
                    l.y1 = it.y2;
                }
            }
        }

        if (subPath.size() > 0)
            addSubPath (destPath, subPath, false, width, maxMiterExtensionSquared, jointStyle, endStyle, arrowhead);
    }
void SimpleTypeLayout::getGlyphLayout (const AttributedString& text, GlyphLayout& glyphLayout)
{
    clear();
    int stringLength = text.getText().length();
    int numCharacterAttributes = text.getCharAttributesSize();
    int rangeStart = 0;
    // Character attributes are applied as a series of ranges. These ranges may overlap or there may
    // be multiple attributes for the same range. We need to come up with a single set of unique and
    // non overlapping ranges.
    // The approach below iterates through every character, looks through all the attributes and their
    // ranges and stores the last font or color that was applied. It think constructs a set of unique
    // and non overlapping ranges by comparing each character's attributes to the previous characters
    // attributes.
    // A more efficient approach may be to split and combine the individual ranges to construct the
    // set of unique and non overlapping ranges. This more efficient approach, if possible, would only
    // need to iterate through all the attributes once. This would be much faster than the current
    // approach which loops through all the attributes for every character.
    Font defaultFont;
    Colour defaultColour (Colours::black);
    Array<RunAttribute> runAttributes;
    Array<CharAttribute> charAttributes;
    charAttributes.ensureStorageAllocated (stringLength);
    // Iterate through every character in the string
    for (int i = 0; i < stringLength; ++i)
    {
        CharAttribute attribute;
        attribute.font = &defaultFont;
        attribute.colour = &defaultColour;
        // Iterate through every character attribute
        for (int j = 0; j < numCharacterAttributes; ++j)
        {
            Attr* attr = text.getCharAttribute (j);
            // Check if the current character falls within the range of a font attribute
            if (attr->attribute == Attr::font && (i >= attr->range.getStart()) && (i < attr->range.getEnd()))
            {
                AttrFont* attrFont = static_cast<AttrFont*>(attr);
                attribute.font = &(attrFont->font);
            }
            // Check if the current character falls within the range of a foreground colour attribute
            if (attr->attribute == Attr::foregroundColour && (i >= attr->range.getStart()) && (i < attr->range.getEnd()))
            {
                AttrColour* attrColour = static_cast<AttrColour*>(attr);
                attribute.colour = &(attrColour->colour);
            }
        }
        charAttributes.add(attribute);
        // Skip the first character since we are comparing to previous characters
        if (i == 0)
            continue;
        if ((charAttributes[i-1].font != charAttributes[i].font) ||
            (charAttributes[i-1].colour != charAttributes[i].colour) ||
            (*(charAttributes[i-1].font) != *(charAttributes[i].font)) ||
            (*(charAttributes[i-1].colour) != *(charAttributes[i].colour)) ||
            (i + 1 == stringLength))
        {
            // The current character has a new font or new color or there is no next character
            RunAttribute attribute;
            attribute.range.setStart (rangeStart);
            attribute.range.setEnd (i);
            if (i + 1 == stringLength)
                attribute.range.setEnd (i+1);
            attribute.font = charAttributes[i-1].font;
            attribute.colour = charAttributes[i-1].colour;
            runAttributes.add (attribute);
            rangeStart = i;
        }

    }
    charAttributes.clear();
    for (int i = 0; i < runAttributes.size(); ++i)
    {
        appendText (text, runAttributes[i].range, *(runAttributes[i].font), *(runAttributes[i].colour));
    }
    runAttributes.clear();
    // Run layout to break strings into words and create lines from words
    layout ((int) glyphLayout.getWidth());
    // Use tokens to create Glyph Structures
    glyphLayout.setNumLines (getNumLines());
    // Set Starting Positions to 0
    int charPosition = 0;
    int lineStartPosition = 0;
    int runStartPosition = 0;
    // Create first GlyphLine and GlyphRun
    GlyphLine* glyphLine = new GlyphLine();
    GlyphRun* glyphRun = new GlyphRun();
    for (int i = 0; i < tokens.size(); ++i)
    {
        const Token* const t = tokens.getUnchecked (i);
        // See TextLayout::draw
        const float xOffset = (float) t->x;
        const float yOffset = (float) t->y;
        // See GlyphArrangement::addCurtailedLineOfText
        Array <int> newGlyphs;
        Array <float> xOffsets;
        t->font.getGlyphPositions (t->text.trimEnd(), newGlyphs, xOffsets);
        // Resize glyph run array
        glyphRun->setNumGlyphs (glyphRun->getNumGlyphs() + newGlyphs.size());
        // Add each glyph in the token to the current GlyphRun
        for (int j = 0; j < newGlyphs.size(); ++j)
        {
            const float thisX = xOffsets.getUnchecked (j);
            // Check if this is the first character in the line
            if (charPosition == lineStartPosition)
            {
                // Save line offset data
                Point<float> origin (xOffset, yOffset + t->font.getAscent());
                glyphLine->setLineOrigin (origin);
            }
            float xPos = glyphLayout.getX() + glyphLine->getLineOrigin().getX() + xOffset + thisX;
            float yPos = glyphLayout.getY() + glyphLine->getLineOrigin().getY();
            Glyph* glyph = new Glyph (newGlyphs.getUnchecked(j), xPos, yPos);
            glyphRun->addGlyph (glyph);
            charPosition++;
        }
        if (t->isWhitespace || t->isNewLine)
            ++charPosition;
        // We have reached the end of a token, we may need to create a new run or line
        if (i + 1 == tokens.size())
        {
            // We have reached the last token
            // Close GlyphRun
            Range<int> runRange (runStartPosition, charPosition);
            glyphRun->setStringRange (runRange);
            glyphRun->setFont (t->font);
            glyphRun->setColour (t->colour);
            // Check if run descent is the largest in the line
            if (t->font.getDescent() > glyphLine->getDescent())
                glyphLine->setDescent (t->font.getDescent());
            glyphLine->addGlyphRun (glyphRun);
            // Close GlyphLine
            Range<int> lineRange (lineStartPosition, charPosition);
            glyphLine->setStringRange (lineRange);
            glyphLayout.addGlyphLine (glyphLine);
        }
        else
        {
            // We have not yet reached the last token
            const Token* const nextt = tokens.getUnchecked (i+1);
            if (t->font != nextt->font || t->colour != nextt->colour)
            {
                //The next token has a new font or new colour
                // Close GlyphRun
                Range<int> runRange (runStartPosition, charPosition);
                glyphRun->setStringRange (runRange);
                glyphRun->setFont (t->font);
                glyphRun->setColour (t->colour);
                // Check if run descent is the largest in the line
                if (t->font.getDescent() > glyphLine->getDescent())
                    glyphLine->setDescent (t->font.getDescent());
                glyphLine->addGlyphRun (glyphRun);
                // Create the next GlyphRun
                runStartPosition = charPosition;
                glyphRun = new GlyphRun();
            }
            if (t->line != nextt->line)
            {
                // The next token is in a new line
                // Close GlyphRun
                Range<int> runRange (runStartPosition, charPosition);
                glyphRun->setStringRange (runRange);
                glyphRun->setFont (t->font);
                glyphRun->setColour (t->colour);
                // Check if run descent is the largest in the line
                if (t->font.getDescent() > glyphLine->getDescent())
                    glyphLine->setDescent (t->font.getDescent());
                glyphLine->addGlyphRun (glyphRun);
                // Close GlyphLine
                Range<int> lineRange (lineStartPosition, charPosition);
                glyphLine->setStringRange (lineRange);
                glyphLayout.addGlyphLine (glyphLine);
                // Create the next GlyphLine and GlyphRun
                runStartPosition = charPosition;
                lineStartPosition = charPosition;
                glyphLine = new GlyphLine();
                glyphRun = new GlyphRun();
            }
        }
    }
    // Apply Layout Text Alignment
    if (text.getTextAlignment() == AttributedString::right ||
        text.getTextAlignment() == AttributedString::center)
    {
        int totalW = (int) glyphLayout.getWidth();
        for (int i = 0; i < getNumLines(); ++i)
        {
            const int lineW = getLineWidth (i);
            int dx = 0;
            if (text.getTextAlignment() == AttributedString::right)
            {
                dx = totalW - lineW;
            }
            else if (text.getTextAlignment() == AttributedString::center)
            {
                dx = (totalW - lineW) / 2;
            }
            GlyphLine& glyphLine = glyphLayout.getGlyphLine (i);
            Point<float> lineOrigin = glyphLine.getLineOrigin();
            lineOrigin.setX (lineOrigin.getX() + dx);
            glyphLine.setLineOrigin (lineOrigin);
        }
    }
}
    // Searches a buffer for a set of spikes that matches those in the test sound
    int findOffsetOfSpikes (const AudioSampleBuffer& buffer) const
    {
        const float minSpikeLevel = 5.0f;
        const double smooth = 0.975;
        const float* s = buffer.getSampleData (0, 0);
        const int spikeDriftAllowed = 5;

        Array <int> spikesFound;
        spikesFound.ensureStorageAllocated (100);
        double runningAverage = 0;
        int lastSpike = 0;

        for (int i = 0; i < buffer.getNumSamples() - 10; ++i)
        {
            const float samp = fabsf (s[i]);

            if (samp > runningAverage * minSpikeLevel && i > lastSpike + 20)
            {
                lastSpike = i;
                spikesFound.add (i);
            }

            runningAverage = runningAverage * smooth + (1.0 - smooth) * samp;
        }

        int bestMatch = -1;
        int bestNumMatches = spikes.size() / 3; // the minimum number of matches required

        if (spikesFound.size() < bestNumMatches)
            return -1;

        for (int offsetToTest = 0; offsetToTest < buffer.getNumSamples() - 2048; ++offsetToTest)
        {
            int numMatchesHere = 0;
            int foundIndex = 0;

            for (int refIndex = 0; refIndex < spikes.size(); ++refIndex)
            {
                const int referenceSpike = spikes.getUnchecked (refIndex) + offsetToTest;
                int spike = 0;

                while ((spike = spikesFound.getUnchecked (foundIndex)) < referenceSpike - spikeDriftAllowed
                         && foundIndex < spikesFound.size() - 1)
                    ++foundIndex;

                if (spike >= referenceSpike - spikeDriftAllowed && spike <= referenceSpike + spikeDriftAllowed)
                    ++numMatchesHere;
            }

            if (numMatchesHere > bestNumMatches)
            {
                bestNumMatches = numMatchesHere;
                bestMatch = offsetToTest;

                if (numMatchesHere == spikes.size())
                    break;
            }
        }

        return bestMatch;
    }
    bool update (CodeDocument& document, int lineNum,
                 CodeDocument::Iterator& source,
                 CodeTokeniser* analyser, const int spacesPerTab,
                 const CodeDocument::Position& selectionStart,
                 const CodeDocument::Position& selectionEnd)
    {
        Array <SyntaxToken> newTokens;
        newTokens.ensureStorageAllocated (8);

        if (analyser == nullptr)
        {
            newTokens.add (SyntaxToken (document.getLine (lineNum), -1));
        }
        else if (lineNum < document.getNumLines())
        {
            const CodeDocument::Position pos (&document, lineNum, 0);
            createTokens (pos.getPosition(), pos.getLineText(),
                          source, analyser, newTokens);
        }

        replaceTabsWithSpaces (newTokens, spacesPerTab);

        int newHighlightStart = 0;
        int newHighlightEnd = 0;

        if (selectionStart.getLineNumber() <= lineNum && selectionEnd.getLineNumber() >= lineNum)
        {
            const String line (document.getLine (lineNum));

            CodeDocument::Position lineStart (&document, lineNum, 0), lineEnd (&document, lineNum + 1, 0);
            newHighlightStart = indexToColumn (jmax (0, selectionStart.getPosition() - lineStart.getPosition()),
                                               line, spacesPerTab);
            newHighlightEnd = indexToColumn (jmin (lineEnd.getPosition() - lineStart.getPosition(), selectionEnd.getPosition() - lineStart.getPosition()),
                                             line, spacesPerTab);
        }

        if (newHighlightStart != highlightColumnStart || newHighlightEnd != highlightColumnEnd)
        {
            highlightColumnStart = newHighlightStart;
            highlightColumnEnd = newHighlightEnd;
        }
        else
        {
            if (tokens.size() == newTokens.size())
            {
                bool allTheSame = true;

                for (int i = newTokens.size(); --i >= 0;)
                {
                    if (tokens.getReference(i) != newTokens.getReference(i))
                    {
                        allTheSame = false;
                        break;
                    }
                }

                if (allTheSame)
                    return false;
            }
        }

        tokens.swapWithArray (newTokens);
        return true;
    }
MPESynthesiserVoice* MPESynthesiser::findVoiceToSteal (MPENote noteToStealVoiceFor) const
{
    // This voice-stealing algorithm applies the following heuristics:
    // - Re-use the oldest notes first
    // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.


    // apparently you are trying to render audio without having any voices...
    jassert (voices.size() > 0);

    // These are the voices we want to protect (ie: only steal if unavoidable)
    MPESynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
    MPESynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase

    // this is a list of voices we can steal, sorted by how long they've been running
    Array<MPESynthesiserVoice*> usableVoices;
    usableVoices.ensureStorageAllocated (voices.size());

    for (int i = 0; i < voices.size(); ++i)
    {
        MPESynthesiserVoice* const voice = voices.getUnchecked (i);
        jassert (voice->isActive()); // We wouldn't be here otherwise

        MPEVoiceAgeSorter sorter;
        usableVoices.addSorted (sorter, voice);

        if (! voice->isPlayingButReleased()) // Don't protect released notes
        {
            const int noteNumber = voice->getCurrentlyPlayingNote().initialNote;

            if (low == nullptr || noteNumber < low->getCurrentlyPlayingNote().initialNote)
                low = voice;

            if (top == nullptr || noteNumber > top->getCurrentlyPlayingNote().initialNote)
                top = voice;
        }
    }

    // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
    if (top == low)
        top = nullptr;

    const int numUsableVoices = usableVoices.size();

    // If we want to re-use the voice to trigger a new note,
    // then The oldest note that's playing the same note number is ideal.
    if (noteToStealVoiceFor.isValid())
    {
        for (int i = 0; i < numUsableVoices; ++i)
        {
            MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);

            if (voice->getCurrentlyPlayingNote().initialNote == noteToStealVoiceFor.initialNote)
                return voice;
        }
    }

    // Oldest voice that has been released (no finger on it and not held by sustain pedal)
    for (int i = 0; i < numUsableVoices; ++i)
    {
        MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != low && voice != top && voice->isPlayingButReleased())
            return voice;
    }

    // Oldest voice that doesn't have a finger on it:
    for (int i = 0; i < numUsableVoices; ++i)
    {
        MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != low && voice != top
             && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDown
             && voice->getCurrentlyPlayingNote().keyState != MPENote::keyDownAndSustained)
            return voice;
    }

    // Oldest voice that isn't protected
    for (int i = 0; i < numUsableVoices; ++i)
    {
        MPESynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != low && voice != top)
            return voice;
    }

    // We've only got "protected" voices now: lowest note takes priority
    jassert (low != nullptr);

    // Duophonic synth: give priority to the bass note:
    if (top != nullptr)
        return top;

    return low;
}
Example #17
0
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
                                                 int /*midiChannel*/, int midiNoteNumber) const
{
    // This voice-stealing algorithm applies the following heuristics:
    // - Re-use the oldest notes first
    // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.

    // apparently you are trying to render audio without having any voices...
    jassert (! voices.isEmpty());

    // These are the voices we want to protect (ie: only steal if unavoidable)
    SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
    SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase

    // this is a list of voices we can steal, sorted by how long they've been running
    Array<SynthesiserVoice*> usableVoices;
    usableVoices.ensureStorageAllocated (voices.size());

    for (auto* voice : voices)
    {
        if (voice->canPlaySound (soundToPlay))
        {
            jassert (voice->isVoiceActive()); // We wouldn't be here otherwise

            usableVoices.add (voice);

            // NB: Using a functor rather than a lambda here due to scare-stories about
            // compilers generating code containing heap allocations..
            struct Sorter
            {
                bool operator() (const SynthesiserVoice* a, const SynthesiserVoice* b) const noexcept { return a->wasStartedBefore (*b); }
            };

            std::sort (usableVoices.begin(), usableVoices.end(), Sorter());

            if (! voice->isPlayingButReleased()) // Don't protect released notes
            {
                auto note = voice->getCurrentlyPlayingNote();

                if (low == nullptr || note < low->getCurrentlyPlayingNote())
                    low = voice;

                if (top == nullptr || note > top->getCurrentlyPlayingNote())
                    top = voice;
            }
        }
    }

    // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
    if (top == low)
        top = nullptr;

    // The oldest note that's playing with the target pitch is ideal..
    for (auto* voice : usableVoices)
        if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
            return voice;

    // Oldest voice that has been released (no finger on it and not held by sustain pedal)
    for (auto* voice : usableVoices)
        if (voice != low && voice != top && voice->isPlayingButReleased())
            return voice;

    // Oldest voice that doesn't have a finger on it:
    for (auto* voice : usableVoices)
        if (voice != low && voice != top && ! voice->isKeyDown())
            return voice;

    // Oldest voice that isn't protected
    for (auto* voice : usableVoices)
        if (voice != low && voice != top)
            return voice;

    // We've only got "protected" voices now: lowest note takes priority
    jassert (low != nullptr);

    // Duophonic synth: give priority to the bass note:
    if (top != nullptr)
        return top;

    return low;
}
Example #18
0
SynthesiserVoice* Synthesiser::findVoiceToSteal (SynthesiserSound* soundToPlay,
                                                 int /*midiChannel*/, int midiNoteNumber) const
{
    // This voice-stealing algorithm applies the following heuristics:
    // - Re-use the oldest notes first
    // - Protect the lowest & topmost notes, even if sustained, but not if they've been released.

    // These are the voices we want to protect (ie: only steal if unavoidable)
    SynthesiserVoice* low = nullptr; // Lowest sounding note, might be sustained, but NOT in release phase
    SynthesiserVoice* top = nullptr; // Highest sounding note, might be sustained, but NOT in release phase

    // this is a list of voices we can steal, sorted by how long they've been running
    Array<SynthesiserVoice*> usableVoices;
    usableVoices.ensureStorageAllocated (voices.size());

    for (int i = 0; i < voices.size(); ++i)
    {
        SynthesiserVoice* const voice = voices.getUnchecked (i);

        if (voice->canPlaySound (soundToPlay))
        {
            jassert (voice->isVoiceActive()); // We wouldn't be here otherwise

            VoiceAgeSorter sorter;
            usableVoices.addSorted (sorter, voice);

            if (! voice->isPlayingButReleased()) // Don't protect released notes
            {
                const int note = voice->getCurrentlyPlayingNote();

                if (low == nullptr || note < low->getCurrentlyPlayingNote())
                    low = voice;

                if (top == nullptr || note > top->getCurrentlyPlayingNote())
                    top = voice;
            }
        }
    }

    // Eliminate pathological cases (ie: only 1 note playing): we always give precedence to the lowest note(s)
    if (top == low)
        top = nullptr;

    const int numUsableVoices = usableVoices.size();

    // The oldest note that's playing with the target pitch is ideal..
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice->getCurrentlyPlayingNote() == midiNoteNumber)
            return voice;
    }

    // Oldest voice that has been released (no finger on it and not held by sustain pedal)
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != low && voice != top && voice->isPlayingButReleased())
            return voice;
    }

    // Oldest voice that doesn't have a finger on it:
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != low && voice != top && ! voice->isKeyDown())
            return voice;
    }

    // Oldest voice that isn't protected
    for (int i = 0; i < numUsableVoices; ++i)
    {
        SynthesiserVoice* const voice = usableVoices.getUnchecked (i);

        if (voice != low && voice != top)
            return voice;
    }

    // We've only got "protected" voices now: lowest note takes priority
    jassert (low != nullptr);

    // Duophonic synth: give priority to the bass note:
    if (top != nullptr)
        return top;

    return low;
}