[{"data":1,"prerenderedAt":10347},["ShallowReactive",2],{"navigation":3,"/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase-post":734,"/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase-surround":2764,"/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase-related":2769},[4,70,207,312,721],{"title":5,"path":6,"stem":7,"children":8,"page":69},"Case Study","/blog/case-study","blog/case-study",[9,13,17,21,25,29,33,37,41,45,49,53,57,61,65],{"title":10,"path":11,"stem":12},"Ambistream – Multi-Layer Streaming Platform","/blog/case-study/ambistream-building-a-multi-layer-streaming-platform-from-a-spark-of-an-idea","blog/case-study/ambistream-building-a-multi-layer-streaming-platform-from-a-spark-of-an-idea",{"title":14,"path":15,"stem":16},"Custom Chromecast & AirPlay Casting App","/blog/case-study/chromecast-airplay-casting-app-case-study","blog/case-study/chromecast-airplay-casting-app-case-study",{"title":18,"path":19,"stem":20},"Direct Music Licensing Platform Case Study","/blog/case-study/dlm-music-catalog-case-study","blog/case-study/dlm-music-catalog-case-study",{"title":22,"path":23,"stem":24},"Assembly Instructions App - Galeco Mobile App","/blog/case-study/galeco-mobile-app-case-study","blog/case-study/galeco-mobile-app-case-study",{"title":26,"path":27,"stem":28},"MemoSonic: Building an Audio Memory Game","/blog/case-study/how-we-built-memosonic-accessible-audio-memory-game-flutter","blog/case-study/how-we-built-memosonic-accessible-audio-memory-game-flutter",{"title":30,"path":31,"stem":32},"Loyalty Program App for Shopping Mall","/blog/case-study/loyalty-program-application-case-study","blog/case-study/loyalty-program-application-case-study",{"title":34,"path":35,"stem":36},"Mobile Application for Music Catalogs","/blog/case-study/mobile-app-for-music-catalog","blog/case-study/mobile-app-for-music-catalog",{"title":38,"path":39,"stem":40},"Universal Music Data Parser for 20+ Platforms","/blog/case-study/musicdata-lab-universal-music-data-parser-case-study","blog/case-study/musicdata-lab-universal-music-data-parser-case-study",{"title":42,"path":43,"stem":44},"Panther ML/AI Pricing Recommendation Tool","/blog/case-study/panther-pricing-recommendation-tool-case-study","blog/case-study/panther-pricing-recommendation-tool-case-study",{"title":46,"path":47,"stem":48},"4D Grupa Roofing Wholesalers Platform","/blog/case-study/roofing-wholesalers-website-case-study","blog/case-study/roofing-wholesalers-website-case-study",{"title":50,"path":51,"stem":52},"Talent Alpha HR Frontend Platform","/blog/case-study/talent-alpha-hr-platform-case-study","blog/case-study/talent-alpha-hr-platform-case-study",{"title":54,"path":55,"stem":56},"Ticketing & Events Platform Development","/blog/case-study/ticketing-events-platform-case-study","blog/case-study/ticketing-events-platform-case-study",{"title":58,"path":59,"stem":60},"Turn Fans into Superfans — Roadie.co","/blog/case-study/turn-fans-into-superfans-roadie-co","blog/case-study/turn-fans-into-superfans-roadie-co",{"title":62,"path":63,"stem":64},"Walkative 2.0 Global Booking Engine","/blog/case-study/walkative-2-booking-platform-case-study","blog/case-study/walkative-2-booking-platform-case-study",{"title":66,"path":67,"stem":68},"Why Merch Is the New Royalty Check, and How Tech Is Closing the Gap","/blog/case-study/why-merch-is-the-new-royalty-check","blog/case-study/why-merch-is-the-new-royalty-check",false,{"title":71,"path":72,"stem":73,"children":74,"page":69},"Music Data","/blog/music-data","blog/music-data",[75,79,83,87,91,95,99,103,107,111,115,119,123,127,131,135,139,143,147,151,155,159,163,167,171,175,179,183,187,191,195,199,203],{"title":76,"path":77,"stem":78},"13 Distributors, 5 File Formats, Zero Standards -The Reality of Music Royalty Data","/blog/music-data/13-distributors-5-file-formats-zero-standards-the-reality-of-music-royalty-data","blog/music-data/13-distributors-5-file-formats-zero-standards-the-reality-of-music-royalty-data",{"title":80,"path":81,"stem":82},"830 Ways to Say Spotify - Normalizing Music Streaming Data","/blog/music-data/830-ways-to-say-spotify-normalizing-music-streaming-data","blog/music-data/830-ways-to-say-spotify-normalizing-music-streaming-data",{"title":84,"path":85,"stem":86},"Why Music Companies Need AI-Powered Analytics (And How We Built One)","/blog/music-data/ai-powered-analytics-dashboard-django-clickhouse-ollama","blog/music-data/ai-powered-analytics-dashboard-django-clickhouse-ollama",{"title":88,"path":89,"stem":90},"AI Rehearsal: Spaced Repetition for Your Musical Ideas","/blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas","blog/music-data/ai-rehearsal-spaced-repetition-for-musical-ideas",{"title":92,"path":93,"stem":94},"Audio Project Organization Is a Mess — Here's Why","/blog/music-data/audio-project-organization-mess","blog/music-data/audio-project-organization-mess",{"title":96,"path":97,"stem":98},"Why Audio Search Is Still Broken and How to Fix It with Embeddings","/blog/music-data/audio-search-broken-fix-with-embeddings","blog/music-data/audio-search-broken-fix-with-embeddings",{"title":100,"path":101,"stem":102},"AI Song Structure Analysis: Intro, Verse, Chorus","/blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus","blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus",{"title":104,"path":105,"stem":106},"The Broken Feedback Loop in Music Collaboration","/blog/music-data/broken-feedback-loop-music-collaboration","blog/music-data/broken-feedback-loop-music-collaboration",{"title":108,"path":109,"stem":110},"Building a Claude Skill for DDEX Validation: Automate Music Metadata Checks with AI","/blog/music-data/building-a-claude-skill-for-ddex-validation-music-metadata","blog/music-data/building-a-claude-skill-for-ddex-validation-music-metadata",{"title":112,"path":113,"stem":114},"Building a Custom Music Delivery Platform on the Revelator API","/blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api","blog/music-data/building-a-custom-music-delivery-platform-on-the-revelator-api",{"title":116,"path":117,"stem":118},"Building a Suno AI Remix App with Nuxt & Firebase","/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase","blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase",{"title":120,"path":121,"stem":122},"C2PA & DDEX: Authenticity Meets Rights in the Age of AI Music","/blog/music-data/c2pa-and-ddex-authenticity-meets-rights-in-the-age-of-ai-music","blog/music-data/c2pa-and-ddex-authenticity-meets-rights-in-the-age-of-ai-music",{"title":124,"path":125,"stem":126},"C2PA in Music: A Claude MCP for Reading Content Provenance","/blog/music-data/c2pa-in-music-mcp","blog/music-data/c2pa-in-music-mcp",{"title":128,"path":129,"stem":130},"Data Modeling in MongoDB Using Design Patterns","/blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns","blog/music-data/data-modeling-in-mongodb-with-the-usage-of-design-patterns",{"title":132,"path":133,"stem":134},"Office Hours with MusicTech Lab's DDEX Expert","/blog/music-data/ddex-office-hours-musictech","blog/music-data/ddex-office-hours-musictech",{"title":136,"path":137,"stem":138},"DDEX Open Source Projects Review","/blog/music-data/ddex-open-source-projects-review","blog/music-data/ddex-open-source-projects-review",{"title":140,"path":141,"stem":142},"Extracting Data from Ableton .als and .asd Files","/blog/music-data/extracting-data-from-ableton-als-asd-files","blog/music-data/extracting-data-from-ableton-als-asd-files",{"title":144,"path":145,"stem":146},"Maciej Dulski on Sound Connections Podcast","/blog/music-data/from-startups-to-musictech-maciej-dulski-on-sound-connections-podcast","blog/music-data/from-startups-to-musictech-maciej-dulski-on-sound-connections-podcast",{"title":148,"path":149,"stem":150},"How to Transcribe Video to Text Using OpenAI Whisper","/blog/music-data/how-to-transcribe-video-to-text-using-whisper","blog/music-data/how-to-transcribe-video-to-text-using-whisper",{"title":152,"path":153,"stem":154},"Epidemic Sound MCP with Claude for Devs","/blog/music-data/how-to-use-epidemic-sound-mcp-with-claude","blog/music-data/how-to-use-epidemic-sound-mcp-with-claude",{"title":156,"path":157,"stem":158},"Hybrid Database Model in Django for Speed","/blog/music-data/hybrid-database-model-in-django-as-a-performance-booster","blog/music-data/hybrid-database-model-in-django-as-a-performance-booster",{"title":160,"path":161,"stem":162},"Introduction to generating DDEX file using Python","/blog/music-data/introduction-to-generating-ddex-file-using-python","blog/music-data/introduction-to-generating-ddex-file-using-python",{"title":164,"path":165,"stem":166},"Maintaining Music Tech Tools: The SLA Dilemma for Small Teams","/blog/music-data/maintaining-music-tech-tools-the-sla-dilemma-for-small-teams","blog/music-data/maintaining-music-tech-tools-the-sla-dilemma-for-small-teams",{"title":168,"path":169,"stem":170},"Querying Bandcamp Revenue Reports with Natural Language — Meet mtl-bandcamp-mcp","/blog/music-data/mtl-bandcamp-mcp-open-source-revenue-dashboard","blog/music-data/mtl-bandcamp-mcp-open-source-revenue-dashboard",{"title":172,"path":173,"stem":174},"mtl-metadata-mcp: Open Source Audio Metadata Embedding for Claude Code","/blog/music-data/mtl-metadata-mcp-open-source-audio-metadata-embedding","blog/music-data/mtl-metadata-mcp-open-source-audio-metadata-embedding",{"title":176,"path":177,"stem":178},"MusicTech Resources for Builders","/blog/music-data/musictech-resources-curated-insights-for-the-musictech-builders","blog/music-data/musictech-resources-curated-insights-for-the-musictech-builders",{"title":180,"path":181,"stem":182},"Poland's Creative Tech and MusicTech Rise","/blog/music-data/polands-creative-tech-sector-is-on-the-rise-and-musictech-is-part-of-it","blog/music-data/polands-creative-tech-sector-is-on-the-rise-and-musictech-is-part-of-it",{"title":184,"path":185,"stem":186},"Batch ISRC Enrichment That Turns Messy Catalogs Into Clean Data","/blog/music-data/scout-isrc-metadata-enrichment-spotify-musicbrainz","blog/music-data/scout-isrc-metadata-enrichment-spotify-musicbrainz",{"title":188,"path":189,"stem":190},"Music Self-Publishing: The Emuze.me Story","/blog/music-data/self-publishing-in-the-music-industry-a-tale-of-emuze-me","blog/music-data/self-publishing-in-the-music-industry-a-tale-of-emuze-me",{"title":192,"path":193,"stem":194},"Understanding the API First Approach","/blog/music-data/understanding-the-api-first-approach","blog/music-data/understanding-the-api-first-approach",{"title":196,"path":197,"stem":198},"The Voice Memo Graveyard Problem","/blog/music-data/voice-memo-graveyard-problem","blog/music-data/voice-memo-graveyard-problem",{"title":200,"path":201,"stem":202},"Which Database for Music Data? Redshift vs BigQuery vs ClickHouse and When to Use Each","/blog/music-data/which-database-for-music-data-redshift-vs-bigquery-vs-clickhouse","blog/music-data/which-database-for-music-data-redshift-vs-bigquery-vs-clickhouse",{"title":204,"path":205,"stem":206},"Why we decided to use wavesurfer.js","/blog/music-data/why-we-decided-to-use-wavesurfer","blog/music-data/why-we-decided-to-use-wavesurfer",{"title":208,"path":209,"stem":210,"children":211,"page":69},"Newsletter","/blog/newsletter","blog/newsletter",[212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296,300,304,308],{"title":213,"path":214,"stem":215},"Music Industry Tech Openings (April 2024 Update)","/blog/newsletter/music-industry-tech-openings-april-2024-update","blog/newsletter/music-industry-tech-openings-april-2024-update",{"title":217,"path":218,"stem":219},"Music Industry Tech Openings (April 2025 Update)","/blog/newsletter/music-industry-tech-openings-april-2025-update","blog/newsletter/music-industry-tech-openings-april-2025-update",{"title":221,"path":222,"stem":223},"Music Industry Tech Openings (August 2024 Update)","/blog/newsletter/music-industry-tech-openings-august-2024-update","blog/newsletter/music-industry-tech-openings-august-2024-update",{"title":225,"path":226,"stem":227},"Music Industry Tech Openings (December 2024 Update)","/blog/newsletter/music-industry-tech-openings-december-2024-update","blog/newsletter/music-industry-tech-openings-december-2024-update",{"title":229,"path":230,"stem":231},"Music Industry Tech Openings (February 2025 Update)","/blog/newsletter/music-industry-tech-openings-february-2025-update","blog/newsletter/music-industry-tech-openings-february-2025-update",{"title":233,"path":234,"stem":235},"Music Industry Tech Openings (January 2025 Update)","/blog/newsletter/music-industry-tech-openings-january-2025-update","blog/newsletter/music-industry-tech-openings-january-2025-update",{"title":237,"path":238,"stem":239},"Music Industry Tech Openings (July 2024 Update)","/blog/newsletter/music-industry-tech-openings-july-2024-update","blog/newsletter/music-industry-tech-openings-july-2024-update",{"title":241,"path":242,"stem":243},"Music Industry Tech Openings (June 2024 Update)","/blog/newsletter/music-industry-tech-openings-june-2024-update","blog/newsletter/music-industry-tech-openings-june-2024-update",{"title":245,"path":246,"stem":247},"Music Industry Tech Openings (March 2025 Update)","/blog/newsletter/music-industry-tech-openings-march-2025-update","blog/newsletter/music-industry-tech-openings-march-2025-update",{"title":249,"path":250,"stem":251},"Music Industry Tech Openings (May 2024 Update)","/blog/newsletter/music-industry-tech-openings-may-2024-update","blog/newsletter/music-industry-tech-openings-may-2024-update",{"title":253,"path":254,"stem":255},"Music Industry Tech Openings (May 2025 Update)","/blog/newsletter/music-industry-tech-openings-may-2025","blog/newsletter/music-industry-tech-openings-may-2025",{"title":257,"path":258,"stem":259},"Music Industry Tech Openings (November 2024 Update)","/blog/newsletter/music-industry-tech-openings-november-2024-update","blog/newsletter/music-industry-tech-openings-november-2024-update",{"title":261,"path":262,"stem":263},"Music Industry Tech Openings (October 2024 Update)","/blog/newsletter/music-industry-tech-openings-october-2024-update","blog/newsletter/music-industry-tech-openings-october-2024-update",{"title":265,"path":266,"stem":267},"Music Industry Tech Openings (September 2024 Update)","/blog/newsletter/music-industry-tech-openings-september-2024-update","blog/newsletter/music-industry-tech-openings-september-2024-update",{"title":269,"path":270,"stem":271},"MusicTech Insights #1 by Maciej Dulski","/blog/newsletter/musictech-insights-1-curated-by-maciej-dulski","blog/newsletter/musictech-insights-1-curated-by-maciej-dulski",{"title":273,"path":274,"stem":275},"Provenance, Not Detection, Is the Durable Answer","/blog/newsletter/musictech-insights-10-curated-by-jim-anderson","blog/newsletter/musictech-insights-10-curated-by-jim-anderson",{"title":277,"path":278,"stem":279},"What CMOs can teach us about innovation in uncertain times","/blog/newsletter/musictech-insights-10-curated-by-joanna-kurkowska","blog/newsletter/musictech-insights-10-curated-by-joanna-kurkowska",{"title":281,"path":282,"stem":283},"Feeling the MusicTech Momentum","/blog/newsletter/musictech-insights-2-curated-by-maciej-dulski","blog/newsletter/musictech-insights-2-curated-by-maciej-dulski",{"title":285,"path":286,"stem":287},"AI in Music: Hype, Hope, and a Human Touch","/blog/newsletter/musictech-insights-3-curated-by-drew-thurlow","blog/newsletter/musictech-insights-3-curated-by-drew-thurlow",{"title":289,"path":290,"stem":291},"The Music Metadata Conundrum","/blog/newsletter/musictech-insights-4-curated-by-amanda-schupf","blog/newsletter/musictech-insights-4-curated-by-amanda-schupf",{"title":293,"path":294,"stem":295},"7 Rounds in the First 10 Days of November 2025","/blog/newsletter/musictech-insights-5-curated-by-maciej-dulski","blog/newsletter/musictech-insights-5-curated-by-maciej-dulski",{"title":297,"path":298,"stem":299},"The End of an Era: It's All About to Crash","/blog/newsletter/musictech-insights-6-curated-by-sigurdur-arnason","blog/newsletter/musictech-insights-6-curated-by-sigurdur-arnason",{"title":301,"path":302,"stem":303},"Low-Code Magic Won't Solve MusicTech Reality","/blog/newsletter/musictech-insights-7-curated-by-mariusz-smenzyk","blog/newsletter/musictech-insights-7-curated-by-mariusz-smenzyk",{"title":305,"path":306,"stem":307},"The New Economics of Game Music","/blog/newsletter/musictech-insights-8-curated-by-kenny-vaughan","blog/newsletter/musictech-insights-8-curated-by-kenny-vaughan",{"title":309,"path":310,"stem":311},"Music Business Meets Direct-to-Fan","/blog/newsletter/musictech-insights-9-curated-by-yaw-asamani","blog/newsletter/musictech-insights-9-curated-by-yaw-asamani",{"title":313,"path":314,"stem":315,"children":316,"page":69},"Software Development","/blog/software-development","blog/software-development",[317,321,325,329,333,337,341,345,349,353,357,361,365,369,373,377,381,385,389,393,397,401,405,409,413,417,421,425,429,433,437,441,445,449,453,457,461,465,469,473,477,481,485,489,493,497,501,505,509,513,517,521,525,529,533,537,541,545,549,553,557,561,565,569,573,577,581,585,589,593,597,601,605,609,613,617,621,625,629,633,637,641,645,649,653,657,661,665,669,673,677,681,685,689,693,697,701,705,709,713,717],{"title":318,"path":319,"stem":320},"Benefits of Outsourcing Software Development","/blog/software-development/10-benefits-of-outsourcing-software-development-services","blog/software-development/10-benefits-of-outsourcing-software-development-services",{"title":322,"path":323,"stem":324},"10 Steps to Find the Best MVP Developers","/blog/software-development/10-steps-to-find-the-best-mvp-developers-for-your-startup-idea","blog/software-development/10-steps-to-find-the-best-mvp-developers-for-your-startup-idea",{"title":326,"path":327,"stem":328},"1,200 Looms Later: How Async Video Became My Development Superpower","/blog/software-development/1200-looms-how-async-video-became-our-development-superpower","blog/software-development/1200-looms-how-async-video-became-our-development-superpower",{"title":330,"path":331,"stem":332},"Communication Strategy in Outsourcing Projects","/blog/software-development/5-steps-to-implement-an-effective-communication-strategy-in-outsourcing-software-development-project","blog/software-development/5-steps-to-implement-an-effective-communication-strategy-in-outsourcing-software-development-project",{"title":334,"path":335,"stem":336},"7 Best Practices for Outsourcing Software Development","/blog/software-development/7-best-practices-for-outsourcing-software-development","blog/software-development/7-best-practices-for-outsourcing-software-development",{"title":338,"path":339,"stem":340},"9 Reasons Why Saleor.io Is Best for eCommerce","/blog/software-development/9-reasons-why-the-saleor-io-platform-is-the-best-choice-for-your-ecommerce-website","blog/software-development/9-reasons-why-the-saleor-io-platform-is-the-best-choice-for-your-ecommerce-website",{"title":342,"path":343,"stem":344},"A Look at Bravelab.io’s Clutch 2021 Year In Review","/blog/software-development/a-look-at-bravelab-ios-clutch-2021-year-in-review","blog/software-development/a-look-at-bravelab-ios-clutch-2021-year-in-review",{"title":346,"path":347,"stem":348},"A quick introduction to profit sharing implementation","/blog/software-development/a-quick-introduction-to-profit-sharing-implementation","blog/software-development/a-quick-introduction-to-profit-sharing-implementation",{"title":350,"path":351,"stem":352},"AI Audio Similarity Search: The Future of Sound Library Discovery","/blog/software-development/ai-audio-similarity-search-for-sound-libraries","blog/software-development/ai-audio-similarity-search-for-sound-libraries",{"title":354,"path":355,"stem":356},"Automate Repetitive Tasks for Better Results","/blog/software-development/automate-repetitive-tasks-to-improve-your-business-performance","blog/software-development/automate-repetitive-tasks-to-improve-your-business-performance",{"title":358,"path":359,"stem":360},"Automating Success: The Art of Unified Documentation","/blog/software-development/automating-success-the-art-of-unified-documentation","blog/software-development/automating-success-the-art-of-unified-documentation",{"title":362,"path":363,"stem":364},"Brave 3.0 Website Redesign, Part 2: Solution","/blog/software-development/brave-3-0-how-we-conducted-website-redesign-part-2-solution","blog/software-development/brave-3-0-how-we-conducted-website-redesign-part-2-solution",{"title":366,"path":367,"stem":368},"Brave 3.0, Part 4: Tech Stack and Recap","/blog/software-development/brave-3-0-part-4-technologies-behind-and-final-series-recap","blog/software-development/brave-3-0-part-4-technologies-behind-and-final-series-recap",{"title":370,"path":371,"stem":372},"Brave 3.0 – redesign process part 1. The Challenge","/blog/software-development/brave-3-0-redesign-process-part-1-challenge","blog/software-development/brave-3-0-redesign-process-part-1-challenge",{"title":374,"path":375,"stem":376},"Brave 3.0 – redesign process, part 3. Lesson learned","/blog/software-development/brave-3-0-redesign-process-part-3-lesson-learned","blog/software-development/brave-3-0-redesign-process-part-3-lesson-learned",{"title":378,"path":379,"stem":380},"Bravelab.io: Top Software Developer by Clutch","/blog/software-development/bravelab-io-is-recognized-as-a-top-custom-software-developer-by-clutch","blog/software-development/bravelab-io-is-recognized-as-a-top-custom-software-developer-by-clutch",{"title":382,"path":383,"stem":384},"Bravelab.io: Top Developer in Poland by Clutch","/blog/software-development/bravelab-io-named-top-software-developer-in-poland-by-clutch","blog/software-development/bravelab-io-named-top-software-developer-in-poland-by-clutch",{"title":386,"path":387,"stem":388},"MusicTech Lab Partners with LALAL.AI","/blog/software-development/bravelab-partners-with-the-audio-lalal-ai","blog/software-development/bravelab-partners-with-the-audio-lalal-ai",{"title":390,"path":391,"stem":392},"MusicTech Lab Partners with The Audio Programmer","/blog/software-development/bravelab-partners-with-the-audio-programmer","blog/software-development/bravelab-partners-with-the-audio-programmer",{"title":394,"path":395,"stem":396},"Bravelab's team about productivity","/blog/software-development/bravelabs-team-about-productivity","blog/software-development/bravelabs-team-about-productivity",{"title":398,"path":399,"stem":400},"Braveloper","/blog/software-development/braveloper","blog/software-development/braveloper",{"title":402,"path":403,"stem":404},"Bravely App: Boost Productivity with Django","/blog/software-development/bravely-app-how-to-be-more-productive-with-django-quick","blog/software-development/bravely-app-how-to-be-more-productive-with-django-quick",{"title":406,"path":407,"stem":408},"DIY MIDI Controller for Ableton with Arduino","/blog/software-development/building-a-diy-midi-controller-for-ableton-live-with-arduino","blog/software-development/building-a-diy-midi-controller-for-ableton-live-with-arduino",{"title":410,"path":411,"stem":412},"C2PA in Ableton: Making AI Music Provenance Visible Inside Your DAW","/blog/software-development/c2pa-in-ableton-max-for-live","blog/software-development/c2pa-in-ableton-max-for-live",{"title":414,"path":415,"stem":416},"Change Detection mechanism in Angular","/blog/software-development/change-detection-mechanism-in-angular","blog/software-development/change-detection-mechanism-in-angular",{"title":418,"path":419,"stem":420},"Communication Channels in Remote Work","/blog/software-development/comparison-of-the-communication-channels-in-remote-work","blog/software-development/comparison-of-the-communication-channels-in-remote-work",{"title":422,"path":423,"stem":424},"Connecting Your Max for Live Device to a Cloud API","/blog/software-development/connecting-your-max-for-live-device-to-a-cloud-api","blog/software-development/connecting-your-max-for-live-device-to-a-cloud-api",{"title":426,"path":427,"stem":428},"From Voice Memo to Studio: The Cross-Platform Problem for Creators","/blog/software-development/cross-platform-problem-for-creators","blog/software-development/cross-platform-problem-for-creators",{"title":430,"path":431,"stem":432},"Cultural transformation through the pandemic era","/blog/software-development/cultural-transformation-through-the-pandemic-era","blog/software-development/cultural-transformation-through-the-pandemic-era",{"title":434,"path":435,"stem":436},"D-Commerce Decoded: Cutting Through the Hype","/blog/software-development/d-commerce-decoded-cutting-through-the-hype","blog/software-development/d-commerce-decoded-cutting-through-the-hype",{"title":438,"path":439,"stem":440},"Dev Meeting 002: Intro to DDD","/blog/software-development/dev-meeting-002-introduction-to-domain-driven-design-ddd","blog/software-development/dev-meeting-002-introduction-to-domain-driven-design-ddd",{"title":442,"path":443,"stem":444},"Dev Meeting 003: Web3 Primer","/blog/software-development/dev-meeting-003-web3-primer","blog/software-development/dev-meeting-003-web3-primer",{"title":446,"path":447,"stem":448},"Dev Meeting 004: Introduction to Event Storming","/blog/software-development/dev-meeting-004-introduction-to-event-storming","blog/software-development/dev-meeting-004-introduction-to-event-storming",{"title":450,"path":451,"stem":452},"Dev Meeting 001: Kubernetes is a Framework","/blog/software-development/dev-meeting-kubernetes-is-a-framework","blog/software-development/dev-meeting-kubernetes-is-a-framework",{"title":454,"path":455,"stem":456},"Did You Know? 10 Developer Tips from Real Codebases","/blog/software-development/did-you-know-dev-tips-part-1","blog/software-development/did-you-know-dev-tips-part-1",{"title":458,"path":459,"stem":460},"10 Surprising MusicTech Facts (Part 2)","/blog/software-development/did-you-know-musictech-facts-part-2","blog/software-development/did-you-know-musictech-facts-part-2",{"title":462,"path":463,"stem":464},"Django-cms and GraphQL","/blog/software-development/django-cms-and-graphql","blog/software-development/django-cms-and-graphql",{"title":466,"path":467,"stem":468},"Does Zappa make it super easy?","/blog/software-development/does-zappa-make-it-super-easy","blog/software-development/does-zappa-make-it-super-easy",{"title":470,"path":471,"stem":472},"Establishing cooperation between Netlify and Bravelab","/blog/software-development/establishing-cooperation-between-netlify-and-bravelab","blog/software-development/establishing-cooperation-between-netlify-and-bravelab",{"title":474,"path":475,"stem":476},"Export Ableton Locators to JSON via Max for Live","/blog/software-development/exporting-ableton-live-locators-to-json-with-max-for-live","blog/software-development/exporting-ableton-live-locators-to-json-with-max-for-live",{"title":478,"path":479,"stem":480},"IT Outsourcing: Success and Failure Factors","/blog/software-development/factors-that-contribute-to-the-success-or-failure-of-an-it-outsourcing-project","blog/software-development/factors-that-contribute-to-the-success-or-failure-of-an-it-outsourcing-project",{"title":482,"path":483,"stem":484},"Flutter 2022 Strategy: Analyzing the Roadmap","/blog/software-development/flutter-strategy-for-2022-analyzing-the-new-flutter-roadmap","blog/software-development/flutter-strategy-for-2022-analyzing-the-new-flutter-roadmap",{"title":486,"path":487,"stem":488},"Git Better #1 — Commit Message Convention","/blog/software-development/git-better-1-see-more-with-a-commit-message-convention","blog/software-development/git-better-1-see-more-with-a-commit-message-convention",{"title":490,"path":491,"stem":492},"Hasura in action. How to use it with Django","/blog/software-development/hasura-in-action","blog/software-development/hasura-in-action",{"title":494,"path":495,"stem":496},"Holacracy why and where we are","/blog/software-development/holacracy-why-and-where-we-are","blog/software-development/holacracy-why-and-where-we-are",{"title":498,"path":499,"stem":500},"How does JavaScript work","/blog/software-development/how-does-javascript-work","blog/software-development/how-does-javascript-work",{"title":502,"path":503,"stem":504},"How important is good UX/UI design?","/blog/software-development/how-important-is-good-ux-ui-design","blog/software-development/how-important-is-good-ux-ui-design",{"title":506,"path":507,"stem":508},"How repetitive tasks impact your business","/blog/software-development/how-repetitive-tasks-impact-your-business","blog/software-development/how-repetitive-tasks-impact-your-business",{"title":510,"path":511,"stem":512},"Becoming a Vue.js Dev: Do Paid Trials Work?","/blog/software-development/how-to-become-a-vue-js-developer-and-whether-paid-trials-in-it-work-out","blog/software-development/how-to-become-a-vue-js-developer-and-whether-paid-trials-in-it-work-out",{"title":514,"path":515,"stem":516},"How to Build an MVP in 6 Steps","/blog/software-development/how-to-build-a-minimum-viable-product-mvp-in-6-steps","blog/software-development/how-to-build-a-minimum-viable-product-mvp-in-6-steps",{"title":518,"path":519,"stem":520},"How to conduct workshops for creative industry?","/blog/software-development/how-to-conduct-workshops-for-creative-industry","blog/software-development/how-to-conduct-workshops-for-creative-industry",{"title":522,"path":523,"stem":524},"How to easily create form in Angular","/blog/software-development/how-to-easily-create-form-in-angular","blog/software-development/how-to-easily-create-form-in-angular",{"title":526,"path":527,"stem":528},"How to export orders in Saleor.io to XLSX file","/blog/software-development/how-to-export-orders-in-saleor-io-to-xlsx-file","blog/software-development/how-to-export-orders-in-saleor-io-to-xlsx-file",{"title":530,"path":531,"stem":532},"Handling High Loads on E-Commerce Platforms","/blog/software-development/how-to-handle-high-loads-on-e-commerce-platform-with-ease","blog/software-development/how-to-handle-high-loads-on-e-commerce-platform-with-ease",{"title":534,"path":535,"stem":536},"How to launch Saleor.io shop instance within 40h","/blog/software-development/how-to-launch-saleor-io-shop-instance-within-40h","blog/software-development/how-to-launch-saleor-io-shop-instance-within-40h",{"title":538,"path":539,"stem":540},"First Steps to Build a Business Relationship","/blog/software-development/how-to-make-the-first-step-to-establish-a-business-relationship","blog/software-development/how-to-make-the-first-step-to-establish-a-business-relationship",{"title":542,"path":543,"stem":544},"Multi-Tenant Apps with Django and Saleor.io","/blog/software-development/how-to-manage-tenants-in-the-multitenant-app-based-on-django-tenants-and-saleor-io-platform","blog/software-development/how-to-manage-tenants-in-the-multitenant-app-based-on-django-tenants-and-saleor-io-platform",{"title":546,"path":547,"stem":548},"Notion Backup Tool Built in 3 Days with Python","/blog/software-development/how-we-built-a-notion-backup-tool-in-3-days-with-pythonvue-and-why","blog/software-development/how-we-built-a-notion-backup-tool-in-3-days-with-pythonvue-and-why",{"title":550,"path":551,"stem":552},"Important new features in Python 3.8","/blog/software-development/important-new-features-in-python-3-8","blog/software-development/important-new-features-in-python-3-8",{"title":554,"path":555,"stem":556},"Installing Proxmox on dedicated server from OVH","/blog/software-development/installing-proxmox-on-dedicated-server-from-ovh","blog/software-development/installing-proxmox-on-dedicated-server-from-ovh",{"title":558,"path":559,"stem":560},"Integrating SignNow E-Signatures into Your Django Application","/blog/software-development/integrating-signnow-e-signatures-into-your-django-application","blog/software-development/integrating-signnow-e-signatures-into-your-django-application",{"title":562,"path":563,"stem":564},"Tempus Metronome and GetSongBPM API","/blog/software-development/integrating-tempus-metronome-with-the-getsongbpm-api-what-bpm-really-means-and-how-to-use-it","blog/software-development/integrating-tempus-metronome-with-the-getsongbpm-api-what-bpm-really-means-and-how-to-use-it",{"title":566,"path":567,"stem":568},"Introducing MusicTech Poland","/blog/software-development/introducing-musictech-poland","blog/software-development/introducing-musictech-poland",{"title":570,"path":571,"stem":572},"Vue.js as a Frontend for Saleor.io","/blog/software-development/is-it-possible-to-use-vue-js-as-a-frontend-for-saleor-io-platform","blog/software-development/is-it-possible-to-use-vue-js-as-a-frontend-for-saleor-io-platform",{"title":574,"path":575,"stem":576},"Is your business ready for the cashless era?","/blog/software-development/is-your-business-ready-for-the-cashless-era","blog/software-development/is-your-business-ready-for-the-cashless-era",{"title":578,"path":579,"stem":580},"Is your face ready to buy?","/blog/software-development/is-your-face-ready-to-buy","blog/software-development/is-your-face-ready-to-buy",{"title":582,"path":583,"stem":584},"JS Frameworks: Trends and Opportunities","/blog/software-development/javascript-trending-frameworks-and-market-opportunities","blog/software-development/javascript-trending-frameworks-and-market-opportunities",{"title":586,"path":587,"stem":588},"Kanban Board: Boost Your Team Productivity","/blog/software-development/kanban-board-methodology-hack-your-companys-productivity","blog/software-development/kanban-board-methodology-hack-your-companys-productivity",{"title":590,"path":591,"stem":592},"Verified Human Cert MCP Server: Prove Your Music Is Human-Made, Right from the Terminal","/blog/software-development/mcp-verified-human-cert-open-source","blog/software-development/mcp-verified-human-cert-open-source",{"title":594,"path":595,"stem":596},"Migrating from TravisCI to Github Actions","/blog/software-development/migrating-from-travisci-to-github-actions","blog/software-development/migrating-from-travisci-to-github-actions",{"title":598,"path":599,"stem":600},"MusicTech Lab: Top Software Developer by Clutch","/blog/software-development/musictechlab-is-recognized-as-a-top-custom-software-developer-by-clutch","blog/software-development/musictechlab-is-recognized-as-a-top-custom-software-developer-by-clutch",{"title":602,"path":603,"stem":604},"MusicTech Lab x Verified Human: Building a Trust Layer for Human-Made Music","/blog/software-development/musictechlab_blog_verified_human_partnership","blog/software-development/musictechlab_blog_verified_human_partnership",{"title":606,"path":607,"stem":608},"MusicXML: Standard for Music Notation","/blog/software-development/musicxml-standard-for-music-notation-and-education","blog/software-development/musicxml-standard-for-music-notation-and-education",{"title":610,"path":611,"stem":612},"Only a few books but dozens of ideas","/blog/software-development/only-a-few-books-but-dozens-of-ideas","blog/software-development/only-a-few-books-but-dozens-of-ideas",{"title":614,"path":615,"stem":616},"Overdue Invoices and Issue Tracker Integration","/blog/software-development/overdue-invoices-integration-with-the-issue-tracking-system","blog/software-development/overdue-invoices-integration-with-the-issue-tracking-system",{"title":618,"path":619,"stem":620},"Performing SAML SSO using JWT in Django","/blog/software-development/performing-saml-sso-using-jwt-in-django","blog/software-development/performing-saml-sso-using-jwt-in-django",{"title":622,"path":623,"stem":624},"Progressive Web Apps for Mobile Development","/blog/software-development/progressive-web-apps-a-new-way-of-creating-mobile-application","blog/software-development/progressive-web-apps-a-new-way-of-creating-mobile-application",{"title":626,"path":627,"stem":628},"Recruitment System: Gmail, Jira, and CRM","/blog/software-development/recruitment-system-integrating-gmail-bravely-jira-slack-and-copper-crm","blog/software-development/recruitment-system-integrating-gmail-bravely-jira-slack-and-copper-crm",{"title":630,"path":631,"stem":632},"Scratch Me: Chrome Extension for Leads","/blog/software-development/scratch-me-a-simple-chrome-extension-which-will-increase-your-productivity","blog/software-development/scratch-me-a-simple-chrome-extension-which-will-increase-your-productivity",{"title":634,"path":635,"stem":636},"Scratch Me – integration with the Copper CRM","/blog/software-development/scratch-me-integration-with-the-copper-crm","blog/software-development/scratch-me-integration-with-the-copper-crm",{"title":638,"path":639,"stem":640},"SignNow MCP Server: E-Signatures Straight from Claude Code","/blog/software-development/signnow-mcp-server-e-signatures-from-claude-code","blog/software-development/signnow-mcp-server-e-signatures-from-claude-code",{"title":642,"path":643,"stem":644},"Music Industry Tech Openings (March 2024 Update)","/blog/software-development/technical-job-opportunities-in-the-music-industry","blog/software-development/technical-job-opportunities-in-the-music-industry",{"title":646,"path":647,"stem":648},"Thanks app – a Management 3.0 solution","/blog/software-development/thanks-app-a-management-3-0-solution","blog/software-development/thanks-app-a-management-3-0-solution",{"title":650,"path":651,"stem":652},"Colonial Pipeline Case: 7 Security Reminders","/blog/software-development/the-case-of-colonial-pipeline-and-7-security-reminders","blog/software-development/the-case-of-colonial-pipeline-and-7-security-reminders",{"title":654,"path":655,"stem":656},"The Evolution and Future of E-commerce Platforms","/blog/software-development/the-evolution-and-future-of-e-commerce-platforms","blog/software-development/the-evolution-and-future-of-e-commerce-platforms",{"title":658,"path":659,"stem":660},"The Gender Gap in the Tech Industry","/blog/software-development/the-gender-gap-in-the-tech-industry","blog/software-development/the-gender-gap-in-the-tech-industry",{"title":662,"path":663,"stem":664},"First Attempt to Implement 4DX at Bravelab.io","/blog/software-development/the-very-first-attempt-to-implement-4dx-in-bravelab-io","blog/software-development/the-very-first-attempt-to-implement-4dx-in-bravelab-io",{"title":666,"path":667,"stem":668},"The WTF Scale: IT Project Complexity","/blog/software-development/the-wtf-programming-scale-measuring-it-project-complexity","blog/software-development/the-wtf-programming-scale-measuring-it-project-complexity",{"title":670,"path":671,"stem":672},"Top 10 articles through the eyes of our developers","/blog/software-development/top-10-articles-through-the-eyes-of-our-developers","blog/software-development/top-10-articles-through-the-eyes-of-our-developers",{"title":674,"path":675,"stem":676},"Top 6 apps made with Flutter","/blog/software-development/top-6-apps-made-with-flutter","blog/software-development/top-6-apps-made-with-flutter",{"title":678,"path":679,"stem":680},"Uber 101: How Uber Made It to the Top","/blog/software-development/uber-101-how-this-ride-sharing-behemoth-made-it-to-the-top","blog/software-development/uber-101-how-this-ride-sharing-behemoth-made-it-to-the-top",{"title":682,"path":683,"stem":684},"MusicTech Lab Partners with Music Glue","/blog/software-development/unifying-artists-and-audiences-exploring-music-glue","blog/software-development/unifying-artists-and-audiences-exploring-music-glue",{"title":686,"path":687,"stem":688},"Why AI Will Defeat Traditional HR","/blog/software-development/warning-why-artificial-intelligence-will-defeat-traditional-hr","blog/software-development/warning-why-artificial-intelligence-will-defeat-traditional-hr",{"title":690,"path":691,"stem":692},"What is a Discovery Document?","/blog/software-development/what-is-discovery-document","blog/software-development/what-is-discovery-document",{"title":694,"path":695,"stem":696},"What is Flutter, and Why is it Worth Considering?","/blog/software-development/what-is-flutter-and-why-is-it-worth-considering","blog/software-development/what-is-flutter-and-why-is-it-worth-considering",{"title":698,"path":699,"stem":700},"What is a Watermarked Song?","/blog/software-development/what-is-watermarked-song","blog/software-development/what-is-watermarked-song",{"title":702,"path":703,"stem":704},"Choosing a Frontend Framework for the Web","/blog/software-development/which-framework-should-you-choose-for-the-frontend-web-platform-development","blog/software-development/which-framework-should-you-choose-for-the-frontend-web-platform-development",{"title":706,"path":707,"stem":708},"Why DAWs Are the Wrong Tool for Starting a Song","/blog/software-development/why-daws-wrong-tool-for-starting-song","blog/software-development/why-daws-wrong-tool-for-starting-song",{"title":710,"path":711,"stem":712},"Why the Programming World Loves Python","/blog/software-development/why-the-programming-world-loves-python","blog/software-development/why-the-programming-world-loves-python",{"title":714,"path":715,"stem":716},"Why We Don't Build Chat From Scratch (And Neither Should You)","/blog/software-development/why-we-dont-build-chat-from-scratch","blog/software-development/why-we-dont-build-chat-from-scratch",{"title":718,"path":719,"stem":720},"Why we use Sanity.io","/blog/software-development/why-we-use-sanity-io","blog/software-development/why-we-use-sanity-io",{"title":722,"path":723,"stem":724,"children":725,"page":69},"Sportstech","/blog/sportstech","blog/sportstech",[726,730],{"title":727,"path":728,"stem":729},"BeatBuddy Replay: Video Analysis App Challenges","/blog/sportstech/beatbuddy-replay-video-analysis-app-for-swimmers-flutter","blog/sportstech/beatbuddy-replay-video-analysis-app-for-swimmers-flutter",{"title":731,"path":732,"stem":733},"How to Create a Watch Face App for Garmin Watch","/blog/sportstech/how-to-create-watch-face-app-for-garmin-watch","blog/sportstech/how-to-create-watch-face-app-for-garmin-watch",{"id":735,"title":116,"authors":736,"badge":742,"body":745,"category":2736,"client":2737,"date":2738,"description":2739,"extension":2740,"faq":2737,"featured":69,"featuredOrder":2737,"hidden":69,"image":2741,"keyTakeaways":2743,"meta":2757,"navigation":1132,"path":117,"seo":2758,"status":2737,"stem":118,"tags":2759,"teaser":2737,"__hash__":2763},"posts/blog/music-data/building-a-suno-remix-app-with-nuxt-and-firebase.md",[737],{"name":738,"to":739,"avatar":740},"Mariusz Smenżyk","https://www.linkedin.com/in/mariusz-smenzyk/",{"src":741},"/images/people/mariusz-smenzyk2.webp",{"label":743,"color":744},"AI Audio","#7c3aed",{"type":746,"value":747,"toc":2716},"minimark",[748,765,772,775,780,790,802,821,827,829,833,840,847,887,892,951,953,957,1041,1043,1047,1050,1055,1062,1577,1584,1588,1591,1939,1960,1964,1967,2118,2125,2127,2131,2134,2160,2163,2170,2177,2183,2186,2192,2196,2203,2250,2253,2255,2259,2262,2427,2430,2438,2441,2464,2466,2470,2473,2545,2547,2551,2558,2564,2571,2581,2584,2586,2590,2622,2624,2628,2631,2664,2666,2670,2683,2690,2692,2696,2713],[749,750,751,752,756,757,760,761,764],"p",{},"Imagine uploading a raw audio recording and getting back a fully remixed version in a completely different genre - Pop to Jazz, Rock to Lo-fi - all powered by AI. That's exactly what we built as a proof of concept using ",[753,754,755],"strong",{},"Suno's AI",", ",[753,758,759],{},"Nuxt 4",", and ",[753,762,763],{},"Firebase",".",[749,766,767,768,771],{},"In this article, we'll walk you through the architecture, key technical decisions, and code behind our ",[753,769,770],{},"Suno Remix PoC",". Whether you're a music tech enthusiast or a developer exploring AI-powered audio tools, this guide will help you build something similar.",[773,774],"hr",{},[776,777,779],"h2",{"id":778},"what-is-suno","What is Suno?",[749,781,782,789],{},[783,784,788],"a",{"href":785,"rel":786},"https://suno.com",[787],"nofollow","Suno"," is one of the most impressive AI music generation platforms available today. It can generate full songs from text prompts, create covers in different styles, extend existing tracks, and even separate stems (vocals vs. instruments).",[749,791,792,793,796,797,801],{},"The catch? ",[753,794,795],{},"Suno doesn't have an official public API."," There's no ",[798,799,800],"code",{},"suno.com/developers"," portal or API key system. Instead, a growing ecosystem of third-party services wraps Suno's capabilities behind a REST API (Application Programming Interface).",[749,803,804,805,812,813,816,817,820],{},"For this PoC, we chose ",[783,806,809],{"href":807,"rel":808},"https://sunoapi.org",[787],[753,810,811],{},"sunoapi.org"," - the most documented third-party provider with support for the ",[798,814,815],{},"upload-cover"," endpoint, which is what enables the \"remix\" functionality. It costs approximately ",[753,818,819],{},"$0.005 per credit",", which is very affordable for experimentation.",[822,823,824],"note",{},[749,825,826],{},"All available Suno API options are unofficial. For a proof of concept this is perfectly fine, but keep an eye on Suno's official announcements if you plan to take this to production.",[773,828],{},[776,830,832],{"id":831},"the-architecture","The Architecture",[749,834,835,836,839],{},"Here's the core challenge: Suno's API doesn't accept file uploads directly. Instead, it requires a ",[753,837,838],{},"public URL"," pointing to the audio file. This means we need an intermediary storage layer.",[749,841,842,843,846],{},"Our solution uses ",[753,844,845],{},"Firebase Storage"," as the bridge:",[848,849,850,857,863,872,881],"ol",{},[851,852,853,856],"li",{},[753,854,855],{},"User uploads a WAV/MP3 file"," → stored in Firebase Storage (client-side SDK)",[851,858,859,862],{},[753,860,861],{},"Firebase returns a public download URL"," → this is the key piece Suno needs",[851,864,865,871],{},[753,866,867,868],{},"Browser calls ",[798,869,870],{},"POST /api/remix"," → our Nitro server route forwards the request to sunoapi.org with the API key",[851,873,874,880],{},[753,875,876,877],{},"Browser polls ",[798,878,879],{},"GET /api/remix/:taskId"," → our server route checks sunoapi.org for the remix status every 30 seconds",[851,882,883,886],{},[753,884,885],{},"Remix is ready"," → the browser plays both original and remixed audio side-by-side using WaveSurfer.js",[749,888,889],{},[753,890,891],{},"Why this architecture?",[893,894,895,908],"table",{},[896,897,898],"thead",{},[899,900,901,905],"tr",{},[902,903,904],"th",{},"Decision",[902,906,907],{},"Reason",[909,910,911,921,931,941],"tbody",{},[899,912,913,918],{},[914,915,916],"td",{},[753,917,845],{},[914,919,920],{},"Gives us a publicly accessible URL for the uploaded WAV file",[899,922,923,928],{},[914,924,925],{},[753,926,927],{},"Nitro server routes",[914,929,930],{},"Keeps the Suno API key on the server - never exposed to the browser",[899,932,933,938],{},[914,934,935],{},[753,936,937],{},"Polling",[914,939,940],{},"Suno processes remixes asynchronously (1-3 min); we poll every 30 seconds",[899,942,943,948],{},[914,944,945],{},[753,946,947],{},"WaveSurfer.js",[914,949,950],{},"Industry-standard waveform visualization for comparing original vs. remixed audio",[773,952],{},[776,954,956],{"id":955},"tech-stack","Tech Stack",[893,958,959,969],{},[896,960,961],{},[899,962,963,966],{},[902,964,965],{},"Layer",[902,967,968],{},"Technology",[909,970,971,981,991,1001,1012,1021,1031],{},[899,972,973,976],{},[914,974,975],{},"Frontend",[914,977,978,980],{},[753,979,759],{}," + Vue 3 + Nuxt UI",[899,982,983,986],{},[914,984,985],{},"Styling",[914,987,988],{},[753,989,990],{},"Tailwind CSS 4",[899,992,993,996],{},[914,994,995],{},"Audio Visualization",[914,997,998],{},[753,999,1000],{},"WaveSurfer.js 7",[899,1002,1003,1006],{},[914,1004,1005],{},"Server",[914,1007,1008,1011],{},[753,1009,1010],{},"Nitro"," (built into Nuxt)",[899,1013,1014,1017],{},[914,1015,1016],{},"Storage",[914,1018,1019],{},[753,1020,845],{},[899,1022,1023,1026],{},[914,1024,1025],{},"Hosting",[914,1027,1028],{},[753,1029,1030],{},"Firebase Hosting",[899,1032,1033,1036],{},[914,1034,1035],{},"API",[914,1037,1038,1040],{},[753,1039,811],{}," (third-party Suno proxy)",[773,1042],{},[776,1044,1046],{"id":1045},"the-key-technical-challenge-upload-flow","The Key Technical Challenge: Upload Flow",[749,1048,1049],{},"The most interesting part of this project is how we bridge the gap between a user's local WAV file and Suno's requirement for a public URL.",[1051,1052,1054],"h3",{"id":1053},"step-1-upload-to-firebase-storage","Step 1: Upload to Firebase Storage",[749,1056,1057,1058,1061],{},"We created a ",[798,1059,1060],{},"useFirebaseStorage"," composable that handles the upload with progress tracking:",[1063,1064,1070],"pre",{"className":1065,"code":1066,"filename":1067,"language":1068,"meta":1069,"style":1069},"language-typescript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { getStorage, ref as storageRef, uploadBytesResumable, getDownloadURL } from 'firebase/storage'\n\nasync function uploadWavFile(file: File): Promise\u003Cstring> {\n  const storage = getStorage(app)\n  const fileName = `${crypto.randomUUID()}.wav`\n  const fileRef = storageRef(storage, `uploads/${fileName}`)\n\n  const uploadTask = uploadBytesResumable(fileRef, file, {\n    contentType: 'audio/wav',\n  })\n\n  return new Promise((resolve, reject) => {\n    uploadTask.on('state_changed',\n      (snapshot) => {\n        // Track progress (0-100%)\n        uploadProgress.value = Math.round(\n          (snapshot.bytesTransferred / snapshot.totalBytes) * 100\n        )\n      },\n      reject,\n      async () => {\n        // Get the public download URL\n        const downloadUrl = await getDownloadURL(uploadTask.snapshot.ref)\n        resolve(downloadUrl)\n      }\n    )\n  })\n}\n","useFirebaseStorage.ts","typescript","",[798,1071,1072,1127,1134,1180,1203,1236,1272,1277,1303,1322,1330,1335,1366,1388,1403,1410,1434,1468,1474,1480,1488,1501,1507,1539,1552,1558,1564,1571],{"__ignoreMap":1069},[1073,1074,1077,1081,1085,1089,1092,1095,1098,1101,1103,1106,1108,1111,1114,1117,1120,1124],"span",{"class":1075,"line":1076},"line",1,[1073,1078,1080],{"class":1079},"s7zQu","import",[1073,1082,1084],{"class":1083},"sMK4o"," {",[1073,1086,1088],{"class":1087},"sTEyZ"," getStorage",[1073,1090,1091],{"class":1083},",",[1073,1093,1094],{"class":1087}," ref",[1073,1096,1097],{"class":1079}," as",[1073,1099,1100],{"class":1087}," storageRef",[1073,1102,1091],{"class":1083},[1073,1104,1105],{"class":1087}," uploadBytesResumable",[1073,1107,1091],{"class":1083},[1073,1109,1110],{"class":1087}," getDownloadURL",[1073,1112,1113],{"class":1083}," }",[1073,1115,1116],{"class":1079}," from",[1073,1118,1119],{"class":1083}," '",[1073,1121,1123],{"class":1122},"sfazB","firebase/storage",[1073,1125,1126],{"class":1083},"'\n",[1073,1128,1130],{"class":1075,"line":1129},2,[1073,1131,1133],{"emptyLinePlaceholder":1132},true,"\n",[1073,1135,1137,1141,1144,1148,1151,1155,1158,1162,1165,1168,1171,1174,1177],{"class":1075,"line":1136},3,[1073,1138,1140],{"class":1139},"spNyl","async",[1073,1142,1143],{"class":1139}," function",[1073,1145,1147],{"class":1146},"s2Zo4"," uploadWavFile",[1073,1149,1150],{"class":1083},"(",[1073,1152,1154],{"class":1153},"sHdIc","file",[1073,1156,1157],{"class":1083},":",[1073,1159,1161],{"class":1160},"sBMFI"," File",[1073,1163,1164],{"class":1083},"):",[1073,1166,1167],{"class":1160}," Promise",[1073,1169,1170],{"class":1083},"\u003C",[1073,1172,1173],{"class":1160},"string",[1073,1175,1176],{"class":1083},">",[1073,1178,1179],{"class":1083}," {\n",[1073,1181,1183,1186,1189,1192,1194,1197,1200],{"class":1075,"line":1182},4,[1073,1184,1185],{"class":1139},"  const",[1073,1187,1188],{"class":1087}," storage",[1073,1190,1191],{"class":1083}," =",[1073,1193,1088],{"class":1146},[1073,1195,1150],{"class":1196},"swJcz",[1073,1198,1199],{"class":1087},"app",[1073,1201,1202],{"class":1196},")\n",[1073,1204,1206,1208,1211,1213,1216,1219,1221,1224,1227,1230,1233],{"class":1075,"line":1205},5,[1073,1207,1185],{"class":1139},[1073,1209,1210],{"class":1087}," fileName",[1073,1212,1191],{"class":1083},[1073,1214,1215],{"class":1083}," `${",[1073,1217,1218],{"class":1087},"crypto",[1073,1220,764],{"class":1083},[1073,1222,1223],{"class":1146},"randomUUID",[1073,1225,1226],{"class":1087},"()",[1073,1228,1229],{"class":1083},"}",[1073,1231,1232],{"class":1122},".wav",[1073,1234,1235],{"class":1083},"`\n",[1073,1237,1239,1241,1244,1246,1248,1250,1253,1255,1258,1261,1264,1267,1270],{"class":1075,"line":1238},6,[1073,1240,1185],{"class":1139},[1073,1242,1243],{"class":1087}," fileRef",[1073,1245,1191],{"class":1083},[1073,1247,1100],{"class":1146},[1073,1249,1150],{"class":1196},[1073,1251,1252],{"class":1087},"storage",[1073,1254,1091],{"class":1083},[1073,1256,1257],{"class":1083}," `",[1073,1259,1260],{"class":1122},"uploads/",[1073,1262,1263],{"class":1083},"${",[1073,1265,1266],{"class":1087},"fileName",[1073,1268,1269],{"class":1083},"}`",[1073,1271,1202],{"class":1196},[1073,1273,1275],{"class":1075,"line":1274},7,[1073,1276,1133],{"emptyLinePlaceholder":1132},[1073,1278,1280,1282,1285,1287,1289,1291,1294,1296,1299,1301],{"class":1075,"line":1279},8,[1073,1281,1185],{"class":1139},[1073,1283,1284],{"class":1087}," uploadTask",[1073,1286,1191],{"class":1083},[1073,1288,1105],{"class":1146},[1073,1290,1150],{"class":1196},[1073,1292,1293],{"class":1087},"fileRef",[1073,1295,1091],{"class":1083},[1073,1297,1298],{"class":1087}," file",[1073,1300,1091],{"class":1083},[1073,1302,1179],{"class":1083},[1073,1304,1306,1309,1311,1313,1316,1319],{"class":1075,"line":1305},9,[1073,1307,1308],{"class":1196},"    contentType",[1073,1310,1157],{"class":1083},[1073,1312,1119],{"class":1083},[1073,1314,1315],{"class":1122},"audio/wav",[1073,1317,1318],{"class":1083},"'",[1073,1320,1321],{"class":1083},",\n",[1073,1323,1325,1328],{"class":1075,"line":1324},10,[1073,1326,1327],{"class":1083},"  }",[1073,1329,1202],{"class":1196},[1073,1331,1333],{"class":1075,"line":1332},11,[1073,1334,1133],{"emptyLinePlaceholder":1132},[1073,1336,1338,1341,1344,1346,1348,1350,1353,1355,1358,1361,1364],{"class":1075,"line":1337},12,[1073,1339,1340],{"class":1079},"  return",[1073,1342,1343],{"class":1083}," new",[1073,1345,1167],{"class":1160},[1073,1347,1150],{"class":1196},[1073,1349,1150],{"class":1083},[1073,1351,1352],{"class":1153},"resolve",[1073,1354,1091],{"class":1083},[1073,1356,1357],{"class":1153}," reject",[1073,1359,1360],{"class":1083},")",[1073,1362,1363],{"class":1139}," =>",[1073,1365,1179],{"class":1083},[1073,1367,1369,1372,1374,1377,1379,1381,1384,1386],{"class":1075,"line":1368},13,[1073,1370,1371],{"class":1087},"    uploadTask",[1073,1373,764],{"class":1083},[1073,1375,1376],{"class":1146},"on",[1073,1378,1150],{"class":1196},[1073,1380,1318],{"class":1083},[1073,1382,1383],{"class":1122},"state_changed",[1073,1385,1318],{"class":1083},[1073,1387,1321],{"class":1083},[1073,1389,1391,1394,1397,1399,1401],{"class":1075,"line":1390},14,[1073,1392,1393],{"class":1083},"      (",[1073,1395,1396],{"class":1153},"snapshot",[1073,1398,1360],{"class":1083},[1073,1400,1363],{"class":1139},[1073,1402,1179],{"class":1083},[1073,1404,1406],{"class":1075,"line":1405},15,[1073,1407,1409],{"class":1408},"sHwdD","        // Track progress (0-100%)\n",[1073,1411,1413,1416,1418,1421,1423,1426,1428,1431],{"class":1075,"line":1412},16,[1073,1414,1415],{"class":1087},"        uploadProgress",[1073,1417,764],{"class":1083},[1073,1419,1420],{"class":1087},"value",[1073,1422,1191],{"class":1083},[1073,1424,1425],{"class":1087}," Math",[1073,1427,764],{"class":1083},[1073,1429,1430],{"class":1146},"round",[1073,1432,1433],{"class":1196},"(\n",[1073,1435,1437,1440,1442,1444,1447,1450,1453,1455,1458,1461,1464],{"class":1075,"line":1436},17,[1073,1438,1439],{"class":1196},"          (",[1073,1441,1396],{"class":1087},[1073,1443,764],{"class":1083},[1073,1445,1446],{"class":1087},"bytesTransferred",[1073,1448,1449],{"class":1083}," /",[1073,1451,1452],{"class":1087}," snapshot",[1073,1454,764],{"class":1083},[1073,1456,1457],{"class":1087},"totalBytes",[1073,1459,1460],{"class":1196},") ",[1073,1462,1463],{"class":1083},"*",[1073,1465,1467],{"class":1466},"sbssI"," 100\n",[1073,1469,1471],{"class":1075,"line":1470},18,[1073,1472,1473],{"class":1196},"        )\n",[1073,1475,1477],{"class":1075,"line":1476},19,[1073,1478,1479],{"class":1083},"      },\n",[1073,1481,1483,1486],{"class":1075,"line":1482},20,[1073,1484,1485],{"class":1087},"      reject",[1073,1487,1321],{"class":1083},[1073,1489,1491,1494,1497,1499],{"class":1075,"line":1490},21,[1073,1492,1493],{"class":1139},"      async",[1073,1495,1496],{"class":1083}," ()",[1073,1498,1363],{"class":1139},[1073,1500,1179],{"class":1083},[1073,1502,1504],{"class":1075,"line":1503},22,[1073,1505,1506],{"class":1408},"        // Get the public download URL\n",[1073,1508,1510,1513,1516,1518,1521,1523,1525,1528,1530,1532,1534,1537],{"class":1075,"line":1509},23,[1073,1511,1512],{"class":1139},"        const",[1073,1514,1515],{"class":1087}," downloadUrl",[1073,1517,1191],{"class":1083},[1073,1519,1520],{"class":1079}," await",[1073,1522,1110],{"class":1146},[1073,1524,1150],{"class":1196},[1073,1526,1527],{"class":1087},"uploadTask",[1073,1529,764],{"class":1083},[1073,1531,1396],{"class":1087},[1073,1533,764],{"class":1083},[1073,1535,1536],{"class":1087},"ref",[1073,1538,1202],{"class":1196},[1073,1540,1542,1545,1547,1550],{"class":1075,"line":1541},24,[1073,1543,1544],{"class":1146},"        resolve",[1073,1546,1150],{"class":1196},[1073,1548,1549],{"class":1087},"downloadUrl",[1073,1551,1202],{"class":1196},[1073,1553,1555],{"class":1075,"line":1554},25,[1073,1556,1557],{"class":1083},"      }\n",[1073,1559,1561],{"class":1075,"line":1560},26,[1073,1562,1563],{"class":1196},"    )\n",[1073,1565,1567,1569],{"class":1075,"line":1566},27,[1073,1568,1327],{"class":1083},[1073,1570,1202],{"class":1196},[1073,1572,1574],{"class":1075,"line":1573},28,[1073,1575,1576],{"class":1083},"}\n",[749,1578,1579,1580,1583],{},"The ",[798,1581,1582],{},"getDownloadURL()"," call returns a publicly accessible Firebase Storage URL - exactly what Suno needs.",[1051,1585,1587],{"id":1586},"step-2-call-the-suno-api-via-server-proxy","Step 2: Call the Suno API (via server proxy)",[749,1589,1590],{},"We never call Suno directly from the browser. Instead, we use a Nitro server route that reads the API key from server-only runtime configuration:",[1063,1592,1595],{"className":1065,"code":1593,"filename":1594,"language":1068,"meta":1069,"style":1069},"export default defineEventHandler(async (event) => {\n  const body = await readBody(event)\n  const config = useRuntimeConfig()\n\n  const result = await fetch('https://api.sunoapi.org/api/v1/generate/upload-cover', {\n    method: 'POST',\n    headers: {\n      'Authorization': `Bearer ${config.sunoApiKey}`,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({\n      uploadUrl: body.uploadUrl,\n      customMode: true,\n      style: body.style,\n      title: body.title,\n      model: 'V4_5',\n    }),\n  })\n\n  const data = await result.json()\n  return { taskId: data.data.taskId }\n})\n","server/api/remix.post.ts",[798,1596,1597,1624,1644,1659,1663,1690,1706,1715,1746,1766,1771,1791,1807,1820,1836,1852,1868,1877,1883,1887,1907,1933],{"__ignoreMap":1069},[1073,1598,1599,1602,1605,1608,1610,1612,1615,1618,1620,1622],{"class":1075,"line":1076},[1073,1600,1601],{"class":1079},"export",[1073,1603,1604],{"class":1079}," default",[1073,1606,1607],{"class":1146}," defineEventHandler",[1073,1609,1150],{"class":1087},[1073,1611,1140],{"class":1139},[1073,1613,1614],{"class":1083}," (",[1073,1616,1617],{"class":1153},"event",[1073,1619,1360],{"class":1083},[1073,1621,1363],{"class":1139},[1073,1623,1179],{"class":1083},[1073,1625,1626,1628,1631,1633,1635,1638,1640,1642],{"class":1075,"line":1129},[1073,1627,1185],{"class":1139},[1073,1629,1630],{"class":1087}," body",[1073,1632,1191],{"class":1083},[1073,1634,1520],{"class":1079},[1073,1636,1637],{"class":1146}," readBody",[1073,1639,1150],{"class":1196},[1073,1641,1617],{"class":1087},[1073,1643,1202],{"class":1196},[1073,1645,1646,1648,1651,1653,1656],{"class":1075,"line":1136},[1073,1647,1185],{"class":1139},[1073,1649,1650],{"class":1087}," config",[1073,1652,1191],{"class":1083},[1073,1654,1655],{"class":1146}," useRuntimeConfig",[1073,1657,1658],{"class":1196},"()\n",[1073,1660,1661],{"class":1075,"line":1182},[1073,1662,1133],{"emptyLinePlaceholder":1132},[1073,1664,1665,1667,1670,1672,1674,1677,1679,1681,1684,1686,1688],{"class":1075,"line":1205},[1073,1666,1185],{"class":1139},[1073,1668,1669],{"class":1087}," result",[1073,1671,1191],{"class":1083},[1073,1673,1520],{"class":1079},[1073,1675,1676],{"class":1146}," fetch",[1073,1678,1150],{"class":1196},[1073,1680,1318],{"class":1083},[1073,1682,1683],{"class":1122},"https://api.sunoapi.org/api/v1/generate/upload-cover",[1073,1685,1318],{"class":1083},[1073,1687,1091],{"class":1083},[1073,1689,1179],{"class":1083},[1073,1691,1692,1695,1697,1699,1702,1704],{"class":1075,"line":1238},[1073,1693,1694],{"class":1196},"    method",[1073,1696,1157],{"class":1083},[1073,1698,1119],{"class":1083},[1073,1700,1701],{"class":1122},"POST",[1073,1703,1318],{"class":1083},[1073,1705,1321],{"class":1083},[1073,1707,1708,1711,1713],{"class":1075,"line":1274},[1073,1709,1710],{"class":1196},"    headers",[1073,1712,1157],{"class":1083},[1073,1714,1179],{"class":1083},[1073,1716,1717,1720,1723,1725,1727,1729,1732,1734,1737,1739,1742,1744],{"class":1075,"line":1279},[1073,1718,1719],{"class":1083},"      '",[1073,1721,1722],{"class":1196},"Authorization",[1073,1724,1318],{"class":1083},[1073,1726,1157],{"class":1083},[1073,1728,1257],{"class":1083},[1073,1730,1731],{"class":1122},"Bearer ",[1073,1733,1263],{"class":1083},[1073,1735,1736],{"class":1087},"config",[1073,1738,764],{"class":1083},[1073,1740,1741],{"class":1087},"sunoApiKey",[1073,1743,1269],{"class":1083},[1073,1745,1321],{"class":1083},[1073,1747,1748,1750,1753,1755,1757,1759,1762,1764],{"class":1075,"line":1305},[1073,1749,1719],{"class":1083},[1073,1751,1752],{"class":1196},"Content-Type",[1073,1754,1318],{"class":1083},[1073,1756,1157],{"class":1083},[1073,1758,1119],{"class":1083},[1073,1760,1761],{"class":1122},"application/json",[1073,1763,1318],{"class":1083},[1073,1765,1321],{"class":1083},[1073,1767,1768],{"class":1075,"line":1324},[1073,1769,1770],{"class":1083},"    },\n",[1073,1772,1773,1776,1778,1781,1783,1786,1788],{"class":1075,"line":1332},[1073,1774,1775],{"class":1196},"    body",[1073,1777,1157],{"class":1083},[1073,1779,1780],{"class":1087}," JSON",[1073,1782,764],{"class":1083},[1073,1784,1785],{"class":1146},"stringify",[1073,1787,1150],{"class":1196},[1073,1789,1790],{"class":1083},"{\n",[1073,1792,1793,1796,1798,1800,1802,1805],{"class":1075,"line":1337},[1073,1794,1795],{"class":1196},"      uploadUrl",[1073,1797,1157],{"class":1083},[1073,1799,1630],{"class":1087},[1073,1801,764],{"class":1083},[1073,1803,1804],{"class":1087},"uploadUrl",[1073,1806,1321],{"class":1083},[1073,1808,1809,1812,1814,1818],{"class":1075,"line":1368},[1073,1810,1811],{"class":1196},"      customMode",[1073,1813,1157],{"class":1083},[1073,1815,1817],{"class":1816},"sfNiH"," true",[1073,1819,1321],{"class":1083},[1073,1821,1822,1825,1827,1829,1831,1834],{"class":1075,"line":1390},[1073,1823,1824],{"class":1196},"      style",[1073,1826,1157],{"class":1083},[1073,1828,1630],{"class":1087},[1073,1830,764],{"class":1083},[1073,1832,1833],{"class":1087},"style",[1073,1835,1321],{"class":1083},[1073,1837,1838,1841,1843,1845,1847,1850],{"class":1075,"line":1405},[1073,1839,1840],{"class":1196},"      title",[1073,1842,1157],{"class":1083},[1073,1844,1630],{"class":1087},[1073,1846,764],{"class":1083},[1073,1848,1849],{"class":1087},"title",[1073,1851,1321],{"class":1083},[1073,1853,1854,1857,1859,1861,1864,1866],{"class":1075,"line":1412},[1073,1855,1856],{"class":1196},"      model",[1073,1858,1157],{"class":1083},[1073,1860,1119],{"class":1083},[1073,1862,1863],{"class":1122},"V4_5",[1073,1865,1318],{"class":1083},[1073,1867,1321],{"class":1083},[1073,1869,1870,1873,1875],{"class":1075,"line":1436},[1073,1871,1872],{"class":1083},"    }",[1073,1874,1360],{"class":1196},[1073,1876,1321],{"class":1083},[1073,1878,1879,1881],{"class":1075,"line":1470},[1073,1880,1327],{"class":1083},[1073,1882,1202],{"class":1196},[1073,1884,1885],{"class":1075,"line":1476},[1073,1886,1133],{"emptyLinePlaceholder":1132},[1073,1888,1889,1891,1894,1896,1898,1900,1902,1905],{"class":1075,"line":1482},[1073,1890,1185],{"class":1139},[1073,1892,1893],{"class":1087}," data",[1073,1895,1191],{"class":1083},[1073,1897,1520],{"class":1079},[1073,1899,1669],{"class":1087},[1073,1901,764],{"class":1083},[1073,1903,1904],{"class":1146},"json",[1073,1906,1658],{"class":1196},[1073,1908,1909,1911,1913,1916,1918,1920,1922,1925,1927,1930],{"class":1075,"line":1490},[1073,1910,1340],{"class":1079},[1073,1912,1084],{"class":1083},[1073,1914,1915],{"class":1196}," taskId",[1073,1917,1157],{"class":1083},[1073,1919,1893],{"class":1087},[1073,1921,764],{"class":1083},[1073,1923,1924],{"class":1087},"data",[1073,1926,764],{"class":1083},[1073,1928,1929],{"class":1087},"taskId",[1073,1931,1932],{"class":1083}," }\n",[1073,1934,1935,1937],{"class":1075,"line":1503},[1073,1936,1229],{"class":1083},[1073,1938,1202],{"class":1087},[1940,1941,1942],"tip",{},[749,1943,1579,1944,1947,1948,1951,1952,1955,1956,1959],{},[798,1945,1946],{},"NUXT_SUNO_API_KEY"," environment variable is mapped to ",[798,1949,1950],{},"runtimeConfig.sunoApiKey"," (without ",[798,1953,1954],{},"public","), which means Nuxt ",[753,1957,1958],{},"never bundles it into client-side JavaScript",". This is the correct way to handle API secrets in Nuxt.",[1051,1961,1963],{"id":1962},"step-3-poll-for-results","Step 3: Poll for Results",[749,1965,1966],{},"Suno processes remixes asynchronously. We poll every 30 seconds:",[1063,1968,1971],{"className":1065,"code":1969,"filename":1970,"language":1068,"meta":1069,"style":1069},"pollInterval = setInterval(async () => {\n  const result = await $fetch(`/api/remix/${taskId.value}`)\n\n  if (result.status === 'SUCCESS') {\n    tracks.value = result.tracks\n    status.value = 'completed'\n    stopPolling()\n  }\n}, 30000)\n","useSunoRemix.ts",[798,1972,1973,1994,2027,2031,2060,2078,2096,2103,2108],{"__ignoreMap":1069},[1073,1974,1975,1978,1981,1984,1986,1988,1990,1992],{"class":1075,"line":1076},[1073,1976,1977],{"class":1087},"pollInterval ",[1073,1979,1980],{"class":1083},"=",[1073,1982,1983],{"class":1146}," setInterval",[1073,1985,1150],{"class":1087},[1073,1987,1140],{"class":1139},[1073,1989,1496],{"class":1083},[1073,1991,1363],{"class":1139},[1073,1993,1179],{"class":1083},[1073,1995,1996,1998,2000,2002,2004,2007,2009,2012,2015,2017,2019,2021,2023,2025],{"class":1075,"line":1129},[1073,1997,1185],{"class":1139},[1073,1999,1669],{"class":1087},[1073,2001,1191],{"class":1083},[1073,2003,1520],{"class":1079},[1073,2005,2006],{"class":1146}," $fetch",[1073,2008,1150],{"class":1196},[1073,2010,2011],{"class":1083},"`",[1073,2013,2014],{"class":1122},"/api/remix/",[1073,2016,1263],{"class":1083},[1073,2018,1929],{"class":1087},[1073,2020,764],{"class":1083},[1073,2022,1420],{"class":1087},[1073,2024,1269],{"class":1083},[1073,2026,1202],{"class":1196},[1073,2028,2029],{"class":1075,"line":1136},[1073,2030,1133],{"emptyLinePlaceholder":1132},[1073,2032,2033,2036,2038,2041,2043,2046,2049,2051,2054,2056,2058],{"class":1075,"line":1182},[1073,2034,2035],{"class":1079},"  if",[1073,2037,1614],{"class":1196},[1073,2039,2040],{"class":1087},"result",[1073,2042,764],{"class":1083},[1073,2044,2045],{"class":1087},"status",[1073,2047,2048],{"class":1083}," ===",[1073,2050,1119],{"class":1083},[1073,2052,2053],{"class":1122},"SUCCESS",[1073,2055,1318],{"class":1083},[1073,2057,1460],{"class":1196},[1073,2059,1790],{"class":1083},[1073,2061,2062,2065,2067,2069,2071,2073,2075],{"class":1075,"line":1205},[1073,2063,2064],{"class":1087},"    tracks",[1073,2066,764],{"class":1083},[1073,2068,1420],{"class":1087},[1073,2070,1191],{"class":1083},[1073,2072,1669],{"class":1087},[1073,2074,764],{"class":1083},[1073,2076,2077],{"class":1087},"tracks\n",[1073,2079,2080,2083,2085,2087,2089,2091,2094],{"class":1075,"line":1238},[1073,2081,2082],{"class":1087},"    status",[1073,2084,764],{"class":1083},[1073,2086,1420],{"class":1087},[1073,2088,1191],{"class":1083},[1073,2090,1119],{"class":1083},[1073,2092,2093],{"class":1122},"completed",[1073,2095,1126],{"class":1083},[1073,2097,2098,2101],{"class":1075,"line":1274},[1073,2099,2100],{"class":1146},"    stopPolling",[1073,2102,1658],{"class":1196},[1073,2104,2105],{"class":1075,"line":1279},[1073,2106,2107],{"class":1083},"  }\n",[1073,2109,2110,2113,2116],{"class":1075,"line":1305},[1073,2111,2112],{"class":1083},"},",[1073,2114,2115],{"class":1466}," 30000",[1073,2117,1202],{"class":1087},[749,2119,2120,2121,2124],{},"When the remix is ready, the response includes an ",[798,2122,2123],{},"audio_url"," pointing to the generated track on Suno's CDN (Content Delivery Network).",[773,2126],{},[776,2128,2130],{"id":2129},"building-the-frontend","Building the Frontend",[749,2132,2133],{},"The UI is intentionally simple - this is a PoC, after all. The entire flow happens on a single page:",[848,2135,2136,2142,2148,2154],{},[851,2137,2138,2141],{},[753,2139,2140],{},"UploadZone"," - Drag-and-drop or file picker with WAV/MP3 validation (max 50 MB)",[851,2143,2144,2147],{},[753,2145,2146],{},"StyleSelector"," - Genre dropdown (16 genres), title input, and instrumental toggle",[851,2149,2150,2153],{},[753,2151,2152],{},"ProgressIndicator"," - 3-step progress bar with upload percentage",[851,2155,2156,2159],{},[753,2157,2158],{},"AudioComparison"," - Side-by-side WaveSurfer.js players for original and remixed audio",[749,2161,2162],{},"Here's what the upload screen looks like:",[749,2164,2165],{},[2166,2167],"img",{"alt":2168,"src":2169},"Upload & Configure - drag-drop zone with genre selector and Remix button","/images/blog/suno-remix-poc-upload.webp",[749,2171,2172,2173,2176],{},"Once you hit ",[753,2174,2175],{},"Remix",", the app uploads the file to Firebase Storage and kicks off the AI processing. The 3-step progress indicator keeps you informed:",[749,2178,2179],{},[2166,2180],{"alt":2181,"src":2182},"Progress indicator - Upload to Cloud → AI Remix → Done","/images/blog/suno-remix-poc-progress.webp",[749,2184,2185],{},"When the remix is complete, you get a side-by-side comparison with waveform visualization - the original on the left, and the AI-generated remix(es) alongside it:",[749,2187,2188],{},[2166,2189],{"alt":2190,"src":2191},"Results - original and remixed audio with WaveSurfer.js waveforms","/images/blog/suno-remix-poc-results.webp",[1051,2193,2195],{"id":2194},"wavesurferjs-integration","WaveSurfer.js Integration",[749,2197,2198,2199,2202],{},"We wrapped WaveSurfer.js in a Vue component with ",[798,2200,2201],{},"\u003CClientOnly>"," to avoid SSR (Server-Side Rendering) issues:",[1063,2204,2209],{"className":2205,"code":2206,"filename":2207,"language":2208,"meta":1069,"style":1069},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003CClientOnly>\n  \u003CWaveformPlayer\n    :audio-url=\"track.audioUrl\"\n    label=\"Remix - Jazz\"\n  />\n\u003C/ClientOnly>\n","WaveformPlayer.vue","vue",[798,2210,2211,2221,2226,2231,2236,2241],{"__ignoreMap":1069},[1073,2212,2213,2215,2218],{"class":1075,"line":1076},[1073,2214,1170],{"class":1083},[1073,2216,2217],{"class":1196},"ClientOnly",[1073,2219,2220],{"class":1083},">\n",[1073,2222,2223],{"class":1075,"line":1129},[1073,2224,2225],{"class":1087},"  \u003CWaveformPlayer\n",[1073,2227,2228],{"class":1075,"line":1136},[1073,2229,2230],{"class":1087},"    :audio-url=\"track.audioUrl\"\n",[1073,2232,2233],{"class":1075,"line":1182},[1073,2234,2235],{"class":1087},"    label=\"Remix - Jazz\"\n",[1073,2237,2238],{"class":1075,"line":1205},[1073,2239,2240],{"class":1087},"  />\n",[1073,2242,2243,2246,2248],{"class":1075,"line":1238},[1073,2244,2245],{"class":1083},"\u003C/",[1073,2247,2217],{"class":1196},[1073,2249,2220],{"class":1083},[749,2251,2252],{},"The player provides play/pause controls, a progress bar, and time display - all styled to match the dark theme.",[773,2254],{},[776,2256,2258],{"id":2257},"deployment-to-firebase","Deployment to Firebase",[749,2260,2261],{},"Firebase Hosting serves the Nuxt static output. The setup is straightforward:",[1063,2263,2267],{"className":2264,"code":2265,"filename":2266,"language":1904,"meta":1069,"style":1069},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"hosting\": {\n    \"public\": \".output/public\",\n    \"cleanUrls\": true,\n    \"rewrites\": [\n      { \"source\": \"**\", \"destination\": \"/index.html\" }\n    ]\n  },\n  \"storage\": {\n    \"rules\": \"firebase-storage.rules\"\n  }\n}\n","firebase.json",[798,2268,2269,2273,2288,2309,2323,2337,2378,2383,2388,2400,2419,2423],{"__ignoreMap":1069},[1073,2270,2271],{"class":1075,"line":1076},[1073,2272,1790],{"class":1083},[1073,2274,2275,2278,2281,2284,2286],{"class":1075,"line":1129},[1073,2276,2277],{"class":1083},"  \"",[1073,2279,2280],{"class":1139},"hosting",[1073,2282,2283],{"class":1083},"\"",[1073,2285,1157],{"class":1083},[1073,2287,1179],{"class":1083},[1073,2289,2290,2293,2295,2297,2299,2302,2305,2307],{"class":1075,"line":1136},[1073,2291,2292],{"class":1083},"    \"",[1073,2294,1954],{"class":1160},[1073,2296,2283],{"class":1083},[1073,2298,1157],{"class":1083},[1073,2300,2301],{"class":1083}," \"",[1073,2303,2304],{"class":1122},".output/public",[1073,2306,2283],{"class":1083},[1073,2308,1321],{"class":1083},[1073,2310,2311,2313,2316,2318,2320],{"class":1075,"line":1182},[1073,2312,2292],{"class":1083},[1073,2314,2315],{"class":1160},"cleanUrls",[1073,2317,2283],{"class":1083},[1073,2319,1157],{"class":1083},[1073,2321,2322],{"class":1083}," true,\n",[1073,2324,2325,2327,2330,2332,2334],{"class":1075,"line":1205},[1073,2326,2292],{"class":1083},[1073,2328,2329],{"class":1160},"rewrites",[1073,2331,2283],{"class":1083},[1073,2333,1157],{"class":1083},[1073,2335,2336],{"class":1083}," [\n",[1073,2338,2339,2342,2344,2347,2349,2351,2353,2356,2358,2360,2362,2365,2367,2369,2371,2374,2376],{"class":1075,"line":1238},[1073,2340,2341],{"class":1083},"      {",[1073,2343,2301],{"class":1083},[1073,2345,2346],{"class":1466},"source",[1073,2348,2283],{"class":1083},[1073,2350,1157],{"class":1083},[1073,2352,2301],{"class":1083},[1073,2354,2355],{"class":1122},"**",[1073,2357,2283],{"class":1083},[1073,2359,1091],{"class":1083},[1073,2361,2301],{"class":1083},[1073,2363,2364],{"class":1466},"destination",[1073,2366,2283],{"class":1083},[1073,2368,1157],{"class":1083},[1073,2370,2301],{"class":1083},[1073,2372,2373],{"class":1122},"/index.html",[1073,2375,2283],{"class":1083},[1073,2377,1932],{"class":1083},[1073,2379,2380],{"class":1075,"line":1274},[1073,2381,2382],{"class":1083},"    ]\n",[1073,2384,2385],{"class":1075,"line":1279},[1073,2386,2387],{"class":1083},"  },\n",[1073,2389,2390,2392,2394,2396,2398],{"class":1075,"line":1305},[1073,2391,2277],{"class":1083},[1073,2393,1252],{"class":1139},[1073,2395,2283],{"class":1083},[1073,2397,1157],{"class":1083},[1073,2399,1179],{"class":1083},[1073,2401,2402,2404,2407,2409,2411,2413,2416],{"class":1075,"line":1324},[1073,2403,2292],{"class":1083},[1073,2405,2406],{"class":1160},"rules",[1073,2408,2283],{"class":1083},[1073,2410,1157],{"class":1083},[1073,2412,2301],{"class":1083},[1073,2414,2415],{"class":1122},"firebase-storage.rules",[1073,2417,2418],{"class":1083},"\"\n",[1073,2420,2421],{"class":1075,"line":1332},[1073,2422,2107],{"class":1083},[1073,2424,2425],{"class":1075,"line":1337},[1073,2426,1576],{"class":1083},[749,2428,2429],{},"Firebase Storage rules restrict uploads to WAV and MP3 files under 50 MB:",[1063,2431,2436],{"className":2432,"code":2434,"language":2435},[2433],"language-text","match /uploads/{fileName} {\n  allow write: if request.resource.size \u003C 50 * 1024 * 1024\n               && (request.resource.contentType == 'audio/wav'\n                   || request.resource.contentType == 'audio/mpeg');\n  allow read: if true;\n}\n","text",[798,2437,2434],{"__ignoreMap":1069},[749,2439,2440],{},"Deploy with:",[1063,2442,2446],{"className":2443,"code":2444,"language":2445,"meta":1069,"style":1069},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm build\nfirebase deploy\n","bash",[798,2447,2448,2456],{"__ignoreMap":1069},[1073,2449,2450,2453],{"class":1075,"line":1076},[1073,2451,2452],{"class":1160},"pnpm",[1073,2454,2455],{"class":1122}," build\n",[1073,2457,2458,2461],{"class":1075,"line":1129},[1073,2459,2460],{"class":1160},"firebase",[1073,2462,2463],{"class":1122}," deploy\n",[773,2465],{},[776,2467,2469],{"id":2468},"security-considerations","Security Considerations",[749,2471,2472],{},"Even for a PoC, security matters:",[893,2474,2475,2485],{},[896,2476,2477],{},[899,2478,2479,2482],{},[902,2480,2481],{},"Concern",[902,2483,2484],{},"Solution",[909,2486,2487,2504,2514,2524],{},[899,2488,2489,2494],{},[914,2490,2491],{},[753,2492,2493],{},"API key exposure",[914,2495,2496,2497,2500,2501],{},"Stored in ",[798,2498,2499],{},"runtimeConfig"," (server-only), never in ",[798,2502,2503],{},"runtimeConfig.public",[899,2505,2506,2511],{},[914,2507,2508],{},[753,2509,2510],{},"File abuse",[914,2512,2513],{},"Firebase Storage rules: WAV/MP3 only, 50 MB max",[899,2515,2516,2521],{},[914,2517,2518],{},[753,2519,2520],{},"Input validation",[914,2522,2523],{},"Server routes validate URL format, string lengths",[899,2525,2526,2531],{},[914,2527,2528],{},[753,2529,2530],{},"Secrets in git",[914,2532,2533,2536,2537,2540,2541,2544],{},[798,2534,2535],{},".env"," in ",[798,2538,2539],{},".gitignore","; only ",[798,2542,2543],{},".env.example"," committed",[773,2546],{},[776,2548,2550],{"id":2549},"watch-out-copyright-detection","Watch Out: Copyright Detection",[749,2552,2553,2554,2557],{},"One thing that caught us off guard during testing - Suno has ",[753,2555,2556],{},"built-in copyright detection",". When we tried remixing a well-known track (AC/DC's \"Back in Black\"), the API returned an error:",[1063,2559,2562],{"className":2560,"code":2561,"language":2435},[2433],"Error code: 413\nError message: Uploaded audio matches existing work of art.\n",[798,2563,2561],{"__ignoreMap":1069},[749,2565,2566,2567,2570],{},"Suno's system fingerprints uploaded audio and compares it against known copyrighted works. If it detects a match, the remix is rejected with a ",[798,2568,2569],{},"GENERATE_AUDIO_FAILED"," status.",[2572,2573,2574],"warning",{},[749,2575,2576,2577,2580],{},"Only upload ",[753,2578,2579],{},"original recordings"," or content you have rights to. Suno will reject copyrighted material, and repeated violations may affect your API account.",[749,2582,2583],{},"This is actually a responsible feature - it prevents the platform from being used to create unauthorized covers or derivatives of copyrighted music. For our PoC, we switched to an original recording and everything worked perfectly.",[773,2585],{},[776,2587,2589],{"id":2588},"what-we-learned","What We Learned",[848,2591,2592,2598,2604,2610,2616],{},[851,2593,2594,2597],{},[753,2595,2596],{},"Suno's \"upload-cover\" endpoint is powerful"," - It takes an audio file and re-imagines it in a completely different style while retaining the core melody. The results can be surprisingly good.",[851,2599,2600,2603],{},[753,2601,2602],{},"Copyright detection is real"," - Don't assume you can remix any audio. Suno actively fingerprints uploads and blocks copyrighted material with error code 413.",[851,2605,2606,2609],{},[753,2607,2608],{},"The public URL requirement adds complexity"," - Having to upload to Firebase Storage first adds a step, but it's a clean pattern that keeps the architecture simple.",[851,2611,2612,2615],{},[753,2613,2614],{},"Polling is fine for a PoC"," - While webhooks would be more efficient, the 30-second polling interval works well for a demo. The typical remix takes 1-3 minutes.",[851,2617,2618,2621],{},[753,2619,2620],{},"Nuxt 4 + Nitro is a great combo"," - Server routes give you a built-in API proxy without needing a separate backend service. Perfect for PoCs and small apps.",[773,2623],{},[776,2625,2627],{"id":2626},"next-steps","Next Steps",[749,2629,2630],{},"This PoC demonstrates the core flow, but there are plenty of ways to extend it:",[2632,2633,2634,2640,2646,2652,2658],"ul",{},[851,2635,2636,2639],{},[753,2637,2638],{},"Multiple styles per remix"," - Let users generate several style variations at once",[851,2641,2642,2645],{},[753,2643,2644],{},"Audio trimming"," - Add a waveform-based trimmer before sending to Suno",[851,2647,2648,2651],{},[753,2649,2650],{},"User accounts"," - Save remix history with Firebase Auth",[851,2653,2654,2657],{},[753,2655,2656],{},"Batch processing"," - Upload multiple files and queue remixes",[851,2659,2660,2663],{},[753,2661,2662],{},"Stem separation"," - Use Suno's stems endpoint to separate vocals and instruments",[773,2665],{},[776,2667,2669],{"id":2668},"try-it-yourself","Try It Yourself",[2632,2671,2672],{},[851,2673,2674,2677,2678],{},[753,2675,2676],{},"Live demo",": ",[783,2679,2682],{"href":2680,"rel":2681},"https://mtl-suno-poc.web.app",[787],"mtl-suno-poc.web.app",[749,2684,2685,2686,2689],{},"You'll need your own Suno API key from ",[783,2687,811],{"href":807,"rel":2688},[787]," and a Firebase project if you want to build something similar.",[773,2691],{},[776,2693,2695],{"id":2694},"related-articles","Related Articles",[2632,2697,2698,2703,2708],{},[851,2699,2700],{},[783,2701,2702],{"href":153},"How to Use Epidemic Sound MCP with Claude",[851,2704,2705],{},[783,2706,2707],{"href":101},"Automatic Song Structure Analysis: How AI Detects Intro, Verse, Chorus",[851,2709,2710],{},[783,2711,2712],{"href":475},"Exporting Ableton Live Locators to JSON with Max for Live",[1833,2714,2715],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":1069,"searchDepth":1129,"depth":1129,"links":2717},[2718,2719,2720,2721,2726,2729,2730,2731,2732,2733,2734,2735],{"id":778,"depth":1129,"text":779},{"id":831,"depth":1129,"text":832},{"id":955,"depth":1129,"text":956},{"id":1045,"depth":1129,"text":1046,"children":2722},[2723,2724,2725],{"id":1053,"depth":1136,"text":1054},{"id":1586,"depth":1136,"text":1587},{"id":1962,"depth":1136,"text":1963},{"id":2129,"depth":1129,"text":2130,"children":2727},[2728],{"id":2194,"depth":1136,"text":2195},{"id":2257,"depth":1129,"text":2258},{"id":2468,"depth":1129,"text":2469},{"id":2549,"depth":1129,"text":2550},{"id":2588,"depth":1129,"text":2589},{"id":2626,"depth":1129,"text":2627},{"id":2668,"depth":1129,"text":2669},{"id":2694,"depth":1129,"text":2695},"music-data",null,"2026-02-06T00:00:00.000Z","A step-by-step guide to building a web app that remixes audio using Suno's AI, Nuxt 4, and Firebase Storage.","md",{"src":2742},"/images/blog/musictechlab_blog_suno-remix-poc.webp",{"enabled":1132,"items":2744},[2745,2748,2751,2754],{"text":2746,"icon":2747},"Suno has no official public API; third-party wrappers like sunoapi.org cost about $0.005 per credit.","i-lucide-code",{"text":2749,"icon":2750},"Firebase Storage bridges the gap between local file uploads and Suno's public URL requirement.","i-lucide-cloud",{"text":2752,"icon":2753},"Suno's built-in copyright detection rejects known copyrighted material with error code 413.","i-lucide-shield",{"text":2755,"icon":2756},"Nitro server routes keep the API key server-side so it never reaches the browser.","i-lucide-lock",{},{"title":116,"description":2739},[2760,2761,2762],"AI","development","audio-analysis","vdlUk3pZwxfUadk0bx3d02X4tJOxLv2oe4g_c_OXEQE",[2765,2767],{"title":112,"path":113,"stem":114,"description":2766,"children":-1},"A practical guide for music distributors evaluating a hybrid approach: custom frontend with Revelator's API as the delivery backbone. Covers architecture, DDEX integration, territory handling, and when to go fully independent.",{"title":120,"path":121,"stem":122,"description":2768,"children":-1},"C2PA proves content is real. DDEX ensures the right people get paid. Together, could they reshape how the music industry handles trust and rights in the AI era?",[2770,5010,6786,10169],{"id":2771,"title":148,"authors":2772,"badge":2737,"body":2776,"category":2736,"client":2737,"date":4989,"description":4990,"extension":2740,"faq":2737,"featured":69,"featuredOrder":2737,"hidden":69,"image":4991,"keyTakeaways":4993,"meta":5006,"navigation":1132,"path":149,"seo":5007,"status":2737,"stem":150,"tags":5008,"teaser":2737,"__hash__":5009,"score":1182},"posts/blog/music-data/how-to-transcribe-video-to-text-using-whisper.md",[2773],{"name":738,"to":2774,"avatar":2775},"https://www.linkedin.com/in/mariusz-smenzyk",{"src":741},{"type":746,"value":2777,"toc":4957},[2778,2782,2789,2792,2794,2798,2809,2811,2815,2818,2822,2838,2842,2868,2872,2881,2884,2897,2899,2903,2906,2921,2924,2953,2955,2959,2962,2995,2998,3024,3027,3062,3064,3068,3072,3097,3101,3207,3216,3220,3246,3263,3265,3269,3272,3276,3303,3307,3331,3335,3342,3348,3350,3354,3357,4205,4208,4220,4222,4226,4229,4446,4449,4455,4457,4461,4464,4600,4602,4606,4610,4624,4684,4688,4691,4725,4729,4745,4806,4808,4812,4881,4884,4895,4897,4901,4904,4922,4925,4927,4931,4954],[776,2779,2781],{"id":2780},"introduction","Introduction",[749,2783,2784,2785,2788],{},"Got a recorded interview, meeting, or podcast that needs transcription? Instead of paying for online transcription services, you can use ",[753,2786,2787],{},"OpenAI Whisper"," - a free, open-source speech recognition model that runs locally on your machine.",[749,2790,2791],{},"In this guide, we'll walk through the complete pipeline: from video file to ready-to-use text transcription.",[773,2793],{},[776,2795,2797],{"id":2796},"requirements","Requirements",[2632,2799,2800,2803,2806],{},[851,2801,2802],{},"Python 3.8+",[851,2804,2805],{},"FFmpeg (for audio/video conversion)",[851,2807,2808],{},"2-10 GB of free disk space (depending on model size)",[773,2810],{},[776,2812,2814],{"id":2813},"step-1-install-ffmpeg","Step 1: Install FFmpeg",[749,2816,2817],{},"FFmpeg is needed to extract audio from video files.",[1051,2819,2821],{"id":2820},"macos-homebrew","macOS (Homebrew)",[1063,2823,2825],{"className":2443,"code":2824,"language":2445,"meta":1069,"style":1069},"brew install ffmpeg\n",[798,2826,2827],{"__ignoreMap":1069},[1073,2828,2829,2832,2835],{"class":1075,"line":1076},[1073,2830,2831],{"class":1160},"brew",[1073,2833,2834],{"class":1122}," install",[1073,2836,2837],{"class":1122}," ffmpeg\n",[1051,2839,2841],{"id":2840},"ubuntudebian","Ubuntu/Debian",[1063,2843,2845],{"className":2443,"code":2844,"language":2445,"meta":1069,"style":1069},"sudo apt update\nsudo apt install ffmpeg\n",[798,2846,2847,2858],{"__ignoreMap":1069},[1073,2848,2849,2852,2855],{"class":1075,"line":1076},[1073,2850,2851],{"class":1160},"sudo",[1073,2853,2854],{"class":1122}," apt",[1073,2856,2857],{"class":1122}," update\n",[1073,2859,2860,2862,2864,2866],{"class":1075,"line":1129},[1073,2861,2851],{"class":1160},[1073,2863,2854],{"class":1122},[1073,2865,2834],{"class":1122},[1073,2867,2837],{"class":1122},[1051,2869,2871],{"id":2870},"windows","Windows",[749,2873,2874,2875,2880],{},"Download from ",[783,2876,2879],{"href":2877,"rel":2878},"https://ffmpeg.org/download.html",[787],"ffmpeg.org"," and add to PATH.",[749,2882,2883],{},"Verify installation:",[1063,2885,2887],{"className":2443,"code":2886,"language":2445,"meta":1069,"style":1069},"ffmpeg -version\n",[798,2888,2889],{"__ignoreMap":1069},[1073,2890,2891,2894],{"class":1075,"line":1076},[1073,2892,2893],{"class":1160},"ffmpeg",[1073,2895,2896],{"class":1122}," -version\n",[773,2898],{},[776,2900,2902],{"id":2901},"step-2-install-openai-whisper","Step 2: Install OpenAI Whisper",[749,2904,2905],{},"Install Whisper via pip:",[1063,2907,2909],{"className":2443,"code":2908,"language":2445,"meta":1069,"style":1069},"pip install openai-whisper\n",[798,2910,2911],{"__ignoreMap":1069},[1073,2912,2913,2916,2918],{"class":1075,"line":1076},[1073,2914,2915],{"class":1160},"pip",[1073,2917,2834],{"class":1122},[1073,2919,2920],{"class":1122}," openai-whisper\n",[749,2922,2923],{},"For faster GPU transcription (NVIDIA):",[1063,2925,2927],{"className":2443,"code":2926,"language":2445,"meta":1069,"style":1069},"pip install openai-whisper torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118\n",[798,2928,2929],{"__ignoreMap":1069},[1073,2930,2931,2933,2935,2938,2941,2944,2947,2950],{"class":1075,"line":1076},[1073,2932,2915],{"class":1160},[1073,2934,2834],{"class":1122},[1073,2936,2937],{"class":1122}," openai-whisper",[1073,2939,2940],{"class":1122}," torch",[1073,2942,2943],{"class":1122}," torchvision",[1073,2945,2946],{"class":1122}," torchaudio",[1073,2948,2949],{"class":1122}," --index-url",[1073,2951,2952],{"class":1122}," https://download.pytorch.org/whl/cu118\n",[773,2954],{},[776,2956,2958],{"id":2957},"step-3-extract-audio-from-video","Step 3: Extract Audio from Video",[749,2960,2961],{},"Pull the audio track from your video file:",[1063,2963,2965],{"className":2443,"code":2964,"language":2445,"meta":1069,"style":1069},"ffmpeg -i interview.mp4 -vn -acodec libmp3lame -q:a 2 interview.mp3\n",[798,2966,2967],{"__ignoreMap":1069},[1073,2968,2969,2971,2974,2977,2980,2983,2986,2989,2992],{"class":1075,"line":1076},[1073,2970,2893],{"class":1160},[1073,2972,2973],{"class":1122}," -i",[1073,2975,2976],{"class":1122}," interview.mp4",[1073,2978,2979],{"class":1122}," -vn",[1073,2981,2982],{"class":1122}," -acodec",[1073,2984,2985],{"class":1122}," libmp3lame",[1073,2987,2988],{"class":1122}," -q:a",[1073,2990,2991],{"class":1466}," 2",[1073,2993,2994],{"class":1122}," interview.mp3\n",[749,2996,2997],{},"Flag breakdown:",[2632,2999,3000,3006,3012,3018],{},[851,3001,3002,3005],{},[798,3003,3004],{},"-i interview.mp4"," - input file",[851,3007,3008,3011],{},[798,3009,3010],{},"-vn"," - no video (audio only)",[851,3013,3014,3017],{},[798,3015,3016],{},"-acodec libmp3lame"," - MP3 codec",[851,3019,3020,3023],{},[798,3021,3022],{},"-q:a 2"," - high audio quality",[749,3025,3026],{},"For a smaller file (sufficient for transcription):",[1063,3028,3030],{"className":2443,"code":3029,"language":2445,"meta":1069,"style":1069},"ffmpeg -i interview.mp4 -vn -ar 16000 -ac 1 -b:a 64k interview.mp3\n",[798,3031,3032],{"__ignoreMap":1069},[1073,3033,3034,3036,3038,3040,3042,3045,3048,3051,3054,3057,3060],{"class":1075,"line":1076},[1073,3035,2893],{"class":1160},[1073,3037,2973],{"class":1122},[1073,3039,2976],{"class":1122},[1073,3041,2979],{"class":1122},[1073,3043,3044],{"class":1122}," -ar",[1073,3046,3047],{"class":1466}," 16000",[1073,3049,3050],{"class":1122}," -ac",[1073,3052,3053],{"class":1466}," 1",[1073,3055,3056],{"class":1122}," -b:a",[1073,3058,3059],{"class":1122}," 64k",[1073,3061,2994],{"class":1122},[773,3063],{},[776,3065,3067],{"id":3066},"step-4-transcribe-with-whisper","Step 4: Transcribe with Whisper",[1051,3069,3071],{"id":3070},"basic-cli-usage","Basic CLI Usage",[1063,3073,3075],{"className":2443,"code":3074,"language":2445,"meta":1069,"style":1069},"whisper interview.mp3 --language English --model medium\n",[798,3076,3077],{"__ignoreMap":1069},[1073,3078,3079,3082,3085,3088,3091,3094],{"class":1075,"line":1076},[1073,3080,3081],{"class":1160},"whisper",[1073,3083,3084],{"class":1122}," interview.mp3",[1073,3086,3087],{"class":1122}," --language",[1073,3089,3090],{"class":1122}," English",[1073,3092,3093],{"class":1122}," --model",[1073,3095,3096],{"class":1122}," medium\n",[1051,3098,3100],{"id":3099},"available-models","Available Models",[893,3102,3103,3122],{},[896,3104,3105],{},[899,3106,3107,3110,3113,3116,3119],{},[902,3108,3109],{},"Model",[902,3111,3112],{},"Size",[902,3114,3115],{},"VRAM",[902,3117,3118],{},"Quality",[902,3120,3121],{},"Speed",[909,3123,3124,3141,3157,3173,3190],{},[899,3125,3126,3129,3132,3135,3138],{},[914,3127,3128],{},"tiny",[914,3130,3131],{},"39 MB",[914,3133,3134],{},"~1 GB",[914,3136,3137],{},"Low",[914,3139,3140],{},"Very fast",[899,3142,3143,3146,3149,3151,3154],{},[914,3144,3145],{},"base",[914,3147,3148],{},"74 MB",[914,3150,3134],{},[914,3152,3153],{},"Medium",[914,3155,3156],{},"Fast",[899,3158,3159,3162,3165,3168,3171],{},[914,3160,3161],{},"small",[914,3163,3164],{},"244 MB",[914,3166,3167],{},"~2 GB",[914,3169,3170],{},"Good",[914,3172,3153],{},[899,3174,3175,3178,3181,3184,3187],{},[914,3176,3177],{},"medium",[914,3179,3180],{},"769 MB",[914,3182,3183],{},"~5 GB",[914,3185,3186],{},"Very good",[914,3188,3189],{},"Slow",[899,3191,3192,3195,3198,3201,3204],{},[914,3193,3194],{},"large",[914,3196,3197],{},"1550 MB",[914,3199,3200],{},"~10 GB",[914,3202,3203],{},"Best",[914,3205,3206],{},"Very slow",[749,3208,3209,3210,3212,3213,3215],{},"For most use cases, ",[798,3211,3161],{}," or ",[798,3214,3177],{}," offers the best quality-to-speed ratio.",[1051,3217,3219],{"id":3218},"save-to-text-file","Save to Text File",[1063,3221,3223],{"className":2443,"code":3222,"language":2445,"meta":1069,"style":1069},"whisper interview.mp3 --language English --model medium --output_format txt\n",[798,3224,3225],{"__ignoreMap":1069},[1073,3226,3227,3229,3231,3233,3235,3237,3240,3243],{"class":1075,"line":1076},[1073,3228,3081],{"class":1160},[1073,3230,3084],{"class":1122},[1073,3232,3087],{"class":1122},[1073,3234,3090],{"class":1122},[1073,3236,3093],{"class":1122},[1073,3238,3239],{"class":1122}," medium",[1073,3241,3242],{"class":1122}," --output_format",[1073,3244,3245],{"class":1122}," txt\n",[749,3247,3248,3249,756,3252,756,3255,756,3258,756,3261],{},"Available output formats: ",[798,3250,3251],{},"txt",[798,3253,3254],{},"vtt",[798,3256,3257],{},"srt",[798,3259,3260],{},"tsv",[798,3262,1904],{},[773,3264],{},[776,3266,3268],{"id":3267},"example-transcribing-a-user-interview","Example: Transcribing a User Interview",[749,3270,3271],{},"Let's say you recorded a 15-minute user feedback session about your product. Here's the complete workflow:",[1051,3273,3275],{"id":3274},"_1-extract-audio","1. Extract Audio",[1063,3277,3279],{"className":2443,"code":3278,"language":2445,"meta":1069,"style":1069},"ffmpeg -i user_feedback_session.mov -vn -ar 16000 -ac 1 feedback.mp3\n",[798,3280,3281],{"__ignoreMap":1069},[1073,3282,3283,3285,3287,3290,3292,3294,3296,3298,3300],{"class":1075,"line":1076},[1073,3284,2893],{"class":1160},[1073,3286,2973],{"class":1122},[1073,3288,3289],{"class":1122}," user_feedback_session.mov",[1073,3291,2979],{"class":1122},[1073,3293,3044],{"class":1122},[1073,3295,3047],{"class":1466},[1073,3297,3050],{"class":1122},[1073,3299,3053],{"class":1466},[1073,3301,3302],{"class":1122}," feedback.mp3\n",[1051,3304,3306],{"id":3305},"_2-run-transcription","2. Run Transcription",[1063,3308,3310],{"className":2443,"code":3309,"language":2445,"meta":1069,"style":1069},"whisper feedback.mp3 --language English --model medium --output_format txt\n",[798,3311,3312],{"__ignoreMap":1069},[1073,3313,3314,3316,3319,3321,3323,3325,3327,3329],{"class":1075,"line":1076},[1073,3315,3081],{"class":1160},[1073,3317,3318],{"class":1122}," feedback.mp3",[1073,3320,3087],{"class":1122},[1073,3322,3090],{"class":1122},[1073,3324,3093],{"class":1122},[1073,3326,3239],{"class":1122},[1073,3328,3242],{"class":1122},[1073,3330,3245],{"class":1122},[1051,3332,3334],{"id":3333},"_3-output","3. Output",[749,3336,3337,3338,3341],{},"Whisper creates ",[798,3339,3340],{},"feedback.txt"," with content like:",[1063,3343,3346],{"className":3344,"code":3345,"language":2435},[2433],"So the first thing about these signals before the interval.\nYes, so you set a 25-second interval and before the interval\nyou get 4 audio signals, let's say, the main signal with an\naccent and then 4 weaker ones again. So basically if you don't\narrive exactly on the signal, you know that you need to speed\nup by two seconds or slow down by two seconds because the sound\ntells you that.\n",[798,3347,3345],{"__ignoreMap":1069},[773,3349],{},[776,3351,3353],{"id":3352},"example-python-script-for-batch-processing","Example: Python Script for Batch Processing",[749,3355,3356],{},"For processing multiple files or integrating into your workflow:",[1063,3358,3362],{"className":3359,"code":3360,"language":3361,"meta":1069,"style":1069},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","#!/usr/bin/env python3\n\"\"\"\nVideo to Text Transcription Pipeline\nUses FFmpeg + OpenAI Whisper\n\"\"\"\n\nimport subprocess\nimport whisper\nfrom pathlib import Path\n\n\ndef extract_audio(video_path: str, audio_path: str) -> None:\n    \"\"\"Extract audio from video using FFmpeg.\"\"\"\n    cmd = [\n        \"ffmpeg\", \"-i\", video_path,\n        \"-vn\", \"-ar\", \"16000\", \"-ac\", \"1\",\n        \"-b:a\", \"64k\", \"-y\",\n        audio_path\n    ]\n    subprocess.run(cmd, check=True, capture_output=True)\n    print(f\"Audio extracted: {audio_path}\")\n\n\ndef transcribe_audio(\n    audio_path: str,\n    output_path: str,\n    model_name: str = \"medium\",\n    language: str = \"en\"\n) -> str:\n    \"\"\"Transcribe audio using Whisper.\"\"\"\n    print(f\"Loading model {model_name}...\")\n    model = whisper.load_model(model_name)\n\n    print(\"Transcribing...\")\n    result = model.transcribe(audio_path, language=language)\n\n    text = result[\"text\"]\n\n    with open(output_path, \"w\", encoding=\"utf-8\") as f:\n        f.write(text)\n\n    print(f\"Transcription saved: {output_path}\")\n    return text\n\n\ndef main():\n    # Configuration\n    video_file = \"meeting_recording.mp4\"\n    audio_file = \"temp_audio.mp3\"\n    output_file = \"meeting_transcript.txt\"\n\n    # Pipeline\n    extract_audio(video_file, audio_file)\n    text = transcribe_audio(audio_file, output_file)\n\n    # Cleanup temp file\n    Path(audio_file).unlink()\n\n    print(f\"\\nTranscription preview:\\n{text[:500]}...\")\n\n\nif __name__ == \"__main__\":\n    main()\n","python",[798,3363,3364,3369,3374,3379,3384,3388,3392,3399,3406,3419,3423,3427,3462,3472,3481,3506,3552,3581,3586,3590,3619,3644,3648,3652,3661,3672,3683,3702,3720,3732,3742,3766,3788,3793,3809,3841,3846,3868,3873,3919,3936,3941,3963,3972,3977,3982,3993,3999,4014,4029,4044,4049,4055,4073,4094,4099,4105,4123,4128,4166,4171,4176,4197],{"__ignoreMap":1069},[1073,3365,3366],{"class":1075,"line":1076},[1073,3367,3368],{"class":1408},"#!/usr/bin/env python3\n",[1073,3370,3371],{"class":1075,"line":1129},[1073,3372,3373],{"class":1079},"\"\"\"\n",[1073,3375,3376],{"class":1075,"line":1136},[1073,3377,3378],{"class":1408},"Video to Text Transcription Pipeline\n",[1073,3380,3381],{"class":1075,"line":1182},[1073,3382,3383],{"class":1408},"Uses FFmpeg + OpenAI Whisper\n",[1073,3385,3386],{"class":1075,"line":1205},[1073,3387,3373],{"class":1079},[1073,3389,3390],{"class":1075,"line":1238},[1073,3391,1133],{"emptyLinePlaceholder":1132},[1073,3393,3394,3396],{"class":1075,"line":1274},[1073,3395,1080],{"class":1079},[1073,3397,3398],{"class":1087}," subprocess\n",[1073,3400,3401,3403],{"class":1075,"line":1279},[1073,3402,1080],{"class":1079},[1073,3404,3405],{"class":1087}," whisper\n",[1073,3407,3408,3411,3414,3416],{"class":1075,"line":1305},[1073,3409,3410],{"class":1079},"from",[1073,3412,3413],{"class":1087}," pathlib ",[1073,3415,1080],{"class":1079},[1073,3417,3418],{"class":1087}," Path\n",[1073,3420,3421],{"class":1075,"line":1324},[1073,3422,1133],{"emptyLinePlaceholder":1132},[1073,3424,3425],{"class":1075,"line":1332},[1073,3426,1133],{"emptyLinePlaceholder":1132},[1073,3428,3429,3432,3435,3437,3440,3442,3445,3447,3450,3452,3454,3456,3459],{"class":1075,"line":1337},[1073,3430,3431],{"class":1139},"def",[1073,3433,3434],{"class":1146}," extract_audio",[1073,3436,1150],{"class":1083},[1073,3438,3439],{"class":1153},"video_path",[1073,3441,1157],{"class":1083},[1073,3443,3444],{"class":1160}," str",[1073,3446,1091],{"class":1083},[1073,3448,3449],{"class":1153}," audio_path",[1073,3451,1157],{"class":1083},[1073,3453,3444],{"class":1160},[1073,3455,1360],{"class":1083},[1073,3457,3458],{"class":1083}," ->",[1073,3460,3461],{"class":1083}," None:\n",[1073,3463,3464,3467,3470],{"class":1075,"line":1368},[1073,3465,3466],{"class":1079},"    \"\"\"",[1073,3468,3469],{"class":1408},"Extract audio from video using FFmpeg.",[1073,3471,3373],{"class":1079},[1073,3473,3474,3477,3479],{"class":1075,"line":1390},[1073,3475,3476],{"class":1087},"    cmd ",[1073,3478,1980],{"class":1083},[1073,3480,2336],{"class":1083},[1073,3482,3483,3486,3488,3490,3492,3494,3497,3499,3501,3504],{"class":1075,"line":1405},[1073,3484,3485],{"class":1083},"        \"",[1073,3487,2893],{"class":1122},[1073,3489,2283],{"class":1083},[1073,3491,1091],{"class":1083},[1073,3493,2301],{"class":1083},[1073,3495,3496],{"class":1122},"-i",[1073,3498,2283],{"class":1083},[1073,3500,1091],{"class":1083},[1073,3502,3503],{"class":1087}," video_path",[1073,3505,1321],{"class":1083},[1073,3507,3508,3510,3512,3514,3516,3518,3521,3523,3525,3527,3530,3532,3534,3536,3539,3541,3543,3545,3548,3550],{"class":1075,"line":1412},[1073,3509,3485],{"class":1083},[1073,3511,3010],{"class":1122},[1073,3513,2283],{"class":1083},[1073,3515,1091],{"class":1083},[1073,3517,2301],{"class":1083},[1073,3519,3520],{"class":1122},"-ar",[1073,3522,2283],{"class":1083},[1073,3524,1091],{"class":1083},[1073,3526,2301],{"class":1083},[1073,3528,3529],{"class":1122},"16000",[1073,3531,2283],{"class":1083},[1073,3533,1091],{"class":1083},[1073,3535,2301],{"class":1083},[1073,3537,3538],{"class":1122},"-ac",[1073,3540,2283],{"class":1083},[1073,3542,1091],{"class":1083},[1073,3544,2301],{"class":1083},[1073,3546,3547],{"class":1122},"1",[1073,3549,2283],{"class":1083},[1073,3551,1321],{"class":1083},[1073,3553,3554,3556,3559,3561,3563,3565,3568,3570,3572,3574,3577,3579],{"class":1075,"line":1436},[1073,3555,3485],{"class":1083},[1073,3557,3558],{"class":1122},"-b:a",[1073,3560,2283],{"class":1083},[1073,3562,1091],{"class":1083},[1073,3564,2301],{"class":1083},[1073,3566,3567],{"class":1122},"64k",[1073,3569,2283],{"class":1083},[1073,3571,1091],{"class":1083},[1073,3573,2301],{"class":1083},[1073,3575,3576],{"class":1122},"-y",[1073,3578,2283],{"class":1083},[1073,3580,1321],{"class":1083},[1073,3582,3583],{"class":1075,"line":1470},[1073,3584,3585],{"class":1087},"        audio_path\n",[1073,3587,3588],{"class":1075,"line":1476},[1073,3589,2382],{"class":1083},[1073,3591,3592,3595,3597,3600,3602,3605,3607,3610,3613,3616],{"class":1075,"line":1482},[1073,3593,3594],{"class":1087},"    subprocess",[1073,3596,764],{"class":1083},[1073,3598,3599],{"class":1146},"run",[1073,3601,1150],{"class":1083},[1073,3603,3604],{"class":1146},"cmd",[1073,3606,1091],{"class":1083},[1073,3608,3609],{"class":1153}," check",[1073,3611,3612],{"class":1083},"=True,",[1073,3614,3615],{"class":1153}," capture_output",[1073,3617,3618],{"class":1083},"=True)\n",[1073,3620,3621,3624,3626,3629,3632,3635,3638,3640,3642],{"class":1075,"line":1490},[1073,3622,3623],{"class":1146},"    print",[1073,3625,1150],{"class":1083},[1073,3627,3628],{"class":1139},"f",[1073,3630,3631],{"class":1122},"\"Audio extracted: ",[1073,3633,3634],{"class":1466},"{",[1073,3636,3637],{"class":1146},"audio_path",[1073,3639,1229],{"class":1466},[1073,3641,2283],{"class":1122},[1073,3643,1202],{"class":1083},[1073,3645,3646],{"class":1075,"line":1503},[1073,3647,1133],{"emptyLinePlaceholder":1132},[1073,3649,3650],{"class":1075,"line":1509},[1073,3651,1133],{"emptyLinePlaceholder":1132},[1073,3653,3654,3656,3659],{"class":1075,"line":1541},[1073,3655,3431],{"class":1139},[1073,3657,3658],{"class":1146}," transcribe_audio",[1073,3660,1433],{"class":1083},[1073,3662,3663,3666,3668,3670],{"class":1075,"line":1554},[1073,3664,3665],{"class":1153},"    audio_path",[1073,3667,1157],{"class":1083},[1073,3669,3444],{"class":1160},[1073,3671,1321],{"class":1083},[1073,3673,3674,3677,3679,3681],{"class":1075,"line":1560},[1073,3675,3676],{"class":1153},"    output_path",[1073,3678,1157],{"class":1083},[1073,3680,3444],{"class":1160},[1073,3682,1321],{"class":1083},[1073,3684,3685,3688,3690,3692,3694,3696,3698,3700],{"class":1075,"line":1566},[1073,3686,3687],{"class":1153},"    model_name",[1073,3689,1157],{"class":1083},[1073,3691,3444],{"class":1160},[1073,3693,1191],{"class":1083},[1073,3695,2301],{"class":1083},[1073,3697,3177],{"class":1122},[1073,3699,2283],{"class":1083},[1073,3701,1321],{"class":1083},[1073,3703,3704,3707,3709,3711,3713,3715,3718],{"class":1075,"line":1573},[1073,3705,3706],{"class":1153},"    language",[1073,3708,1157],{"class":1083},[1073,3710,3444],{"class":1160},[1073,3712,1191],{"class":1083},[1073,3714,2301],{"class":1083},[1073,3716,3717],{"class":1122},"en",[1073,3719,2418],{"class":1083},[1073,3721,3723,3725,3727,3729],{"class":1075,"line":3722},29,[1073,3724,1360],{"class":1083},[1073,3726,3458],{"class":1083},[1073,3728,3444],{"class":1160},[1073,3730,3731],{"class":1083},":\n",[1073,3733,3735,3737,3740],{"class":1075,"line":3734},30,[1073,3736,3466],{"class":1079},[1073,3738,3739],{"class":1408},"Transcribe audio using Whisper.",[1073,3741,3373],{"class":1079},[1073,3743,3745,3747,3749,3751,3754,3756,3759,3761,3764],{"class":1075,"line":3744},31,[1073,3746,3623],{"class":1146},[1073,3748,1150],{"class":1083},[1073,3750,3628],{"class":1139},[1073,3752,3753],{"class":1122},"\"Loading model ",[1073,3755,3634],{"class":1466},[1073,3757,3758],{"class":1146},"model_name",[1073,3760,1229],{"class":1466},[1073,3762,3763],{"class":1122},"...\"",[1073,3765,1202],{"class":1083},[1073,3767,3769,3772,3774,3777,3779,3782,3784,3786],{"class":1075,"line":3768},32,[1073,3770,3771],{"class":1087},"    model ",[1073,3773,1980],{"class":1083},[1073,3775,3776],{"class":1087}," whisper",[1073,3778,764],{"class":1083},[1073,3780,3781],{"class":1146},"load_model",[1073,3783,1150],{"class":1083},[1073,3785,3758],{"class":1146},[1073,3787,1202],{"class":1083},[1073,3789,3791],{"class":1075,"line":3790},33,[1073,3792,1133],{"emptyLinePlaceholder":1132},[1073,3794,3796,3798,3800,3802,3805,3807],{"class":1075,"line":3795},34,[1073,3797,3623],{"class":1146},[1073,3799,1150],{"class":1083},[1073,3801,2283],{"class":1083},[1073,3803,3804],{"class":1122},"Transcribing...",[1073,3806,2283],{"class":1083},[1073,3808,1202],{"class":1083},[1073,3810,3812,3815,3817,3820,3822,3825,3827,3829,3831,3834,3836,3839],{"class":1075,"line":3811},35,[1073,3813,3814],{"class":1087},"    result ",[1073,3816,1980],{"class":1083},[1073,3818,3819],{"class":1087}," model",[1073,3821,764],{"class":1083},[1073,3823,3824],{"class":1146},"transcribe",[1073,3826,1150],{"class":1083},[1073,3828,3637],{"class":1146},[1073,3830,1091],{"class":1083},[1073,3832,3833],{"class":1153}," language",[1073,3835,1980],{"class":1083},[1073,3837,3838],{"class":1146},"language",[1073,3840,1202],{"class":1083},[1073,3842,3844],{"class":1075,"line":3843},36,[1073,3845,1133],{"emptyLinePlaceholder":1132},[1073,3847,3849,3852,3854,3856,3859,3861,3863,3865],{"class":1075,"line":3848},37,[1073,3850,3851],{"class":1087},"    text ",[1073,3853,1980],{"class":1083},[1073,3855,1669],{"class":1087},[1073,3857,3858],{"class":1083},"[",[1073,3860,2283],{"class":1083},[1073,3862,2435],{"class":1122},[1073,3864,2283],{"class":1083},[1073,3866,3867],{"class":1083},"]\n",[1073,3869,3871],{"class":1075,"line":3870},38,[1073,3872,1133],{"emptyLinePlaceholder":1132},[1073,3874,3876,3879,3882,3884,3887,3889,3891,3894,3896,3898,3901,3903,3905,3908,3910,3912,3914,3917],{"class":1075,"line":3875},39,[1073,3877,3878],{"class":1079},"    with",[1073,3880,3881],{"class":1146}," open",[1073,3883,1150],{"class":1083},[1073,3885,3886],{"class":1146},"output_path",[1073,3888,1091],{"class":1083},[1073,3890,2301],{"class":1083},[1073,3892,3893],{"class":1122},"w",[1073,3895,2283],{"class":1083},[1073,3897,1091],{"class":1083},[1073,3899,3900],{"class":1153}," encoding",[1073,3902,1980],{"class":1083},[1073,3904,2283],{"class":1083},[1073,3906,3907],{"class":1122},"utf-8",[1073,3909,2283],{"class":1083},[1073,3911,1360],{"class":1083},[1073,3913,1097],{"class":1079},[1073,3915,3916],{"class":1087}," f",[1073,3918,3731],{"class":1083},[1073,3920,3922,3925,3927,3930,3932,3934],{"class":1075,"line":3921},40,[1073,3923,3924],{"class":1087},"        f",[1073,3926,764],{"class":1083},[1073,3928,3929],{"class":1146},"write",[1073,3931,1150],{"class":1083},[1073,3933,2435],{"class":1146},[1073,3935,1202],{"class":1083},[1073,3937,3939],{"class":1075,"line":3938},41,[1073,3940,1133],{"emptyLinePlaceholder":1132},[1073,3942,3944,3946,3948,3950,3953,3955,3957,3959,3961],{"class":1075,"line":3943},42,[1073,3945,3623],{"class":1146},[1073,3947,1150],{"class":1083},[1073,3949,3628],{"class":1139},[1073,3951,3952],{"class":1122},"\"Transcription saved: ",[1073,3954,3634],{"class":1466},[1073,3956,3886],{"class":1146},[1073,3958,1229],{"class":1466},[1073,3960,2283],{"class":1122},[1073,3962,1202],{"class":1083},[1073,3964,3966,3969],{"class":1075,"line":3965},43,[1073,3967,3968],{"class":1079},"    return",[1073,3970,3971],{"class":1087}," text\n",[1073,3973,3975],{"class":1075,"line":3974},44,[1073,3976,1133],{"emptyLinePlaceholder":1132},[1073,3978,3980],{"class":1075,"line":3979},45,[1073,3981,1133],{"emptyLinePlaceholder":1132},[1073,3983,3985,3987,3990],{"class":1075,"line":3984},46,[1073,3986,3431],{"class":1139},[1073,3988,3989],{"class":1146}," main",[1073,3991,3992],{"class":1083},"():\n",[1073,3994,3996],{"class":1075,"line":3995},47,[1073,3997,3998],{"class":1408},"    # Configuration\n",[1073,4000,4002,4005,4007,4009,4012],{"class":1075,"line":4001},48,[1073,4003,4004],{"class":1087},"    video_file ",[1073,4006,1980],{"class":1083},[1073,4008,2301],{"class":1083},[1073,4010,4011],{"class":1122},"meeting_recording.mp4",[1073,4013,2418],{"class":1083},[1073,4015,4017,4020,4022,4024,4027],{"class":1075,"line":4016},49,[1073,4018,4019],{"class":1087},"    audio_file ",[1073,4021,1980],{"class":1083},[1073,4023,2301],{"class":1083},[1073,4025,4026],{"class":1122},"temp_audio.mp3",[1073,4028,2418],{"class":1083},[1073,4030,4032,4035,4037,4039,4042],{"class":1075,"line":4031},50,[1073,4033,4034],{"class":1087},"    output_file ",[1073,4036,1980],{"class":1083},[1073,4038,2301],{"class":1083},[1073,4040,4041],{"class":1122},"meeting_transcript.txt",[1073,4043,2418],{"class":1083},[1073,4045,4047],{"class":1075,"line":4046},51,[1073,4048,1133],{"emptyLinePlaceholder":1132},[1073,4050,4052],{"class":1075,"line":4051},52,[1073,4053,4054],{"class":1408},"    # Pipeline\n",[1073,4056,4058,4061,4063,4066,4068,4071],{"class":1075,"line":4057},53,[1073,4059,4060],{"class":1146},"    extract_audio",[1073,4062,1150],{"class":1083},[1073,4064,4065],{"class":1146},"video_file",[1073,4067,1091],{"class":1083},[1073,4069,4070],{"class":1146}," audio_file",[1073,4072,1202],{"class":1083},[1073,4074,4076,4078,4080,4082,4084,4087,4089,4092],{"class":1075,"line":4075},54,[1073,4077,3851],{"class":1087},[1073,4079,1980],{"class":1083},[1073,4081,3658],{"class":1146},[1073,4083,1150],{"class":1083},[1073,4085,4086],{"class":1146},"audio_file",[1073,4088,1091],{"class":1083},[1073,4090,4091],{"class":1146}," output_file",[1073,4093,1202],{"class":1083},[1073,4095,4097],{"class":1075,"line":4096},55,[1073,4098,1133],{"emptyLinePlaceholder":1132},[1073,4100,4102],{"class":1075,"line":4101},56,[1073,4103,4104],{"class":1408},"    # Cleanup temp file\n",[1073,4106,4108,4111,4113,4115,4118,4121],{"class":1075,"line":4107},57,[1073,4109,4110],{"class":1146},"    Path",[1073,4112,1150],{"class":1083},[1073,4114,4086],{"class":1146},[1073,4116,4117],{"class":1083},").",[1073,4119,4120],{"class":1146},"unlink",[1073,4122,1658],{"class":1083},[1073,4124,4126],{"class":1075,"line":4125},58,[1073,4127,1133],{"emptyLinePlaceholder":1132},[1073,4129,4131,4133,4135,4137,4139,4142,4145,4147,4149,4151,4154,4157,4160,4162,4164],{"class":1075,"line":4130},59,[1073,4132,3623],{"class":1146},[1073,4134,1150],{"class":1083},[1073,4136,3628],{"class":1139},[1073,4138,2283],{"class":1122},[1073,4140,4141],{"class":1087},"\\n",[1073,4143,4144],{"class":1122},"Transcription preview:",[1073,4146,4141],{"class":1087},[1073,4148,3634],{"class":1466},[1073,4150,2435],{"class":1146},[1073,4152,4153],{"class":1083},"[:",[1073,4155,4156],{"class":1466},"500",[1073,4158,4159],{"class":1083},"]",[1073,4161,1229],{"class":1466},[1073,4163,3763],{"class":1122},[1073,4165,1202],{"class":1083},[1073,4167,4169],{"class":1075,"line":4168},60,[1073,4170,1133],{"emptyLinePlaceholder":1132},[1073,4172,4174],{"class":1075,"line":4173},61,[1073,4175,1133],{"emptyLinePlaceholder":1132},[1073,4177,4179,4182,4185,4188,4190,4193,4195],{"class":1075,"line":4178},62,[1073,4180,4181],{"class":1079},"if",[1073,4183,4184],{"class":1087}," __name__ ",[1073,4186,4187],{"class":1083},"==",[1073,4189,2301],{"class":1083},[1073,4191,4192],{"class":1122},"__main__",[1073,4194,2283],{"class":1083},[1073,4196,3731],{"class":1083},[1073,4198,4200,4203],{"class":1075,"line":4199},63,[1073,4201,4202],{"class":1146},"    main",[1073,4204,1658],{"class":1083},[749,4206,4207],{},"Run it:",[1063,4209,4211],{"className":2443,"code":4210,"language":2445,"meta":1069,"style":1069},"python transcribe.py\n",[798,4212,4213],{"__ignoreMap":1069},[1073,4214,4215,4217],{"class":1075,"line":1076},[1073,4216,3361],{"class":1160},[1073,4218,4219],{"class":1122}," transcribe.py\n",[773,4221],{},[776,4223,4225],{"id":4224},"example-transcription-with-timestamps","Example: Transcription with Timestamps",[749,4227,4228],{},"Need timestamps for subtitles or reference? Use the segments feature:",[1063,4230,4232],{"className":3359,"code":4231,"language":3361,"meta":1069,"style":1069},"import whisper\n\nmodel = whisper.load_model(\"medium\")\nresult = model.transcribe(\"podcast_episode.mp3\", language=\"en\")\n\n# Print segments with timestamps\nfor segment in result[\"segments\"]:\n    start = segment[\"start\"]\n    end = segment[\"end\"]\n    text = segment[\"text\"].strip()\n    print(f\"[{start:.1f}s - {end:.1f}s] {text}\")\n",[798,4233,4234,4240,4244,4267,4303,4307,4312,4337,4358,4378,4402],{"__ignoreMap":1069},[1073,4235,4236,4238],{"class":1075,"line":1076},[1073,4237,1080],{"class":1079},[1073,4239,3405],{"class":1087},[1073,4241,4242],{"class":1075,"line":1129},[1073,4243,1133],{"emptyLinePlaceholder":1132},[1073,4245,4246,4249,4251,4253,4255,4257,4259,4261,4263,4265],{"class":1075,"line":1136},[1073,4247,4248],{"class":1087},"model ",[1073,4250,1980],{"class":1083},[1073,4252,3776],{"class":1087},[1073,4254,764],{"class":1083},[1073,4256,3781],{"class":1146},[1073,4258,1150],{"class":1083},[1073,4260,2283],{"class":1083},[1073,4262,3177],{"class":1122},[1073,4264,2283],{"class":1083},[1073,4266,1202],{"class":1083},[1073,4268,4269,4272,4274,4276,4278,4280,4282,4284,4287,4289,4291,4293,4295,4297,4299,4301],{"class":1075,"line":1182},[1073,4270,4271],{"class":1087},"result ",[1073,4273,1980],{"class":1083},[1073,4275,3819],{"class":1087},[1073,4277,764],{"class":1083},[1073,4279,3824],{"class":1146},[1073,4281,1150],{"class":1083},[1073,4283,2283],{"class":1083},[1073,4285,4286],{"class":1122},"podcast_episode.mp3",[1073,4288,2283],{"class":1083},[1073,4290,1091],{"class":1083},[1073,4292,3833],{"class":1153},[1073,4294,1980],{"class":1083},[1073,4296,2283],{"class":1083},[1073,4298,3717],{"class":1122},[1073,4300,2283],{"class":1083},[1073,4302,1202],{"class":1083},[1073,4304,4305],{"class":1075,"line":1205},[1073,4306,1133],{"emptyLinePlaceholder":1132},[1073,4308,4309],{"class":1075,"line":1238},[1073,4310,4311],{"class":1408},"# Print segments with timestamps\n",[1073,4313,4314,4317,4320,4323,4325,4327,4329,4332,4334],{"class":1075,"line":1274},[1073,4315,4316],{"class":1079},"for",[1073,4318,4319],{"class":1087}," segment ",[1073,4321,4322],{"class":1079},"in",[1073,4324,1669],{"class":1087},[1073,4326,3858],{"class":1083},[1073,4328,2283],{"class":1083},[1073,4330,4331],{"class":1122},"segments",[1073,4333,2283],{"class":1083},[1073,4335,4336],{"class":1083},"]:\n",[1073,4338,4339,4342,4344,4347,4349,4351,4354,4356],{"class":1075,"line":1279},[1073,4340,4341],{"class":1087},"    start ",[1073,4343,1980],{"class":1083},[1073,4345,4346],{"class":1087}," segment",[1073,4348,3858],{"class":1083},[1073,4350,2283],{"class":1083},[1073,4352,4353],{"class":1122},"start",[1073,4355,2283],{"class":1083},[1073,4357,3867],{"class":1083},[1073,4359,4360,4363,4365,4367,4369,4371,4374,4376],{"class":1075,"line":1305},[1073,4361,4362],{"class":1087},"    end ",[1073,4364,1980],{"class":1083},[1073,4366,4346],{"class":1087},[1073,4368,3858],{"class":1083},[1073,4370,2283],{"class":1083},[1073,4372,4373],{"class":1122},"end",[1073,4375,2283],{"class":1083},[1073,4377,3867],{"class":1083},[1073,4379,4380,4382,4384,4386,4388,4390,4392,4394,4397,4400],{"class":1075,"line":1324},[1073,4381,3851],{"class":1087},[1073,4383,1980],{"class":1083},[1073,4385,4346],{"class":1087},[1073,4387,3858],{"class":1083},[1073,4389,2283],{"class":1083},[1073,4391,2435],{"class":1122},[1073,4393,2283],{"class":1083},[1073,4395,4396],{"class":1083},"].",[1073,4398,4399],{"class":1146},"strip",[1073,4401,1658],{"class":1083},[1073,4403,4404,4406,4408,4410,4413,4415,4417,4420,4422,4425,4427,4429,4431,4433,4436,4438,4440,4442,4444],{"class":1075,"line":1332},[1073,4405,3623],{"class":1146},[1073,4407,1150],{"class":1083},[1073,4409,3628],{"class":1139},[1073,4411,4412],{"class":1122},"\"[",[1073,4414,3634],{"class":1466},[1073,4416,4353],{"class":1146},[1073,4418,4419],{"class":1139},":.1f",[1073,4421,1229],{"class":1466},[1073,4423,4424],{"class":1122},"s - ",[1073,4426,3634],{"class":1466},[1073,4428,4373],{"class":1146},[1073,4430,4419],{"class":1139},[1073,4432,1229],{"class":1466},[1073,4434,4435],{"class":1122},"s] ",[1073,4437,3634],{"class":1466},[1073,4439,2435],{"class":1146},[1073,4441,1229],{"class":1466},[1073,4443,2283],{"class":1122},[1073,4445,1202],{"class":1083},[749,4447,4448],{},"Output:",[1063,4450,4453],{"className":4451,"code":4452,"language":2435},[2433],"[0.0s - 4.2s] Welcome to the show. Today we're talking about...\n[4.2s - 8.7s] ...building hardware products for athletes.\n[8.7s - 15.3s] Our guest has been working on a tempo trainer device.\n",[798,4454,4452],{"__ignoreMap":1069},[773,4456],{},[776,4458,4460],{"id":4459},"example-multi-language-detection","Example: Multi-Language Detection",[749,4462,4463],{},"Don't know the language? Let Whisper detect it:",[1063,4465,4467],{"className":3359,"code":4466,"language":3361,"meta":1069,"style":1069},"import whisper\n\nmodel = whisper.load_model(\"medium\")\n\n# Auto-detect language\nresult = model.transcribe(\"unknown_language.mp3\")\n\nprint(f\"Detected language: {result['language']}\")\nprint(f\"Text: {result['text']}\")\n",[798,4468,4469,4475,4479,4501,4505,4510,4533,4537,4569],{"__ignoreMap":1069},[1073,4470,4471,4473],{"class":1075,"line":1076},[1073,4472,1080],{"class":1079},[1073,4474,3405],{"class":1087},[1073,4476,4477],{"class":1075,"line":1129},[1073,4478,1133],{"emptyLinePlaceholder":1132},[1073,4480,4481,4483,4485,4487,4489,4491,4493,4495,4497,4499],{"class":1075,"line":1136},[1073,4482,4248],{"class":1087},[1073,4484,1980],{"class":1083},[1073,4486,3776],{"class":1087},[1073,4488,764],{"class":1083},[1073,4490,3781],{"class":1146},[1073,4492,1150],{"class":1083},[1073,4494,2283],{"class":1083},[1073,4496,3177],{"class":1122},[1073,4498,2283],{"class":1083},[1073,4500,1202],{"class":1083},[1073,4502,4503],{"class":1075,"line":1182},[1073,4504,1133],{"emptyLinePlaceholder":1132},[1073,4506,4507],{"class":1075,"line":1205},[1073,4508,4509],{"class":1408},"# Auto-detect language\n",[1073,4511,4512,4514,4516,4518,4520,4522,4524,4526,4529,4531],{"class":1075,"line":1238},[1073,4513,4271],{"class":1087},[1073,4515,1980],{"class":1083},[1073,4517,3819],{"class":1087},[1073,4519,764],{"class":1083},[1073,4521,3824],{"class":1146},[1073,4523,1150],{"class":1083},[1073,4525,2283],{"class":1083},[1073,4527,4528],{"class":1122},"unknown_language.mp3",[1073,4530,2283],{"class":1083},[1073,4532,1202],{"class":1083},[1073,4534,4535],{"class":1075,"line":1274},[1073,4536,1133],{"emptyLinePlaceholder":1132},[1073,4538,4539,4542,4544,4546,4549,4551,4553,4555,4557,4559,4561,4563,4565,4567],{"class":1075,"line":1279},[1073,4540,4541],{"class":1146},"print",[1073,4543,1150],{"class":1083},[1073,4545,3628],{"class":1139},[1073,4547,4548],{"class":1122},"\"Detected language: ",[1073,4550,3634],{"class":1466},[1073,4552,2040],{"class":1146},[1073,4554,3858],{"class":1083},[1073,4556,1318],{"class":1083},[1073,4558,3838],{"class":1122},[1073,4560,1318],{"class":1083},[1073,4562,4159],{"class":1083},[1073,4564,1229],{"class":1466},[1073,4566,2283],{"class":1122},[1073,4568,1202],{"class":1083},[1073,4570,4571,4573,4575,4577,4580,4582,4584,4586,4588,4590,4592,4594,4596,4598],{"class":1075,"line":1305},[1073,4572,4541],{"class":1146},[1073,4574,1150],{"class":1083},[1073,4576,3628],{"class":1139},[1073,4578,4579],{"class":1122},"\"Text: ",[1073,4581,3634],{"class":1466},[1073,4583,2040],{"class":1146},[1073,4585,3858],{"class":1083},[1073,4587,1318],{"class":1083},[1073,4589,2435],{"class":1122},[1073,4591,1318],{"class":1083},[1073,4593,4159],{"class":1083},[1073,4595,1229],{"class":1466},[1073,4597,2283],{"class":1122},[1073,4599,1202],{"class":1083},[773,4601],{},[776,4603,4605],{"id":4604},"tips-and-optimizations","Tips and Optimizations",[1051,4607,4609],{"id":4608},"_1-faster-transcription-on-apple-silicon","1. Faster Transcription on Apple Silicon",[1063,4611,4613],{"className":2443,"code":4612,"language":2445,"meta":1069,"style":1069},"pip install mlx-whisper\n",[798,4614,4615],{"__ignoreMap":1069},[1073,4616,4617,4619,4621],{"class":1075,"line":1076},[1073,4618,2915],{"class":1160},[1073,4620,2834],{"class":1122},[1073,4622,4623],{"class":1122}," mlx-whisper\n",[1063,4625,4627],{"className":3359,"code":4626,"language":3361,"meta":1069,"style":1069},"import mlx_whisper\n\nresult = mlx_whisper.transcribe(\n    \"audio.mp3\",\n    path_or_hf_repo=\"mlx-community/whisper-medium-mlx\"\n)\n",[798,4628,4629,4636,4640,4655,4666,4680],{"__ignoreMap":1069},[1073,4630,4631,4633],{"class":1075,"line":1076},[1073,4632,1080],{"class":1079},[1073,4634,4635],{"class":1087}," mlx_whisper\n",[1073,4637,4638],{"class":1075,"line":1129},[1073,4639,1133],{"emptyLinePlaceholder":1132},[1073,4641,4642,4644,4646,4649,4651,4653],{"class":1075,"line":1136},[1073,4643,4271],{"class":1087},[1073,4645,1980],{"class":1083},[1073,4647,4648],{"class":1087}," mlx_whisper",[1073,4650,764],{"class":1083},[1073,4652,3824],{"class":1146},[1073,4654,1433],{"class":1083},[1073,4656,4657,4659,4662,4664],{"class":1075,"line":1182},[1073,4658,2292],{"class":1083},[1073,4660,4661],{"class":1122},"audio.mp3",[1073,4663,2283],{"class":1083},[1073,4665,1321],{"class":1083},[1073,4667,4668,4671,4673,4675,4678],{"class":1075,"line":1205},[1073,4669,4670],{"class":1153},"    path_or_hf_repo",[1073,4672,1980],{"class":1083},[1073,4674,2283],{"class":1083},[1073,4676,4677],{"class":1122},"mlx-community/whisper-medium-mlx",[1073,4679,2418],{"class":1083},[1073,4681,4682],{"class":1075,"line":1238},[1073,4683,1202],{"class":1083},[1051,4685,4687],{"id":4686},"_2-handling-long-recordings","2. Handling Long Recordings",[749,4689,4690],{},"For recordings over 1 hour, consider splitting:",[1063,4692,4694],{"className":2443,"code":4693,"language":2445,"meta":1069,"style":1069},"ffmpeg -i long_recording.mp3 -f segment -segment_time 1800 -c copy chunk_%03d.mp3\n",[798,4695,4696],{"__ignoreMap":1069},[1073,4697,4698,4700,4702,4705,4708,4710,4713,4716,4719,4722],{"class":1075,"line":1076},[1073,4699,2893],{"class":1160},[1073,4701,2973],{"class":1122},[1073,4703,4704],{"class":1122}," long_recording.mp3",[1073,4706,4707],{"class":1122}," -f",[1073,4709,4346],{"class":1122},[1073,4711,4712],{"class":1122}," -segment_time",[1073,4714,4715],{"class":1466}," 1800",[1073,4717,4718],{"class":1122}," -c",[1073,4720,4721],{"class":1122}," copy",[1073,4723,4724],{"class":1122}," chunk_%03d.mp3\n",[1051,4726,4728],{"id":4727},"_3-improving-quality-for-difficult-audio","3. Improving Quality for Difficult Audio",[2632,4730,4731,4739],{},[851,4732,4733,4734,4736,4737,1360],{},"Use a larger model (",[798,4735,3194],{}," instead of ",[798,4738,3177],{},[851,4740,4741,4742,1157],{},"Add context with ",[798,4743,4744],{},"initial_prompt",[1063,4746,4748],{"className":3359,"code":4747,"language":3361,"meta":1069,"style":1069},"result = model.transcribe(\n    \"audio.mp3\",\n    language=\"en\",\n    initial_prompt=\"This is a conversation about swimming training and interval timers.\"\n)\n",[798,4749,4750,4764,4774,4788,4802],{"__ignoreMap":1069},[1073,4751,4752,4754,4756,4758,4760,4762],{"class":1075,"line":1076},[1073,4753,4271],{"class":1087},[1073,4755,1980],{"class":1083},[1073,4757,3819],{"class":1087},[1073,4759,764],{"class":1083},[1073,4761,3824],{"class":1146},[1073,4763,1433],{"class":1083},[1073,4765,4766,4768,4770,4772],{"class":1075,"line":1129},[1073,4767,2292],{"class":1083},[1073,4769,4661],{"class":1122},[1073,4771,2283],{"class":1083},[1073,4773,1321],{"class":1083},[1073,4775,4776,4778,4780,4782,4784,4786],{"class":1075,"line":1136},[1073,4777,3706],{"class":1153},[1073,4779,1980],{"class":1083},[1073,4781,2283],{"class":1083},[1073,4783,3717],{"class":1122},[1073,4785,2283],{"class":1083},[1073,4787,1321],{"class":1083},[1073,4789,4790,4793,4795,4797,4800],{"class":1075,"line":1182},[1073,4791,4792],{"class":1153},"    initial_prompt",[1073,4794,1980],{"class":1083},[1073,4796,2283],{"class":1083},[1073,4798,4799],{"class":1122},"This is a conversation about swimming training and interval timers.",[1073,4801,2418],{"class":1083},[1073,4803,4804],{"class":1075,"line":1205},[1073,4805,1202],{"class":1083},[773,4807],{},[776,4809,4811],{"id":4810},"comparison-with-alternatives","Comparison with Alternatives",[893,4813,4814,4828],{},[896,4815,4816],{},[899,4817,4818,4820,4823,4826],{},[902,4819,2484],{},[902,4821,4822],{},"Cost",[902,4824,4825],{},"Privacy",[902,4827,3118],{},[909,4829,4830,4843,4857,4869],{},[899,4831,4832,4835,4838,4841],{},[914,4833,4834],{},"Whisper (local)",[914,4836,4837],{},"Free",[914,4839,4840],{},"Full privacy",[914,4842,3186],{},[899,4844,4845,4848,4851,4854],{},[914,4846,4847],{},"OpenAI API",[914,4849,4850],{},"$0.006/min",[914,4852,4853],{},"Cloud-based",[914,4855,4856],{},"Excellent",[899,4858,4859,4862,4865,4867],{},[914,4860,4861],{},"Google Speech-to-Text",[914,4863,4864],{},"$0.016/min",[914,4866,4853],{},[914,4868,3186],{},[899,4870,4871,4874,4877,4879],{},[914,4872,4873],{},"AssemblyAI",[914,4875,4876],{},"$0.015/min",[914,4878,4853],{},[914,4880,3186],{},[749,4882,4883],{},"Choose local Whisper when:",[2632,4885,4886,4889,4892],{},[851,4887,4888],{},"Data privacy matters",[851,4890,4891],{},"You have lots of content to transcribe",[851,4893,4894],{},"You want to avoid recurring costs",[773,4896],{},[776,4898,4900],{"id":4899},"summary","Summary",[749,4902,4903],{},"The video → mp3 → text pipeline with Whisper is straightforward:",[848,4905,4906,4914],{},[851,4907,4908,2677,4911],{},[753,4909,4910],{},"Extract audio",[798,4912,4913],{},"ffmpeg -i video.mp4 -vn audio.mp3",[851,4915,4916,2677,4919],{},[753,4917,4918],{},"Transcribe",[798,4920,4921],{},"whisper audio.mp3 --language English --model medium",[749,4923,4924],{},"Everything runs locally, it's free, and delivers production-quality results.",[773,4926],{},[776,4928,4930],{"id":4929},"resources","Resources",[2632,4932,4933,4940,4947],{},[851,4934,4935],{},[783,4936,4939],{"href":4937,"rel":4938},"https://github.com/openai/whisper",[787],"OpenAI Whisper GitHub",[851,4941,4942],{},[783,4943,4946],{"href":4944,"rel":4945},"https://ffmpeg.org/documentation.html",[787],"FFmpeg Documentation",[851,4948,4949],{},[783,4950,4953],{"href":4951,"rel":4952},"https://github.com/openai/whisper/blob/main/model-card.md",[787],"Whisper Model Card",[1833,4955,4956],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}",{"title":1069,"searchDepth":1129,"depth":1129,"links":4958},[4959,4960,4961,4966,4967,4968,4973,4978,4979,4980,4981,4986,4987,4988],{"id":2780,"depth":1129,"text":2781},{"id":2796,"depth":1129,"text":2797},{"id":2813,"depth":1129,"text":2814,"children":4962},[4963,4964,4965],{"id":2820,"depth":1136,"text":2821},{"id":2840,"depth":1136,"text":2841},{"id":2870,"depth":1136,"text":2871},{"id":2901,"depth":1129,"text":2902},{"id":2957,"depth":1129,"text":2958},{"id":3066,"depth":1129,"text":3067,"children":4969},[4970,4971,4972],{"id":3070,"depth":1136,"text":3071},{"id":3099,"depth":1136,"text":3100},{"id":3218,"depth":1136,"text":3219},{"id":3267,"depth":1129,"text":3268,"children":4974},[4975,4976,4977],{"id":3274,"depth":1136,"text":3275},{"id":3305,"depth":1136,"text":3306},{"id":3333,"depth":1136,"text":3334},{"id":3352,"depth":1129,"text":3353},{"id":4224,"depth":1129,"text":4225},{"id":4459,"depth":1129,"text":4460},{"id":4604,"depth":1129,"text":4605,"children":4982},[4983,4984,4985],{"id":4608,"depth":1136,"text":4609},{"id":4686,"depth":1136,"text":4687},{"id":4727,"depth":1136,"text":4728},{"id":4810,"depth":1129,"text":4811},{"id":4899,"depth":1129,"text":4900},{"id":4929,"depth":1129,"text":4930},"2026-02-03T00:00:00.000Z","A practical guide to the video → mp3 → text pipeline using OpenAI Whisper. Free, local transcription for interviews, podcasts, and meetings.",{"src":4992},"/images/blog/musictechlab_blog_how-to-transcribe-video-to-text-using-whisper.webp",{"enabled":1132,"items":4994},[4995,4997,5000,5003],{"text":4996,"icon":2753},"Whisper runs locally and for free, with no data sent to the cloud.",{"text":4998,"icon":4999},"The medium model offers the best quality-to-speed ratio for most transcription tasks.","i-lucide-zap",{"text":5001,"icon":5002},"FFmpeg extracts audio from video in one command before Whisper processes it.","i-lucide-terminal",{"text":5004,"icon":5005},"Supports 90+ languages with automatic language detection when the source is unknown.","i-lucide-globe",{},{"title":148,"description":4990},[2760,2761],"BuviUKMZPE9TPst9p1kaETZ_AO8QysHB5AZaUwEOroo",{"id":5011,"title":140,"authors":5012,"badge":5015,"body":5017,"category":2736,"client":2737,"date":6762,"description":6763,"extension":2740,"faq":2737,"featured":69,"featuredOrder":2737,"hidden":69,"image":6764,"keyTakeaways":6766,"meta":6780,"navigation":1132,"path":141,"seo":6781,"status":2737,"stem":142,"tags":6782,"teaser":2737,"__hash__":6785,"score":1182},"posts/blog/music-data/extracting-data-from-ableton-als-asd-files.md",[5013],{"name":738,"to":739,"avatar":5014},{"src":741},{"label":5016,"color":744},"Open Source",{"type":746,"value":5018,"toc":6736},[5019,5034,5037,5041,5044,5094,5111,5115,5121,5125,5206,5210,5213,5227,5230,5234,5241,5446,5449,5463,5467,5472,5476,5542,5546,5552,5799,5820,5829,5833,5837,5840,5849,5852,5878,5880,5883,6292,6295,6306,6310,6320,6323,6343,6347,6350,6383,6387,6425,6447,6451,6483,6487,6515,6519,6621,6625,6628,6656,6660,6663,6689,6691,6693,6717,6719,6733],[749,5020,5021,5022,5025,5026,5029,5030,5033],{},"If you've ever wondered what data Ableton Live stores in its project files, you're not alone. As part of our work at MusicTech Lab, we needed to extract metadata from Ableton projects – tempo, time signature, track names, and song sections – to power our music analysis workflows. The result is ",[753,5023,5024],{},"MTL Ableton Analyser",", an open-source Python tool that parses both ",[798,5027,5028],{},".als"," (Ableton Live Set) and ",[798,5031,5032],{},".asd"," (Ableton Sample Data) files.",[749,5035,5036],{},"In this article, we'll explore what's inside these files and how to extract useful data from them programmatically.",[776,5038,5040],{"id":5039},"understanding-abletons-file-formats","Understanding Ableton's File Formats",[749,5042,5043],{},"Ableton Live uses two primary file formats for storing project and analysis data:",[893,5045,5046,5062],{},[896,5047,5048],{},[899,5049,5050,5053,5056,5059],{},[902,5051,5052],{},"Format",[902,5054,5055],{},"Full Name",[902,5057,5058],{},"Type",[902,5060,5061],{},"Purpose",[909,5063,5064,5079],{},[899,5065,5066,5070,5073,5076],{},[914,5067,5068],{},[798,5069,5028],{},[914,5071,5072],{},"Ableton Live Set",[914,5074,5075],{},"Gzip-compressed XML",[914,5077,5078],{},"Complete project data – tracks, clips, tempo, arrangement",[899,5080,5081,5085,5088,5091],{},[914,5082,5083],{},[798,5084,5032],{},[914,5086,5087],{},"Ableton Sample Data",[914,5089,5090],{},"Binary",[914,5092,5093],{},"Analysis cache – detected BPM, warp markers, waveform peaks",[749,5095,5096,5097,5099,5100,5103,5104,5106,5107,5110],{},"The key insight is that ",[798,5098,5028],{}," files are actually ",[753,5101,5102],{},"compressed XML",", making them relatively straightforward to parse. The ",[798,5105,5032],{}," files, however, are ",[753,5108,5109],{},"proprietary binary"," and require reverse-engineering to extract data.",[776,5112,5114],{"id":5113},"data-extractable-from-als-files","Data Extractable from .als Files",[749,5116,5117,5118,5120],{},"When you decompress an ",[798,5119,5028],{}," file, you get a well-structured XML document containing the entire project state. Here's what we can reliably extract:",[1051,5122,5124],{"id":5123},"project-metadata","Project Metadata",[893,5126,5127,5140],{},[896,5128,5129],{},[899,5130,5131,5134,5137],{},[902,5132,5133],{},"Data Point",[902,5135,5136],{},"Description",[902,5138,5139],{},"XML Path",[909,5141,5142,5157,5172,5187],{},[899,5143,5144,5149,5152],{},[914,5145,5146],{},[753,5147,5148],{},"Tempo (BPM)",[914,5150,5151],{},"Project tempo",[914,5153,5154],{},[798,5155,5156],{},".//Tempo/Manual",[899,5158,5159,5164,5167],{},[914,5160,5161],{},[753,5162,5163],{},"Time Signature",[914,5165,5166],{},"Numerator and denominator",[914,5168,5169],{},[798,5170,5171],{},".//TimeSignature/*",[899,5173,5174,5179,5182],{},[914,5175,5176],{},[753,5177,5178],{},"Track Count",[914,5180,5181],{},"Number of tracks in project",[914,5183,5184],{},[798,5185,5186],{},".//Tracks",[899,5188,5189,5194,5197],{},[914,5190,5191],{},[753,5192,5193],{},"Track Names",[914,5195,5196],{},"User-defined track names",[914,5198,5199,756,5202,5205],{},[798,5200,5201],{},"EffectiveName",[798,5203,5204],{},"UserName"," attributes",[1051,5207,5209],{"id":5208},"warp-markers","Warp Markers",[749,5211,5212],{},"Warp markers define the relationship between audio time (seconds) and musical time (beats). Each marker contains:",[2632,5214,5215,5221],{},[851,5216,5217,5220],{},[753,5218,5219],{},"SecTime"," – Position in the audio file (seconds)",[851,5222,5223,5226],{},[753,5224,5225],{},"BeatTime"," – Corresponding position in beats",[749,5228,5229],{},"This data is essential for beat-synced playback and tempo manipulation.",[1051,5231,5233],{"id":5232},"locators-section-markers","Locators (Section Markers)",[749,5235,5236,5237,5240],{},"Perhaps the most valuable data for music analysis – ",[753,5238,5239],{},"locators"," represent named positions in your arrangement:",[1063,5242,5244],{"className":2264,"code":5243,"language":1904,"meta":1069,"style":1069},"{\n  \"sections\": [\n    { \"name\": \"INT1\", \"beat\": 0 },\n    { \"name\": \"VER1\", \"beat\": 32 },\n    { \"name\": \"CHO1\", \"beat\": 64 },\n    { \"name\": \"BRD1\", \"beat\": 96 },\n    { \"name\": \"OUT1\", \"beat\": 128 }\n  ]\n}\n",[798,5245,5246,5250,5263,5301,5335,5369,5403,5437,5442],{"__ignoreMap":1069},[1073,5247,5248],{"class":1075,"line":1076},[1073,5249,1790],{"class":1083},[1073,5251,5252,5254,5257,5259,5261],{"class":1075,"line":1129},[1073,5253,2277],{"class":1083},[1073,5255,5256],{"class":1139},"sections",[1073,5258,2283],{"class":1083},[1073,5260,1157],{"class":1083},[1073,5262,2336],{"class":1083},[1073,5264,5265,5268,5270,5273,5275,5277,5279,5282,5284,5286,5288,5291,5293,5295,5298],{"class":1075,"line":1136},[1073,5266,5267],{"class":1083},"    {",[1073,5269,2301],{"class":1083},[1073,5271,5272],{"class":1160},"name",[1073,5274,2283],{"class":1083},[1073,5276,1157],{"class":1083},[1073,5278,2301],{"class":1083},[1073,5280,5281],{"class":1122},"INT1",[1073,5283,2283],{"class":1083},[1073,5285,1091],{"class":1083},[1073,5287,2301],{"class":1083},[1073,5289,5290],{"class":1160},"beat",[1073,5292,2283],{"class":1083},[1073,5294,1157],{"class":1083},[1073,5296,5297],{"class":1466}," 0",[1073,5299,5300],{"class":1083}," },\n",[1073,5302,5303,5305,5307,5309,5311,5313,5315,5318,5320,5322,5324,5326,5328,5330,5333],{"class":1075,"line":1182},[1073,5304,5267],{"class":1083},[1073,5306,2301],{"class":1083},[1073,5308,5272],{"class":1160},[1073,5310,2283],{"class":1083},[1073,5312,1157],{"class":1083},[1073,5314,2301],{"class":1083},[1073,5316,5317],{"class":1122},"VER1",[1073,5319,2283],{"class":1083},[1073,5321,1091],{"class":1083},[1073,5323,2301],{"class":1083},[1073,5325,5290],{"class":1160},[1073,5327,2283],{"class":1083},[1073,5329,1157],{"class":1083},[1073,5331,5332],{"class":1466}," 32",[1073,5334,5300],{"class":1083},[1073,5336,5337,5339,5341,5343,5345,5347,5349,5352,5354,5356,5358,5360,5362,5364,5367],{"class":1075,"line":1205},[1073,5338,5267],{"class":1083},[1073,5340,2301],{"class":1083},[1073,5342,5272],{"class":1160},[1073,5344,2283],{"class":1083},[1073,5346,1157],{"class":1083},[1073,5348,2301],{"class":1083},[1073,5350,5351],{"class":1122},"CHO1",[1073,5353,2283],{"class":1083},[1073,5355,1091],{"class":1083},[1073,5357,2301],{"class":1083},[1073,5359,5290],{"class":1160},[1073,5361,2283],{"class":1083},[1073,5363,1157],{"class":1083},[1073,5365,5366],{"class":1466}," 64",[1073,5368,5300],{"class":1083},[1073,5370,5371,5373,5375,5377,5379,5381,5383,5386,5388,5390,5392,5394,5396,5398,5401],{"class":1075,"line":1238},[1073,5372,5267],{"class":1083},[1073,5374,2301],{"class":1083},[1073,5376,5272],{"class":1160},[1073,5378,2283],{"class":1083},[1073,5380,1157],{"class":1083},[1073,5382,2301],{"class":1083},[1073,5384,5385],{"class":1122},"BRD1",[1073,5387,2283],{"class":1083},[1073,5389,1091],{"class":1083},[1073,5391,2301],{"class":1083},[1073,5393,5290],{"class":1160},[1073,5395,2283],{"class":1083},[1073,5397,1157],{"class":1083},[1073,5399,5400],{"class":1466}," 96",[1073,5402,5300],{"class":1083},[1073,5404,5405,5407,5409,5411,5413,5415,5417,5420,5422,5424,5426,5428,5430,5432,5435],{"class":1075,"line":1274},[1073,5406,5267],{"class":1083},[1073,5408,2301],{"class":1083},[1073,5410,5272],{"class":1160},[1073,5412,2283],{"class":1083},[1073,5414,1157],{"class":1083},[1073,5416,2301],{"class":1083},[1073,5418,5419],{"class":1122},"OUT1",[1073,5421,2283],{"class":1083},[1073,5423,1091],{"class":1083},[1073,5425,2301],{"class":1083},[1073,5427,5290],{"class":1160},[1073,5429,2283],{"class":1083},[1073,5431,1157],{"class":1083},[1073,5433,5434],{"class":1466}," 128",[1073,5436,1932],{"class":1083},[1073,5438,5439],{"class":1075,"line":1279},[1073,5440,5441],{"class":1083},"  ]\n",[1073,5443,5444],{"class":1075,"line":1305},[1073,5445,1576],{"class":1083},[749,5447,5448],{},"These locators, when combined with tempo data, give you a complete song structure map with precise timestamps.",[5450,5451,5456],"div",{"className":5452},[5453,5454,5455],"flex","justify-center","my-8",[749,5457,5458],{},[2166,5459],{"alt":5460,"src":5461,"width":5462},"ALS Analysis Output","/images/blog/musictechlab_blog_ableton_als_analysis.webp",600,[776,5464,5466],{"id":5465},"data-extractable-from-asd-files","Data Extractable from .asd Files",[749,5468,1579,5469,5471],{},[798,5470,5032],{}," format is more challenging. Ableton creates these files automatically when you analyze audio, storing:",[1051,5473,5475],{"id":5474},"detected-metadata","Detected Metadata",[893,5477,5478,5490],{},[896,5479,5480],{},[899,5481,5482,5484,5487],{},[902,5483,5133],{},[902,5485,5486],{},"Detection Method",[902,5488,5489],{},"Notes",[909,5491,5492,5505,5517,5529],{},[899,5493,5494,5499,5502],{},[914,5495,5496],{},[753,5497,5498],{},"Format Version",[914,5500,5501],{},"First byte",[914,5503,5504],{},"Typically version 6",[899,5506,5507,5511,5514],{},[914,5508,5509],{},[753,5510,5148],{},[914,5512,5513],{},"Binary search at known offsets",[914,5515,5516],{},"Ableton's auto-detected BPM",[899,5518,5519,5523,5526],{},[914,5520,5521],{},[753,5522,5163],{},[914,5524,5525],{},"Marker string search",[914,5527,5528],{},"Numerator/Denominator extraction",[899,5530,5531,5536,5539],{},[914,5532,5533],{},[753,5534,5535],{},"Waveform Data",[914,5537,5538],{},"Presence check",[914,5540,5541],{},"Boolean – are peaks pre-computed?",[1051,5543,5545],{"id":5544},"bpm-detection-strategy","BPM Detection Strategy",[749,5547,5548,5549,5551],{},"The BPM value in ",[798,5550,5032],{}," files isn't at a fixed offset. Our parser searches multiple known locations:",[1063,5553,5555],{"className":3359,"code":5554,"language":3361,"meta":1069,"style":1069},"KNOWN_BPM_OFFSETS = [7736, 8448, 8632, 7685, 7740, 8440]\n\ndef find_bpm(data: bytes) -> Optional[float]:\n    for offset in KNOWN_BPM_OFFSETS:\n        if offset + 4 \u003C= len(data):\n            value = struct.unpack('\u003Cf', data[offset:offset+4])[0]\n            if 60.0 \u003C= value \u003C= 200.0:\n                return round(value, 2)\n    # Fallback: scan byte range for valid BPM\n    return scan_for_bpm(data, range_start=6000, range_end=9500)\n",[798,5556,5557,5597,5601,5631,5646,5672,5722,5743,5761,5766],{"__ignoreMap":1069},[1073,5558,5559,5562,5564,5567,5570,5572,5575,5577,5580,5582,5585,5587,5590,5592,5595],{"class":1075,"line":1076},[1073,5560,5561],{"class":1087},"KNOWN_BPM_OFFSETS ",[1073,5563,1980],{"class":1083},[1073,5565,5566],{"class":1083}," [",[1073,5568,5569],{"class":1466},"7736",[1073,5571,1091],{"class":1083},[1073,5573,5574],{"class":1466}," 8448",[1073,5576,1091],{"class":1083},[1073,5578,5579],{"class":1466}," 8632",[1073,5581,1091],{"class":1083},[1073,5583,5584],{"class":1466}," 7685",[1073,5586,1091],{"class":1083},[1073,5588,5589],{"class":1466}," 7740",[1073,5591,1091],{"class":1083},[1073,5593,5594],{"class":1466}," 8440",[1073,5596,3867],{"class":1083},[1073,5598,5599],{"class":1075,"line":1129},[1073,5600,1133],{"emptyLinePlaceholder":1132},[1073,5602,5603,5605,5608,5610,5612,5614,5617,5619,5621,5624,5626,5629],{"class":1075,"line":1136},[1073,5604,3431],{"class":1139},[1073,5606,5607],{"class":1146}," find_bpm",[1073,5609,1150],{"class":1083},[1073,5611,1924],{"class":1153},[1073,5613,1157],{"class":1083},[1073,5615,5616],{"class":1160}," bytes",[1073,5618,1360],{"class":1083},[1073,5620,3458],{"class":1083},[1073,5622,5623],{"class":1087}," Optional",[1073,5625,3858],{"class":1083},[1073,5627,5628],{"class":1160},"float",[1073,5630,4336],{"class":1083},[1073,5632,5633,5636,5639,5641,5644],{"class":1075,"line":1182},[1073,5634,5635],{"class":1079},"    for",[1073,5637,5638],{"class":1087}," offset ",[1073,5640,4322],{"class":1079},[1073,5642,5643],{"class":1087}," KNOWN_BPM_OFFSETS",[1073,5645,3731],{"class":1083},[1073,5647,5648,5651,5653,5656,5659,5662,5665,5667,5669],{"class":1075,"line":1205},[1073,5649,5650],{"class":1079},"        if",[1073,5652,5638],{"class":1087},[1073,5654,5655],{"class":1083},"+",[1073,5657,5658],{"class":1466}," 4",[1073,5660,5661],{"class":1083}," \u003C=",[1073,5663,5664],{"class":1146}," len",[1073,5666,1150],{"class":1083},[1073,5668,1924],{"class":1146},[1073,5670,5671],{"class":1083},"):\n",[1073,5673,5674,5677,5679,5682,5684,5687,5689,5691,5694,5696,5698,5700,5702,5705,5707,5709,5711,5714,5717,5720],{"class":1075,"line":1238},[1073,5675,5676],{"class":1087},"            value ",[1073,5678,1980],{"class":1083},[1073,5680,5681],{"class":1087}," struct",[1073,5683,764],{"class":1083},[1073,5685,5686],{"class":1146},"unpack",[1073,5688,1150],{"class":1083},[1073,5690,1318],{"class":1083},[1073,5692,5693],{"class":1122},"\u003Cf",[1073,5695,1318],{"class":1083},[1073,5697,1091],{"class":1083},[1073,5699,1893],{"class":1146},[1073,5701,3858],{"class":1083},[1073,5703,5704],{"class":1146},"offset",[1073,5706,1157],{"class":1083},[1073,5708,5704],{"class":1146},[1073,5710,5655],{"class":1083},[1073,5712,5713],{"class":1466},"4",[1073,5715,5716],{"class":1083},"])[",[1073,5718,5719],{"class":1466},"0",[1073,5721,3867],{"class":1083},[1073,5723,5724,5727,5730,5732,5735,5738,5741],{"class":1075,"line":1274},[1073,5725,5726],{"class":1079},"            if",[1073,5728,5729],{"class":1466}," 60.0",[1073,5731,5661],{"class":1083},[1073,5733,5734],{"class":1087}," value ",[1073,5736,5737],{"class":1083},"\u003C=",[1073,5739,5740],{"class":1466}," 200.0",[1073,5742,3731],{"class":1083},[1073,5744,5745,5748,5751,5753,5755,5757,5759],{"class":1075,"line":1279},[1073,5746,5747],{"class":1079},"                return",[1073,5749,5750],{"class":1146}," round",[1073,5752,1150],{"class":1083},[1073,5754,1420],{"class":1146},[1073,5756,1091],{"class":1083},[1073,5758,2991],{"class":1466},[1073,5760,1202],{"class":1083},[1073,5762,5763],{"class":1075,"line":1305},[1073,5764,5765],{"class":1408},"    # Fallback: scan byte range for valid BPM\n",[1073,5767,5768,5770,5773,5775,5777,5779,5782,5784,5787,5789,5792,5794,5797],{"class":1075,"line":1324},[1073,5769,3968],{"class":1079},[1073,5771,5772],{"class":1146}," scan_for_bpm",[1073,5774,1150],{"class":1083},[1073,5776,1924],{"class":1146},[1073,5778,1091],{"class":1083},[1073,5780,5781],{"class":1153}," range_start",[1073,5783,1980],{"class":1083},[1073,5785,5786],{"class":1466},"6000",[1073,5788,1091],{"class":1083},[1073,5790,5791],{"class":1153}," range_end",[1073,5793,1980],{"class":1083},[1073,5795,5796],{"class":1466},"9500",[1073,5798,1202],{"class":1083},[822,5800,5801],{},[749,5802,5803,5806,5807,5809,5810,5813,5814,5816,5817,5819],{},[753,5804,5805],{},"Important:"," The BPM in ",[798,5808,5032],{}," files represents Ableton's ",[753,5811,5812],{},"auto-detection",", which may differ from the project tempo in the ",[798,5815,5028],{}," file. The ",[798,5818,5028],{}," tempo is always the authoritative value.",[5450,5821,5823],{"className":5822},[5453,5454,5455],[749,5824,5825],{},[2166,5826],{"alt":5827,"src":5828,"width":5462},"ASD Analysis Output","/images/blog/musictechlab_blog_ableton_asd_analysis.webp",[776,5830,5832],{"id":5831},"practical-applications","Practical Applications",[1051,5834,5836],{"id":5835},"waveform-visualization-with-sections","Waveform Visualization with Sections",[749,5838,5839],{},"By combining data from both file types, we can generate rich visualizations:",[5450,5841,5843],{"className":5842},[5453,5454,5455],[749,5844,5845],{},[2166,5846],{"alt":5836,"src":5847,"width":5848},"/images/blog/musictechlab_blog_ableton_asd_waveform.webp",800,[749,5850,5851],{},"The visualization includes:",[2632,5853,5854,5860,5866,5872],{},[851,5855,5856,5859],{},[753,5857,5858],{},"Waveform amplitude"," – extracted from the audio file",[851,5861,5862,5865],{},[753,5863,5864],{},"Beat grid"," – calculated from BPM and time signature",[851,5867,5868,5871],{},[753,5869,5870],{},"Section overlays"," – color-coded backgrounds from locator data",[851,5873,5874,5877],{},[753,5875,5876],{},"Downbeat markers"," – emphasized beats at bar boundaries",[1051,5879,2195],{"id":2194},[749,5881,5882],{},"For web applications, we export a JSON format compatible with WaveSurfer.js:",[1063,5884,5886],{"className":2264,"code":5885,"language":1904,"meta":1069,"style":1069},"{\n  \"version\": \"1.0\",\n  \"filename\": \"track.mp3\",\n  \"duration\": 180.5,\n  \"sampleRate\": 44100,\n  \"bpm\": 128,\n  \"beatsPerBar\": 4,\n  \"totalBeats\": 385,\n  \"peaks\": [0.12, 0.45, 0.89, ...],\n  \"beats\": [\n    { \"time\": 0.0, \"beat\": 1, \"isDownbeat\": true },\n    { \"time\": 0.469, \"beat\": 2, \"isDownbeat\": false }\n  ],\n  \"sections\": [\n    { \"name\": \"INT1\", \"start\": 0.0, \"end\": 15.0, \"color\": \"#1E3A5F\" },\n    { \"name\": \"VER1\", \"start\": 15.0, \"end\": 30.0, \"color\": \"#3D1A5F\" }\n  ]\n}\n",[798,5887,5888,5892,5912,5932,5948,5964,5979,5994,6010,6044,6057,6100,6142,6147,6159,6222,6284,6288],{"__ignoreMap":1069},[1073,5889,5890],{"class":1075,"line":1076},[1073,5891,1790],{"class":1083},[1073,5893,5894,5896,5899,5901,5903,5905,5908,5910],{"class":1075,"line":1129},[1073,5895,2277],{"class":1083},[1073,5897,5898],{"class":1139},"version",[1073,5900,2283],{"class":1083},[1073,5902,1157],{"class":1083},[1073,5904,2301],{"class":1083},[1073,5906,5907],{"class":1122},"1.0",[1073,5909,2283],{"class":1083},[1073,5911,1321],{"class":1083},[1073,5913,5914,5916,5919,5921,5923,5925,5928,5930],{"class":1075,"line":1136},[1073,5915,2277],{"class":1083},[1073,5917,5918],{"class":1139},"filename",[1073,5920,2283],{"class":1083},[1073,5922,1157],{"class":1083},[1073,5924,2301],{"class":1083},[1073,5926,5927],{"class":1122},"track.mp3",[1073,5929,2283],{"class":1083},[1073,5931,1321],{"class":1083},[1073,5933,5934,5936,5939,5941,5943,5946],{"class":1075,"line":1182},[1073,5935,2277],{"class":1083},[1073,5937,5938],{"class":1139},"duration",[1073,5940,2283],{"class":1083},[1073,5942,1157],{"class":1083},[1073,5944,5945],{"class":1466}," 180.5",[1073,5947,1321],{"class":1083},[1073,5949,5950,5952,5955,5957,5959,5962],{"class":1075,"line":1205},[1073,5951,2277],{"class":1083},[1073,5953,5954],{"class":1139},"sampleRate",[1073,5956,2283],{"class":1083},[1073,5958,1157],{"class":1083},[1073,5960,5961],{"class":1466}," 44100",[1073,5963,1321],{"class":1083},[1073,5965,5966,5968,5971,5973,5975,5977],{"class":1075,"line":1238},[1073,5967,2277],{"class":1083},[1073,5969,5970],{"class":1139},"bpm",[1073,5972,2283],{"class":1083},[1073,5974,1157],{"class":1083},[1073,5976,5434],{"class":1466},[1073,5978,1321],{"class":1083},[1073,5980,5981,5983,5986,5988,5990,5992],{"class":1075,"line":1274},[1073,5982,2277],{"class":1083},[1073,5984,5985],{"class":1139},"beatsPerBar",[1073,5987,2283],{"class":1083},[1073,5989,1157],{"class":1083},[1073,5991,5658],{"class":1466},[1073,5993,1321],{"class":1083},[1073,5995,5996,5998,6001,6003,6005,6008],{"class":1075,"line":1279},[1073,5997,2277],{"class":1083},[1073,5999,6000],{"class":1139},"totalBeats",[1073,6002,2283],{"class":1083},[1073,6004,1157],{"class":1083},[1073,6006,6007],{"class":1466}," 385",[1073,6009,1321],{"class":1083},[1073,6011,6012,6014,6017,6019,6021,6023,6026,6028,6031,6033,6036,6038,6041],{"class":1075,"line":1305},[1073,6013,2277],{"class":1083},[1073,6015,6016],{"class":1139},"peaks",[1073,6018,2283],{"class":1083},[1073,6020,1157],{"class":1083},[1073,6022,5566],{"class":1083},[1073,6024,6025],{"class":1466},"0.12",[1073,6027,1091],{"class":1083},[1073,6029,6030],{"class":1466}," 0.45",[1073,6032,1091],{"class":1083},[1073,6034,6035],{"class":1466}," 0.89",[1073,6037,1091],{"class":1083},[1073,6039,6040],{"class":1087}," ...",[1073,6042,6043],{"class":1083},"],\n",[1073,6045,6046,6048,6051,6053,6055],{"class":1075,"line":1324},[1073,6047,2277],{"class":1083},[1073,6049,6050],{"class":1139},"beats",[1073,6052,2283],{"class":1083},[1073,6054,1157],{"class":1083},[1073,6056,2336],{"class":1083},[1073,6058,6059,6061,6063,6066,6068,6070,6073,6075,6077,6079,6081,6083,6085,6087,6089,6092,6094,6096,6098],{"class":1075,"line":1332},[1073,6060,5267],{"class":1083},[1073,6062,2301],{"class":1083},[1073,6064,6065],{"class":1160},"time",[1073,6067,2283],{"class":1083},[1073,6069,1157],{"class":1083},[1073,6071,6072],{"class":1466}," 0.0",[1073,6074,1091],{"class":1083},[1073,6076,2301],{"class":1083},[1073,6078,5290],{"class":1160},[1073,6080,2283],{"class":1083},[1073,6082,1157],{"class":1083},[1073,6084,3053],{"class":1466},[1073,6086,1091],{"class":1083},[1073,6088,2301],{"class":1083},[1073,6090,6091],{"class":1160},"isDownbeat",[1073,6093,2283],{"class":1083},[1073,6095,1157],{"class":1083},[1073,6097,1817],{"class":1083},[1073,6099,5300],{"class":1083},[1073,6101,6102,6104,6106,6108,6110,6112,6115,6117,6119,6121,6123,6125,6127,6129,6131,6133,6135,6137,6140],{"class":1075,"line":1337},[1073,6103,5267],{"class":1083},[1073,6105,2301],{"class":1083},[1073,6107,6065],{"class":1160},[1073,6109,2283],{"class":1083},[1073,6111,1157],{"class":1083},[1073,6113,6114],{"class":1466}," 0.469",[1073,6116,1091],{"class":1083},[1073,6118,2301],{"class":1083},[1073,6120,5290],{"class":1160},[1073,6122,2283],{"class":1083},[1073,6124,1157],{"class":1083},[1073,6126,2991],{"class":1466},[1073,6128,1091],{"class":1083},[1073,6130,2301],{"class":1083},[1073,6132,6091],{"class":1160},[1073,6134,2283],{"class":1083},[1073,6136,1157],{"class":1083},[1073,6138,6139],{"class":1083}," false",[1073,6141,1932],{"class":1083},[1073,6143,6144],{"class":1075,"line":1368},[1073,6145,6146],{"class":1083},"  ],\n",[1073,6148,6149,6151,6153,6155,6157],{"class":1075,"line":1390},[1073,6150,2277],{"class":1083},[1073,6152,5256],{"class":1139},[1073,6154,2283],{"class":1083},[1073,6156,1157],{"class":1083},[1073,6158,2336],{"class":1083},[1073,6160,6161,6163,6165,6167,6169,6171,6173,6175,6177,6179,6181,6183,6185,6187,6189,6191,6193,6195,6197,6199,6202,6204,6206,6209,6211,6213,6215,6218,6220],{"class":1075,"line":1405},[1073,6162,5267],{"class":1083},[1073,6164,2301],{"class":1083},[1073,6166,5272],{"class":1160},[1073,6168,2283],{"class":1083},[1073,6170,1157],{"class":1083},[1073,6172,2301],{"class":1083},[1073,6174,5281],{"class":1122},[1073,6176,2283],{"class":1083},[1073,6178,1091],{"class":1083},[1073,6180,2301],{"class":1083},[1073,6182,4353],{"class":1160},[1073,6184,2283],{"class":1083},[1073,6186,1157],{"class":1083},[1073,6188,6072],{"class":1466},[1073,6190,1091],{"class":1083},[1073,6192,2301],{"class":1083},[1073,6194,4373],{"class":1160},[1073,6196,2283],{"class":1083},[1073,6198,1157],{"class":1083},[1073,6200,6201],{"class":1466}," 15.0",[1073,6203,1091],{"class":1083},[1073,6205,2301],{"class":1083},[1073,6207,6208],{"class":1160},"color",[1073,6210,2283],{"class":1083},[1073,6212,1157],{"class":1083},[1073,6214,2301],{"class":1083},[1073,6216,6217],{"class":1122},"#1E3A5F",[1073,6219,2283],{"class":1083},[1073,6221,5300],{"class":1083},[1073,6223,6224,6226,6228,6230,6232,6234,6236,6238,6240,6242,6244,6246,6248,6250,6252,6254,6256,6258,6260,6262,6265,6267,6269,6271,6273,6275,6277,6280,6282],{"class":1075,"line":1412},[1073,6225,5267],{"class":1083},[1073,6227,2301],{"class":1083},[1073,6229,5272],{"class":1160},[1073,6231,2283],{"class":1083},[1073,6233,1157],{"class":1083},[1073,6235,2301],{"class":1083},[1073,6237,5317],{"class":1122},[1073,6239,2283],{"class":1083},[1073,6241,1091],{"class":1083},[1073,6243,2301],{"class":1083},[1073,6245,4353],{"class":1160},[1073,6247,2283],{"class":1083},[1073,6249,1157],{"class":1083},[1073,6251,6201],{"class":1466},[1073,6253,1091],{"class":1083},[1073,6255,2301],{"class":1083},[1073,6257,4373],{"class":1160},[1073,6259,2283],{"class":1083},[1073,6261,1157],{"class":1083},[1073,6263,6264],{"class":1466}," 30.0",[1073,6266,1091],{"class":1083},[1073,6268,2301],{"class":1083},[1073,6270,6208],{"class":1160},[1073,6272,2283],{"class":1083},[1073,6274,1157],{"class":1083},[1073,6276,2301],{"class":1083},[1073,6278,6279],{"class":1122},"#3D1A5F",[1073,6281,2283],{"class":1083},[1073,6283,1932],{"class":1083},[1073,6285,6286],{"class":1075,"line":1436},[1073,6287,5441],{"class":1083},[1073,6289,6290],{"class":1075,"line":1470},[1073,6291,1576],{"class":1083},[749,6293,6294],{},"This powers interactive, beat-synced waveform players in the browser.",[5450,6296,6298],{"className":6297},[5453,5454,5455],[6299,6300,6301,6302],"video",{"controls":1132,"width":5848},"\n  ",[2346,6303],{"src":6304,"type":6305},"/videos/blog/musictechlab_blog_ableton_analyser_wavesurfer.mp4","video/mp4",[1051,6307,6309],{"id":6308},"live-demo","Live Demo",[749,6311,6312,6315,6316],{},[753,6313,6314],{},"Try it yourself:"," ",[783,6317,6318],{"href":6318,"rel":6319},"https://mtl-ableton-analyser.web.app",[787],[749,6321,6322],{},"The web app lets you:",[2632,6324,6325,6328,6334,6337,6340],{},[851,6326,6327],{},"Load audio files (MP3, WAV) directly in the browser",[851,6329,6330,6331,6333],{},"Parse ",[798,6332,5028],{}," files client-side – no backend required",[851,6335,6336],{},"View waveform with beat grid and section overlays",[851,6338,6339],{},"Toggle between bars and time-based timelines",[851,6341,6342],{},"Navigate by clicking on sections or the waveform",[776,6344,6346],{"id":6345},"using-mtl-ableton-analyser","Using MTL Ableton Analyser",[749,6348,6349],{},"The tool is available as an open-source Python CLI:",[1063,6351,6354],{"className":2443,"code":6352,"filename":6353,"language":2445,"meta":1069,"style":1069},"git clone https://github.com/musictechlab/mtl-ableton-analyser\ncd mtl-ableton-analyser\npoetry install\n","Installation",[798,6355,6356,6367,6375],{"__ignoreMap":1069},[1073,6357,6358,6361,6364],{"class":1075,"line":1076},[1073,6359,6360],{"class":1160},"git",[1073,6362,6363],{"class":1122}," clone",[1073,6365,6366],{"class":1122}," https://github.com/musictechlab/mtl-ableton-analyser\n",[1073,6368,6369,6372],{"class":1075,"line":1129},[1073,6370,6371],{"class":1146},"cd",[1073,6373,6374],{"class":1122}," mtl-ableton-analyser\n",[1073,6376,6377,6380],{"class":1075,"line":1136},[1073,6378,6379],{"class":1160},"poetry",[1073,6381,6382],{"class":1122}," install\n",[1051,6384,6386],{"id":6385},"analyze-files","Analyze Files",[1063,6388,6391],{"className":2443,"code":6389,"filename":6390,"language":2445,"meta":1069,"style":1069},"poetry run mtl-ableton-analyser analyze /path/to/project.als --verbose\npoetry run mtl-ableton-analyser analyze /path/to/audio.mp3.asd\n","Single File Analysis",[798,6392,6393,6412],{"__ignoreMap":1069},[1073,6394,6395,6397,6400,6403,6406,6409],{"class":1075,"line":1076},[1073,6396,6379],{"class":1160},[1073,6398,6399],{"class":1122}," run",[1073,6401,6402],{"class":1122}," mtl-ableton-analyser",[1073,6404,6405],{"class":1122}," analyze",[1073,6407,6408],{"class":1122}," /path/to/project.als",[1073,6410,6411],{"class":1122}," --verbose\n",[1073,6413,6414,6416,6418,6420,6422],{"class":1075,"line":1129},[1073,6415,6379],{"class":1160},[1073,6417,6399],{"class":1122},[1073,6419,6402],{"class":1122},[1073,6421,6405],{"class":1122},[1073,6423,6424],{"class":1122}," /path/to/audio.mp3.asd\n",[1063,6426,6429],{"className":2443,"code":6427,"filename":6428,"language":2445,"meta":1069,"style":1069},"poetry run mtl-ableton-analyser analyze /path/to/projects --recursive\n","Directory Scanning",[798,6430,6431],{"__ignoreMap":1069},[1073,6432,6433,6435,6437,6439,6441,6444],{"class":1075,"line":1076},[1073,6434,6379],{"class":1160},[1073,6436,6399],{"class":1122},[1073,6438,6402],{"class":1122},[1073,6440,6405],{"class":1122},[1073,6442,6443],{"class":1122}," /path/to/projects",[1073,6445,6446],{"class":1122}," --recursive\n",[1051,6448,6450],{"id":6449},"generate-visualizations","Generate Visualizations",[1063,6452,6455],{"className":2443,"code":6453,"filename":6454,"language":2445,"meta":1069,"style":1069},"poetry run mtl-ableton-analyser visualize audio.mp3 --als project.als -o waveform.png\n","Waveform with Sections",[798,6456,6457],{"__ignoreMap":1069},[1073,6458,6459,6461,6463,6465,6468,6471,6474,6477,6480],{"class":1075,"line":1076},[1073,6460,6379],{"class":1160},[1073,6462,6399],{"class":1122},[1073,6464,6402],{"class":1122},[1073,6466,6467],{"class":1122}," visualize",[1073,6469,6470],{"class":1122}," audio.mp3",[1073,6472,6473],{"class":1122}," --als",[1073,6475,6476],{"class":1122}," project.als",[1073,6478,6479],{"class":1122}," -o",[1073,6481,6482],{"class":1122}," waveform.png\n",[1051,6484,6486],{"id":6485},"export-for-web","Export for Web",[1063,6488,6491],{"className":2443,"code":6489,"filename":6490,"language":2445,"meta":1069,"style":1069},"poetry run mtl-ableton-analyser export audio.mp3 --als project.als -o waveform.json\n","WaveSurfer JSON Export",[798,6492,6493],{"__ignoreMap":1069},[1073,6494,6495,6497,6499,6501,6504,6506,6508,6510,6512],{"class":1075,"line":1076},[1073,6496,6379],{"class":1160},[1073,6498,6399],{"class":1122},[1073,6500,6402],{"class":1122},[1073,6502,6503],{"class":1122}," export",[1073,6505,6470],{"class":1122},[1073,6507,6473],{"class":1122},[1073,6509,6476],{"class":1122},[1073,6511,6479],{"class":1122},[1073,6513,6514],{"class":1122}," waveform.json\n",[776,6516,6518],{"id":6517},"capabilities-summary","Capabilities Summary",[893,6520,6521,6534],{},[896,6522,6523],{},[899,6524,6525,6528,6530,6532],{},[902,6526,6527],{},"Capability",[902,6529,5028],{},[902,6531,5032],{},[902,6533,5489],{},[909,6535,6536,6550,6562,6574,6585,6597,6609],{},[899,6537,6538,6541,6544,6547],{},[914,6539,6540],{},"Extract BPM",[914,6542,6543],{},"✅ Exact",[914,6545,6546],{},"✅ Detected",[914,6548,6549],{},".als is authoritative",[899,6551,6552,6554,6557,6559],{},[914,6553,5163],{},[914,6555,6556],{},"✅",[914,6558,6556],{},[914,6560,6561],{},"Both reliable",[899,6563,6564,6566,6568,6571],{},[914,6565,5193],{},[914,6567,6556],{},[914,6569,6570],{},"❌",[914,6572,6573],{},"Only in project files",[899,6575,6576,6578,6580,6582],{},[914,6577,5209],{},[914,6579,6556],{},[914,6581,6570],{},[914,6583,6584],{},"Beat-to-time mapping",[899,6586,6587,6590,6592,6594],{},[914,6588,6589],{},"Section Markers",[914,6591,6556],{},[914,6593,6570],{},[914,6595,6596],{},"From locators",[899,6598,6599,6602,6604,6606],{},[914,6600,6601],{},"Waveform Presence",[914,6603,6570],{},[914,6605,6556],{},[914,6607,6608],{},"Cache indicator",[899,6610,6611,6614,6616,6618],{},[914,6612,6613],{},"File Size",[914,6615,6556],{},[914,6617,6556],{},[914,6619,6620],{},"Basic metadata",[776,6622,6624],{"id":6623},"limitations","Limitations",[749,6626,6627],{},"While we can extract a lot, some data remains inaccessible:",[2632,6629,6630,6636,6644,6650],{},[851,6631,6632,6635],{},[753,6633,6634],{},"Detailed onset data"," – Ableton stores transient detection but in an undocumented format",[851,6637,6638,6641,6642],{},[753,6639,6640],{},"Embedded waveform peaks"," – Detected but not directly extractable from ",[798,6643,5032],{},[851,6645,6646,6649],{},[753,6647,6648],{},"Plugin states"," – Complex binary data within the XML",[851,6651,6652,6655],{},[753,6653,6654],{},"Write capability"," – This is a read-only tool",[776,6657,6659],{"id":6658},"whats-next","What's Next?",[749,6661,6662],{},"This tool is part of our broader work on music analysis at MusicTech Lab. Combined with our other Ableton integrations, you can build powerful workflows:",[848,6664,6665,6671,6677,6683],{},[851,6666,6667,6670],{},[753,6668,6669],{},"Create section markers in Ableton"," → Export with our Max for Live device",[851,6672,6673,6676],{},[753,6674,6675],{},"Analyze files programmatically"," → Use MTL Ableton Analyser",[851,6678,6679,6682],{},[753,6680,6681],{},"Visualize in web applications"," → WaveSurfer.js integration",[851,6684,6685,6688],{},[753,6686,6687],{},"Enhance with AI"," → Feed structure data to ML models",[773,6690],{},[776,6692,2695],{"id":2694},[2632,6694,6695,6700,6705,6711],{},[851,6696,6697,6699],{},[783,6698,2712],{"href":475}," – Create and export section markers directly from Ableton",[851,6701,6702,6704],{},[783,6703,422],{"href":423}," – Send Ableton data to web services",[851,6706,6707,6710],{},[783,6708,6709],{"href":101},"Automatic Song Structure Analysis – How AI Detects Intro, Verse, and Chorus"," – AI-powered section detection",[851,6712,6713,6716],{},[783,6714,6715],{"href":407},"Building a DIY MIDI Controller for Ableton Live with Arduino"," – Hardware integration with Ableton",[773,6718],{},[1940,6720,6721],{},[749,6722,6723,6726,6727,6732],{},[753,6724,6725],{},"Open Source:"," MTL Ableton Analyser is available on ",[783,6728,6731],{"href":6729,"rel":6730},"https://github.com/musictechlab/mtl-ableton-analyser",[787],"GitHub",". Contributions welcome!",[1833,6734,6735],{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":1069,"searchDepth":1129,"depth":1129,"links":6737},[6738,6739,6744,6748,6753,6758,6759,6760,6761],{"id":5039,"depth":1129,"text":5040},{"id":5113,"depth":1129,"text":5114,"children":6740},[6741,6742,6743],{"id":5123,"depth":1136,"text":5124},{"id":5208,"depth":1136,"text":5209},{"id":5232,"depth":1136,"text":5233},{"id":5465,"depth":1129,"text":5466,"children":6745},[6746,6747],{"id":5474,"depth":1136,"text":5475},{"id":5544,"depth":1136,"text":5545},{"id":5831,"depth":1129,"text":5832,"children":6749},[6750,6751,6752],{"id":5835,"depth":1136,"text":5836},{"id":2194,"depth":1136,"text":2195},{"id":6308,"depth":1136,"text":6309},{"id":6345,"depth":1129,"text":6346,"children":6754},[6755,6756,6757],{"id":6385,"depth":1136,"text":6386},{"id":6449,"depth":1136,"text":6450},{"id":6485,"depth":1136,"text":6486},{"id":6517,"depth":1129,"text":6518},{"id":6623,"depth":1129,"text":6624},{"id":6658,"depth":1129,"text":6659},{"id":2694,"depth":1129,"text":2695},"2026-01-31T00:00:00.000Z","A deep dive into parsing Ableton Live project files. Learn what metadata hides inside .als and .asd files and how to extract it with Python.",{"src":6765},"/images/blog/musictechlab_blog_ableton_als_asd_extraction.webp",{"enabled":1132,"items":6767},[6768,6771,6774,6777],{"text":6769,"icon":6770},"Ableton .als files are gzip-compressed XML, making them straightforward to parse with Python.","i-lucide-file-code",{"text":6772,"icon":6773},"Locators from .als files provide a complete song structure map with precise beat positions.","i-lucide-music",{"text":6775,"icon":6776},"BPM in .asd files is auto-detected and may differ from the authoritative .als project tempo.","i-lucide-timer",{"text":6778,"icon":6779},"Output exports to JSON compatible with WaveSurfer.js for interactive web-based visualization.","i-lucide-bar-chart-3",{},{"title":140,"description":6763},[6783,2761,6784,2762],"music-production","daw","sP2M7DpVovlE_0fKXV3tAiegedrHDDXqdc2vqO4Pqh8",{"id":6787,"title":100,"authors":6788,"badge":6791,"body":6792,"category":2736,"client":2737,"date":10150,"description":10151,"extension":2740,"faq":2737,"featured":69,"featuredOrder":2737,"hidden":69,"image":10152,"keyTakeaways":10153,"meta":10165,"navigation":1132,"path":101,"seo":10166,"status":2737,"stem":102,"tags":10167,"teaser":2737,"__hash__":10168,"score":1182},"posts/blog/music-data/automatic-song-structure-analysis-how-ai-detects-intro-verse-chorus.md",[6789],{"name":738,"to":739,"avatar":6790},{"src":741},{"label":5016,"color":744},{"type":746,"value":6793,"toc":10112},[6794,6805,6808,6812,6815,6879,6882,6886,6889,6963,6967,6974,6993,7120,7128,7132,7139,7227,7231,7234,7277,7280,7294,7298,7301,7430,7433,7437,7440,7801,7810,7814,7817,7845,7849,7866,7888,7891,7897,7901,7904,7919,7922,8011,8014,8053,8056,8152,8156,8405,8409,8412,8556,8559,8591,8593,8597,8600,8897,8901,8904,8921,8925,8928,8946,8950,8953,9022,9025,9043,9047,9051,9054,9060,9115,9125,9166,9170,9173,9340,9450,9454,9539,9551,9553,9556,9593,9595,9599,9602,9606,9700,9704,9768,9772,9779,9847,9859,9863,9866,9886,9889,9893,9900,9966,9971,10018,10021,10025,10074,10076,10078,10097,10099,10109],[749,6795,6796,6797,6800,6801,6804],{},"When working with music at scale – whether for DJ software, music education, or content creation – one of the most valuable pieces of metadata is the ",[753,6798,6799],{},"song structure",". Where does the chorus start? How long is the intro? When does the bridge appear? Traditionally, this information required manual tagging or expensive commercial solutions. With ",[753,6802,6803],{},"MTL Audio Locators",", we've built an open-source tool that detects song sections automatically using AI and signal processing.",[749,6806,6807],{},"In this article, we'll explore how automatic structure analysis works and how you can use it in your projects.",[776,6809,6811],{"id":6810},"the-challenge-why-song-structure-matters","The Challenge: Why Song Structure Matters",[749,6813,6814],{},"Song structure – the arrangement of sections like INTRO, VERSE, CHORUS, BRIDGE, and OUTRO – is fundamental to how we experience music. For technical applications, this data enables:",[893,6816,6817,6827],{},[896,6818,6819],{},[899,6820,6821,6824],{},[902,6822,6823],{},"Use Case",[902,6825,6826],{},"Application",[909,6828,6829,6839,6849,6859,6869],{},[899,6830,6831,6836],{},[914,6832,6833],{},[753,6834,6835],{},"DJ Software",[914,6837,6838],{},"Auto-sync to chorus, smart mixing",[899,6840,6841,6846],{},[914,6842,6843],{},[753,6844,6845],{},"Music Education",[914,6847,6848],{},"Visual learning aids, section practice",[899,6850,6851,6856],{},[914,6852,6853],{},[753,6854,6855],{},"Content Creation",[914,6857,6858],{},"Quick navigation, highlight extraction",[899,6860,6861,6866],{},[914,6862,6863],{},[753,6864,6865],{},"DAW Integration",[914,6867,6868],{},"Import markers for editing",[899,6870,6871,6876],{},[914,6872,6873],{},[753,6874,6875],{},"Recommendation Systems",[914,6877,6878],{},"Structure-aware similarity matching",[749,6880,6881],{},"The challenge is that this information rarely exists in metadata. Even when producers add markers in their DAW, these don't export with the audio file.",[776,6883,6885],{"id":6884},"available-analysis-engines","Available Analysis Engines",[749,6887,6888],{},"MTL Audio Locators provides three analysis backends, automatically selecting the best available option:",[893,6890,6891,6907],{},[896,6892,6893],{},[899,6894,6895,6898,6900,6903,6905],{},[902,6896,6897],{},"Engine",[902,6899,5058],{},[902,6901,6902],{},"Accuracy",[902,6904,3121],{},[902,6906,6823],{},[909,6908,6909,6928,6945],{},[899,6910,6911,6916,6919,6922,6925],{},[914,6912,6913],{},[753,6914,6915],{},"allin1",[914,6917,6918],{},"Deep Learning",[914,6920,6921],{},"High",[914,6923,6924],{},"Slower",[914,6926,6927],{},"Production-quality analysis",[899,6929,6930,6935,6938,6940,6942],{},[914,6931,6932],{},[753,6933,6934],{},"MSAF",[914,6936,6937],{},"Traditional MIR",[914,6939,3153],{},[914,6941,3153],{},[914,6943,6944],{},"Research, comparison",[899,6946,6947,6952,6955,6958,6960],{},[914,6948,6949],{},[753,6950,6951],{},"librosa",[914,6953,6954],{},"Spectral",[914,6956,6957],{},"Basic",[914,6959,3156],{},[914,6961,6962],{},"Fallback, quick scans",[1051,6964,6966],{"id":6965},"allin1-ai-powered-analysis","allin1 – AI-Powered Analysis",[749,6968,1579,6969,6973],{},[783,6970,6915],{"href":6971,"rel":6972},"https://github.com/mir-aidj/all-in-one",[787]," model represents the state-of-the-art in music structure analysis. It's a deep learning model trained specifically on song segmentation tasks. The analysis pipeline:",[848,6975,6976,6981,6987],{},[851,6977,6978,6980],{},[753,6979,2662],{}," – Isolates vocals, drums, bass, and other instruments using Demucs",[851,6982,6983,6986],{},[753,6984,6985],{},"Spectrogram extraction"," – Converts each stem to time-frequency representation",[851,6988,6989,6992],{},[753,6990,6991],{},"Structure prediction"," – Neural network predicts section boundaries and labels",[1063,6994,6996],{"className":3359,"code":6995,"language":3361,"meta":1069,"style":1069},"# Using allin1 directly\nimport allin1\n\nresult = allin1.analyze(\"track.mp3\")\nprint(f\"BPM: {result.bpm}\")\nfor segment in result.segments:\n    print(f\"{segment.start:.2f}s - {segment.label}\")\n",[798,6997,6998,7003,7010,7014,7038,7063,7079],{"__ignoreMap":1069},[1073,6999,7000],{"class":1075,"line":1076},[1073,7001,7002],{"class":1408},"# Using allin1 directly\n",[1073,7004,7005,7007],{"class":1075,"line":1129},[1073,7006,1080],{"class":1079},[1073,7008,7009],{"class":1087}," allin1\n",[1073,7011,7012],{"class":1075,"line":1136},[1073,7013,1133],{"emptyLinePlaceholder":1132},[1073,7015,7016,7018,7020,7023,7025,7028,7030,7032,7034,7036],{"class":1075,"line":1182},[1073,7017,4271],{"class":1087},[1073,7019,1980],{"class":1083},[1073,7021,7022],{"class":1087}," allin1",[1073,7024,764],{"class":1083},[1073,7026,7027],{"class":1146},"analyze",[1073,7029,1150],{"class":1083},[1073,7031,2283],{"class":1083},[1073,7033,5927],{"class":1122},[1073,7035,2283],{"class":1083},[1073,7037,1202],{"class":1083},[1073,7039,7040,7042,7044,7046,7049,7051,7053,7055,7057,7059,7061],{"class":1075,"line":1205},[1073,7041,4541],{"class":1146},[1073,7043,1150],{"class":1083},[1073,7045,3628],{"class":1139},[1073,7047,7048],{"class":1122},"\"BPM: ",[1073,7050,3634],{"class":1466},[1073,7052,2040],{"class":1146},[1073,7054,764],{"class":1083},[1073,7056,5970],{"class":1196},[1073,7058,1229],{"class":1466},[1073,7060,2283],{"class":1122},[1073,7062,1202],{"class":1083},[1073,7064,7065,7067,7069,7071,7073,7075,7077],{"class":1075,"line":1238},[1073,7066,4316],{"class":1079},[1073,7068,4319],{"class":1087},[1073,7070,4322],{"class":1079},[1073,7072,1669],{"class":1087},[1073,7074,764],{"class":1083},[1073,7076,4331],{"class":1196},[1073,7078,3731],{"class":1083},[1073,7080,7081,7083,7085,7087,7089,7091,7094,7096,7098,7101,7103,7105,7107,7109,7111,7114,7116,7118],{"class":1075,"line":1274},[1073,7082,3623],{"class":1146},[1073,7084,1150],{"class":1083},[1073,7086,3628],{"class":1139},[1073,7088,2283],{"class":1122},[1073,7090,3634],{"class":1466},[1073,7092,7093],{"class":1146},"segment",[1073,7095,764],{"class":1083},[1073,7097,4353],{"class":1196},[1073,7099,7100],{"class":1139},":.2f",[1073,7102,1229],{"class":1466},[1073,7104,4424],{"class":1122},[1073,7106,3634],{"class":1466},[1073,7108,7093],{"class":1146},[1073,7110,764],{"class":1083},[1073,7112,7113],{"class":1196},"label",[1073,7115,1229],{"class":1466},[1073,7117,2283],{"class":1122},[1073,7119,1202],{"class":1083},[822,7121,7122],{},[749,7123,7124,7127],{},[753,7125,7126],{},"First Run:"," allin1 downloads ~1.5GB of models on first use (Demucs for demixing + Harmonix for structure).",[1051,7129,7131],{"id":7130},"msaf-music-structure-analysis-framework","MSAF – Music Structure Analysis Framework",[749,7133,7134,7138],{},[783,7135,6934],{"href":7136,"rel":7137},"https://github.com/urinieto/msaf",[787]," provides traditional Music Information Retrieval (MIR) algorithms. We use spectral flux for boundary detection combined with Fourier Magnitude Coefficients for labeling:",[1063,7140,7142],{"className":3359,"code":7141,"language":3361,"meta":1069,"style":1069},"import msaf\n\nboundaries, labels = msaf.process(\n    \"track.mp3\",\n    boundaries_id='sf',   # Spectral flux\n    labels_id='fmc2d'     # 2D Fourier Magnitude Coefficients\n)\n",[798,7143,7144,7151,7155,7177,7187,7206,7223],{"__ignoreMap":1069},[1073,7145,7146,7148],{"class":1075,"line":1076},[1073,7147,1080],{"class":1079},[1073,7149,7150],{"class":1087}," msaf\n",[1073,7152,7153],{"class":1075,"line":1129},[1073,7154,1133],{"emptyLinePlaceholder":1132},[1073,7156,7157,7160,7162,7165,7167,7170,7172,7175],{"class":1075,"line":1136},[1073,7158,7159],{"class":1087},"boundaries",[1073,7161,1091],{"class":1083},[1073,7163,7164],{"class":1087}," labels ",[1073,7166,1980],{"class":1083},[1073,7168,7169],{"class":1087}," msaf",[1073,7171,764],{"class":1083},[1073,7173,7174],{"class":1146},"process",[1073,7176,1433],{"class":1083},[1073,7178,7179,7181,7183,7185],{"class":1075,"line":1182},[1073,7180,2292],{"class":1083},[1073,7182,5927],{"class":1122},[1073,7184,2283],{"class":1083},[1073,7186,1321],{"class":1083},[1073,7188,7189,7192,7194,7196,7199,7201,7203],{"class":1075,"line":1205},[1073,7190,7191],{"class":1153},"    boundaries_id",[1073,7193,1980],{"class":1083},[1073,7195,1318],{"class":1083},[1073,7197,7198],{"class":1122},"sf",[1073,7200,1318],{"class":1083},[1073,7202,1091],{"class":1083},[1073,7204,7205],{"class":1408},"   # Spectral flux\n",[1073,7207,7208,7211,7213,7215,7218,7220],{"class":1075,"line":1238},[1073,7209,7210],{"class":1153},"    labels_id",[1073,7212,1980],{"class":1083},[1073,7214,1318],{"class":1083},[1073,7216,7217],{"class":1122},"fmc2d",[1073,7219,1318],{"class":1083},[1073,7221,7222],{"class":1408},"     # 2D Fourier Magnitude Coefficients\n",[1073,7224,7225],{"class":1075,"line":1274},[1073,7226,1202],{"class":1083},[1051,7228,7230],{"id":7229},"librosa-spectral-fallback","librosa – Spectral Fallback",[749,7232,7233],{},"When neither allin1 nor MSAF is available, we use a custom algorithm based on librosa that combines multiple features:",[893,7235,7236,7245],{},[896,7237,7238],{},[899,7239,7240,7243],{},[902,7241,7242],{},"Feature",[902,7244,5061],{},[909,7246,7247,7257,7267],{},[899,7248,7249,7254],{},[914,7250,7251],{},[753,7252,7253],{},"Chroma CQT",[914,7255,7256],{},"Harmonic content (chord changes)",[899,7258,7259,7264],{},[914,7260,7261],{},[753,7262,7263],{},"MFCC",[914,7265,7266],{},"Timbral characteristics (texture changes)",[899,7268,7269,7274],{},[914,7270,7271],{},[753,7272,7273],{},"Spectral Contrast",[914,7275,7276],{},"Brightness/darkness patterns",[749,7278,7279],{},"The algorithm:",[848,7281,7282,7285,7288,7291],{},[851,7283,7284],{},"Extracts all features and stacks them",[851,7286,7287],{},"Uses agglomerative clustering to find segment boundaries",[851,7289,7290],{},"Snaps boundaries to nearest beats for musical alignment",[851,7292,7293],{},"Labels sections using position heuristics and K-means clustering",[776,7295,7297],{"id":7296},"section-labeling-convention","Section Labeling Convention",[749,7299,7300],{},"All backends output sections with a consistent naming convention designed for DAW compatibility:",[893,7302,7303,7319],{},[896,7304,7305],{},[899,7306,7307,7310,7313,7316],{},[902,7308,7309],{},"Section",[902,7311,7312],{},"Prefix",[902,7314,7315],{},"Color",[902,7317,7318],{},"Example",[909,7320,7321,7334,7348,7362,7376,7390,7403,7417],{},[899,7322,7323,7326,7329,7332],{},[914,7324,7325],{},"Intro",[914,7327,7328],{},"INT",[914,7330,7331],{},"#808080",[914,7333,5281],{},[899,7335,7336,7339,7342,7345],{},[914,7337,7338],{},"Verse",[914,7340,7341],{},"VRS",[914,7343,7344],{},"#4CAF50",[914,7346,7347],{},"VRS1, VRS2",[899,7349,7350,7353,7356,7359],{},[914,7351,7352],{},"Pre-Chorus",[914,7354,7355],{},"PRE",[914,7357,7358],{},"#8BC34A",[914,7360,7361],{},"PRE1",[899,7363,7364,7367,7370,7373],{},[914,7365,7366],{},"Chorus",[914,7368,7369],{},"CHO",[914,7371,7372],{},"#FF5722",[914,7374,7375],{},"CHO1, CHO2",[899,7377,7378,7381,7384,7387],{},[914,7379,7380],{},"Bridge",[914,7382,7383],{},"BRG",[914,7385,7386],{},"#00BCD4",[914,7388,7389],{},"BRG1",[899,7391,7392,7395,7398,7401],{},[914,7393,7394],{},"Breakdown",[914,7396,7397],{},"BRD",[914,7399,7400],{},"#2196F3",[914,7402,5385],{},[899,7404,7405,7408,7411,7414],{},[914,7406,7407],{},"Build",[914,7409,7410],{},"BUI",[914,7412,7413],{},"#9C27B0",[914,7415,7416],{},"BUI1",[899,7418,7419,7422,7425,7428],{},[914,7420,7421],{},"Outro",[914,7423,7424],{},"OUT",[914,7426,7427],{},"#607D8B",[914,7429,5419],{},[749,7431,7432],{},"The numbered suffix (VRS1, VRS2) indicates occurrence order – useful for identifying repeating sections.",[776,7434,7436],{"id":7435},"output-format","Output Format",[749,7438,7439],{},"Analysis results follow a JSON schema designed for easy integration:",[1063,7441,7443],{"className":2264,"code":7442,"language":1904,"meta":1069,"style":1069},"{\n  \"track_id\": \"my_song\",\n  \"bpm\": 128,\n  \"sections\": [\n    {\"label\": \"INT1\", \"time_s\": 0.0, \"color\": \"#808080\"},\n    {\"label\": \"VRS1\", \"time_s\": 15.5, \"color\": \"#4CAF50\"},\n    {\"label\": \"CHO1\", \"time_s\": 45.2, \"color\": \"#FF5722\"},\n    {\"label\": \"VRS2\", \"time_s\": 75.0, \"color\": \"#4CAF50\"},\n    {\"label\": \"CHO2\", \"time_s\": 105.3, \"color\": \"#FF5722\"},\n    {\"label\": \"OUT1\", \"time_s\": 135.8, \"color\": \"#607D8B\"}\n  ]\n}\n",[798,7444,7445,7449,7469,7483,7495,7545,7595,7644,7694,7744,7793,7797],{"__ignoreMap":1069},[1073,7446,7447],{"class":1075,"line":1076},[1073,7448,1790],{"class":1083},[1073,7450,7451,7453,7456,7458,7460,7462,7465,7467],{"class":1075,"line":1129},[1073,7452,2277],{"class":1083},[1073,7454,7455],{"class":1139},"track_id",[1073,7457,2283],{"class":1083},[1073,7459,1157],{"class":1083},[1073,7461,2301],{"class":1083},[1073,7463,7464],{"class":1122},"my_song",[1073,7466,2283],{"class":1083},[1073,7468,1321],{"class":1083},[1073,7470,7471,7473,7475,7477,7479,7481],{"class":1075,"line":1136},[1073,7472,2277],{"class":1083},[1073,7474,5970],{"class":1139},[1073,7476,2283],{"class":1083},[1073,7478,1157],{"class":1083},[1073,7480,5434],{"class":1466},[1073,7482,1321],{"class":1083},[1073,7484,7485,7487,7489,7491,7493],{"class":1075,"line":1182},[1073,7486,2277],{"class":1083},[1073,7488,5256],{"class":1139},[1073,7490,2283],{"class":1083},[1073,7492,1157],{"class":1083},[1073,7494,2336],{"class":1083},[1073,7496,7497,7499,7501,7503,7505,7507,7509,7511,7513,7515,7517,7520,7522,7524,7526,7528,7530,7532,7534,7536,7538,7540,7542],{"class":1075,"line":1205},[1073,7498,5267],{"class":1083},[1073,7500,2283],{"class":1083},[1073,7502,7113],{"class":1160},[1073,7504,2283],{"class":1083},[1073,7506,1157],{"class":1083},[1073,7508,2301],{"class":1083},[1073,7510,5281],{"class":1122},[1073,7512,2283],{"class":1083},[1073,7514,1091],{"class":1083},[1073,7516,2301],{"class":1083},[1073,7518,7519],{"class":1160},"time_s",[1073,7521,2283],{"class":1083},[1073,7523,1157],{"class":1083},[1073,7525,6072],{"class":1466},[1073,7527,1091],{"class":1083},[1073,7529,2301],{"class":1083},[1073,7531,6208],{"class":1160},[1073,7533,2283],{"class":1083},[1073,7535,1157],{"class":1083},[1073,7537,2301],{"class":1083},[1073,7539,7331],{"class":1122},[1073,7541,2283],{"class":1083},[1073,7543,7544],{"class":1083},"},\n",[1073,7546,7547,7549,7551,7553,7555,7557,7559,7562,7564,7566,7568,7570,7572,7574,7577,7579,7581,7583,7585,7587,7589,7591,7593],{"class":1075,"line":1238},[1073,7548,5267],{"class":1083},[1073,7550,2283],{"class":1083},[1073,7552,7113],{"class":1160},[1073,7554,2283],{"class":1083},[1073,7556,1157],{"class":1083},[1073,7558,2301],{"class":1083},[1073,7560,7561],{"class":1122},"VRS1",[1073,7563,2283],{"class":1083},[1073,7565,1091],{"class":1083},[1073,7567,2301],{"class":1083},[1073,7569,7519],{"class":1160},[1073,7571,2283],{"class":1083},[1073,7573,1157],{"class":1083},[1073,7575,7576],{"class":1466}," 15.5",[1073,7578,1091],{"class":1083},[1073,7580,2301],{"class":1083},[1073,7582,6208],{"class":1160},[1073,7584,2283],{"class":1083},[1073,7586,1157],{"class":1083},[1073,7588,2301],{"class":1083},[1073,7590,7344],{"class":1122},[1073,7592,2283],{"class":1083},[1073,7594,7544],{"class":1083},[1073,7596,7597,7599,7601,7603,7605,7607,7609,7611,7613,7615,7617,7619,7621,7623,7626,7628,7630,7632,7634,7636,7638,7640,7642],{"class":1075,"line":1274},[1073,7598,5267],{"class":1083},[1073,7600,2283],{"class":1083},[1073,7602,7113],{"class":1160},[1073,7604,2283],{"class":1083},[1073,7606,1157],{"class":1083},[1073,7608,2301],{"class":1083},[1073,7610,5351],{"class":1122},[1073,7612,2283],{"class":1083},[1073,7614,1091],{"class":1083},[1073,7616,2301],{"class":1083},[1073,7618,7519],{"class":1160},[1073,7620,2283],{"class":1083},[1073,7622,1157],{"class":1083},[1073,7624,7625],{"class":1466}," 45.2",[1073,7627,1091],{"class":1083},[1073,7629,2301],{"class":1083},[1073,7631,6208],{"class":1160},[1073,7633,2283],{"class":1083},[1073,7635,1157],{"class":1083},[1073,7637,2301],{"class":1083},[1073,7639,7372],{"class":1122},[1073,7641,2283],{"class":1083},[1073,7643,7544],{"class":1083},[1073,7645,7646,7648,7650,7652,7654,7656,7658,7661,7663,7665,7667,7669,7671,7673,7676,7678,7680,7682,7684,7686,7688,7690,7692],{"class":1075,"line":1279},[1073,7647,5267],{"class":1083},[1073,7649,2283],{"class":1083},[1073,7651,7113],{"class":1160},[1073,7653,2283],{"class":1083},[1073,7655,1157],{"class":1083},[1073,7657,2301],{"class":1083},[1073,7659,7660],{"class":1122},"VRS2",[1073,7662,2283],{"class":1083},[1073,7664,1091],{"class":1083},[1073,7666,2301],{"class":1083},[1073,7668,7519],{"class":1160},[1073,7670,2283],{"class":1083},[1073,7672,1157],{"class":1083},[1073,7674,7675],{"class":1466}," 75.0",[1073,7677,1091],{"class":1083},[1073,7679,2301],{"class":1083},[1073,7681,6208],{"class":1160},[1073,7683,2283],{"class":1083},[1073,7685,1157],{"class":1083},[1073,7687,2301],{"class":1083},[1073,7689,7344],{"class":1122},[1073,7691,2283],{"class":1083},[1073,7693,7544],{"class":1083},[1073,7695,7696,7698,7700,7702,7704,7706,7708,7711,7713,7715,7717,7719,7721,7723,7726,7728,7730,7732,7734,7736,7738,7740,7742],{"class":1075,"line":1305},[1073,7697,5267],{"class":1083},[1073,7699,2283],{"class":1083},[1073,7701,7113],{"class":1160},[1073,7703,2283],{"class":1083},[1073,7705,1157],{"class":1083},[1073,7707,2301],{"class":1083},[1073,7709,7710],{"class":1122},"CHO2",[1073,7712,2283],{"class":1083},[1073,7714,1091],{"class":1083},[1073,7716,2301],{"class":1083},[1073,7718,7519],{"class":1160},[1073,7720,2283],{"class":1083},[1073,7722,1157],{"class":1083},[1073,7724,7725],{"class":1466}," 105.3",[1073,7727,1091],{"class":1083},[1073,7729,2301],{"class":1083},[1073,7731,6208],{"class":1160},[1073,7733,2283],{"class":1083},[1073,7735,1157],{"class":1083},[1073,7737,2301],{"class":1083},[1073,7739,7372],{"class":1122},[1073,7741,2283],{"class":1083},[1073,7743,7544],{"class":1083},[1073,7745,7746,7748,7750,7752,7754,7756,7758,7760,7762,7764,7766,7768,7770,7772,7775,7777,7779,7781,7783,7785,7787,7789,7791],{"class":1075,"line":1324},[1073,7747,5267],{"class":1083},[1073,7749,2283],{"class":1083},[1073,7751,7113],{"class":1160},[1073,7753,2283],{"class":1083},[1073,7755,1157],{"class":1083},[1073,7757,2301],{"class":1083},[1073,7759,5419],{"class":1122},[1073,7761,2283],{"class":1083},[1073,7763,1091],{"class":1083},[1073,7765,2301],{"class":1083},[1073,7767,7519],{"class":1160},[1073,7769,2283],{"class":1083},[1073,7771,1157],{"class":1083},[1073,7773,7774],{"class":1466}," 135.8",[1073,7776,1091],{"class":1083},[1073,7778,2301],{"class":1083},[1073,7780,6208],{"class":1160},[1073,7782,2283],{"class":1083},[1073,7784,1157],{"class":1083},[1073,7786,2301],{"class":1083},[1073,7788,7427],{"class":1122},[1073,7790,2283],{"class":1083},[1073,7792,1576],{"class":1083},[1073,7794,7795],{"class":1075,"line":1332},[1073,7796,5441],{"class":1083},[1073,7798,7799],{"class":1075,"line":1337},[1073,7800,1576],{"class":1083},[5450,7802,7804],{"className":7803},[5453,5454,5455],[749,7805,7806],{},[2166,7807],{"alt":7808,"src":7809,"width":5462},"Structure Analysis Output","/images/blog/musictechlab_blog_song-structure-analysis.webp",[776,7811,7813],{"id":7812},"using-mtl-audio-locators","Using MTL Audio Locators",[1051,7815,6353],{"id":7816},"installation",[1063,7818,7821],{"className":2443,"code":7819,"filename":7820,"language":2445,"meta":1069,"style":1069},"git clone https://github.com/musictechlab/mtl-audiolocators\ncd mtl-audiolocators\npoetry install\n","Clone and Install",[798,7822,7823,7832,7839],{"__ignoreMap":1069},[1073,7824,7825,7827,7829],{"class":1075,"line":1076},[1073,7826,6360],{"class":1160},[1073,7828,6363],{"class":1122},[1073,7830,7831],{"class":1122}," https://github.com/musictechlab/mtl-audiolocators\n",[1073,7833,7834,7836],{"class":1075,"line":1129},[1073,7835,6371],{"class":1146},[1073,7837,7838],{"class":1122}," mtl-audiolocators\n",[1073,7840,7841,7843],{"class":1075,"line":1136},[1073,7842,6379],{"class":1160},[1073,7844,6382],{"class":1122},[1051,7846,7848],{"id":7847},"cli-usage","CLI Usage",[1063,7850,7853],{"className":2443,"code":7851,"filename":7852,"language":2445,"meta":1069,"style":1069},"poetry run analyze track.mp3\n","Analyze Single Track",[798,7854,7855],{"__ignoreMap":1069},[1073,7856,7857,7859,7861,7863],{"class":1075,"line":1076},[1073,7858,6379],{"class":1160},[1073,7860,6399],{"class":1122},[1073,7862,6405],{"class":1122},[1073,7864,7865],{"class":1122}," track.mp3\n",[1063,7867,7870],{"className":2443,"code":7868,"filename":7869,"language":2445,"meta":1069,"style":1069},"poetry run analyze track.mp3 -o structure.json\n","Custom Output Path",[798,7871,7872],{"__ignoreMap":1069},[1073,7873,7874,7876,7878,7880,7883,7885],{"class":1075,"line":1076},[1073,7875,6379],{"class":1160},[1073,7877,6399],{"class":1122},[1073,7879,6405],{"class":1122},[1073,7881,7882],{"class":1122}," track.mp3",[1073,7884,6479],{"class":1122},[1073,7886,7887],{"class":1122}," structure.json\n",[749,7889,7890],{},"Sample CLI output:",[1063,7892,7895],{"className":7893,"code":7894,"language":2435},[2433],"Analyzing: track.mp3\nUsing allin1 (AI-based analysis)\n\n========================================\nTrack: track\nBPM: 128\nSections: 8\n========================================\n\n  0:00.00  INT1\n  0:15.50  VRS1\n  0:45.20  CHO1\n  1:15.00  VRS2\n  1:45.30  CHO2\n  2:15.80  BRG1\n  2:45.00  CHO3\n  3:15.80  OUT1\n\n-> Import to REAPER: Run import_structure_markers.lua\n   and select: track_structure.json\n",[798,7896,7894],{"__ignoreMap":1069},[1051,7898,7900],{"id":7899},"rest-api","REST API",[749,7902,7903],{},"For integration into web applications, MTL Audio Locators includes a FastAPI server:",[1063,7905,7908],{"className":2443,"code":7906,"filename":7907,"language":2445,"meta":1069,"style":1069},"poetry run serve\n","Start Server",[798,7909,7910],{"__ignoreMap":1069},[1073,7911,7912,7914,7916],{"class":1075,"line":1076},[1073,7913,6379],{"class":1160},[1073,7915,6399],{"class":1122},[1073,7917,7918],{"class":1122}," serve\n",[749,7920,7921],{},"Available endpoints:",[893,7923,7924,7936],{},[896,7925,7926],{},[899,7927,7928,7931,7934],{},[902,7929,7930],{},"Endpoint",[902,7932,7933],{},"Method",[902,7935,5136],{},[909,7937,7938,7950,7962,7974,7987,7999],{},[899,7939,7940,7945,7947],{},[914,7941,7942],{},[798,7943,7944],{},"/analyze",[914,7946,1701],{},[914,7948,7949],{},"Full structure analysis",[899,7951,7952,7957,7959],{},[914,7953,7954],{},[798,7955,7956],{},"/analyze/features",[914,7958,1701],{},[914,7960,7961],{},"Audio features only (BPM, energy)",[899,7963,7964,7969,7971],{},[914,7965,7966],{},[798,7967,7968],{},"/waveform",[914,7970,1701],{},[914,7972,7973],{},"Waveform data for visualization",[899,7975,7976,7981,7984],{},[914,7977,7978],{},[798,7979,7980],{},"/engines",[914,7982,7983],{},"GET",[914,7985,7986],{},"List available backends",[899,7988,7989,7994,7996],{},[914,7990,7991],{},[798,7992,7993],{},"/logs/stream",[914,7995,7983],{},[914,7997,7998],{},"SSE stream for progress updates",[899,8000,8001,8006,8008],{},[914,8002,8003],{},[798,8004,8005],{},"/health",[914,8007,7983],{},[914,8009,8010],{},"Service health check",[749,8012,8013],{},"Example API call:",[1063,8015,8018],{"className":2443,"code":8016,"filename":8017,"language":2445,"meta":1069,"style":1069},"curl -X POST \"http://localhost:8000/analyze\" \\\n  -F \"file=@track.mp3\"\n","cURL",[798,8019,8020,8041],{"__ignoreMap":1069},[1073,8021,8022,8025,8028,8031,8033,8036,8038],{"class":1075,"line":1076},[1073,8023,8024],{"class":1160},"curl",[1073,8026,8027],{"class":1122}," -X",[1073,8029,8030],{"class":1122}," POST",[1073,8032,2301],{"class":1083},[1073,8034,8035],{"class":1122},"http://localhost:8000/analyze",[1073,8037,2283],{"class":1083},[1073,8039,8040],{"class":1087}," \\\n",[1073,8042,8043,8046,8048,8051],{"class":1075,"line":1129},[1073,8044,8045],{"class":1122},"  -F",[1073,8047,2301],{"class":1083},[1073,8049,8050],{"class":1122},"file=@track.mp3",[1073,8052,2418],{"class":1083},[749,8054,8055],{},"Response:",[1063,8057,8059],{"className":2264,"code":8058,"language":1904,"meta":1069,"style":1069},"{\n  \"track_id\": \"track\",\n  \"bpm\": 128,\n  \"duration_s\": 210.5,\n  \"sections\": [...],\n  \"analysis_method\": \"allin1\"\n}\n",[798,8060,8061,8065,8084,8098,8114,8131,8148],{"__ignoreMap":1069},[1073,8062,8063],{"class":1075,"line":1076},[1073,8064,1790],{"class":1083},[1073,8066,8067,8069,8071,8073,8075,8077,8080,8082],{"class":1075,"line":1129},[1073,8068,2277],{"class":1083},[1073,8070,7455],{"class":1139},[1073,8072,2283],{"class":1083},[1073,8074,1157],{"class":1083},[1073,8076,2301],{"class":1083},[1073,8078,8079],{"class":1122},"track",[1073,8081,2283],{"class":1083},[1073,8083,1321],{"class":1083},[1073,8085,8086,8088,8090,8092,8094,8096],{"class":1075,"line":1136},[1073,8087,2277],{"class":1083},[1073,8089,5970],{"class":1139},[1073,8091,2283],{"class":1083},[1073,8093,1157],{"class":1083},[1073,8095,5434],{"class":1466},[1073,8097,1321],{"class":1083},[1073,8099,8100,8102,8105,8107,8109,8112],{"class":1075,"line":1182},[1073,8101,2277],{"class":1083},[1073,8103,8104],{"class":1139},"duration_s",[1073,8106,2283],{"class":1083},[1073,8108,1157],{"class":1083},[1073,8110,8111],{"class":1466}," 210.5",[1073,8113,1321],{"class":1083},[1073,8115,8116,8118,8120,8122,8124,8126,8129],{"class":1075,"line":1205},[1073,8117,2277],{"class":1083},[1073,8119,5256],{"class":1139},[1073,8121,2283],{"class":1083},[1073,8123,1157],{"class":1083},[1073,8125,5566],{"class":1083},[1073,8127,8128],{"class":1087},"...",[1073,8130,6043],{"class":1083},[1073,8132,8133,8135,8138,8140,8142,8144,8146],{"class":1075,"line":1238},[1073,8134,2277],{"class":1083},[1073,8136,8137],{"class":1139},"analysis_method",[1073,8139,2283],{"class":1083},[1073,8141,1157],{"class":1083},[1073,8143,2301],{"class":1083},[1073,8145,6915],{"class":1122},[1073,8147,2418],{"class":1083},[1073,8149,8150],{"class":1075,"line":1274},[1073,8151,1576],{"class":1083},[1051,8153,8155],{"id":8154},"python-api","Python API",[1063,8157,8159],{"className":3359,"code":8158,"language":3361,"meta":1069,"style":1069},"from audiolocators import analyze_track, extract_audio_features\n\n# Full structure analysis\nresult = analyze_track(\"track.mp3\")\nprint(f\"BPM: {result['bpm']}\")\nfor section in result['sections']:\n    print(f\"{section['time_s']}s - {section['label']}\")\n\n# Features only (faster)\nfeatures = extract_audio_features(\"track.mp3\")\nprint(f\"Duration: {features['duration_s']}s\")\nprint(f\"Energy segments: {len(features['energy_segments'])}\")\n",[798,8160,8161,8178,8182,8187,8205,8235,8256,8305,8309,8314,8334,8367],{"__ignoreMap":1069},[1073,8162,8163,8165,8168,8170,8173,8175],{"class":1075,"line":1076},[1073,8164,3410],{"class":1079},[1073,8166,8167],{"class":1087}," audiolocators ",[1073,8169,1080],{"class":1079},[1073,8171,8172],{"class":1087}," analyze_track",[1073,8174,1091],{"class":1083},[1073,8176,8177],{"class":1087}," extract_audio_features\n",[1073,8179,8180],{"class":1075,"line":1129},[1073,8181,1133],{"emptyLinePlaceholder":1132},[1073,8183,8184],{"class":1075,"line":1136},[1073,8185,8186],{"class":1408},"# Full structure analysis\n",[1073,8188,8189,8191,8193,8195,8197,8199,8201,8203],{"class":1075,"line":1182},[1073,8190,4271],{"class":1087},[1073,8192,1980],{"class":1083},[1073,8194,8172],{"class":1146},[1073,8196,1150],{"class":1083},[1073,8198,2283],{"class":1083},[1073,8200,5927],{"class":1122},[1073,8202,2283],{"class":1083},[1073,8204,1202],{"class":1083},[1073,8206,8207,8209,8211,8213,8215,8217,8219,8221,8223,8225,8227,8229,8231,8233],{"class":1075,"line":1205},[1073,8208,4541],{"class":1146},[1073,8210,1150],{"class":1083},[1073,8212,3628],{"class":1139},[1073,8214,7048],{"class":1122},[1073,8216,3634],{"class":1466},[1073,8218,2040],{"class":1146},[1073,8220,3858],{"class":1083},[1073,8222,1318],{"class":1083},[1073,8224,5970],{"class":1122},[1073,8226,1318],{"class":1083},[1073,8228,4159],{"class":1083},[1073,8230,1229],{"class":1466},[1073,8232,2283],{"class":1122},[1073,8234,1202],{"class":1083},[1073,8236,8237,8239,8242,8244,8246,8248,8250,8252,8254],{"class":1075,"line":1238},[1073,8238,4316],{"class":1079},[1073,8240,8241],{"class":1087}," section ",[1073,8243,4322],{"class":1079},[1073,8245,1669],{"class":1087},[1073,8247,3858],{"class":1083},[1073,8249,1318],{"class":1083},[1073,8251,5256],{"class":1122},[1073,8253,1318],{"class":1083},[1073,8255,4336],{"class":1083},[1073,8257,8258,8260,8262,8264,8266,8268,8271,8273,8275,8277,8279,8281,8283,8285,8287,8289,8291,8293,8295,8297,8299,8301,8303],{"class":1075,"line":1274},[1073,8259,3623],{"class":1146},[1073,8261,1150],{"class":1083},[1073,8263,3628],{"class":1139},[1073,8265,2283],{"class":1122},[1073,8267,3634],{"class":1466},[1073,8269,8270],{"class":1146},"section",[1073,8272,3858],{"class":1083},[1073,8274,1318],{"class":1083},[1073,8276,7519],{"class":1122},[1073,8278,1318],{"class":1083},[1073,8280,4159],{"class":1083},[1073,8282,1229],{"class":1466},[1073,8284,4424],{"class":1122},[1073,8286,3634],{"class":1466},[1073,8288,8270],{"class":1146},[1073,8290,3858],{"class":1083},[1073,8292,1318],{"class":1083},[1073,8294,7113],{"class":1122},[1073,8296,1318],{"class":1083},[1073,8298,4159],{"class":1083},[1073,8300,1229],{"class":1466},[1073,8302,2283],{"class":1122},[1073,8304,1202],{"class":1083},[1073,8306,8307],{"class":1075,"line":1279},[1073,8308,1133],{"emptyLinePlaceholder":1132},[1073,8310,8311],{"class":1075,"line":1305},[1073,8312,8313],{"class":1408},"# Features only (faster)\n",[1073,8315,8316,8319,8321,8324,8326,8328,8330,8332],{"class":1075,"line":1324},[1073,8317,8318],{"class":1087},"features ",[1073,8320,1980],{"class":1083},[1073,8322,8323],{"class":1146}," extract_audio_features",[1073,8325,1150],{"class":1083},[1073,8327,2283],{"class":1083},[1073,8329,5927],{"class":1122},[1073,8331,2283],{"class":1083},[1073,8333,1202],{"class":1083},[1073,8335,8336,8338,8340,8342,8345,8347,8350,8352,8354,8356,8358,8360,8362,8365],{"class":1075,"line":1332},[1073,8337,4541],{"class":1146},[1073,8339,1150],{"class":1083},[1073,8341,3628],{"class":1139},[1073,8343,8344],{"class":1122},"\"Duration: ",[1073,8346,3634],{"class":1466},[1073,8348,8349],{"class":1146},"features",[1073,8351,3858],{"class":1083},[1073,8353,1318],{"class":1083},[1073,8355,8104],{"class":1122},[1073,8357,1318],{"class":1083},[1073,8359,4159],{"class":1083},[1073,8361,1229],{"class":1466},[1073,8363,8364],{"class":1122},"s\"",[1073,8366,1202],{"class":1083},[1073,8368,8369,8371,8373,8375,8378,8380,8383,8385,8387,8389,8391,8394,8396,8399,8401,8403],{"class":1075,"line":1337},[1073,8370,4541],{"class":1146},[1073,8372,1150],{"class":1083},[1073,8374,3628],{"class":1139},[1073,8376,8377],{"class":1122},"\"Energy segments: ",[1073,8379,3634],{"class":1466},[1073,8381,8382],{"class":1146},"len",[1073,8384,1150],{"class":1083},[1073,8386,8349],{"class":1146},[1073,8388,3858],{"class":1083},[1073,8390,1318],{"class":1083},[1073,8392,8393],{"class":1122},"energy_segments",[1073,8395,1318],{"class":1083},[1073,8397,8398],{"class":1083},"])",[1073,8400,1229],{"class":1466},[1073,8402,2283],{"class":1122},[1073,8404,1202],{"class":1083},[776,8406,8408],{"id":8407},"real-time-progress-streaming","Real-Time Progress Streaming",[749,8410,8411],{},"For long-running analyses, the API provides Server-Sent Events (SSE) for real-time progress updates:",[1063,8413,8417],{"className":8414,"code":8415,"language":8416,"meta":1069,"style":1069},"language-javascript shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","const eventSource = new EventSource('http://localhost:8000/logs/stream');\n\neventSource.onmessage = (event) => {\n  const log = JSON.parse(event.data);\n  console.log(`[${log.source}] ${log.message}`);\n  // Example: [allin1] Separating stems (vocals, drums, bass, other)...\n};\n","javascript",[798,8418,8419,8448,8452,8474,8502,8546,8551],{"__ignoreMap":1069},[1073,8420,8421,8424,8427,8429,8431,8434,8436,8438,8441,8443,8445],{"class":1075,"line":1076},[1073,8422,8423],{"class":1139},"const",[1073,8425,8426],{"class":1087}," eventSource ",[1073,8428,1980],{"class":1083},[1073,8430,1343],{"class":1083},[1073,8432,8433],{"class":1146}," EventSource",[1073,8435,1150],{"class":1087},[1073,8437,1318],{"class":1083},[1073,8439,8440],{"class":1122},"http://localhost:8000/logs/stream",[1073,8442,1318],{"class":1083},[1073,8444,1360],{"class":1087},[1073,8446,8447],{"class":1083},";\n",[1073,8449,8450],{"class":1075,"line":1129},[1073,8451,1133],{"emptyLinePlaceholder":1132},[1073,8453,8454,8457,8459,8462,8464,8466,8468,8470,8472],{"class":1075,"line":1136},[1073,8455,8456],{"class":1087},"eventSource",[1073,8458,764],{"class":1083},[1073,8460,8461],{"class":1146},"onmessage",[1073,8463,1191],{"class":1083},[1073,8465,1614],{"class":1083},[1073,8467,1617],{"class":1153},[1073,8469,1360],{"class":1083},[1073,8471,1363],{"class":1139},[1073,8473,1179],{"class":1083},[1073,8475,8476,8478,8481,8483,8485,8487,8490,8492,8494,8496,8498,8500],{"class":1075,"line":1182},[1073,8477,1185],{"class":1139},[1073,8479,8480],{"class":1087}," log",[1073,8482,1191],{"class":1083},[1073,8484,1780],{"class":1087},[1073,8486,764],{"class":1083},[1073,8488,8489],{"class":1146},"parse",[1073,8491,1150],{"class":1196},[1073,8493,1617],{"class":1087},[1073,8495,764],{"class":1083},[1073,8497,1924],{"class":1087},[1073,8499,1360],{"class":1196},[1073,8501,8447],{"class":1083},[1073,8503,8504,8507,8509,8512,8514,8516,8518,8520,8522,8524,8526,8528,8531,8533,8535,8537,8540,8542,8544],{"class":1075,"line":1205},[1073,8505,8506],{"class":1087},"  console",[1073,8508,764],{"class":1083},[1073,8510,8511],{"class":1146},"log",[1073,8513,1150],{"class":1196},[1073,8515,2011],{"class":1083},[1073,8517,3858],{"class":1122},[1073,8519,1263],{"class":1083},[1073,8521,8511],{"class":1087},[1073,8523,764],{"class":1083},[1073,8525,2346],{"class":1087},[1073,8527,1229],{"class":1083},[1073,8529,8530],{"class":1122},"] ",[1073,8532,1263],{"class":1083},[1073,8534,8511],{"class":1087},[1073,8536,764],{"class":1083},[1073,8538,8539],{"class":1087},"message",[1073,8541,1269],{"class":1083},[1073,8543,1360],{"class":1196},[1073,8545,8447],{"class":1083},[1073,8547,8548],{"class":1075,"line":1238},[1073,8549,8550],{"class":1408},"  // Example: [allin1] Separating stems (vocals, drums, bass, other)...\n",[1073,8552,8553],{"class":1075,"line":1274},[1073,8554,8555],{"class":1083},"};\n",[749,8557,8558],{},"Progress stages for allin1 analysis:",[848,8560,8561,8567,8573,8579,8585],{},[851,8562,8563,8566],{},[753,8564,8565],{},"Starting"," – Pipeline initialization",[851,8568,8569,8572],{},[753,8570,8571],{},"Demixing"," – Stem separation (0-100%)",[851,8574,8575,8578],{},[753,8576,8577],{},"Spectrograms"," – Feature extraction",[851,8580,8581,8584],{},[753,8582,8583],{},"Analyzing"," – Neural network inference",[851,8586,8587,8590],{},[753,8588,8589],{},"Complete"," – Results ready",[776,8592,5832],{"id":5831},[1051,8594,8596],{"id":8595},"integration-with-wavesurferjs","Integration with WaveSurfer.js",[749,8598,8599],{},"Combine structure data with waveform visualization:",[1063,8601,8603],{"className":8414,"code":8602,"language":8416,"meta":1069,"style":1069},"import WaveSurfer from 'wavesurfer.js';\nimport RegionsPlugin from 'wavesurfer.js/dist/plugins/regions.js';\n\nconst wavesurfer = WaveSurfer.create({\n  container: '#waveform',\n  plugins: [RegionsPlugin.create()]\n});\n\n// After analysis\nstructure.sections.forEach((section, i) => {\n  const nextSection = structure.sections[i + 1];\n  wavesurfer.addRegion({\n    start: section.time_s,\n    end: nextSection ? nextSection.time_s : wavesurfer.getDuration(),\n    color: section.color + '40', // Add transparency\n    content: section.label\n  });\n});\n",[798,8604,8605,8623,8641,8645,8666,8682,8699,8707,8711,8716,8747,8777,8791,8807,8840,8867,8881,8889],{"__ignoreMap":1069},[1073,8606,8607,8609,8612,8614,8616,8619,8621],{"class":1075,"line":1076},[1073,8608,1080],{"class":1079},[1073,8610,8611],{"class":1087}," WaveSurfer ",[1073,8613,3410],{"class":1079},[1073,8615,1119],{"class":1083},[1073,8617,8618],{"class":1122},"wavesurfer.js",[1073,8620,1318],{"class":1083},[1073,8622,8447],{"class":1083},[1073,8624,8625,8627,8630,8632,8634,8637,8639],{"class":1075,"line":1129},[1073,8626,1080],{"class":1079},[1073,8628,8629],{"class":1087}," RegionsPlugin ",[1073,8631,3410],{"class":1079},[1073,8633,1119],{"class":1083},[1073,8635,8636],{"class":1122},"wavesurfer.js/dist/plugins/regions.js",[1073,8638,1318],{"class":1083},[1073,8640,8447],{"class":1083},[1073,8642,8643],{"class":1075,"line":1136},[1073,8644,1133],{"emptyLinePlaceholder":1132},[1073,8646,8647,8649,8652,8654,8657,8659,8662,8664],{"class":1075,"line":1182},[1073,8648,8423],{"class":1139},[1073,8650,8651],{"class":1087}," wavesurfer ",[1073,8653,1980],{"class":1083},[1073,8655,8656],{"class":1087}," WaveSurfer",[1073,8658,764],{"class":1083},[1073,8660,8661],{"class":1146},"create",[1073,8663,1150],{"class":1087},[1073,8665,1790],{"class":1083},[1073,8667,8668,8671,8673,8675,8678,8680],{"class":1075,"line":1205},[1073,8669,8670],{"class":1196},"  container",[1073,8672,1157],{"class":1083},[1073,8674,1119],{"class":1083},[1073,8676,8677],{"class":1122},"#waveform",[1073,8679,1318],{"class":1083},[1073,8681,1321],{"class":1083},[1073,8683,8684,8687,8689,8692,8694,8696],{"class":1075,"line":1238},[1073,8685,8686],{"class":1196},"  plugins",[1073,8688,1157],{"class":1083},[1073,8690,8691],{"class":1087}," [RegionsPlugin",[1073,8693,764],{"class":1083},[1073,8695,8661],{"class":1146},[1073,8697,8698],{"class":1087},"()]\n",[1073,8700,8701,8703,8705],{"class":1075,"line":1274},[1073,8702,1229],{"class":1083},[1073,8704,1360],{"class":1087},[1073,8706,8447],{"class":1083},[1073,8708,8709],{"class":1075,"line":1279},[1073,8710,1133],{"emptyLinePlaceholder":1132},[1073,8712,8713],{"class":1075,"line":1305},[1073,8714,8715],{"class":1408},"// After analysis\n",[1073,8717,8718,8721,8723,8725,8727,8730,8732,8734,8736,8738,8741,8743,8745],{"class":1075,"line":1324},[1073,8719,8720],{"class":1087},"structure",[1073,8722,764],{"class":1083},[1073,8724,5256],{"class":1087},[1073,8726,764],{"class":1083},[1073,8728,8729],{"class":1146},"forEach",[1073,8731,1150],{"class":1087},[1073,8733,1150],{"class":1083},[1073,8735,8270],{"class":1153},[1073,8737,1091],{"class":1083},[1073,8739,8740],{"class":1153}," i",[1073,8742,1360],{"class":1083},[1073,8744,1363],{"class":1139},[1073,8746,1179],{"class":1083},[1073,8748,8749,8751,8754,8756,8759,8761,8763,8765,8768,8771,8773,8775],{"class":1075,"line":1332},[1073,8750,1185],{"class":1139},[1073,8752,8753],{"class":1087}," nextSection",[1073,8755,1191],{"class":1083},[1073,8757,8758],{"class":1087}," structure",[1073,8760,764],{"class":1083},[1073,8762,5256],{"class":1087},[1073,8764,3858],{"class":1196},[1073,8766,8767],{"class":1087},"i",[1073,8769,8770],{"class":1083}," +",[1073,8772,3053],{"class":1466},[1073,8774,4159],{"class":1196},[1073,8776,8447],{"class":1083},[1073,8778,8779,8782,8784,8787,8789],{"class":1075,"line":1337},[1073,8780,8781],{"class":1087},"  wavesurfer",[1073,8783,764],{"class":1083},[1073,8785,8786],{"class":1146},"addRegion",[1073,8788,1150],{"class":1196},[1073,8790,1790],{"class":1083},[1073,8792,8793,8796,8798,8801,8803,8805],{"class":1075,"line":1368},[1073,8794,8795],{"class":1196},"    start",[1073,8797,1157],{"class":1083},[1073,8799,8800],{"class":1087}," section",[1073,8802,764],{"class":1083},[1073,8804,7519],{"class":1087},[1073,8806,1321],{"class":1083},[1073,8808,8809,8812,8814,8816,8819,8821,8823,8825,8828,8831,8833,8836,8838],{"class":1075,"line":1390},[1073,8810,8811],{"class":1196},"    end",[1073,8813,1157],{"class":1083},[1073,8815,8753],{"class":1087},[1073,8817,8818],{"class":1083}," ?",[1073,8820,8753],{"class":1087},[1073,8822,764],{"class":1083},[1073,8824,7519],{"class":1087},[1073,8826,8827],{"class":1083}," :",[1073,8829,8830],{"class":1087}," wavesurfer",[1073,8832,764],{"class":1083},[1073,8834,8835],{"class":1146},"getDuration",[1073,8837,1226],{"class":1196},[1073,8839,1321],{"class":1083},[1073,8841,8842,8845,8847,8849,8851,8853,8855,8857,8860,8862,8864],{"class":1075,"line":1405},[1073,8843,8844],{"class":1196},"    color",[1073,8846,1157],{"class":1083},[1073,8848,8800],{"class":1087},[1073,8850,764],{"class":1083},[1073,8852,6208],{"class":1087},[1073,8854,8770],{"class":1083},[1073,8856,1119],{"class":1083},[1073,8858,8859],{"class":1122},"40",[1073,8861,1318],{"class":1083},[1073,8863,1091],{"class":1083},[1073,8865,8866],{"class":1408}," // Add transparency\n",[1073,8868,8869,8872,8874,8876,8878],{"class":1075,"line":1412},[1073,8870,8871],{"class":1196},"    content",[1073,8873,1157],{"class":1083},[1073,8875,8800],{"class":1087},[1073,8877,764],{"class":1083},[1073,8879,8880],{"class":1087},"label\n",[1073,8882,8883,8885,8887],{"class":1075,"line":1436},[1073,8884,1327],{"class":1083},[1073,8886,1360],{"class":1196},[1073,8888,8447],{"class":1083},[1073,8890,8891,8893,8895],{"class":1075,"line":1470},[1073,8892,1229],{"class":1083},[1073,8894,1360],{"class":1087},[1073,8896,8447],{"class":1083},[1051,8898,8900],{"id":8899},"daw-import-reaper","DAW Import (REAPER)",[749,8902,8903],{},"The JSON format is designed for easy DAW import. A companion Lua script for REAPER can read the output and create project markers:",[1063,8905,8909],{"className":8906,"code":8907,"language":8908,"meta":1069,"style":1069},"language-lua shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","-- In REAPER: Actions > Run ReaScript\n-- Select: import_structure_markers.lua\n","lua",[798,8910,8911,8916],{"__ignoreMap":1069},[1073,8912,8913],{"class":1075,"line":1076},[1073,8914,8915],{},"-- In REAPER: Actions > Run ReaScript\n",[1073,8917,8918],{"class":1075,"line":1129},[1073,8919,8920],{},"-- Select: import_structure_markers.lua\n",[1051,8922,8924],{"id":8923},"combined-with-mtl-ableton-analyser","Combined with MTL Ableton Analyser",[749,8926,8927],{},"Use both tools together for comprehensive music analysis:",[848,8929,8930,8935,8940],{},[851,8931,8932,8934],{},[753,8933,6803],{}," – Analyze raw audio files",[851,8936,8937,8939],{},[753,8938,5024],{}," – Extract data from Ableton projects",[851,8941,8942,8945],{},[753,8943,8944],{},"Compare"," – Validate AI detection against manual markers",[776,8947,8949],{"id":8948},"accuracy-considerations","Accuracy Considerations",[749,8951,8952],{},"Structure detection accuracy depends on several factors:",[893,8954,8955,8968],{},[896,8956,8957],{},[899,8958,8959,8962,8965],{},[902,8960,8961],{},"Factor",[902,8963,8964],{},"Impact",[902,8966,8967],{},"Mitigation",[909,8969,8970,8983,8996,9009],{},[899,8971,8972,8977,8980],{},[914,8973,8974],{},[753,8975,8976],{},"Genre",[914,8978,8979],{},"Pop/EDM highest accuracy",[914,8981,8982],{},"Use genre-appropriate backend",[899,8984,8985,8990,8993],{},[914,8986,8987],{},[753,8988,8989],{},"Mixing",[914,8991,8992],{},"Clear separations help",[914,8994,8995],{},"allin1 handles complex mixes well",[899,8997,8998,9003,9006],{},[914,8999,9000],{},[753,9001,9002],{},"Length",[914,9004,9005],{},"Very long tracks may segment oddly",[914,9007,9008],{},"Consider splitting before analysis",[899,9010,9011,9016,9019],{},[914,9012,9013],{},[753,9014,9015],{},"Tempo changes",[914,9017,9018],{},"Can confuse boundary detection",[914,9020,9021],{},"Manual review recommended",[749,9023,9024],{},"For production use, we recommend:",[2632,9026,9027,9032,9037],{},[851,9028,9029,9031],{},[753,9030,6915],{}," for final output (highest accuracy)",[851,9033,9034,9036],{},[753,9035,6951],{}," for quick previews or batch processing",[851,9038,9039,9042],{},[753,9040,9041],{},"Manual review"," for critical applications",[776,9044,9046],{"id":9045},"platform-specific-installation-notes","Platform-Specific Installation Notes",[1051,9048,9050],{"id":9049},"the-allin1-dependency-challenge","The allin1 Dependency Challenge",[749,9052,9053],{},"The allin1 engine provides the highest quality analysis, but it has a complex dependency chain that currently causes compatibility issues across platforms:",[1063,9055,9058],{"className":9056,"code":9057,"language":2435},[2433],"allin1 1.1.0\n├── natten (Neighborhood Attention)\n│   └── API changed in 0.21.x, allin1 expects older API\n├── madmom (Music Beat Detection)\n│   └── Uses collections.MutableSequence, removed in Python 3.10+\n└── demucs (stem separation)\n    └── Works fine\n",[798,9059,9057],{"__ignoreMap":1069},[893,9061,9062,9074],{},[896,9063,9064],{},[899,9065,9066,9069,9072],{},[902,9067,9068],{},"Dependency",[902,9070,9071],{},"Problem",[902,9073,8964],{},[909,9075,9076,9090,9104],{},[899,9077,9078,9084,9087],{},[914,9079,9080,9083],{},[798,9081,9082],{},"natten"," 0.21.x",[914,9085,9086],{},"New API incompatible with allin1",[914,9088,9089],{},"Import fails on all platforms",[899,9091,9092,9098,9101],{},[914,9093,9094,9097],{},[798,9095,9096],{},"madmom"," 0.16.1",[914,9099,9100],{},"Uses deprecated Python API",[914,9102,9103],{},"Breaks on Python 3.10+",[899,9105,9106,9109,9112],{},[914,9107,9108],{},"shi-labs.com wheels",[914,9110,9111],{},"SSL certificate expired",[914,9113,9114],{},"Can't download pre-built binaries",[749,9116,9117,9120,9121,9124],{},[753,9118,9119],{},"Current recommendation:"," Use ",[753,9122,9123],{},"librosa fallback"," which works reliably across all platforms. The allin1 ecosystem requires updates from upstream maintainers.",[1063,9126,9128],{"className":2443,"code":9127,"language":2445,"meta":1069,"style":1069},"# What you'll see when running analysis:\n$ poetry run analyze track.mp3\nUsing librosa enhanced analysis  # allin1 not available, fallback works fine\n",[798,9129,9130,9135,9149],{"__ignoreMap":1069},[1073,9131,9132],{"class":1075,"line":1076},[1073,9133,9134],{"class":1408},"# What you'll see when running analysis:\n",[1073,9136,9137,9140,9143,9145,9147],{"class":1075,"line":1129},[1073,9138,9139],{"class":1160},"$",[1073,9141,9142],{"class":1122}," poetry",[1073,9144,6399],{"class":1122},[1073,9146,6405],{"class":1122},[1073,9148,7865],{"class":1122},[1073,9150,9151,9154,9157,9160,9163],{"class":1075,"line":1136},[1073,9152,9153],{"class":1160},"Using",[1073,9155,9156],{"class":1122}," librosa",[1073,9158,9159],{"class":1122}," enhanced",[1073,9161,9162],{"class":1122}," analysis",[1073,9164,9165],{"class":1408},"  # allin1 not available, fallback works fine\n",[1051,9167,9169],{"id":9168},"running-with-docker","Running with Docker",[749,9171,9172],{},"We provide a Dockerfile for containerized deployment:",[1063,9174,9179],{"className":9175,"code":9176,"filename":9177,"language":9178,"meta":1069,"style":1069},"language-dockerfile shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","FROM python:3.10-slim\n\nWORKDIR /app\n\n# System dependencies\nRUN apt-get update && apt-get install -y --no-install-recommends \\\n    ffmpeg libsndfile1 git cmake build-essential \\\n    && rm -rf /var/lib/apt/lists/*\n\nRUN pip install --upgrade pip Cython numpy poetry\n\nCOPY pyproject.toml poetry.lock README.md ./\nRUN poetry config virtualenvs.create false \\\n    && poetry install --no-interaction --no-ansi --no-root\n\nCOPY src/ ./src/\nRUN poetry install --no-interaction --no-ansi --only-root\n\nEXPOSE 8000\nCMD [\"python\", \"-m\", \"uvicorn\", \"audiolocators.server:app\", \"--host\", \"0.0.0.0\", \"--port\", \"8000\"]\n","Dockerfile","dockerfile",[798,9180,9181,9189,9193,9201,9205,9210,9218,9223,9228,9232,9239,9243,9251,9258,9263,9267,9274,9281,9285,9293],{"__ignoreMap":1069},[1073,9182,9183,9186],{"class":1075,"line":1076},[1073,9184,9185],{"class":1466},"FROM",[1073,9187,9188],{"class":1087}," python:3.10-slim\n",[1073,9190,9191],{"class":1075,"line":1129},[1073,9192,1133],{"emptyLinePlaceholder":1132},[1073,9194,9195,9198],{"class":1075,"line":1136},[1073,9196,9197],{"class":1466},"WORKDIR",[1073,9199,9200],{"class":1087}," /app\n",[1073,9202,9203],{"class":1075,"line":1182},[1073,9204,1133],{"emptyLinePlaceholder":1132},[1073,9206,9207],{"class":1075,"line":1205},[1073,9208,9209],{"class":1408},"# System dependencies\n",[1073,9211,9212,9215],{"class":1075,"line":1238},[1073,9213,9214],{"class":1466},"RUN",[1073,9216,9217],{"class":1087}," apt-get update && apt-get install -y --no-install-recommends \\\n",[1073,9219,9220],{"class":1075,"line":1274},[1073,9221,9222],{"class":1087},"    ffmpeg libsndfile1 git cmake build-essential \\\n",[1073,9224,9225],{"class":1075,"line":1279},[1073,9226,9227],{"class":1087},"    && rm -rf /var/lib/apt/lists/*\n",[1073,9229,9230],{"class":1075,"line":1305},[1073,9231,1133],{"emptyLinePlaceholder":1132},[1073,9233,9234,9236],{"class":1075,"line":1324},[1073,9235,9214],{"class":1466},[1073,9237,9238],{"class":1087}," pip install --upgrade pip Cython numpy poetry\n",[1073,9240,9241],{"class":1075,"line":1332},[1073,9242,1133],{"emptyLinePlaceholder":1132},[1073,9244,9245,9248],{"class":1075,"line":1337},[1073,9246,9247],{"class":1466},"COPY",[1073,9249,9250],{"class":1087}," pyproject.toml poetry.lock README.md ./\n",[1073,9252,9253,9255],{"class":1075,"line":1368},[1073,9254,9214],{"class":1466},[1073,9256,9257],{"class":1087}," poetry config virtualenvs.create false \\\n",[1073,9259,9260],{"class":1075,"line":1390},[1073,9261,9262],{"class":1087},"    && poetry install --no-interaction --no-ansi --no-root\n",[1073,9264,9265],{"class":1075,"line":1405},[1073,9266,1133],{"emptyLinePlaceholder":1132},[1073,9268,9269,9271],{"class":1075,"line":1412},[1073,9270,9247],{"class":1466},[1073,9272,9273],{"class":1087}," src/ ./src/\n",[1073,9275,9276,9278],{"class":1075,"line":1436},[1073,9277,9214],{"class":1466},[1073,9279,9280],{"class":1087}," poetry install --no-interaction --no-ansi --only-root\n",[1073,9282,9283],{"class":1075,"line":1470},[1073,9284,1133],{"emptyLinePlaceholder":1132},[1073,9286,9287,9290],{"class":1075,"line":1476},[1073,9288,9289],{"class":1466},"EXPOSE",[1073,9291,9292],{"class":1087}," 8000\n",[1073,9294,9295,9298,9300,9303,9305,9308,9310,9313,9315,9318,9320,9323,9325,9328,9330,9333,9335,9338],{"class":1075,"line":1482},[1073,9296,9297],{"class":1466},"CMD",[1073,9299,5566],{"class":1087},[1073,9301,9302],{"class":1122},"\"python\"",[1073,9304,756],{"class":1087},[1073,9306,9307],{"class":1122},"\"-m\"",[1073,9309,756],{"class":1087},[1073,9311,9312],{"class":1122},"\"uvicorn\"",[1073,9314,756],{"class":1087},[1073,9316,9317],{"class":1122},"\"audiolocators.server:app\"",[1073,9319,756],{"class":1087},[1073,9321,9322],{"class":1122},"\"--host\"",[1073,9324,756],{"class":1087},[1073,9326,9327],{"class":1122},"\"0.0.0.0\"",[1073,9329,756],{"class":1087},[1073,9331,9332],{"class":1122},"\"--port\"",[1073,9334,756],{"class":1087},[1073,9336,9337],{"class":1122},"\"8000\"",[1073,9339,3867],{"class":1087},[1063,9341,9344],{"className":2443,"code":9342,"filename":9343,"language":2445,"meta":1069,"style":1069},"# Build the image\ndocker build -t mtl-audiolocators .\n\n# Run analysis (uses librosa)\ndocker run -v $(pwd)/tracks:/app/tracks mtl-audiolocators \\\n    python -c \"from audiolocators import analyze_track; print(analyze_track('/app/tracks/song.mp3'))\"\n\n# Start the API server\ndocker run -p 8000:8000 mtl-audiolocators\ncurl http://localhost:8000/health\n# {\"status\":\"ok\",\"allin1_available\":false}\n","Build and Run",[798,9345,9346,9351,9368,9372,9377,9401,9415,9419,9424,9438,9445],{"__ignoreMap":1069},[1073,9347,9348],{"class":1075,"line":1076},[1073,9349,9350],{"class":1408},"# Build the image\n",[1073,9352,9353,9356,9359,9362,9365],{"class":1075,"line":1129},[1073,9354,9355],{"class":1160},"docker",[1073,9357,9358],{"class":1122}," build",[1073,9360,9361],{"class":1122}," -t",[1073,9363,9364],{"class":1122}," mtl-audiolocators",[1073,9366,9367],{"class":1122}," .\n",[1073,9369,9370],{"class":1075,"line":1136},[1073,9371,1133],{"emptyLinePlaceholder":1132},[1073,9373,9374],{"class":1075,"line":1182},[1073,9375,9376],{"class":1408},"# Run analysis (uses librosa)\n",[1073,9378,9379,9381,9383,9386,9389,9392,9394,9397,9399],{"class":1075,"line":1205},[1073,9380,9355],{"class":1160},[1073,9382,6399],{"class":1122},[1073,9384,9385],{"class":1122}," -v",[1073,9387,9388],{"class":1083}," $(",[1073,9390,9391],{"class":1146},"pwd",[1073,9393,1360],{"class":1083},[1073,9395,9396],{"class":1122},"/tracks:/app/tracks",[1073,9398,9364],{"class":1122},[1073,9400,8040],{"class":1087},[1073,9402,9403,9406,9408,9410,9413],{"class":1075,"line":1238},[1073,9404,9405],{"class":1122},"    python",[1073,9407,4718],{"class":1122},[1073,9409,2301],{"class":1083},[1073,9411,9412],{"class":1122},"from audiolocators import analyze_track; print(analyze_track('/app/tracks/song.mp3'))",[1073,9414,2418],{"class":1083},[1073,9416,9417],{"class":1075,"line":1274},[1073,9418,1133],{"emptyLinePlaceholder":1132},[1073,9420,9421],{"class":1075,"line":1279},[1073,9422,9423],{"class":1408},"# Start the API server\n",[1073,9425,9426,9428,9430,9433,9436],{"class":1075,"line":1305},[1073,9427,9355],{"class":1160},[1073,9429,6399],{"class":1122},[1073,9431,9432],{"class":1122}," -p",[1073,9434,9435],{"class":1122}," 8000:8000",[1073,9437,7838],{"class":1122},[1073,9439,9440,9442],{"class":1075,"line":1324},[1073,9441,8024],{"class":1160},[1073,9443,9444],{"class":1122}," http://localhost:8000/health\n",[1073,9446,9447],{"class":1075,"line":1332},[1073,9448,9449],{"class":1408},"# {\"status\":\"ok\",\"allin1_available\":false}\n",[1051,9451,9453],{"id":9452},"version-compatibility-matrix","Version Compatibility Matrix",[893,9455,9456,9472],{},[896,9457,9458],{},[899,9459,9460,9463,9465,9467,9469],{},[902,9461,9462],{},"Python",[902,9464,9096],{},[902,9466,9082],{},[902,9468,6915],{},[902,9470,9471],{},"Status",[909,9473,9474,9491,9506,9522],{},[899,9475,9476,9479,9482,9485,9488],{},[914,9477,9478],{},"3.9",[914,9480,9481],{},"0.16.1",[914,9483,9484],{},"0.17.x",[914,9486,9487],{},"1.1.0",[914,9489,9490],{},"Best chance for allin1",[899,9492,9493,9496,9498,9501,9503],{},[914,9494,9495],{},"3.10+",[914,9497,9481],{},[914,9499,9500],{},"any",[914,9502,9487],{},[914,9504,9505],{},"madmom fails (deprecated API)",[899,9507,9508,9511,9514,9517,9519],{},[914,9509,9510],{},"3.11",[914,9512,9513],{},"–",[914,9515,9516],{},"0.21.x",[914,9518,9487],{},[914,9520,9521],{},"natten API mismatch",[899,9523,9524,9528,9530,9532,9534],{},[914,9525,9526],{},[753,9527,9500],{},[914,9529,9513],{},[914,9531,9513],{},[914,9533,9513],{},[914,9535,9536],{},[753,9537,9538],{},"librosa fallback always works",[822,9540,9541],{},[749,9542,9543,9546,9547,764],{},[753,9544,9545],{},"Contributing:"," If you get allin1 working reliably, please share your setup! Open a PR or issue on ",[783,9548,6731],{"href":9549,"rel":9550},"https://github.com/musictechlab/mtl-audiolocators",[787],[776,9552,6624],{"id":6623},[749,9554,9555],{},"Current limitations to be aware of:",[2632,9557,9558,9564,9575,9581,9587],{},[851,9559,9560,9563],{},[753,9561,9562],{},"allin1 availability"," – Due to dependency conflicts (natten API changes, madmom Python 3.10+ incompatibility), allin1 currently doesn't work on most setups. Use librosa fallback instead.",[851,9565,9566,9569,9570,9574],{},[753,9567,9568],{},"Label accuracy"," – Section ",[9571,9572,9573],"em",{},"types"," (verse vs. chorus) are heuristic-based, boundaries are more reliable than labels",[851,9576,9577,9580],{},[753,9578,9579],{},"Genre dependency"," – Works best on pop/EDM with clear structure; experimental/ambient music may produce inconsistent results",[851,9582,9583,9586],{},[753,9584,9585],{},"Processing time"," – If allin1 were working, it would take 2-5x real-time on CPU",[851,9588,9589,9592],{},[753,9590,9591],{},"Model downloads"," – allin1 requires ~1.5GB model download on first run",[773,9594],{},[776,9596,9598],{"id":9597},"alternative-mir-tools","Alternative MIR Tools",[749,9600,9601],{},"If you're exploring music structure analysis beyond MTL Audio Locators, here are other tools worth considering:",[1051,9603,9605],{"id":9604},"open-source-libraries-for-structure-analysis","Open Source Libraries for Structure Analysis",[893,9607,9608,9623],{},[896,9609,9610],{},[899,9611,9612,9615,9618,9621],{},[902,9613,9614],{},"Tool",[902,9616,9617],{},"Focus",[902,9619,9620],{},"Structure Detection",[902,9622,5489],{},[909,9624,9625,9643,9661,9681],{},[899,9626,9627,9634,9637,9640],{},[914,9628,9629],{},[753,9630,9631],{},[783,9632,6915],{"href":6971,"rel":9633},[787],[914,9635,9636],{},"Full structure",[914,9638,9639],{},"Yes (verse, chorus, bridge)",[914,9641,9642],{},"Best quality, but dependency issues",[899,9644,9645,9652,9655,9658],{},[914,9646,9647],{},[753,9648,9649],{},[783,9650,6934],{"href":7136,"rel":9651},[787],[914,9653,9654],{},"Segmentation",[914,9656,9657],{},"Abstract labels (A, B, C)",[914,9659,9660],{},"Academic, multiple algorithms",[899,9662,9663,9672,9675,9678],{},[914,9664,9665],{},[753,9666,9667],{},[783,9668,9671],{"href":9669,"rel":9670},"https://essentia.upf.edu",[787],"Essentia",[914,9673,9674],{},"Comprehensive MIR",[914,9676,9677],{},"Basic segmentation",[914,9679,9680],{},"MTG Barcelona, well maintained",[899,9682,9683,9691,9694,9697],{},[914,9684,9685],{},[753,9686,9687],{},[783,9688,6951],{"href":9689,"rel":9690},"https://librosa.org",[787],[914,9692,9693],{},"Audio analysis",[914,9695,9696],{},"Heuristic-based",[914,9698,9699],{},"Always works, lower accuracy",[1051,9701,9703],{"id":9702},"beattempo-detection-no-structure-labels","Beat/Tempo Detection (No Structure Labels)",[893,9705,9706,9716],{},[896,9707,9708],{},[899,9709,9710,9712,9714],{},[902,9711,9614],{},[902,9713,9617],{},[902,9715,5489],{},[909,9717,9718,9734,9751],{},[899,9719,9720,9728,9731],{},[914,9721,9722],{},[753,9723,9724],{},[783,9725,9096],{"href":9726,"rel":9727},"https://github.com/CPJKU/madmom",[787],[914,9729,9730],{},"Beat/onset detection",[914,9732,9733],{},"Strong beat tracking, Python 3.9 only",[899,9735,9736,9745,9748],{},[914,9737,9738],{},[753,9739,9740],{},[783,9741,9744],{"href":9742,"rel":9743},"https://aubio.org",[787],"Aubio",[914,9746,9747],{},"Lightweight MIR",[914,9749,9750],{},"Fast, C library with Python bindings",[899,9752,9753,9762,9765],{},[914,9754,9755],{},[753,9756,9757],{},[783,9758,9761],{"href":9759,"rel":9760},"https://github.com/mjhydri/BeatNet",[787],"BeatNet",[914,9763,9764],{},"Beat/downbeat",[914,9766,9767],{},"Real-time, deep learning",[1051,9769,9771],{"id":9770},"commercial-apis-no-structure-detection","Commercial APIs (No Structure Detection)",[2572,9773,9774],{},[749,9775,9776,9778],{},[753,9777,5805],{}," No major commercial API currently offers song structure detection (verse/chorus/bridge). The services below provide other audio analysis features:",[893,9780,9781,9794],{},[896,9782,9783],{},[899,9784,9785,9788,9791],{},[902,9786,9787],{},"Service",[902,9789,9790],{},"Features",[902,9792,9793],{},"What It Does NOT Do",[909,9795,9796,9813,9831],{},[899,9797,9798,9807,9810],{},[914,9799,9800],{},[753,9801,9802],{},[783,9803,9806],{"href":9804,"rel":9805},"https://cyanite.ai",[787],"Cyanite.ai",[914,9808,9809],{},"Mood, genre, instruments (15s segments)",[914,9811,9812],{},"No verse/chorus labels",[899,9814,9815,9825,9828],{},[914,9816,9817,9824],{},[753,9818,9819],{},[783,9820,9823],{"href":9821,"rel":9822},"https://www.beatport.com",[787],"Musiio"," (Beatport)",[914,9826,9827],{},"Genre, mood, BPM",[914,9829,9830],{},"No structure detection",[899,9832,9833,9842,9845],{},[914,9834,9835],{},[753,9836,9837],{},[783,9838,9841],{"href":9839,"rel":9840},"https://www.acrcloud.com",[787],"ACRCloud",[914,9843,9844],{},"Audio fingerprinting, recognition",[914,9846,9830],{},[822,9848,9849],{},[749,9850,9851,9854,9855,9858],{},[753,9852,9853],{},"Spotify Audio Analysis API"," was the only major commercial option for structure detection (sections, segments, tempo, key). It was ",[753,9856,9857],{},"deprecated in November 2024"," and is no longer available.",[1051,9860,9862],{"id":9861},"why-no-commercial-structure-apis","Why No Commercial Structure APIs?",[749,9864,9865],{},"Song structure detection is a niche problem with limited commercial demand:",[2632,9867,9868,9874,9880],{},[851,9869,9870,9873],{},[753,9871,9872],{},"Music streaming"," – services use internal solutions (not exposed via API)",[851,9875,9876,9879],{},[753,9877,9878],{},"DJ software"," – built-in proprietary algorithms",[851,9881,9882,9885],{},[753,9883,9884],{},"Academic"," – research papers, not production services",[749,9887,9888],{},"This leaves open source as the primary option for structure analysis.",[1051,9890,9892],{"id":9891},"the-open-source-mir-maintenance-problem","The Open Source MIR Maintenance Problem",[749,9894,9895,9896,9899],{},"Many MIR libraries suffer from a common pattern: ",[753,9897,9898],{},"written once, then abandoned",". Here's why:",[893,9901,9902,9910],{},[896,9903,9904],{},[899,9905,9906,9908],{},[902,9907,8961],{},[902,9909,8964],{},[909,9911,9912,9922,9932,9942,9956],{},[899,9913,9914,9919],{},[914,9915,9916],{},[753,9917,9918],{},"Academic origins",[914,9920,9921],{},"Most MIR tools (madmom, MSAF, allin1) come from PhD research. When the researcher graduates or changes projects, development stops.",[899,9923,9924,9929],{},[914,9925,9926],{},[753,9927,9928],{},"Publication vs. maintenance",[914,9930,9931],{},"In academia, publishing a paper = success. Maintaining software ≠ academic credit.",[899,9933,9934,9939],{},[914,9935,9936],{},[753,9937,9938],{},"Niche community",[914,9940,9941],{},"Small user base means few contributors and little pressure to fix bugs.",[899,9943,9944,9949],{},[914,9945,9946],{},[753,9947,9948],{},"Rapid Python/ML changes",[914,9950,9951,9952,9955],{},"Python 3.10 removed ",[798,9953,9954],{},"collections.MutableSequence",", PyTorch changes API constantly. Keeping up requires active maintenance.",[899,9957,9958,9963],{},[914,9959,9960],{},[753,9961,9962],{},"No commercial sponsorship",[914,9964,9965],{},"Unlike web frameworks (React/Meta, Angular/Google), MIR tools have no corporate backing.",[749,9967,9968],{},[753,9969,9970],{},"Real examples:",[893,9972,9973,9986],{},[896,9974,9975],{},[899,9976,9977,9980,9983],{},[902,9978,9979],{},"Project",[902,9981,9982],{},"Last PyPI Release",[902,9984,9985],{},"Issue",[909,9987,9988,9998,10008],{},[899,9989,9990,9992,9995],{},[914,9991,9096],{},[914,9993,9994],{},"2019",[914,9996,9997],{},"Uses deprecated Python APIs",[899,9999,10000,10002,10005],{},[914,10001,6934],{},[914,10003,10004],{},"2020",[914,10006,10007],{},"Minimal maintenance",[899,10009,10010,10012,10015],{},[914,10011,6915],{},[914,10013,10014],{},"2024",[914,10016,10017],{},"Active, but blocked by natten/madmom issues",[749,10019,10020],{},"This is why MTL Audio Locators defaults to librosa – it's the only dependency that's actively maintained and works across all Python versions.",[1051,10022,10024],{"id":10023},"source-separation-preprocessing","Source Separation (Preprocessing)",[893,10026,10027,10038],{},[896,10028,10029],{},[899,10030,10031,10033,10036],{},[902,10032,3109],{},[902,10034,10035],{},"Source",[902,10037,5489],{},[909,10039,10040,10057],{},[899,10041,10042,10048,10054],{},[914,10043,10044,10047],{},[753,10045,10046],{},"Demucs"," (Meta)",[914,10049,10050],{},[783,10051,6731],{"href":10052,"rel":10053},"https://github.com/facebookresearch/demucs",[787],[914,10055,10056],{},"Best quality, used by allin1",[899,10058,10059,10065,10071],{},[914,10060,10061,10064],{},[753,10062,10063],{},"Spleeter"," (Deezer)",[914,10066,10067],{},[783,10068,6731],{"href":10069,"rel":10070},"https://github.com/deezer/spleeter",[787],[914,10072,10073],{},"Faster, good for batch processing",[773,10075],{},[776,10077,2695],{"id":2694},[2632,10079,10080,10086,10091],{},[851,10081,10082,10085],{},[783,10083,10084],{"href":141},"What Data Can We Extract from Ableton's .als and .asd Files"," – Parsing Ableton project files",[851,10087,10088,10090],{},[783,10089,2712],{"href":475}," – Manual section marking",[851,10092,10093,10096],{},[783,10094,10095],{"href":205},"Why We Decided to Use WaveSurfer"," – Audio visualization in the browser",[773,10098],{},[1940,10100,10101],{},[749,10102,10103,10105,10106,6732],{},[753,10104,6725],{}," MTL Audio Locators is available on ",[783,10107,6731],{"href":9549,"rel":10108},[787],[1833,10110,10111],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":1069,"searchDepth":1129,"depth":1129,"links":10113},[10114,10115,10120,10121,10122,10128,10129,10134,10135,10140,10141,10149],{"id":6810,"depth":1129,"text":6811},{"id":6884,"depth":1129,"text":6885,"children":10116},[10117,10118,10119],{"id":6965,"depth":1136,"text":6966},{"id":7130,"depth":1136,"text":7131},{"id":7229,"depth":1136,"text":7230},{"id":7296,"depth":1129,"text":7297},{"id":7435,"depth":1129,"text":7436},{"id":7812,"depth":1129,"text":7813,"children":10123},[10124,10125,10126,10127],{"id":7816,"depth":1136,"text":6353},{"id":7847,"depth":1136,"text":7848},{"id":7899,"depth":1136,"text":7900},{"id":8154,"depth":1136,"text":8155},{"id":8407,"depth":1129,"text":8408},{"id":5831,"depth":1129,"text":5832,"children":10130},[10131,10132,10133],{"id":8595,"depth":1136,"text":8596},{"id":8899,"depth":1136,"text":8900},{"id":8923,"depth":1136,"text":8924},{"id":8948,"depth":1129,"text":8949},{"id":9045,"depth":1129,"text":9046,"children":10136},[10137,10138,10139],{"id":9049,"depth":1136,"text":9050},{"id":9168,"depth":1136,"text":9169},{"id":9452,"depth":1136,"text":9453},{"id":6623,"depth":1129,"text":6624},{"id":9597,"depth":1129,"text":9598,"children":10142},[10143,10144,10145,10146,10147,10148],{"id":9604,"depth":1136,"text":9605},{"id":9702,"depth":1136,"text":9703},{"id":9770,"depth":1136,"text":9771},{"id":9861,"depth":1136,"text":9862},{"id":9891,"depth":1136,"text":9892},{"id":10023,"depth":1136,"text":10024},{"id":2694,"depth":1129,"text":2695},"2026-01-29T00:00:00.000Z","A technical look at automatic song structure detection using AI and signal processing. Identify song sections with MTL Audio Locators.",{"src":7809},{"enabled":1132,"items":10154},[10155,10158,10160,10162],{"text":10156,"icon":10157},"Three analysis backends (allin1, MSAF, librosa) auto-select the best available engine.","i-lucide-brain",{"text":10159,"icon":6773},"Output uses a consistent labeling convention (VRS1, CHO1, BRG1) designed for DAW import.",{"text":10161,"icon":2747},"Includes a REST API, CLI, and Python API for integration into any workflow.",{"text":10163,"icon":10164},"The librosa fallback works reliably on all platforms despite allin1 dependency issues.","i-lucide-cpu",{},{"title":100,"description":10151},[2760,2736,2762],"Uuc7eBWbp0AzznCW1MhGn_O--FeZDiA7QrG4g3zdaVc",{"id":10170,"title":204,"authors":10171,"badge":2737,"body":10176,"category":2736,"client":2737,"date":10329,"description":10330,"extension":2740,"faq":2737,"featured":69,"featuredOrder":2737,"hidden":69,"image":10331,"keyTakeaways":10333,"meta":10343,"navigation":1132,"path":205,"seo":10344,"status":2737,"stem":206,"tags":10345,"teaser":2737,"__hash__":10346,"score":1182},"posts/blog/music-data/why-we-decided-to-use-wavesurfer.md",[10172],{"name":10173,"avatar":10174},"Mateusz Bryzik",{"src":10175},"/images/people/mateusz-bryzik.webp",{"type":746,"value":10177,"toc":10321},[10178,10182,10185,10208,10212,10215,10219,10222,10227,10231,10239,10256,10259,10263,10274,10288,10291,10294,10299,10302],[776,10179,10181],{"id":10180},"the-main-challenge","The main challenge",[749,10183,10184],{},"In one of our projects, we faced the business requirement to create a custom audio player — nothing complex, with several controls: play, pause, previous and next track, and changing volume. The challenge was the player should also be responsible for drawing the audio spectrum.",[5450,10186,10192,10198,10203],{"className":10187},[10188,10189,10190,10191,5455],"grid","grid-cols-1","md:grid-cols-3","gap-4",[10193,10194],"spotlight-card",{"description":10195,"icon":10196,"title":10197},"Play, pause, previous/next track, volume control.","i-lucide-play","Playback Controls",[10193,10199],{"description":10200,"icon":10201,"title":10202},"Visual representation of the audio signal drawn in real time.","i-lucide-audio-waveform","Waveform Spectrum",[10193,10204],{"description":10205,"icon":10206,"title":10207},"Must match our existing UI — no generic player widgets.","i-lucide-paintbrush","Custom Design",[776,10209,10211],{"id":10210},"available-solutions","Available solutions",[749,10213,10214],{},"Building a basic audio player with the browser's Audio API is straightforward. Drawing a waveform spectrum is where it gets complex. There are algorithms available on the web, but adjusting them to specific designs and interactions is time-consuming. We decided to look for a ready-made library.",[776,10216,10218],{"id":10217},"does-a-vuejs-audio-player-library-exist","Does a Vue.js Audio Player library exist?",[749,10220,10221],{},"At MusicTech Lab, our standard frontend stack is Vue.js. Our first idea was to find a Vue.js audio library. Unfortunately, none of the available options matched our needs — some could play tracks and draw a spectrum, but customising them to our designs was impossible. Time was running out, and we had only a few weeks to close this topic.",[2572,10223,10224],{},[749,10225,10226],{},"We evaluated several Vue.js audio libraries, but none offered the combination of waveform rendering and design flexibility we needed. Most were either too opinionated in their UI or lacked spectrum visualisation entirely.",[776,10228,10230],{"id":10229},"our-solution-wavesurferjs","Our solution: WaveSurfer.js",[749,10232,10233,10234,10238],{},"Finally, we found ",[783,10235,947],{"href":10236,"rel":10237},"https://wavesurfer-js.org/",[787]," — a customisable audio spectrum visualisation library based on the Audio API and HTML5 Canvas. With over ten years of development, it was a proven candidate.",[5450,10240,10242,10246,10251],{"className":10241},[10188,10189,10190,10191,5455],[10193,10243],{"description":10244,"icon":10245,"title":10197},"Play, pause, switch tracks, volume — all supported out of the box.","i-lucide-circle-play",[10193,10247],{"description":10248,"icon":10249,"title":10250},"Fully customisable spectrum — colours, height, progress bar, container size.","i-lucide-palette","Waveform Rendering",[10193,10252],{"description":10253,"icon":10254,"title":10255},"Extend functionality with plugins like Regions, Timeline, and Minimap.","i-lucide-puzzle","Plugin System",[749,10257,10258],{},"We quickly configured the waveform container — height, width, colours for the wave and progress — and it matched our designs. A few lines of configuration replaced hundreds of lines of custom code.",[776,10260,10262],{"id":10261},"the-regions-plugin","The Regions Plugin",[749,10264,10265,10266,10269,10270,10273],{},"Our business requirement was to let users play ",[753,10267,10268],{},"hooks"," — specific parts of a track like a chorus or guitar riff, highlighted on the spectrum. The ",[753,10271,10272],{},"Regions"," plugin was exactly what we needed.",[5450,10275,10278,10283],{"className":10276},[10188,10189,10277,10191,5455],"md:grid-cols-2",[10193,10279],{"description":10280,"icon":10281,"title":10282},"Creates highlighted regions on the waveform that can be resized, moved, and played in a loop.","i-lucide-scissors","Visual Markers",[10193,10284],{"description":10285,"icon":10286,"title":10287},"Users select a hook — chorus, riff, intro — and the Regions plugin handles looped playback of that segment.","i-lucide-repeat","Loop Playback",[749,10289,10290],{},"The hook menu above the spectrum is a custom component, but the hook highlighting and playback is fully powered by the Regions plugin.",[776,10292,10293],{"id":2040},"Result",[1940,10295,10296],{},[749,10297,10298],{},"WaveSurfer.js saved us significant development time. The configuration is simple, the library is stable, and you can achieve what you need quickly. The only downside is the documentation — it could use more examples for advanced methods.",[749,10300,10301],{},"We recommend this library to anyone building a similar audio component.",[5450,10303,10307,10313,10318],{"className":10304},[5453,10305,10306,5455],"flex-wrap","gap-3",[10308,10309],"u-button",{"color":10310,"label":947,"target":10311,"to":10236,"variant":10312},"primary","_blank","subtle",[10308,10314],{"color":10315,"label":10316,"target":10311,"to":10317,"variant":10312},"neutral","Plugins","https://wavesurfer-js.org/plugins/",[10308,10319],{"color":10315,"label":6731,"target":10311,"to":10320,"variant":10312},"https://github.com/katspaugh/wavesurfer.js",{"title":1069,"searchDepth":1129,"depth":1129,"links":10322},[10323,10324,10325,10326,10327,10328],{"id":10180,"depth":1129,"text":10181},{"id":10210,"depth":1129,"text":10211},{"id":10217,"depth":1129,"text":10218},{"id":10229,"depth":1129,"text":10230},{"id":10261,"depth":1129,"text":10262},{"id":2040,"depth":1129,"text":10293},"2022-07-05T00:00:00.000Z","Creating a simple, custom audio player with main functionality is relatively easy. Are you sure? Let's see how we tackled this issue",{"src":10332},"/images/blog/musictechlab_blog_why-we-decided-to-use-wavesurfer.webp",{"enabled":1132,"items":10334},[10335,10337,10340],{"text":10336,"icon":2747},"WaveSurfer.js replaced hundreds of lines of custom waveform code with a few config lines.",{"text":10338,"icon":10339},"The Regions plugin handles hook highlighting and looped playback out of the box.","i-lucide-headphones",{"text":10341,"icon":10342},"No Vue.js audio library offered both waveform rendering and design flexibility.","i-lucide-search",{},{"title":204,"description":10330},[6783,2761,2762],"X1tV_GLxKKa2kXdZt19ajJ2Me10bpOEFQWfvwqnY8j4",1780305239308]