{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "cloth-2",
  "type": "registry:block",
  "title": "Cloth 2",
  "author": "Kenney <https://kenney.nl>",
  "description": "A distinct, moderate rustling sound of cloth, suitable for character actions like equipping an item, or dynamic UI elements like opening a fabric-like menu.",
  "files": [
    {
      "path": "registry/soundcn/sounds/cloth-2/cloth-2.ts",
      "content": "import type { SoundAsset } from \"@/lib/sound-types\";\n\nexport const cloth2Sound: SoundAsset = {\n  name: \"cloth-2\",\n  dataUri: \"data:audio/mpeg;base64,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BSaSyjPN+mSe+ZJKICieka1dYAADBYAORCapBIVkYIBoJr/jiZEkiA0Bx6qqTND1Qz2Zdy5KKMiJ6zD/+1LEF4AKCGNNhhhuYUcSJpmEiagfQNJByDwUPLNsFFnhWRK3qHCWVQea863Ou2v8ltUuluMAEAQoBQKUqaiwQBKqMI6jskpKkPUY2dSOwcJiIyUIkc20D2M66W7JaS0hUIix+DsNCACBXltmBlo17lRxIUSRHF+dPJHLMoLSJ4rR1qUSxsRPAyEDeVREXHHAHEgplByliRZiox8YhuIWeqlU5O1zSxtlbbLNjwiubAXI5yHd1jCijNBzLbVV/IzMZ+0sZFJ4xYwQqSdTHuxK9//7UsQkgAm4kUPnpG5hSBIm8PMN5Dnhqke0AAC5tfXSwMZUswhxcUaTER8hK+TiyNRJOkaoGGgkMiGK/n1SzlmrY4Ukg0Z5P0WlDtJ4cZ7IwEGUFEWhU8uYQeOi8CoO0KSdT6/Xv/3tp///rgAPzfgtqOkUeN7VAlBgaAXEVCsSZS5BiGhkYCLUsGEEt1Z5I0/FN/KLHjIp0uk2st6XvY/E4lTHkTQ8eScJeJnExAwaZJZE9CaVot3////X/NffUNHlRjFtIsRU5US8KYv0GqlT//tSxDKCCfx7JwwwykE0DiUlh5goB1g4EFqLc8xC5U2NFqy1F9nzppx5pe9VFiE6VhMXCZ08wjAq17QDEhgsISigwNWx//p/9Dv//9cCQgH6YGqLAM9gi4OC7Idlns6u17SU6MuNMFSRrrMuesu05gopSSGoPok7qEPiI46hjcb/gUqGWgWFDWdOLg6UuauKZv9Vn/+33f/9JCAIQLQGvEjYrlrpdBYzDqEWEGKEQ4hSgeQ9U7NpHBwSFexoIRlDMuqdKDHXZRd3odSDxhdJFQr/+1LEQgIJjIkcrCRrATqQI0mEjSCIRp4kGXhwa54XNE0afKaf+Av97vd/6gARp9ZBhNmBEk2JKdMI8mV2plw/vjTIp1Bx41AFJJwS3DaJRTAh5ODiKZ5jeoORjLPIr5ydMayQdBB7XJUiedofivShP//X1f32GwABskkAPhBNCckChJoSUFORw5I5I2REJRowKnREIhKWAo1wdOh1NQ86dPHX+dO5ZPkmf0Ee/b1K2NJFlq/hVx08STWp/qUqTEFNRTMuMTAwqqqqqqqqqqqqqv/7UsRSgAlglx8nmG8BGIWiCMMYkKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq\",\n  duration: 0.415,\n  format: \"mp3\",\n  license: \"CC0\",\n  author: \"Kenney\",\n};\n",
      "type": "registry:lib"
    },
    {
      "path": "registry/soundcn/lib/sound-types.ts",
      "content": "export interface SoundAsset {\r\n  /** Unique identifier for the sound */\r\n  name: string;\r\n  /** Base64-encoded data URI (data:audio/mpeg;base64,...) */\r\n  dataUri: string;\r\n  /** Duration in seconds */\r\n  duration: number;\r\n  /** Audio format */\r\n  format: \"mp3\" | \"wav\" | \"ogg\";\r\n  /** License identifier */\r\n  license: \"CC0\" | \"OGA-BY\" | \"MIT\";\r\n  /** Original author/creator */\r\n  author: string;\r\n}\r\n\r\nexport interface UseSoundOptions {\r\n  /** Volume level from 0 to 1. Default: 1 */\r\n  volume?: number;\r\n  /** Playback speed multiplier. Default: 1 */\r\n  playbackRate?: number;\r\n  /** If true, calling play() stops current playback first. Default: false */\r\n  interrupt?: boolean;\r\n  /** If false, play() does nothing. Useful for user preferences. Default: true */\r\n  soundEnabled?: boolean;\r\n  /** Called when playback starts */\r\n  onPlay?: () => void;\r\n  /** Called when playback ends naturally */\r\n  onEnd?: () => void;\r\n  /** Called when pause() is called */\r\n  onPause?: () => void;\r\n  /** Called when stop() is called */\r\n  onStop?: () => void;\r\n}\r\n\r\nexport type PlayFunction = (overrides?: {\r\n  volume?: number;\r\n  playbackRate?: number;\r\n}) => void;\r\n\r\nexport interface SoundControls {\r\n  stop: () => void;\r\n  pause: () => void;\r\n  isPlaying: boolean;\r\n  duration: number | null;\r\n  sound: SoundAsset;\r\n}\r\n\r\nexport type UseSoundReturn = readonly [PlayFunction, SoundControls];\r\n",
      "type": "registry:lib"
    },
    {
      "path": "registry/soundcn/lib/sound-engine.ts",
      "content": "let audioContext: AudioContext | null = null;\nconst bufferCache = new Map<string, AudioBuffer>();\n\nexport function getAudioContext(): AudioContext {\n  if (!audioContext) {\n    audioContext = new AudioContext();\n  }\n  return audioContext;\n}\n\nexport async function decodeAudioData(dataUri: string): Promise<AudioBuffer> {\n  const cached = bufferCache.get(dataUri);\n  if (cached) return cached;\n\n  const ctx = getAudioContext();\n  const base64 = dataUri.split(\",\")[1];\n  const binaryString = atob(base64);\n  const bytes = new Uint8Array(binaryString.length);\n  for (let i = 0; i < binaryString.length; i++) {\n    bytes[i] = binaryString.charCodeAt(i);\n  }\n\n  const audioBuffer = await ctx.decodeAudioData(bytes.buffer.slice(0));\n  bufferCache.set(dataUri, audioBuffer);\n  return audioBuffer;\n}\n\nexport interface PlaySoundOptions {\n  volume?: number;\n  playbackRate?: number;\n  onEnd?: () => void;\n}\n\nexport interface SoundPlayback {\n  stop: () => void;\n}\n\nexport async function playSound(\n  dataUri: string,\n  options: PlaySoundOptions = {}\n): Promise<SoundPlayback> {\n  const { volume = 1, playbackRate = 1, onEnd } = options;\n  const ctx = getAudioContext();\n  if (ctx.state === \"suspended\") {\n    await ctx.resume();\n  }\n\n  const buffer = await decodeAudioData(dataUri);\n  const source = ctx.createBufferSource();\n  const gain = ctx.createGain();\n\n  source.buffer = buffer;\n  source.playbackRate.value = playbackRate;\n  gain.gain.value = volume;\n\n  source.connect(gain);\n  gain.connect(ctx.destination);\n\n  source.onended = () => {\n    onEnd?.();\n  };\n\n  source.start(0);\n\n  return {\n    stop: () => {\n      try {\n        source.stop();\n      } catch {\n        // No-op if already stopped.\n      }\n    },\n  };\n}\n",
      "type": "registry:lib"
    }
  ],
  "meta": {
    "duration": 0.415,
    "format": "mp3",
    "sizeKb": 4,
    "license": "CC0",
    "tags": [
      "cloth",
      "2"
    ],
    "keywords": [
      "character action",
      "equip item",
      "dynamic ui",
      "rustle",
      "distinct",
      "moderate",
      "fabric",
      "game",
      "animation",
      "menu open",
      "texture",
      "movement",
      "clear",
      "organic",
      "action",
      "interact",
      "game ui",
      "clothing",
      "item",
      "medium"
    ]
  },
  "categories": [
    "cloth"
  ]
}