{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "book-flip-2",
  "type": "registry:block",
  "title": "Book Flip 2",
  "author": "Kenney <https://kenney.nl>",
  "description": "A distinct, medium-paced sound of a page being flipped, suggesting a deliberate action in a digital book or catalog.",
  "files": [
    {
      "path": "registry/soundcn/sounds/book-flip-2/book-flip-2.ts",
      "content": "import type { SoundAsset } from \"@/lib/sound-types\";\n\nexport const bookFlip2Sound: SoundAsset = {\n  name: \"book-flip-2\",\n  dataUri: \"data:audio/mpeg;base64,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Pw13aCFMzTdFMolNyR+HD/8zMzQMpa9ECx5b4qC8QBAy1Z9wmLn1Mxarsocf///ouk6ghe7pZ5ZVQzL/+1LEKQAJoH9lxLxjUTKH7PhsMAoqQBY0IKhE/Q0i2BQIIEzkwNjCyU+fYsV1lt9qjbqVZGHgeBQCAAGR1eH0kmGDy5YnjUj3e0kn/1Crm2ms9//2IDpcxGqeUNLPuWZ2RCcIAKLjULA9YhHDkYbJ9HvR9cPloDQUTzaEkyL740hn2E7/SnUmUA8D4YLIc0c1lUOBqCEmla01ElKKLx+UYS96O3+VToizVBgLuUHzdzDuioQkmAAAI3RXTBVnXBeSnFdDpkwC5YlsnRhYGTbN0f/7UsQ6AApMWWHnpMpBSo6svPSJOCEcG1sLrZLJtU2wjfme/o4z2iRLAWMw5Ohp9sVNVEj98azSh////xYoCD2R7FFnhOuZlmUyAwBAAgdDkEUQ5F+IlD+HStCYTk1MqWsmORseQDwtFsUyqq5WLlbSjMb7tpMKcsHAaDQK7yqQVGHhi3BQCosSIup/t79P9cSliURZ5G4qz07EyCQDoAIMOxYDQaEQwFiqqEYZFYqaaBPGB77vGPLbm8M7Pq4O0w1IsAjB1uAj4s520qiVlosH//tSxEWACjyFV8YgUIEvjKk4ZIwwq+v1866/f/agPGc6BiplCUr/pjHNZCgsAgiC4zUKiAIjwqLihEuStwGgoGR21VwKZD8M4GHDxcBAuZDoWLHgaWdYVOig9zZIY0UB0ItFhIDQkPNPWcr+lp7/00EkT5YOhNBX6y1LbSDQjbjr9ZgbSJyQtFIxqDglYFOivsSJTpScxpWYWpAh8zKaNFJXI6uSw4dWgsy4DL9sLrStjd5NR6Hw+HXY+odcoqzbA2sBfyW+yorZcmoCLgoEDFT/+1LEVIAKIFc7gaRlAUUa5izDDaAmKTrQ0DJiaIoySZFZK5RKUUSpLNyWoqMw+6orE4l2bIEsFwsqXl9LKcNDOHjdpEgwkUBgSOe9D3ueFRL0v1HK7G1rEDKFsTIJoa0yADfTaCgJQ+VuZuOrzaKaZSZZjLc8jrG+6xv28WYFootmG0iSeO+ZRufZN7hz5CsX61t+DiSVV3eYxAApoImw8me8sVuDtQq5/+AIKmGsV+gFACCgJMfGb4zMx1muzNVaM1El9UuMawuGv/VXZmasav/7UsRhAAo8xykBGGuBQJokpGCbgFzCwhRQ/r/tRoB2f5k2AYwKNRp4K//2RFX76zoNVDxgMkbOdQIQ1VVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tSxG4DyOzREkGE3AAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=\",\n  duration: 0.430,\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.43,
    "format": "mp3",
    "sizeKb": 4,
    "license": "CC0",
    "tags": [
      "book",
      "flip",
      "2"
    ],
    "keywords": [
      "book",
      "flip",
      "page",
      "turn",
      "scroll",
      "navigate",
      "ui",
      "document",
      "content",
      "read",
      "distinct",
      "clear",
      "paper",
      "interaction",
      "progress",
      "next",
      "browse",
      "medium",
      "deliberate",
      "tangible",
      "smooth"
    ]
  },
  "categories": [
    "book"
  ]
}