Completed
Push — master ( a29145...af3ab7 )
by Patrick
02:03
created

VibratingString::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace ExtendedStrings\Strings;
6
7
class VibratingString
8
{
9
    private $frequency;
10
11
    /**
12
     * VibratingString constructor.
13
     *
14
     * @param float $frequency The open string frequency (in Hz).
15
     */
16
    public function __construct(float $frequency)
17
    {
18
        $this->frequency = $frequency;
19
    }
20
21
    /**
22
     * Calculate the frequency for a stop.
23
     *
24
     * @param float $stringLength The length of the vibrating part of the
25
     *                            string, as a fraction of the whole string.
26
     *
27
     * @return float A frequency (in Hz).
28
     */
29
    public function getStoppedFrequency(float $stringLength = 1.0): float
30
    {
31
        self::validateStringLength($stringLength);
32
        $centsOverString = Cent::frequenciesToCents($stringLength, 1);
33
34
        return Cent::centsToFrequency($centsOverString, $this->frequency);
35
    }
36
37
    /**
38
     * Find the sounding frequency of a harmonic-pressure stop on this string.
39
     *
40
     * @param float $stringLength The string length, as a fraction, between the
41
     *                            open string and the stop (either side: both
42
     *                            .2 and .8 would produce the same result).
43
     *
44
     * @return float The sounding frequency of a harmonic-pressure stop at the
45
     *               given string length.
46
     */
47
    public function getHarmonicSoundingFrequency(float $stringLength = 1.0): float
48
    {
49
        self::validateStringLength($stringLength);
50
51
        return $this->frequency * self::getHarmonicNumber($stringLength);
52
    }
53
54
    /**
55
     * Calculate the length of this string needed to produce a given frequency.
56
     *
57
     * @param float $frequency The frequency for which to calculate the string
58
     *                         length.
59
     *
60
     * @return float
61
     *   The length of the vibrating part of the string, as a fraction of the
62
     *   whole string's length.
63
     */
64
    public function getStringLength(float $frequency): float
65
    {
66
        if (Math::isZero($frequency)) {
67
            return 0;
68
        }
69
        $centsOverString = Cent::frequenciesToCents($this->frequency, $frequency);
70
71
        return $this->centsToStringLength($centsOverString);
72
    }
73
74
    /**
75
     * Convert a string length to a harmonic number.
76
     *
77
     * @param float $stringLength The string length between the open string and
78
     *                            the harmonic-pressure stop (either side), as a
79
     *                            fraction.
80
     *
81
     * @throws \InvalidArgumentException If the stop is not a sounding natural
82
     *                                   harmonic.
83
     *
84
     * @return int The harmonic number.
85
     */
86
    public static function getHarmonicNumber(float $stringLength): int
87
    {
88
        self::validateStringLength($stringLength);
89
        $number = intval(1 / Math::gcd(1, $stringLength));
90
        if ($number > 100) {
91
            throw new \InvalidArgumentException(sprintf('Invalid string length for a harmonic: %f', $stringLength));
92
        }
93
94
        return $number;
95
    }
96
97
    /**
98
     * Validate a string length.
99
     *
100
     * @param float $length
101
     *
102
     * @throws \InvalidArgumentException If the string length is invalid.
103
     */
104
    public static function validateStringLength(float $length): void
105
    {
106
        if ($length < 0 || Math::isZero($length) || $length > 1) {
107
            throw new \InvalidArgumentException(sprintf('Invalid string length: %f', $length));
108
        }
109
        // @todo validate against string width
110
    }
111
112
    /**
113
     * Convert a number of cents to a string length.
114
     *
115
     * @param float $cents The number of cents between the open string and the
116
     *                     stopped pitch.
117
     *
118
     * @return float The length of the vibrating string, as a fraction of the
119
     * whole string's length.
120
     */
121
    private function centsToStringLength(float $cents): float
122
    {
123
        return 1 / pow(2, $cents / 1200);
124
    }
125
}
126