3688ceacymsim: Python port of cymbalsim with a modal linear model and a PyTorch von Karman modelJeremy Magland 1"""The strike and the listener."""
3from __future__ import annotations
5import math
6from dataclasses import dataclass
10class Strike:
11 """A drumstick strike: a free mass arriving at a point of the plate with a Hertz contact.
13 The default contact stiffness is softer than the Hertz value for a hard
14 wooden tip on metal (of order 1e8 to 1e9); it stands for the compliance of
15 a real tip and gives a rounder attack.
16 """
18 x: float = 0.9
19 """Strike point as a fraction of the bounding-box width from the left edge."""
20 y: float = 0.5
21 """Strike point as a fraction of the bounding-box height from the bottom edge."""
22 velocity: float = 3.0
23 """Stick tip speed at impact [m/s]."""
24 mass: float = 0.03
25 """Effective mass of the stick [kg]."""
26 stiffness: float = 1e7
27 """Hertz contact stiffness K [N/m^alpha]."""
28 exponent: float = 1.5
29 """Hertz exponent alpha (1.5 for a rounded tip)."""
30 radius: float = 5e-3
31 """Radius of the contact patch [m]."""
33 def __post_init__(self) -> None:
34 if not (0 <= self.x <= 1 and 0 <= self.y <= 1):
35 raise ValueError("the strike point (x, y) is given as fractions of the bounding box in [0, 1]")
36 if not (self.velocity > 0 and self.mass > 0 and self.stiffness > 0 and self.exponent >= 1 and self.radius >= 0):
37 raise ValueError("velocity, mass and stiffness must be positive, exponent at least 1, radius non-negative")
41class Listener:
42 """The listener (a microphone) in spherical coordinates about the center of the plate."""
44 distance: float = 0.5
45 """Distance from the center of the plate [m]."""
46 angle: float = 30.0
47 """Angle from the plate's normal [deg]; 0 is straight above the plate."""
48 azimuth: float = 0.0
49 """Rotation about the normal [deg]; 0 is toward +x."""
51 def __post_init__(self) -> None:
52 if not self.distance > 0:
53 raise ValueError("distance must be positive")
56 def position(self) -> tuple[float, float, float]:
57 """Cartesian position relative to the center of the plate, z along the normal [m]."""
58 th = math.radians(self.angle)
59 ph = math.radians(self.azimuth)
60 return (
61 self.distance * math.sin(th) * math.cos(ph),
62 self.distance * math.sin(th) * math.sin(ph),
63 self.distance * math.cos(th),
64 )