| 501 | auto_release_sample, |
| 502 | ) |
| 503 | } |
| 504 | |
| 505 | fn new_internal( |
| 506 | id: Option<u64>, |
| 507 | note: Note, |
| 508 | velocity: u8, |
| 509 | volume: f32, |
| 510 | program: MidiProgram, |
| 511 | auto_release_sample: Option<u64>, |
| 512 | ) -> Self { |
| 513 | let wave = program_wave(program); |
| 514 | Self { |
| 515 | id, |
| 516 | note, |
| 517 | velocity: (velocity as f32 / 127.0).clamp(0.0, 1.0) * volume.max(0.0) * OUTPUT_GAIN, |
| 518 | volume: 1.0, |
| 519 | gain: 0.0, |
| 520 | release_gain: 1.0, |
| 521 | phase: 0.0, |
| 522 | phase_step: TAU * note.frequency_hz() / SAMPLE_RATE as f32, |
| 523 | age_samples: 0, |
| 524 | auto_release_sample, |
| 525 | released: false, |
| 526 | wave, |
| 527 | noise: 0x1234_5678 ^ ((note.0 as u32) << 8) ^ program.0 as u32, |
| 528 | } |
| 529 | } |
| 530 | |
| 531 | fn release(&mut self) { |
| 532 | self.released = true; |
| 533 | } |
| 534 | |
| 535 | fn next_sample(&mut self, sine: &[f32; SINE_TABLE_SIZE]) -> Option<f32> { |
| 536 | if self |
| 537 | .auto_release_sample |
| 538 | .is_some_and(|sample| self.age_samples >= sample) |
| 539 | { |
| 540 | self.release(); |
| 541 | self.auto_release_sample = None; |
| 542 | } |
| 543 | if self.gain < 1.0 { |
| 544 | self.gain = (self.gain + ATTACK_STEP).min(1.0); |
| 545 | } |
| 546 | if self.released { |
| 547 | self.release_gain -= RELEASE_STEP; |
| 548 | } |
| 549 | if self.release_gain <= 0.0 { |