javascript - Tone.js หยุดเสียงทั้งหมดที่กำลังเล่นอย่างสมบูรณ์
โดยสรุป เมื่อกดปุ่ม ผมอยากเล่นโน้ตไม่กี่ตัวโดยใช้ PolySynth และ Sequence หากผู้ใช้กดปุ่มซ้ำ ๆ ผมอยากให้สิ่งที่กำลังเล่นอยู่ถูกหยุด และเริ่มเล่นใหม่อีกครั้ง
ปัญหา: ไม่ว่าจะพยายามอย่างไร ฉันก็ไม่สามารถยกเลิก/ทำให้โน้ตที่เล่นก่อนหน้านี้เงียบสนิทได้ในกรณีที่เริ่มลำดับใหม่ (กดปุ่มอีกครั้ง) ซึ่งสาเหตุส่วนใหญ่น่าจะมาจากการลดทอน/การคงเสียงของ envelope
ซินธ์ของฉัน:
import { PolySynth } from 'tone'
const synth = new PolySynth(Synth, {
oscillator: {
type: 'sine4',
volume: -6,
},
envelope: {
attack: 0.01,
decay: 0.5,
sustain: 0.1,
release: 1,
},
}).toDestination()
synth.maxPolyphony = 4 // max notes playing at a time, not sure if necessary
ลำดับของฉัน:
import { Sequence } from 'tone'
// Play the 2 notes individually then play them together
const notes = [
{ note: 'C4', duration: '8n' },
{ note: 'G4', duration: '8n' },
{ note: ['C4', 'G4'], duration: '4n' }
]
// The sequence that should play the notes after one another
const sequence = new Sequence({
subdivision: '8n',
loop: false,
events: notes,
callback: (time, note) => synth.triggerAttackRelease(note.note, note.duration, time),
})
ในแบบที่ฉันเล่น มันคือผู้จัดการเหตุการณ์:
import { start, Transport } from 'tone'
// Event handler simply attached to a button's onClick
function onButtonClicked() {
// Call whatever this start is, doc says it can only happen in an event handler
start()
// Try everything to kill current sound
Transport.cancel()
Transport.stop()
// Start it again
Transport.start()
sequence.start()
}
ฉันจะทำให้เสียงทั้งหมดเงียบสนิทได้อย่างไร (ถ้ามีเสียง) ก่อนที่จะเริ่มเล่นมัน?
คำตอบ #1
คำตอบสั้น
คิดเกี่ยวกับเรื่องนี้ค่อนข้างมาก ถ้าฉันเข้าใจคุณถูกต้อง นี่จริง ๆ แล้วเป็นพฤติกรรมตามที่ตั้งใจไว้ คุณกำลังกระตุ้นโน้ตบนซินธ์ (ซึ่งโดยพื้นฐานคือ AudioWorkletNode) ดังนั้นทันทีที่โน้ตถูกกระตุ้น ซินธ์ก็จะเล่นโน้ตนั้นจบ วิธีเดียวที่จะหยุดโน้ตนั้นจากการเล่นคือการปิดเสียงซินธ์เอง
คำตอบยาว
ในคอมเมนต์คุณกล่าวว่าคุณอาจพลาดบางสิ่งในเชิงแนวคิด และฉันคิดว่าคุณกำลังอยู่บนเส้นทางที่ถูกต้องในเรื่องนั้น
ลองมาคิดกันว่ามีการสร้างเสียงด้วย MIDI อย่างไร
- คุณกำลังเชื่อมต่อซินธ์ (ซึ่งรับโน้ต MIDI และสร้างเสียง) ไปยังเอาต์พุต
- คุณกำลังกำหนดโน้ต MIDI บางส่วนบนตัวส่ง
- คุณเริ่มการขนส่ง
- ทันทีที่การขนส่งถึงเวลาที่กำหนดสำหรับโน้ต ค่ามิดีไอนนั้นจะถูกส่งไปยังซินธิไซเซอร์
เนื่องจาก Synth โดยพื้นฐานแล้วเป็น AudioWorkletNode ที่มี Envelope Generator, Synth จะรับโน้ต MIDI นั้นและกระตุ้นการสร้างเสียงภายใน (ผ่าน envelope) ดังนั้นโน้ต MIDI ในช่วงเวลาที่เฉพาะเจาะจงจะกระตุ้นการสร้างเสียงยาวในช่วงเวลาที่เฉพาะเจาะจง (ซึ่งจะเป็นส่วน ADS) แม้ว่าโน้ต MIDI จะมีระยะเวลาเพียง 1 มิลลิวินาทีในตัวอย่างของคุณ, การสร้างเสียงจะคงอยู่ไม่น้อยกว่า 1.001 วินาที (Release บวกกับระยะเวลาโน้ต MIDI 1 มิลลิวินาที) มาลองแยกเรื่องนี้ออกมาเพิ่มเติม:
- โน้ต MIDI มีจุดเริ่มต้นและจุดสิ้นสุดบนเส้นเวลาขนส่งสมมติ
- เริ่มต้นทำให้ส่วน ADS ของซองสัญญาณทำงาน
- End กระตุ้นส่วน R ของซองเสียง
- เมื่อโน้ต MIDI ของคุณกระตุ้นซองเสียง เสียงจะถูกสร้างขึ้น
ดังนั้นเมื่อคุณหยุดการขนส่งของคุณหรือแม้แต่ลำดับนั้น มันจะทำอะไร? หากโน้ต MIDI ได้กระตุ้น Envelope แล้ว Envelope จะได้รับทริกเกอร์สิ้นสุด MIDI และกระตุ้น Envelope แบบ Release
ดังนั้นจะมีเสียงต่อเนื่องของซินธิไซเซอร์ของคุณเสมอ เพราะว่าโน้ต MIDI ไม่ได้กำหนดจุดเริ่มต้นและจุดสิ้นสุดของซินธิไซเซอร์ของคุณ แต่เป็นตัวกระตุ้นบางส่วนของ Envelope ของคุณ ดังนั้นจริง ๆ แล้วซินธิไซเซอร์ของคุณสร้างเสียงขึ้นมา และสิ่งนั้นไม่ได้ผูกติดกับการควบคุมการเล่นเพลง และก็ไม่สามารถผูกติดได้เช่นกัน
หวังว่าคำอธิบายนั้นช่วยคุณได้บ้าง หากฉันเข้าใจคุณผิด ฉันยินดีที่จะแก้ไข
คำตอบ #2
โน้ตยังคงเล่นต่อไปเพราะการขนส่งหยุดหลังจากที่โน้ตถูกกระตุ้นและก่อนที่จะถูกปล่อย ดังนั้นวิธีแก้ปัญหาคือการกระตุ้นการปล่อยของโน้ตทั้งหมดเมื่อคุณกดปุ่มหยุด ฉันประสบปัญหาแบบเดียวกันขณะใช้ tambien/piano (อิงจาก Tonejs)
Tone.Transport.toggle()
if (Tone.Transport.state === 'stopped') {
for (let i=9; i<97; i++) {
piano.keyUp({midi: i}, '+0')
}
}
