Completed
Push — master ( af3ab7...be78bc )
by Patrick
02:17
created

HarmonicCalculator::findNaturalHarmonics()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 22
rs 8.9197
c 0
b 0
f 0
cc 4
eloc 12
nc 4
nop 3
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace ExtendedStrings\Strings;
6
7
use ExtendedStrings\Strings\Instrument\InstrumentInterface;
8
9
class HarmonicCalculator
10
{
11
    /**
12
     * @param \ExtendedStrings\Strings\Note                           $soundingNote
13
     * @param \ExtendedStrings\Strings\Instrument\InstrumentInterface $instrument
14
     * @param float                                                   $tolerance
15
     *
16
     * @return Harmonic[]
17
     */
18
    public function getHarmonicsForSoundingNote(Note $soundingNote, InstrumentInterface $instrument, float $tolerance = 50.0): array
19
    {
20
        $harmonics = [];
21
        foreach ($instrument->getStringFrequencies() as $stringFrequency) {
22
            $string = new VibratingString($stringFrequency);
23
            $harmonics += $this->findNaturalHarmonics($soundingNote, $string, $tolerance)
24
                + $this->findArtificialHarmonics($soundingNote, $string);
25
        }
26
27
        return $harmonics;
28
    }
29
30
    private function findArtificialHarmonics(Note $soundingNote, VibratingString $string): array
31
    {
32
        $harmonics = [];
33
        $soundingNoteFrequency = $soundingNote->getFrequency();
34
        $stringFrequency = $string->getStoppedFrequency();
35
        foreach (range(6, 2) as $number) {
36
            $fundamental = $soundingNoteFrequency / $number;
37
            if ($fundamental > $stringFrequency) {
38
                $baseStop = $string->getStringLength($fundamental);
39
                $halfStop = (($number - 1) / $number) * $baseStop;
40
41
                $harmonics[] = new Harmonic($halfStop, $baseStop, $string);
42
            }
43
        }
44
45
        return $harmonics;
46
    }
47
48
    private function findNaturalHarmonics(Note $soundingNote, VibratingString $string, float $tolerance = 50.0): array
49
    {
50
        $harmonics = [];
51
        $soundingCents = $soundingNote->getCents();
52
        foreach (range(1, 8) as $number) {
53
            // Convert harmonic number to (possible sounding) frequency.
54
            $candidateFrequency = $string->getHarmonicSoundingFrequency(1 / $number);
55
56
            // Convert (possible sounding) frequency to cents above C4, for comparison.
57
            $candidate = Note::fromFrequency($candidateFrequency, 440.0, [$soundingNote->getAccidental()]);
58
            $difference = abs($candidate->getCents() - $soundingCents);
59
60
            if ($difference < $tolerance) {
61
                $stringLengths = Harmonic::getStringLengthsFromNumber($number, true);
62
                foreach ($stringLengths as $stringLength) {
63
                    $harmonics[] = new Harmonic($stringLength, 1.0, $string);
64
                }
65
            }
66
        }
67
68
        return $harmonics;
69
    }
70
}
71