1 : <?php
2 :
3 : /**
4 : * \Midi\Util\Timer
5 : *
6 : * @package Midi
7 : * @subpackage Util
8 : * @copyright © 2009 Tommy Montgomery <http://phpmidiparser.com/>
9 : * @since 1.0
10 : */
11 :
12 : namespace Midi\Util;
13 :
14 : /**
15 : * Class for calculating human-usable musical time divisions
16 : * from the internal MIDI clock ticks
17 : *
18 : * @package Midi
19 : * @subpackage Util
20 : * @since 1.0
21 : */
22 1 : class Timer {
23 :
24 : /**
25 : * The number of ticks per quarter note
26 : *
27 : * @var int
28 : */
29 : protected $timeDivision;
30 :
31 : /**
32 : * Storage for triplet division calculations
33 : *
34 : * @var array
35 : */
36 : protected $triplets;
37 :
38 : /**
39 : * Constructor
40 : *
41 : * @since 1.0
42 : *
43 : * @param int $timeDivision The number of ticks per quarter note
44 : * @throws InvalidArgumentException
45 : * @throws DomainException
46 : */
47 : public function __construct($timeDivision) {
48 16 : if (!is_int($timeDivision)) {
49 1 : throw new \InvalidArgumentException('1st argument must be an integer');
50 : }
51 16 : if ($timeDivision < 1) {
52 1 : throw new \DomainException('Time division must be greater than zero');
53 : }
54 :
55 16 : $this->timeDivision = $timeDivision;
56 16 : $this->triplets = array();
57 16 : }
58 :
59 : /**
60 : * Gets the time division
61 : *
62 : * @since 1.0
63 : *
64 : * @return int
65 : */
66 : public final function getTimeDivision() {
67 2 : return $this->timeDivision;
68 : }
69 :
70 : /**
71 : * Gets the number of ticks needed for the specified
72 : * note division
73 : *
74 : * @since 1.0
75 : *
76 : * @param int $division The note division
77 : * @return int
78 : */
79 : protected function getTicksForNoteDivision($division) {
80 7 : return (int)floor($this->timeDivision / $division);
81 : }
82 :
83 : /**
84 : * Gets the number of ticks needed for a 64th note
85 : *
86 : * @since 1.0
87 : * @uses getTicksForNoteDivision()
88 : *
89 : * @return int
90 : */
91 : public final function sixtyFourthNote() {
92 1 : return $this->getTicksForNoteDivision(16);
93 : }
94 :
95 : /**
96 : * Gets the number of ticks needed for a 32nd note
97 : *
98 : * @since 1.0
99 : * @uses getTicksForNoteDivision()
100 : *
101 : * @return int
102 : */
103 : public final function thirtySecondNote() {
104 1 : return $this->getTicksForNoteDivision(8);
105 : }
106 :
107 : /**
108 : * Gets the number of ticks needed for a sixteenth note
109 : *
110 : * @since 1.0
111 : * @uses getTicksForNoteDivision()
112 : *
113 : * @return int
114 : */
115 : public final function sixteenthNote() {
116 1 : return $this->getTicksForNoteDivision(4);
117 : }
118 :
119 : /**
120 : * Gets the number of ticks needed for an eighth note
121 : *
122 : * @since 1.0
123 : * @uses getTicksForNoteDivision()
124 : *
125 : * @return int
126 : */
127 : public final function eighthNote() {
128 1 : return $this->getTicksForNoteDivision(2);
129 : }
130 :
131 : /**
132 : * Gets the number of ticks needed for a quarter note
133 : *
134 : * @since 1.0
135 : * @uses getTicksForNoteDivision()
136 : *
137 : * @return int
138 : */
139 : public final function quarterNote() {
140 1 : return $this->getTicksForNoteDivision(1);
141 : }
142 :
143 : /**
144 : * Gets the number of ticks needed for a half note
145 : *
146 : * @since 1.0
147 : * @uses getTicksForNoteDivision()
148 : *
149 : * @return int
150 : */
151 : public final function halfNote() {
152 1 : return $this->getTicksForNoteDivision(.5);
153 : }
154 :
155 : /**
156 : * Gets the number of ticks needed for a whole note
157 : *
158 : * @since 1.0
159 : * @uses getTicksForNoteDivision()
160 : *
161 : * @return int
162 : */
163 : public final function wholeNote() {
164 1 : return $this->getTicksForNoteDivision(.25);
165 : }
166 :
167 : /**
168 : * Dot-izes the number of ticks
169 : *
170 : * @since 1.0
171 : *
172 : * @param int $ticks
173 : * @return int
174 : */
175 : public function dot($ticks) {
176 1 : return (int)floor($ticks * 1.5);
177 : }
178 :
179 : /**
180 : * Triplet-izes the number of ticks
181 : *
182 : * @since 1.0
183 : * @uses resolveTriplet()
184 : *
185 : * @param int $ticks
186 : * @return int
187 : */
188 : public function triplet($ticks) {
189 1 : $tripletTicks = (int)floor($ticks / 3);
190 1 : if (isset($this->triplets[$ticks])) {
191 1 : if ($this->triplets[$ticks]=== $tripletTicks * 2) {
192 1 : $tripletTicks = $this->resolveTriplet($ticks);
193 1 : } else {
194 1 : $this->triplets[$ticks] += $tripletTicks;
195 : }
196 1 : } else {
197 1 : $this->triplets[$ticks] = $tripletTicks;
198 : }
199 :
200 1 : return $tripletTicks;
201 : }
202 :
203 : /**
204 : * Resolves rounding errors when calculating triplets
205 : *
206 : * For example, when calculating the number of ticks for
207 : * a triplet that should be 100 ticks long, the default
208 : * division will be 33, 33 and 33 for each note of the
209 : * triplet. That can mess up your calculations later on
210 : * since the division is one tick short of what it should
211 : * be. This method will solve those rounding errors by
212 : * returning the remainder for the third note of the triplet,
213 : * e.g. 33, 33 and 34 for a total tick count of 100.
214 : *
215 : * This function should not be used directly unless you're
216 : * trying to calculate a triplet rest. Use {@link triplet()}
217 : * for actual triplets of notes.
218 : *
219 : * @since 1.0
220 : * @uses resolveTriplet()
221 : *
222 : * @param int $ticks
223 : * @throws RuntimeException if the triplet division has not been registered
224 : * @return int
225 : */
226 : public function resolveTriplet($ticks) {
227 2 : if (!isset($this->triplets[$ticks])) {
228 1 : throw new \RuntimeException('This triplet division has not been registered');
229 : }
230 :
231 1 : $resolvedTicks = $ticks - $this->triplets[$ticks];
232 1 : unset($this->triplets[$ticks]);
233 1 : return $resolvedTicks;
234 : }
235 :
236 : /**
237 : * Stoccato-izes the number of ticks
238 : *
239 : * @since 1.0
240 : *
241 : * @param int $ticks
242 : * @return int
243 : */
244 : public function stoccato($ticks) {
245 2 : return (int)floor($ticks * .75);
246 : }
247 :
248 : /**
249 : * un-Stoccato-izes the number of ticks
250 : *
251 : * @since 1.0
252 : *
253 : * @param int $ticks
254 : * @return int
255 : */
256 : public function unstoccato($ticks) {
257 1 : return $ticks - $this->stoccato($ticks);
258 : }
259 :
260 : }
261 :
|