pads.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. // Moving, copying and swapping pads (build plan M6), shared by drag and drop
  2. // and the "Move to pad" / "Copy to pad" buttons.
  3. import { Service, type Pad } from "./api";
  4. import { app, confirm, message, run, ui } from "./store.svelte";
  5. import { PER_BANK } from "./slots";
  6. export type Transfer = "move" | "swap" | "copy";
  7. /** What dropping or sending pad `from` onto `to` does. */
  8. export function transferKind(from: Pad, to: Pad, copy: boolean): Transfer {
  9. if (copy) return "copy";
  10. return to.file ? "swap" : "move";
  11. }
  12. /**
  13. * Moves, swaps or copies a pad, asking first when that would replace a
  14. * sample or change what recorded notes play. Recorded notes stay on their
  15. * slots (format doc section 3): a note keeps pointing at the slot, not the
  16. * sample.
  17. */
  18. export async function transfer(kind: Transfer, from: number, to: number): Promise<void> {
  19. try {
  20. await doTransfer(kind, from, to);
  21. } catch (e) {
  22. app.error = message(e);
  23. }
  24. }
  25. async function doTransfer(kind: Transfer, from: number, to: number): Promise<void> {
  26. const pads = app.state?.pads;
  27. if (!pads || from === to) return;
  28. const src = pads[from];
  29. const dst = pads[to];
  30. const items: string[] = [];
  31. if (kind === "copy" && dst.file) {
  32. items.push(`${dst.label} (${dst.name}) will be replaced.`);
  33. }
  34. // Pads whose sound changes: both for move and swap, the target for copy.
  35. const changed = kind === "copy" ? [dst] : [src, dst];
  36. for (const p of changed) {
  37. if (p.hasNotes) {
  38. items.push(`${p.label} has recorded notes. They stay on ${p.label} and will play ${soundAfter(kind, p, src, dst)}.`);
  39. }
  40. }
  41. if (items.length) {
  42. const ok = await confirm({
  43. title: `${verb(kind)} ${src.label} ${kind === "swap" ? "and" : "to"} ${dst.label}?`,
  44. message: "Please check:",
  45. items,
  46. action: verb(kind),
  47. });
  48. if (!ok) return;
  49. }
  50. const call =
  51. kind === "move" ? () => Service.MovePad(from, to) : kind === "swap" ? () => Service.SwapPads(from, to) : () => Service.CopyPad(from, to);
  52. if (await run(call)) {
  53. ui.padBank = Math.floor(to / PER_BANK);
  54. ui.padSlot = to;
  55. }
  56. }
  57. function verb(kind: Transfer): string {
  58. return kind === "move" ? "Move" : kind === "swap" ? "Swap" : "Copy";
  59. }
  60. // What a pad's notes will play once the transfer is done.
  61. function soundAfter(kind: Transfer, p: Pad, src: Pad, dst: Pad): string {
  62. if (p.slot === src.slot) {
  63. return kind === "swap" ? describe(dst) : "nothing (the pad will be empty)";
  64. }
  65. return describe(src);
  66. }
  67. function describe(p: Pad): string {
  68. return p.file ? `"${p.name}"` : "nothing (the pad will be empty)";
  69. }