<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ music - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ music - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 23 May 2026 22:20:26 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/music/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Learn Music Production with FL Studio ]]>
                </title>
                <description>
                    <![CDATA[ We just posted a comprehensive music production course on the freeCodeCamp.org YouTube channel that bridges the gap between basic theory and professional execution. Miguel guides you through the entire lifecycle of a song using tools like FL Studio. ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-music-production-with-fl-studio/</link>
                <guid isPermaLink="false">695d4101b1490823e299f4ec</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 06 Jan 2026 17:06:09 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1767718966641/3d1b66bc-19ad-4cdb-bcf9-9119c81f89f3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>We just posted a comprehensive music production course on the freeCodeCamp.org YouTube channel that bridges the gap between basic theory and professional execution. Miguel guides you through the entire lifecycle of a song using tools like FL Studio. You will master sound design and mixing before applying your skills to build a high-energy Bass House track from scratch.</p>
<p>The course also covers the business side, teaching you how to distribute your music to major platforms and properly collect your royalties. By the end, you will have the practical knowledge needed to transform your creative ideas into a fully released, monetized product.</p>
<p>Here are the sections in this course:</p>
<ul>
<li><p>Introduction and Welcome</p>
</li>
<li><p>Course Overview: Theory vs. Application</p>
</li>
<li><p>Tools and Setup (FL Studio, Fabric, Samples)</p>
</li>
<li><p>Sound Design Basics with Fabric</p>
</li>
<li><p>Understanding Pre-processing vs. Post-processing</p>
</li>
<li><p>Parallel Processing and Filters</p>
</li>
<li><p>Alternative Tools: Using Vital</p>
</li>
<li><p>Working with Samples as Building Blocks</p>
</li>
<li><p>Building a Basic House Beat</p>
</li>
<li><p>Mixing Theory: The Box Concept</p>
</li>
<li><p>Practical Mixing Techniques in FL Studio</p>
</li>
<li><p>Melody Creation and Music Theory</p>
</li>
<li><p>Creating Motives and Variations</p>
</li>
<li><p>Melodic Symmetry and Flow</p>
</li>
<li><p>Music Distribution and Publishing Rights</p>
</li>
<li><p>Application: Making a Bass House Track from Scratch</p>
</li>
<li><p>reakdown: Baseline and Vocal Setup</p>
</li>
<li><p>Breakdown: Adding Effects and Ambience</p>
</li>
<li><p>Breakdown: Drum Progression</p>
</li>
<li><p>Filling empty spaces using "Call and Response" synth melodies.</p>
</li>
<li><p>Creating vocal ambience through pitch-shifting and reverse sampling.</p>
</li>
<li><p>Designing the Main Drop: Combining drums and aggressive baselines.</p>
</li>
<li><p>Advanced Bass Sequencing: Rhythm, variations, and groove.</p>
</li>
<li><p>Using Automation: Adding reverb and pitch shifts for dynamic transitions.</p>
</li>
<li><p>Buildup Techniques: Creating tension with filters and snare rolls.</p>
</li>
<li><p>Song Arrangement: Copying sections and creating "Fake Drops" for variation.</p>
</li>
<li><p>Final Mixdown: Leveling, EQ filtering, and stereo placement.</p>
</li>
<li><p>Sidechaining: Using "ducking" effects to keep the kick and bass from clashing.</p>
</li>
<li><p>Sub-bass Management: Separating low-end frequencies for club systems.</p>
</li>
<li><p>Mastering: Utilizing limiters to achieve professional loudness without clipping.</p>
</li>
<li><p>Export Settings: High-quality MP3 rendering for distribution.</p>
</li>
<li><p>Song Distribution: Step-by-step guide to uploading through RouteNote.</p>
</li>
<li><p>Strategic Release Planning: Choosing dates and managing store territories.</p>
</li>
<li><p>Collecting Royalties: Using Monetunes to claim publishing and intellectual property rights.</p>
</li>
<li><p>Final Encouragement and Closing Remarks.</p>
</li>
</ul>
<p>Watch the full course on <a target="_blank" href="https://youtu.be/r36xQmTJe1c">the freeCodeCamp.org YouTube channel</a> (3-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/r36xQmTJe1c" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Guitar Theory Course for Beginners – Learn Fretboard, Major Scale and Triads ]]>
                </title>
                <description>
                    <![CDATA[ Learn to play the guitar! We just posted a course on the freeCodeCamp.org YouTube channel that will teach you essential guitar theory including the fretboard, the major scale and the triads. It was created by baritone guitarist, Alex Gordon Hi-Fi. He... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/guitar-theory-course-for-beginners-learn-fretboard-major-scale-and-triads/</link>
                <guid isPermaLink="false">690ce6ccd9dcaa769b671c7e</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 06 Nov 2025 18:19:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762453168574/3b0a237a-f4ab-4a89-a787-443ec1239e5a.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Learn to play the guitar!</p>
<p>We just posted a course on the freeCodeCamp.org YouTube channel that will teach you essential guitar theory including the fretboard, the major scale and the triads. It was created by baritone guitarist, Alex Gordon Hi-Fi.</p>
<p>Here are the sections in this course:</p>
<ul>
<li><p>Introduction</p>
</li>
<li><p>The Crux of Self-Contained Guitar</p>
</li>
<li><p>This Notion of Practice - part 1</p>
</li>
</ul>
<h3 id="heading-chapter-1-the-fretboard">Chapter 1 - THE FRETBOARD</h3>
<ul>
<li><p>The 12 Chromatic Notes</p>
</li>
<li><p>Half Steps &amp; Whole Steps</p>
</li>
<li><p>Unisons &amp; Octaves</p>
</li>
<li><p>Paperwork - The Fretboard</p>
</li>
<li><p>Fingerwork - The Fretboard</p>
</li>
<li><p>Fretboard Summation</p>
</li>
</ul>
<h3 id="heading-chapter-2-the-major-scale">Chapter 2 - THE MAJOR SCALE</h3>
<ul>
<li><p>The Major Scale Formula</p>
</li>
<li><p>Paperwork - The Major Scale Formula</p>
</li>
<li><p>Major Scale Intervals</p>
</li>
<li><p>Paperwork - Major Scale Intervals</p>
</li>
<li><p>Fingerwork - Major Scale Intervals</p>
</li>
<li><p>Major Scale Fretboard Patterns</p>
</li>
<li><p>Fingerwork - Major Scale Fretboard Patterns</p>
</li>
<li><p>The 12 Major Scale Keys</p>
</li>
<li><p>Major Scale Summation</p>
</li>
</ul>
<h3 id="heading-chapter-3-the-triads">Chapter 3 - THE TRIADS</h3>
<ul>
<li><p>Triad Formulas</p>
</li>
<li><p>Paperwork - Triad Formulas</p>
</li>
<li><p>Chord Voicings - part 1 - Inversions</p>
</li>
<li><p>Fingerwork - Inversions</p>
</li>
<li><p>Chord Voicings - part 2 - Barre Chords</p>
</li>
<li><p>Fingerwork - Barre Chords</p>
</li>
<li><p>The Harmonized Major Scale</p>
</li>
<li><p>Paperwork - The Harmonized Major Scale</p>
</li>
<li><p>Fingerwork - The Harmonized Major Scale</p>
</li>
<li><p>Chord Progressions</p>
</li>
<li><p>Paperwork - Chord Progressions</p>
</li>
<li><p>Fingerwork - Chord Progressions</p>
</li>
<li><p>Triads Summation</p>
</li>
<li><p>This Notion of Practice - part 2</p>
</li>
</ul>
<p>Watch the full course below or <a target="_blank" href="https://youtu.be/mjXLt97_Cr8">on the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/mjXLt97_Cr8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn Music Production for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ Are you interested in exploring the world of music production but don't know where to start? We just posted comprehensive course designed for beginners that will guide you through the process of creating music tracks in various styles using FL Studio... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-music-production-for-beginners/</link>
                <guid isPermaLink="false">66e1f8b2fd5bd0f4a063f328</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Wed, 11 Sep 2024 20:08:18 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1726085276839/735597b4-50a4-4792-be6a-7bca34a732f9.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Are you interested in exploring the world of music production but don't know where to start? We just posted comprehensive course designed for beginners that will guide you through the process of creating music tracks in various styles using FL Studio. Developed by Tristan Wilcox, this course requires no prior music experience and offers a hands-on approach to learning music production.</p>
<h3 id="heading-course-overview">Course Overview</h3>
<p>This course is structured to take you from the basics of music production to mastering your own tracks. Here's a breakdown of what you'll learn:</p>
<ol>
<li><p><strong>Introduction</strong>: Get an overview of the course and what to expect as you embark on your music production journey.</p>
</li>
<li><p><strong>Chapter 1 - Instruments</strong>: Learn about the different instruments available in FL Studio and how to use them to create unique sounds.</p>
</li>
<li><p><strong>Chapter 2 - Chords, Melody, Bass</strong>: Discover the fundamentals of music theory, including how to create chords, melodies, and bass lines that form the backbone of your tracks.</p>
</li>
<li><p><strong>Chapter 3 - Playlist</strong>: Understand how to organize and arrange your music using the playlist feature in FL Studio.</p>
</li>
<li><p><strong>Chapters 4/5 - Drums and FX</strong>: Explore the world of percussion and sound effects, learning how to add depth and rhythm to your music.</p>
</li>
<li><p><strong>Chapter 6 - Structure</strong>: Learn how to structure your tracks to create engaging and dynamic compositions.</p>
</li>
<li><p><strong>Chapter 7 - Mixing</strong>: Dive into the art of mixing, balancing different elements of your track to achieve a polished sound.</p>
</li>
<li><p><strong>Chapter 8 - Automation</strong>: Discover how to use automation to add movement and interest to your music.</p>
</li>
<li><p><strong>Chapter 9 - Mastering</strong>: Learn the final steps of music production, mastering your tracks to ensure they sound great on all playback systems.</p>
</li>
<li><p><strong>Chapter 10 - The Final Chapter</strong>: Bring everything together and complete your first music production project.</p>
</li>
</ol>
<h3 id="heading-understanding-fl-studio">Understanding FL Studio</h3>
<p>FL Studio, also known as FruityLoops, is a powerful digital audio workstation (DAW) that is widely used by music producers around the world. It offers an intuitive interface and a wide range of features that make it ideal for beginners and professionals alike. Here’s a closer look at what makes FL Studio a great choice for music production:</p>
<ul>
<li><p><strong>User-Friendly Interface</strong>: FL Studio's interface is designed to be easy to navigate, with a pattern-based music sequencer that allows you to create and edit music with ease.</p>
</li>
<li><p><strong>Unlimited Free Trial</strong>: One of the standout features of FL Studio is its unlimited free trial, which allows you to explore all its features without any time restrictions. This makes it an excellent choice for beginners who want to experiment with music production before committing to a purchase.</p>
</li>
<li><p><strong>Comprehensive Toolset</strong>: FL Studio comes equipped with a wide array of tools and plugins, including synthesizers, samplers, and effects processors, giving you everything you need to create professional-quality music.</p>
</li>
<li><p><strong>Versatility</strong>: Whether you're interested in electronic music, hip-hop, pop, or any other genre, FL Studio provides the flexibility to produce music in a variety of styles.</p>
</li>
<li><p><strong>Strong Community Support</strong>: With a large and active user community, FL Studio offers plenty of resources, tutorials, and forums where you can learn from other producers and share your own experiences.</p>
</li>
</ul>
<h3 id="heading-get-started-today">Get Started Today</h3>
<p>If you're ready to start your music production journey, this course is the perfect place to begin. Watch the full course on the freeCodeCamp.org YouTube channel.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Podcast: How the Insane Pressure of Classical Music Prepared Jessica Wilkins for Tech ]]>
                </title>
                <description>
                    <![CDATA[ On this week's episode of the podcast, I interview orchestral musician-turned software engineer Jessica Wilkins. Jessica found success in the extremely competitive field of classical music, playing the Oboe in orchestras, recording sessions, and even... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/podcast-jessica-wilkins-classical-music-learning-to-code/</link>
                <guid isPermaLink="false">66b8d51fce55d3ba4d93597c</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp Curriculum ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learn to code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ podcast ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Fri, 16 Feb 2024 18:47:35 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/02/jessica-wilkins-freecodecamp-podcast.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>On this week's episode of the podcast, I interview orchestral musician-turned software engineer Jessica Wilkins.</p>
<p>Jessica found success in the extremely competitive field of classical music, playing the Oboe in orchestras, recording sessions, and even at major events such as the NFL awards on national television.</p>
<p>She started her own business – a sheet music e-commerce website. This not only helped her survive in the high cost of living city of Los Angeles – it also helped her learn web development.</p>
<p>During the pandemic, many of her performance and recording gigs were cancelled. This inspired her to dive much deeper into coding. She now works as a software engineer at freeCodeCamp, and has contributed substantially to freeCodeCamp's core curriculum. Also, her many freeCodeCamp tutorial articles have more than 400,000 readers each month.</p>
<p>During our conversation, Jessica talks about the insane pressure she faced as a musician, where standards are incredibly high. So many people want to be professional musicians, and there is so little money in the industry. Jessica was a rare case of finding success. But even that success could not dissuade her from diving into software development.</p>
<p>This is a long, intimate conversation with one of the sharpest minds behind freeCodeCamp.org. It was a blast talking with Jessica for more than two hours. I think you'll dig it.</p>
<p>Here are some timestamps in case you want to skip some our lengthy discussion about music education and the music industry:</p>
<ul>
<li>0:00:00 My bass intro. See if you can guess this 1970 classic bassline.</li>
<li>0:01:00 Our discussion of Jessica's upbringing by a school teacher and single mom, and her journey into classical music</li>
<li>1:07:00 Jessica Learns to code and builds a profitable sheet music e-commerce business</li>
<li>1:35:00 Jessica's decision to go all in on software development</li>
<li>1:44:00 Contract work and thoughts on what caused recent tech layoffs</li>
</ul>
<p>You can watch this interview on YouTube:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/tM6OOJt0S2Y" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Or you can listen to the podcast in Apple Podcasts, Spotify, or your favorite podcast app. You can also listen to the podcast below, right in your browser:</p>
<div class="embed-wrapper"><iframe src="https://play.libsyn.com/embed/episode/id/29988338/height/192/theme/modern/size/large/thumbnail/yes/custom-color/2a4061/time-start/00:00:00/playlist-height/200/direction/backward/download/yes/font-color/FFFFFF" height="192" width="100%" style="border:none" title="Embedded content" loading="lazy"></iframe></div>

<p>Links we talk about during the interview:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/javascript-projects-for-beginners/">One of Jessica's articles - 40 JavaScript Projects for Beginners – Easy Ideas to Get Started Coding JS</a></li>
<li><a target="_blank" href="https://blackexcellencemusicproject.com/">The Black Excellence Music Project, Jessica's first React project</a></li>
<li><a target="_blank" href="https://freecodecamp.libsyn.com/site/were-back-danny-thompsons-journey-from-chicken-fryer-to-software-engineer">Danny Thompson freeCodeCamp Podcast interview</a></li>
<li><a target="_blank" href="https://www.linkedin.com/learning/linkedin-profiles-for-technical-professionals/main-visuals-on-your-profile">Danny's LinkedIn course that Quincy mentions</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Produce Music with FL Studio ]]>
                </title>
                <description>
                    <![CDATA[ FL Studio is a powerful digital audio workstation (DAW) used by music producers all over the world. It has a wide range of features that allow users to create, produce, and mix music with ease. Whether you are a beginner or an experienced producer, t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-produce-music-with-fl-studio/</link>
                <guid isPermaLink="false">66b2034227569435a9255ac5</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 05 Jan 2023 15:44:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/01/flstudio.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>FL Studio is a powerful digital audio workstation (DAW) used by music producers all over the world. It has a wide range of features that allow users to create, produce, and mix music with ease. Whether you are a beginner or an experienced producer, this course is designed to help you get the most out of FL Studio.</p>
<p>We just published a course on the freeCodeCamp.org YouTube channel that will teach you how to use FL Studio to produce music. </p>
<p>Tristan Willcox developed this course. Tristan is a professional music producer with over 10,000 hours of experience in music production.</p>
<p>Our course is structured in an easy-to-follow manner, starting with the basics and gradually building up to more advanced topics. We cover everything from setting up the software to creating and arranging music, as well as mixing and mastering your tracks. Along the way, we'll be demonstrating various techniques and tips to help you get the most out of FL Studio.</p>
<p>In this course, you'll learn how to use the piano roll, the sequencer, and the synthesizers to create and edit music. We'll also be covering some of the more advanced features of the software, such as using effects and automation to add depth and complexity to your tracks.</p>
<p>Whether you're new to music production or looking to take your skills to the next level, this course is the perfect resource for learning how to use FL Studio. </p>
<p>This course is broken up into the following ten chapters:</p>
<ul>
<li>Chapter 1: Virtual Instruments </li>
<li>Chapter 2: Melodies and Chords </li>
<li>Chapter 3: Using Patterns </li>
<li>Chapter 4: Using Samples</li>
<li>Chapter 5: Layering and Arranging</li>
<li>Chapter 6: Leveling</li>
<li>Chapter 7: Adding to the song</li>
<li>Chapter 8: Mixing + FX</li>
<li>Chapter 9: Automation</li>
<li>Chapter 10: Mastering + Exporting</li>
</ul>
<p>Watch the full course below or on <a target="_blank" href="https://youtu.be/BUjdnxgBgzM">the freeCodeCamp.org YouTube channel</a> (2-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/BUjdnxgBgzM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Download and Trim MP3s from YouTube with Python ]]>
                </title>
                <description>
                    <![CDATA[ By Otavio Ehrenberger Everybody's different, but I believe that nearly all of us enjoy listening to music. If you want to keep a local version of audio streams you often listen to, you'll need to download these files. Sometimes you'll also want to cl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/download-trim-mp3-from-youtube-with-python/</link>
                <guid isPermaLink="false">66d851eb990ea4c503b968ac</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 21 Dec 2022 18:57:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/12/pexels-pixabay-164821.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Otavio Ehrenberger</p>
<p>Everybody's different, but I believe that nearly all of us enjoy listening to music.</p>
<p>If you want to keep a local version of audio streams you often listen to, you'll need to download these files. Sometimes you'll also want to clip a portion of this audio file instead of having only the whole thing available.</p>
<p>You can develop a Python script to do exactly these things. You can also extend it with additional functionality if you'd like. And I'll show you how to do it in this tutorial.</p>
<h2 id="heading-a-note-on-copyrights">A Note on Copyrights</h2>
<p>If you've used the internet before, you're likely aware that copyright issues can make a lot of people upset – on both sides of a debate about how free content should be. </p>
<p><a target="_blank" href="https://github.blog/2020-11-16-standing-up-for-developers-youtube-dl-is-back/">The very library we'll use has had its share of copyright troubles</a>. </p>
<p>Thankfully, there is copyright-free material available for us to both enjoy and play with in our programs. So we'll use the royalty-free <a target="_blank" href="https://www.youtube.com/watch?v=8OAPLk20epo">Beethoven's 4th Movement of the 9th Symphony</a> audio piece in this tutorial. </p>
<p>This guide assumes you'll use the following methods to download copyright-free material as well. Don't use this information to infringe on any copyrights!</p>
<h2 id="heading-what-well-do-in-this-tutorial">What We'll Do in This Tutorial</h2>
<p>First we're going to install the basic dependency, FFMPEG. Then we'll install the <code>youtube-dl</code> library (which also works with Vimeo and many other platforms) to download audio from a YouTube URL and use it on Python code.</p>
<p>Next, we'll download the <code>pydub</code> library to trim audio files and implement this functionality in our code.</p>
<p>Finally, we'll create some friendly user interface so we can reuse this script later without having to edit the code.</p>
<p>This will all be executed inside a <code>main()</code> function so we can separate functionality, implementation, and use.</p>
<h2 id="heading-how-to-install-the-ffmpeg-package">How to Install the FFMPEG Package</h2>
<p>This package is at the core of many multimedia programs (and all of the open-source ones I've used so far). We are going to need it for both of the Python libraries we'll install very soon.</p>
<h3 id="heading-how-to-install-on-linux">How to Install on Linux</h3>
<p>If you are on a Debian-based machine (such as Ubuntu or Kali), here's the command to install FFMPEG:</p>
<pre><code>sudo apt-get install ffmpeg
</code></pre><p>If you are using other kinds of distributions, install instructions are <a target="_blank" href="https://ffmpeg.org/download.html">here</a>.</p>
<h3 id="heading-how-to-install-on-windows">How to Install on Windows</h3>
<p>First, install the <a target="_blank" href="https://chocolatey.org/how-chocolatey-works">Chocolatey Package Manager</a>. Install instructions are <a target="_blank" href="https://chocolatey.org/install">here</a>, I'll be waiting.</p>
<p>Once you've properly installed chocolatey (if you didn't have it already), download the package from an <strong>administrative instance of Powershell</strong>:</p>
<pre><code class="lang-powershell">choco install ffmpeg
</code></pre>
<h3 id="heading-how-to-install-on-mac">How to Install on Mac</h3>
<p>If you don't have it already, <a target="_blank" href="https://brew.sh/">install homebrew</a>. Then, in a terminal:</p>
<pre><code>brew install ffmpeg
</code></pre><h2 id="heading-how-to-programmatically-download-audio-from-youtube-urls">How to Programmatically Download Audio from YouTube URLs</h2>
<p>First of all, download the youtube-dl package from pip. It's <a target="_blank" href="https://github.com/ytdl-org/youtube-dl">one of the most starred on GitHub</a>.</p>
<p><code>pip install youtube-dl</code></p>
<p>We're going to import this module and then declare a function that downloads the audio in mp3 format with reasonable quality from a YouTube URL.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> youtube_dl <span class="hljs-comment"># client to many multimedia portals</span>

<span class="hljs-comment"># downloads yt_url to the same directory from which the script runs</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">download_audio</span>(<span class="hljs-params">yt_url</span>):</span>
    ydl_opts = {
        <span class="hljs-string">'format'</span>: <span class="hljs-string">'bestaudio/best'</span>,
        <span class="hljs-string">'postprocessors'</span>: [{
            <span class="hljs-string">'key'</span>: <span class="hljs-string">'FFmpegExtractAudio'</span>,
            <span class="hljs-string">'preferredcodec'</span>: <span class="hljs-string">'mp3'</span>,
            <span class="hljs-string">'preferredquality'</span>: <span class="hljs-string">'192'</span>,
        }],
    }
    <span class="hljs-keyword">with</span> youtube_dl.YoutubeDL(ydl_opts) <span class="hljs-keyword">as</span> ydl:
        ydl.download([yt_url])

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    yt_url = <span class="hljs-string">"https://www.youtube.com/watch?v=8OAPLk20epo"</span>
    download_audio(yt_url)

main()
</code></pre>
<p>The <code>.download()</code> method will gradually download the audio stream as a <code>.webm</code> file. Once it detects that the whole file is available, it will use <code>ffmpeg</code> to convert it to an MP3 audio file. This means that if something happens, say the internet connection goes down when the file is 90% downloaded, it'll resume download at 90% instead of from the beginning – pretty neat. </p>
<p>Note that if you already have the mp3 file downloaded, the download will restart and overwrite the file.</p>
<p>Run your Python script. The audio download for this interpretation of Beethoven's 4th Movement of the 9th Symphony should have a ~33 Mb MP3 file with the video's title available locally. It'll probably be somewhat slow, so go make yourself some tea.</p>
<p>As you can see, it is possible to pass optional parameters to youtube-dl (which will also be available as a standalone CLI program outside the script). One of its capabilities is <a target="_blank" href="https://github.com/ytdl-org/youtube-dl#video-selection">downloading a series of videos from a playlist URL</a>. If you are more interested, you can read their documentation. I'll keep usage more trivial throughout this tutorial.</p>
<h2 id="heading-how-to-trim-the-downloaded-file">How to Trim the Downloaded File</h2>
<p>With the file downloaded, we're now going to arbitrarily slice it locally (you might have considered whether it is possible to simply download a clip from YouTube. All reliable methods I've found will essentially boil down to downloading the whole and then editing locally). For that we'll use the <a target="_blank" href="https://github.com/jiaaro/pydub">pydub library</a>. You can install it like this:</p>
<pre><code class="lang-python">pip install pydub
</code></pre>
<p>This is a pretty nice library that enables you to completely manipulate the audio, reducing or boosting volume at certain intervals, repeating clips, and so on. For now, we're just interested in trimming.</p>
<p>In order to trim our downloaded file, we'll have to get the filename of our newly downloaded MP3, convert the start and end points of our desired audio interval from 'hours:minutes:seconds' to milliseconds, and finally use <code>pydub</code> to slice our audio file.</p>
<h3 id="heading-how-to-get-the-file-name">How to get the file name</h3>
<p>Unfortunately, the <code>.download()</code> method won't return our generated filename, which we also won't have access to since we're just passing the URL as a parameter. But we have Python and this is a fantastic tool.</p>
<p>We know that we are looking for an <code>.mp3</code> file which was generated just before our file name lookup operation (Python is single-threaded and will execute code synchronously by default). We'll get the name of our most recent MP3 in the script directory, and that's gonna be our file.</p>
<p>We can perform this operation by listing all <code>.mp3</code> files in the local directory, collecting their timestamps as integers (which means time in milliseconds counted from a given date in the past. This means that the higher the value, the further in time the file was created.), and getting the file with the highest value.</p>
<p>For this we'll need the modules <code>glob</code> to navigate the directory and <code>os</code> to get timestamp information, both available natively.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> glob
<span class="hljs-keyword">import</span> os

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">newest_mp3_filename</span>():</span>
    <span class="hljs-comment"># lists all mp3s in local directory</span>
    list_of_mp3s = glob.glob(<span class="hljs-string">'./*.mp3'</span>)
    <span class="hljs-comment"># returns mp3 with highest timestamp value</span>
    <span class="hljs-keyword">return</span> max(list_of_mp3s, key = os.path.getctime)
</code></pre>
<h3 id="heading-how-to-get-hhmmss-as-milliseconds">How to get HH:MM:SS as milliseconds</h3>
<p>Once we slice our file, <code>pydub</code> will expect time intervals expressed in milliseconds. But for we humans, calculating the exact moment in milliseconds every time we want to trim a video would be pretty annoying, so we'll respectfully ask the computer to do it for us.</p>
<p>Our input is going to be a string in the <strong>HH:MM:SS</strong> format. This works well if we want to trim a video longer than one hour, but most of the time we'll just want to get the time interval from a minute:second interval to another. So we have to take this into consideration as well. </p>
<p>A millisecond is 1/1000 of a second, a minute is 60 seconds, and an hour is 60 minutes. So we have to get the value for hours, then for minutes, then for seconds, perform the millisecond conversion on each, and then sum the parts to reach the result.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_video_time_in_ms</span>(<span class="hljs-params">video_timestamp</span>):</span>
    vt_split = video_timestamp.split(<span class="hljs-string">":"</span>)
    <span class="hljs-keyword">if</span> (len(vt_split) == <span class="hljs-number">3</span>): <span class="hljs-comment"># if in HH:MM:SS format</span>
        hours = int(vt_split[<span class="hljs-number">0</span>]) * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>
        minutes = int(vt_split[<span class="hljs-number">1</span>]) * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>
        seconds = int(vt_split[<span class="hljs-number">2</span>]) * <span class="hljs-number">1000</span>
    <span class="hljs-keyword">else</span>: <span class="hljs-comment"># MM:SS format</span>
        hours = <span class="hljs-number">0</span>
        minutes = int(vt_split[<span class="hljs-number">0</span>]) * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>
        seconds = int(vt_split[<span class="hljs-number">1</span>]) * <span class="hljs-number">1000</span>
    <span class="hljs-comment"># time point in miliseconds</span>
    <span class="hljs-keyword">return</span> hours + minutes + seconds
</code></pre>
<h3 id="heading-how-to-get-the-trimmed-audio">How to Get the Trimmed Audio</h3>
<p>We'll now read our MP3 as a <code>pydub</code> object and slice our desired interval. The syntax is exactly the same as slicing operations on strings and arrays, but instead of an index for an element we'll use milliseconds for specific instants within the audio.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_trimmed</span>(<span class="hljs-params">mp3_filename, initial, final = <span class="hljs-string">""</span></span>):</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">not</span> mp3_filename):
        <span class="hljs-comment"># raise an error to immediately halt program execution</span>
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"No MP3 found in local directory."</span>)
    <span class="hljs-comment"># reads mp3 as a PyDub object</span>
    sound = AudioSegment.from_mp3(mp3_filename)
    t0 = get_video_time_in_ms(initial)
    print(<span class="hljs-string">"Beginning trimming process for file "</span>, mp3_filename, <span class="hljs-string">".\n"</span>)
    print(<span class="hljs-string">"Starting from "</span>, initial, <span class="hljs-string">"..."</span>)
    <span class="hljs-keyword">if</span> (len(final) &gt; <span class="hljs-number">0</span>):
        print(<span class="hljs-string">"...up to "</span>, final, <span class="hljs-string">".\n"</span>)
        t1 = get_video_time_in_ms(final)
        <span class="hljs-keyword">return</span> sound[t0:t1] <span class="hljs-comment"># t0 up to t1</span>
    <span class="hljs-keyword">return</span> sound[t0:] <span class="hljs-comment"># t0 up to the end</span>
</code></pre>
<h2 id="heading-how-to-put-it-all-together">How to Put it All Together</h2>
<blockquote>
<p>Alle Menschen werden Brüder,<br>Wo dein sanfter Flügel weilt.<br>-- Friedrich Schiller</p>
</blockquote>
<p>In case you were wondering, the previous fragment translates to "All men shall become brothers, wherever your gentle wings hover". It's a fragment of <em>Ode to Joy</em>, a poem by Friedrich Schiller, which serves as most of the lyrics to the choral parts of the 4th Movement. </p>
<p>This is the most famous fragment of the most famous movement of the most famous Beethoven symphony. Whoever you are, whenever and however you were raised, you are very likely to recognize this piece.</p>
<p>We'll now put together what we have done. We'll download the audio from YouTube, slice the <em>Ode to Joy</em> choral (from <code>9:51</code> to <code>14:04</code>), and save it as <code>&lt;filename&gt; - TRIM.mp3</code>.</p>
<p>If you have followed the tutorial correctly, update your <code>main()</code> function to execute each step in a way that'll leave you with the full MP3 and the trimmed version of it as files available in the directory you'll be running the script from. Don't forget to run the <code>main()</code> function at the end of the script.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    yt_url = <span class="hljs-string">"https://www.youtube.com/watch?v=8OAPLk20epo"</span>
    download_audio(yt_url)
    initial = <span class="hljs-string">"9:51"</span>
    final = <span class="hljs-string">"14:04"</span>
    filename = newest_mp3_filename()
    trimmed_file = get_trimmed(filename, initial, final)
    trimmed_filename = <span class="hljs-string">""</span>.join([filename.split(<span class="hljs-string">".mp3"</span>)[<span class="hljs-number">0</span>], <span class="hljs-string">"- TRIM.mp3"</span>])
    print(<span class="hljs-string">"Process concluded successfully. Saving trimmed file as "</span>, trimmed_filename)
    <span class="hljs-comment"># saves file with newer filename</span>
    trimmed_file.export(trimmed_filename, format=<span class="hljs-string">"mp3"</span>)
</code></pre>
<h2 id="heading-how-to-add-user-interaction-directly-from-the-cli">How to Add User Interaction Directly from the CLI</h2>
<p>For this part we'll need the <code>sys</code> Python module, which reads input passed from the command line (among other things). We'll simply update the variables in the <code>main()</code> function to read input from the CLI instead of the currently hard-coded data. </p>
<p>ARGV reads input sequentially as an array, starting from index 1 (0 represents the running Python script name). We'll set it up to read a URL as the first argument, then (optional) initial and final trimming instants.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sys

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">not</span> len(sys.argv) &gt; <span class="hljs-number">1</span>):
        print(<span class="hljs-string">"Please insert a multimedia-platform URL supported by youtube-dl as your first argument."</span>)
        <span class="hljs-keyword">return</span>
    yt_url = sys.argv[<span class="hljs-number">1</span>]
    download_audio(yt_url)
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">not</span> len(sys.argv &gt; <span class="hljs-number">2</span>)): <span class="hljs-comment"># exit if no instants as args</span>
        <span class="hljs-keyword">return</span>
    initial = sys.argv[<span class="hljs-number">2</span>]
    final = <span class="hljs-string">""</span>
    <span class="hljs-keyword">if</span> (sys.argv[<span class="hljs-number">3</span>]):
        final = sys.argv[<span class="hljs-number">3</span>]
    filename = newest_mp3_filename()
    trimmed_file = get_trimmed(filename, initial, final)
    trimmed_filename = <span class="hljs-string">""</span>.join([filename.split(<span class="hljs-string">".mp3"</span>)[<span class="hljs-number">0</span>], <span class="hljs-string">"- TRIM.mp3"</span>])
    print(<span class="hljs-string">"Process concluded successfully. Saving trimmed file as "</span>, trimmed_filename)
    <span class="hljs-comment"># saves file with newer filename</span>
    trimmed_file.export(trimmed_filename, format=<span class="hljs-string">"mp3"</span>)
</code></pre>
<p>Run the file to test it. Remember to update the script name to the same one that's on your machine.</p>
<pre><code class="lang-bash">python ytauddown.py https://www.youtube.com/watch?v=8OAPLk20epo 9:51 14:04
</code></pre>
<h2 id="heading-final-script">Final Script</h2>
<p>This is the final version with everything put together. Note that the comments on modules are related only to what we're using them for and that the <code>main()</code> function is being invoked at the last line.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> youtube_dl <span class="hljs-comment"># client to download from many multimedia portals</span>
<span class="hljs-keyword">import</span> glob <span class="hljs-comment"># directory operations</span>
<span class="hljs-keyword">import</span> os <span class="hljs-comment"># interface to os-provided info on files</span>
<span class="hljs-keyword">import</span> sys <span class="hljs-comment"># interface to command line</span>
<span class="hljs-keyword">from</span> pydub <span class="hljs-keyword">import</span> AudioSegment <span class="hljs-comment"># only audio operations</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">newest_mp3_filename</span>():</span>
    <span class="hljs-comment"># lists all mp3s in local directory</span>
    list_of_mp3s = glob.glob(<span class="hljs-string">'./*.mp3'</span>)
    <span class="hljs-comment"># returns mp3 with highest timestamp value</span>
    <span class="hljs-keyword">return</span> max(list_of_mp3s, key = os.path.getctime)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_video_time_in_ms</span>(<span class="hljs-params">video_timestamp</span>):</span>
    vt_split = video_timestamp.split(<span class="hljs-string">":"</span>)
    <span class="hljs-keyword">if</span> (len(vt_split) == <span class="hljs-number">3</span>): <span class="hljs-comment"># if in HH:MM:SS format</span>
        hours = int(vt_split[<span class="hljs-number">0</span>]) * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>
        minutes = int(vt_split[<span class="hljs-number">1</span>]) * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>
        seconds = int(vt_split[<span class="hljs-number">2</span>]) * <span class="hljs-number">1000</span>
    <span class="hljs-keyword">else</span>: <span class="hljs-comment"># MM:SS format</span>
        hours = <span class="hljs-number">0</span>
        minutes = int(vt_split[<span class="hljs-number">0</span>]) * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>
        seconds = int(vt_split[<span class="hljs-number">1</span>]) * <span class="hljs-number">1000</span>
    <span class="hljs-comment"># time point in miliseconds</span>
    <span class="hljs-keyword">return</span> hours + minutes + seconds

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_trimmed</span>(<span class="hljs-params">mp3_filename, initial, final = <span class="hljs-string">""</span></span>):</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">not</span> mp3_filename):
        <span class="hljs-comment"># raise an error to immediately halt program execution</span>
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"No MP3 found in local directory."</span>)
    <span class="hljs-comment"># reads mp3 as a PyDub object</span>
    sound = AudioSegment.from_mp3(mp3_filename)
    t0 = get_video_time_in_ms(initial)
    print(<span class="hljs-string">"Beginning trimming process for file "</span>, mp3_filename, <span class="hljs-string">".\n"</span>)
    print(<span class="hljs-string">"Starting from "</span>, initial, <span class="hljs-string">"..."</span>)
    <span class="hljs-keyword">if</span> (len(final) &gt; <span class="hljs-number">0</span>):
        print(<span class="hljs-string">"...up to "</span>, final, <span class="hljs-string">".\n"</span>)
        t1 = get_video_time_in_ms(final)
        <span class="hljs-keyword">return</span> sound[t0:t1] <span class="hljs-comment"># t0 up to t1</span>
    <span class="hljs-keyword">return</span> sound[t0:] <span class="hljs-comment"># t0 up to the end</span>



<span class="hljs-comment"># downloads yt_url to the same directory from which the script runs</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">download_audio</span>(<span class="hljs-params">yt_url</span>):</span>
    ydl_opts = {
        <span class="hljs-string">'format'</span>: <span class="hljs-string">'bestaudio/best'</span>,
        <span class="hljs-string">'postprocessors'</span>: [{
            <span class="hljs-string">'key'</span>: <span class="hljs-string">'FFmpegExtractAudio'</span>,
            <span class="hljs-string">'preferredcodec'</span>: <span class="hljs-string">'mp3'</span>,
            <span class="hljs-string">'preferredquality'</span>: <span class="hljs-string">'192'</span>,
        }],
    }
    <span class="hljs-keyword">with</span> youtube_dl.YoutubeDL(ydl_opts) <span class="hljs-keyword">as</span> ydl:
        ydl.download([yt_url])

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">not</span> len(sys.argv) &gt; <span class="hljs-number">1</span>):
        print(<span class="hljs-string">"Please insert a multimedia-platform URL supported by youtube-dl as your first argument."</span>)
        <span class="hljs-keyword">return</span>
    yt_url = sys.argv[<span class="hljs-number">1</span>]
    download_audio(yt_url)
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">not</span> len(sys.argv &gt; <span class="hljs-number">2</span>)): <span class="hljs-comment"># exit if no instants as args</span>
        <span class="hljs-keyword">return</span>
    initial = sys.argv[<span class="hljs-number">2</span>]
    final = <span class="hljs-string">""</span>
    <span class="hljs-keyword">if</span> (sys.argv[<span class="hljs-number">3</span>]):
        final = sys.argv[<span class="hljs-number">3</span>]
    filename = newest_mp3_filename()
    trimmed_file = get_trimmed(filename, initial, final)
    trimmed_filename = <span class="hljs-string">""</span>.join([filename.split(<span class="hljs-string">".mp3"</span>)[<span class="hljs-number">0</span>], <span class="hljs-string">"- TRIM.mp3"</span>])
    print(<span class="hljs-string">"Process concluded successfully. Saving trimmed file as "</span>, trimmed_filename)
    <span class="hljs-comment"># saves file with newer filename</span>
    trimmed_file.export(trimmed_filename, format=<span class="hljs-string">"mp3"</span>)

<span class="hljs-comment"># example usage:</span>
<span class="hljs-comment"># python ytauddown.py https://www.youtube.com/watch?v=8OAPLk20epo 9:51 14:04</span>
main()
</code></pre>
<h2 id="heading-suggested-exercises">Suggested Exercises</h2>
<ol>
<li>Detect whether the first input is a valid URL or not. Take a look at Python RegEx if you don't know where to start.</li>
<li>Detect whether the second and third inputs are in the valid format (hours:minutes:seconds <strong>OR</strong> minutes:seconds).</li>
<li>Add an option to rename the MP3 file directly from the CLI. Remember that ARGV arguments are executed in order.</li>
<li>Refactor this script in order to interact with its functionality using a GUI. Can be either a web or local app, your choice.</li>
</ol>
<h2 id="heading-final-considerations">Final Considerations</h2>
<p>I hope you'll have fun with this project and put it to good use. </p>
<p>Remember that making a living as an artist is pretty hard, especially for the majority without corporate backing. Remember to support the artists whose work you enjoy whenever you can and also remember to support open-source software.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Music for Programming – Coding Music Playlists, Radio Stations, Videos, and Lives ]]>
                </title>
                <description>
                    <![CDATA[ Do you like listening to music while you code? If so, you're in luck. This blog post is all about music for programming.  In this reference guide, I'll be sharing a variety of different playlists, radio stations, and videos that you can listen to whi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/music-for-programming-youtube-channels-twitchtv-web-radios-and-more/</link>
                <guid isPermaLink="false">66bb9216add24ba427325101</guid>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Productivity ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Gaël Thomas ]]>
                </dc:creator>
                <pubDate>Mon, 14 Mar 2022 19:16:43 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/03/Big-Text-How-to-Youtube-Thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Do you like listening to music while you code? If so, you're in luck. This blog post is all about music for programming. </p>
<p>In this reference guide, I'll be sharing a variety of different playlists, radio stations, and videos that you can listen to while coding. Whether you're a beginner or an experienced programmer, you will find something to listen to. </p>
<p>So put on your headphones and get to work!</p>
<h2 id="heading-coding-music-on-spotify">Coding music on Spotify</h2>
<p>One of my favorite ways to listen to music while working is through Spotify. If you're not familiar with Spotify, it's a music streaming service that you can use for free (with ads) or for a monthly fee (no ads). </p>
<p>I like using Spotify because it has a huge selection of songs and albums, and you can create your own playlists. Plus, if you have the paid version, you can download songs and listen to them offline.</p>
<p>If you're looking for some good coding music, here are a few Spotify playlists:</p>
<ul>
<li><a target="_blank" href="https://open.spotify.com/playlist/37i9dQZF1DWWQRwui0ExPn?si=29e9d713abbd4a91">Lofi Beats</a></li>
<li><a target="_blank" href="https://open.spotify.com/album/5oxHFqFU06ufRSZt8izJX6?si=MX_8hXM7SaWYefMFPN_4Ag">Electronic Music for Coding: Fast Programming Instrumentals for Concentration</a></li>
<li><a target="_blank" href="https://open.spotify.com/playlist/37i9dQZF1DX5trt9i14X7j?si=96dcbb8950ab4bbe">Coding Mode</a></li>
<li><a target="_blank" href="https://open.spotify.com/playlist/0vvXsWCC9xrXsKd4FyS8kM?si=c615a01d6e4646f6">Lofi Hip Hop Music - Beats to Relax/Study by Lofi Girl</a></li>
<li><a target="_blank" href="https://open.spotify.com/playlist/37i9dQZF1DWZeKCadgRdKQ?si=06a40e58cffc4fdc">Deep Focus</a></li>
<li><a target="_blank" href="https://open.spotify.com/playlist/37i9dQZF1DX0wMD4IoQ5aJ?si=16161a1feec54601">Electronic Focus</a></li>
<li><a target="_blank" href="https://open.spotify.com/playlist/37i9dQZF1DX8wtrGDH81Oa?si=c93d44084cc247bc">House Focus</a></li>
</ul>
<h2 id="heading-music-for-programming-on-youtube">Music for programming on YouTube</h2>
<p>YouTube is another great resource for finding music to listen to while programming. In fact, there are entire channels dedicated to that kind of music.</p>
<p>Here are a few productivity/programming/concentration music channels on YouTube:</p>
<ul>
<li><a target="_blank" href="https://www.youtube.com/channel/UCwVQIkAtyZzQSA-OY1rsGig">Chill Music Lab</a></li>
<li><a target="_blank" href="https://www.youtube.com/c/FilFar">Filfar</a></li>
<li><a target="_blank" href="https://www.youtube.com/channel/UCDK54OyzWnOczY7LzEd9V2Q/">Lofi Ghostie</a></li>
<li><a target="_blank" href="https://www.youtube.com/c/lofigeek/">Lofi Geek</a></li>
<li><a target="_blank" href="https://www.youtube.com/c/LofiGirl/">Lofi Girl</a></li>
<li><a target="_blank" href="https://www.youtube.com/c/GreenredProductions/">Greenred Productions</a></li>
<li><a target="_blank" href="https://www.youtube.com/channel/UCNl1nMcK1X9EHsW_b5dGzaA/">Productivity Music by Evan Carmichael</a></li>
<li><a target="_blank" href="https://www.youtube.com/c/Fluidified/">Fluidified</a></li>
</ul>
<p>There are also many 24/7 coding music lives. Let me share with you some YouTube queries to find them:</p>
<ul>
<li><a target="_blank" href="https://www.youtube.com/results?search_query=24%2F7+coding">24/7 coding</a></li>
<li><a target="_blank" href="https://www.youtube.com/results?search_query=24%2F7+lofi">24/7 lofi</a></li>
<li>P<a target="_blank" href="https://www.youtube.com/results?search_query=programming+music&amp;sp=EgJAAQ%3D%3D">rogramming music</a></li>
<li><a target="_blank" href="https://www.youtube.com/results?search_query=coffee+music&amp;sp=EgJAAQ%3D%3D">Coffee music</a></li>
<li><a target="_blank" href="https://www.youtube.com/results?search_query=focus+music&amp;sp=EgJAAQ%3D%3D">Focus music</a></li>
</ul>
<h2 id="heading-music-for-coding-on-twitchtv">Music for coding on TwitchTV</h2>
<p>TwitchTV is a live streaming video platform where you can watch people play video games, listen to music, and more. There is a lot of great content that you can watch and listen to while coding.</p>
<p>Here are a few music channels on TwitchTV:</p>
<ul>
<li><a target="_blank" href="https://www.twitch.tv/leekbeats">LeekBeats</a></li>
<li><a target="_blank" href="https://www.twitch.tv/kubomusic">KuboMusic</a></li>
<li><a target="_blank" href="https://www.twitch.tv/sixtysevenradio">SixtySevenRadio</a></li>
<li><a target="_blank" href="https://www.twitch.tv/spinninrecords">SpinninRecords</a></li>
</ul>
<p>On this platform, some streamers like to create virtual offices. Most of the time, they are streaming in the <a target="_blank" href="https://www.twitch.tv/directory/game/Just%20Chatting">"Just Chatting"</a> or <a target="_blank" href="https://www.twitch.tv/directory/game/Software%20and%20Game%20Development">"Software and Game Development"</a> sections. You can launch their streaming in the background, work with them and listen to good music.</p>
<p>It can be motivating when you're working alone at home and need to have the feeling of working with someone. Some of these streamers are even doing Pomodoro Sessions that you can follow to <a target="_blank" href="https://herewecode.io/blog/low-productivity-how-to-stop-wasting-time/">get your code done and improve your focus</a>.</p>
<h2 id="heading-web-radio-broadcasting-music-for-developers">Web radio broadcasting music for developers</h2>
<p>If you're looking for something a little more low-key, you can try listening to radio dedicated for developers. There are a few stations that play nothing but programming music.</p>
<p>Here are a few of them:</p>
<ul>
<li><a target="_blank" href="https://coderadio.freecodecamp.org/">Code radio by freeCodeCamp</a></li>
<li><a target="_blank" href="https://musicforprogramming.net/latest/">musicForProgramming();</a></li>
<li><a target="_blank" href="https://radio.x-team.com/">X-Team Radio</a></li>
<li><a target="_blank" href="https://www.lofi.cafe/">Lofi Cafe</a></li>
</ul>
<h2 id="heading-wrapping-up">Wrapping up</h2>
<p>Thank you for reading this article. I hope these links will be helpful and motivate you in your day-to-day developer work. Keep coding, and keep hustling.</p>
<p>I regularly deliver content about web development, personal growth as a developer, and my journey as an aspiring digital nomad and remote software engineer. If you don't want to miss them, I invite you to <a target="_blank" href="https://twitter.com/gaelgthomas">follow me on Twitter</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to Code RPG – Full Soundtrack + How I Made It ]]>
                </title>
                <description>
                    <![CDATA[ First: what you probably came here for. Here's the full soundtrack to Learn to Code RPG. 36 minutes of jazz. https://youtu.be/8TDsGUFFXSY If you dig it, subscribe to my new music channel and leave a comment with feedback or your favorite track. I am ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-code-rpg-soundtrack/</link>
                <guid isPermaLink="false">66b8d49bf583f6362a68ce13</guid>
                
                    <category>
                        <![CDATA[ Game Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GameDev ]]>
                    </category>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Thu, 23 Dec 2021 18:53:07 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/Splash-Art-2-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>First: what you probably came here for. Here's the full soundtrack to Learn to Code RPG. 36 minutes of jazz.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/8TDsGUFFXSY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>If you dig it, subscribe to my new music channel and leave a comment with feedback or your favorite track.</p>
<p>I am currently uploading this to Spotify.</p>
<h2 id="heading-how-i-made-the-learn-to-code-rpg-soundtrack">How I Made The Learn to Code RPG Soundtrack</h2>
<p>The Funk of the 70s.<br>The Jazz of the 80s.<br>The Hip Hop of the 90s.</p>
<p>I was a lover of music. But I never really learned how to play.</p>
<p>That changed 3 months into the pandemic. I bought an electric bass. I clocked several hundred hours in Rocksmith practicing the bass lines of Nathan East, Bootsy Collins, and Andy Rourke from The Smiths.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/Bootsy_Collins-04.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Bootsy Collins, the legendary bassist for both Parliament and James Brown</em></p>
<p>For theory, I made heavy use of Musician YouTube, including <a target="_blank" href="https://www.youtube.com/watch?v=qFVpSjRUD2E&amp;list=PLW0NGgv1qnfzb1klL6Vw9B0aiM7ryfXV_">Rick Beato</a> and <a target="_blank" href="https://www.youtube.com/watch?v=kBIXDov8G80&amp;list=PLBcZM94cb2Ux02BDGRwFcD09EsFe8T7rr">Adam Neely</a>, and most importantly <a target="_blank" href="https://www.youtube.com/watch?v=OgipSAdZiKw&amp;list=PL_KwW-re--9GGknaye6BBmWeRNqL95xiJ">Paul Del Bello</a>.</p>
<p>I also got guidance from two musicians in the freeCodeCamp community: Lawrence Yeo (AKA <a target="_blank" href="https://open.spotify.com/artist/5K2ELFl9mZj4wSulXtgUcV">Trebles and Blues</a>) and Devin Lane (AKA <a target="_blank" href="https://open.spotify.com/artist/7dFoXdChS3pVrba3MyuuTu">Gentle Return</a>). Check out their tunes.</p>
<p>Before long I came to realize that almost any piece of music could potentially sound good with nothing more than drums, bass, and a piano. But I didn't want to find a drummer or a pianist, so I set out to learn how to play those instruments myself.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/gear.jpeg" alt="Image" width="600" height="400" loading="lazy">
<em>My gear – a Fender Precision bass, Roland Octopad, and M-Audio midi controller</em></p>
<p>I started by learning to read music, doing ear training, memorizing the circle of fifths – all the things I presume you learn in music school. But I quickly abandoned that in favor of just sitting down and writing songs.</p>
<p>I bought a $100 midi controller and a $200 copy of Logic Pro, then got to work learning how to compose chords and melodies, and play to a metronome.</p>
<p>It took about 6 months, but I recorded dozens of instrumental tracks, 10 of which made it into the first release of Learn to Code RPG. I am working on several more songs with similar tempos and instrumentation for future expansions to the game.</p>
<h2 id="heading-how-do-you-make-your-keyboard-sounds">How do you make your keyboard sounds?</h2>
<p>I build my synth patches using Logic's built-in Alchemy additive synth and its analog synth. I also use some of Logic's stock pianos, electric pianos, organs, and its celesta.</p>
<h2 id="heading-what-do-you-use-for-drums">What do you use for drums?</h2>
<p>I play a heavily modified Logic producer drum kit on a Roland Octopad. I use heavy muting and very few plugins. I'm going for a dry, crisp 1970s sound.</p>
<p>You may notice that I only use the kick, snare, ride, and hi-hat. That is all that the 70s greats like Clyde Stubblefield and Gregory Coleman needed to build grooves. So I imposed that constraint upon myself. (I find that reasonable constraints can accelerate the creative process.)</p>
<p>Rather than playing the classic 70s breakbeats, I attempt to weave my own around the song's bass line.</p>
<h2 id="heading-what-is-that-kick-drum-sound">What is that kick drum sound?</h2>
<p>A booming house music kick layered on top of a clicky death metal kick.</p>
<h2 id="heading-what-is-that-bass-sound">What is that bass sound?</h2>
<p>The bass you hear on the Learn to Code RPG soundtrack is a fattened-up analog synth sound with some delay and distortion. I can't quite play bass tight enough for studio-grade recordings, and rather than chopping it my playing, I just played on a keyboard. I EQ the bass at 40 Hz to make room room for the kick, and so it still sounds OK on a phone's speakers.</p>
<h2 id="heading-can-i-use-your-music-in-my-videos-on-my-live-stream">Can I use your music in my videos / on my live stream?</h2>
<p>DM me on Twitter and we can discuss this.</p>
<h2 id="heading-can-you-play-my-tech-conference-roller-skating-rink-art-opening">Can you play my tech conference / roller skating rink / art opening?</h2>
<p>That sounds like a blast. DM me on Twitter.</p>
<p>Thanks for reading about my music. I never thought I'd discover a new all-consuming passion at age 40. But it has been a blast. And I am just getting started. If I am lucky, I will still have 40 years left in me to learn music and create work I am proud of.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Python to Detect Music Onsets ]]>
                </title>
                <description>
                    <![CDATA[ In music terminology, an onset refers to the beginning of a musical note or other sound. In this post, we will look at how to detect music onsets with Python's audio signal processing libraries, Aubio and librosa. This tutorial is relevant even if yo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/use-python-to-detect-music-onsets/</link>
                <guid isPermaLink="false">66d4602a55db48792eed3f79</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jul 2021 16:56:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/07/Python-Music-Onset-Detection.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In music terminology, an <strong>onset</strong> refers to <strong>the beginning of a musical note or other sound.</strong> In this post, we will look at how to detect music onsets with Python's audio signal processing libraries, <a target="_blank" href="https://aubio.org/">Aubio</a> and <a target="_blank" href="https://librosa.org/doc/latest/index.html">librosa</a>.</p>
<p>This tutorial is relevant even if your application doesn't use Python - for example, you are building a game in Unity and C# which doesn't have robust libraries for onset detection.</p>
<p>If that is the case, you may export the detected onset timestamps to a text file to read into your engine of choice.</p>
<p>If you prefer a video tutorial to an article, here's the video version of this tutorial.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/aMMI0nAKgI0" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-applications-of-music-onset-detection">Applications of Music Onset Detection</h2>
<p>I came across this music onset detection technique when I was building <strong>a rhythm game</strong> and wanted a way to <strong>automatically</strong> generate beat maps for any song.</p>
<p>Check out the end of this article for my <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">open-source rhythm game</a> and <a target="_blank" href="https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973">my step-by-step course on how I built it.</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/ezgif.com-gif-maker.gif" alt="Image of a rhythm game similar to Guitar Hero" width="600" height="400" loading="lazy"></p>
<p><em>My rhythm game showcase</em></p>
<p>Besides building a rhythm game, this technique has a lot of other applications.</p>
<p>For example, detecting onsets is usually the first step in <strong>music information retrieval and analysis</strong>.</p>
<p>Another example could be that we are building a game in which there are combat scenes. We may detect the onsets in the BGM and spawn an enemy at every onset. This can create a unique pacing in our game.</p>
<p>I'll demonstrate how to detect music onsets using two different Python packages for <strong>audio signal processing</strong>, <a target="_blank" href="https://aubio.org/">Aubio</a> and <a target="_blank" href="https://librosa.org/doc/latest/index.html">librosa</a>. Both packages detect onsets pretty accurately. The small difference is that librosa works for the <strong>OGG</strong> format while Aubio doesn't.</p>
<h2 id="heading-how-to-set-up-the-development-environment">How to Set up the Development Environment</h2>
<p>We will be installing our packages in a virtual environment.</p>
<p>In the command line, we create a virtual environment named <code>python-aubio-librosa</code> as follows. <code>-m</code> stands for <code>module</code>.</p>
<pre><code class="lang-pgsql">$ python3 -m venv python-aubio-librosa
</code></pre>
<p>Then we activate the virtual environment:</p>
<pre><code class="lang-pgsql">$ . python-aubio-librosa/bin/activate
</code></pre>
<p>Note that if you try to activate the environment using the following command, you will get an error:</p>
<pre><code class="lang-pgsql">$ ./python-aubio-librosa/bin/activate
-bash: ./python-aubio-librosa/bin/activate: Permission denied
</code></pre>
<p>Once your environment is activated, the name of the environment will show in parentheses:</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ ...
</code></pre>
<p>We can check that if we invoke <code>python</code> or <code>pip</code>, the invoked programs will be those in our virtual environment instead of the system-level ones.</p>
<p>If we haven't activated our environment, the output will point to the system-level programs.</p>
<pre><code class="lang-pgsql">$ which python
/usr/bin/python
$ which pip
/usr/<span class="hljs-keyword">local</span>/bin/pip
</code></pre>
<p>Once we have activated our environment, the output will point to the local ones.</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ which python
/Users/USERNAME/Desktop/python-aubio-librosa/bin/python
(python-aubio-librosa) $ which pip
/Users/USERNAME/Desktop/python-aubio-librosa/bin/pip
</code></pre>
<h2 id="heading-how-to-install-and-use-aubio">How to Install and Use Aubio</h2>
<p>We will install Aubio via <code>pip</code>:</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ pip install aubio
</code></pre>
<p>The function that we will use to generate a list of onset timestamps as floating point numbers in seconds is as follows. This function comes from Aubio's <a target="_blank" href="https://github.com/aubio/aubio/blob/master/python/demos/demo_onset.py">official documentations</a>, so we can just use it without learning about the nitty-gritty details (like FFT, Fast-Fourier Transformations) in audio signal processing.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> aubio <span class="hljs-keyword">import</span> source, onset

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_onset_times</span>(<span class="hljs-params">file_path</span>):</span>
    window_size = <span class="hljs-number">1024</span> <span class="hljs-comment"># FFT size</span>
    hop_size = window_size // <span class="hljs-number">4</span>

    sample_rate = <span class="hljs-number">0</span>
    src_func = source(file_path, sample_rate, hop_size)
    sample_rate = src_func.samplerate
    onset_func = onset(<span class="hljs-string">'default'</span>, window_size, hop_size)

    duration = float(src_func.duration) / src_func.samplerate

    onset_times = [] <span class="hljs-comment"># seconds</span>
    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>: <span class="hljs-comment"># read frames</span>
        samples, num_frames_read = src_func()
        <span class="hljs-keyword">if</span> onset_func(samples):
            onset_time = onset_func.get_last_s()
            <span class="hljs-keyword">if</span> onset_time &lt; duration:
                onset_times.append(onset_time)
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">break</span>
        <span class="hljs-keyword">if</span> num_frames_read &lt; hop_size:
            <span class="hljs-keyword">break</span>

    <span class="hljs-keyword">return</span> onset_times
</code></pre>
<p>Then, we write a <code>main</code> function that takes in the path to an audio file, and outputs the onset timestamps to a file, keeping the first four decimal places in each float, one float per line.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    file_path = <span class="hljs-string">'../game/audio/my-music.mp3'</span>
    onset_times = get_onset_times(file_path)
    <span class="hljs-comment"># remove extension, .mp3, .wav etc.</span>
    file_name_no_extension, _ = os.path.splitext(file_path)
    output_name = file_name_no_extension + <span class="hljs-string">'.beatmap.txt'</span>
    <span class="hljs-keyword">with</span> open(output_name, <span class="hljs-string">'wt'</span>) <span class="hljs-keyword">as</span> f:
        f.write(<span class="hljs-string">'\n'</span>.join([<span class="hljs-string">'%.4f'</span> % onset_time <span class="hljs-keyword">for</span> onset_time <span class="hljs-keyword">in</span> onset_times]))
</code></pre>
<p>Let's invoke the script from the command line. Aubio might raise a warning about accuracy but my experimentation shows that Aubio is still pretty accurate.</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ python generate_beatmap_aubio.py 
[mp3 @ <span class="hljs-number">0x7fe671031e00</span>] Estimating duration <span class="hljs-keyword">from</span> bitrate, this may be inaccurate
</code></pre>
<p>An example output file would look like below. For a short 15-second music clip, Aubio detected 26 onsets. These are the timestamps that we can use for our application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/07/Screen-Shot-2021-07-20-at-13.54.18.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>An example output file consisting of onset timestamps</em></p>
<p>And that's it for Aubio.</p>
<h2 id="heading-how-to-install-and-use-librosa">How to Install and Use Librosa</h2>
<p>Similar to Aubio, we will install librosa also via <code>pip</code>:</p>
<pre><code class="lang-pgsql">(python-aubio-librosa) $ pip install librosa
</code></pre>
<p>Compared to Aubio, librosa's library methods are easier to use. <code>librosa.load</code> returns a NumPy array <code>x</code> and a sampling rate <code>sr</code>, which we pass to <code>librosa.onset.onset_detect</code> to get a list of onset frames.</p>
<p>Finally we convert onset frames into onset timestamps, and write each timestamp to an output file like we did for Aubio.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> librosa

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    file_path = <span class="hljs-string">'../game/audio/my-music.ogg'</span>
    x, sr = librosa.load(file_path)
    onset_frames = librosa.onset.onset_detect(x, sr=sr, wait=<span class="hljs-number">1</span>, pre_avg=<span class="hljs-number">1</span>, post_avg=<span class="hljs-number">1</span>, pre_max=<span class="hljs-number">1</span>, post_max=<span class="hljs-number">1</span>)
    onset_times = librosa.frames_to_time(onset_frames)
    <span class="hljs-comment"># remove extension, .mp3, .wav etc.</span>
    file_name_no_extension, _ = os.path.splitext(file_path)
    output_name = file_name_no_extension + <span class="hljs-string">'.beatmap.txt'</span>
    <span class="hljs-keyword">with</span> open(output_name, <span class="hljs-string">'wt'</span>) <span class="hljs-keyword">as</span> f:
        f.write(<span class="hljs-string">'\n'</span>.join([<span class="hljs-string">'%.4f'</span> % onset_time <span class="hljs-keyword">for</span> onset_time <span class="hljs-keyword">in</span> onset_times]))
</code></pre>
<p>The output file will be in the same format as shown above for Aubio.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Thanks for reading and I hope you are ready to apply this onset detection technique to your next project. 🎶</p>
<p>As a recap of the differences between Aubio and Librosa, both detect onsets pretty accurately based on my experimentation.</p>
<p>Aubio is more restricted in terms of audio file formats: it raises a warning about accuracy for MP3 files and doesn't handle OGG files.</p>
<p>On the flip side, Librosa is able to handle most common audio file formats: MP3, OGG, FLAC, and M4A. Librosa's library interface is also easier to use than Aubio's, especially for those of us who aren't pros in signal processing.</p>
<p>Check out the resources below if you'd like to learn more or get inspired for your next project!</p>
<h2 id="heading-resources">Resources</h2>
<p>You can check out the code used in this tutorial <a target="_blank" href="https://github.com/RuolinZheng08/renpy-minigames101/tree/master/generate_beatmap">on my GitHub</a> or <a target="_blank" href="https://youtu.be/aMMI0nAKgI0">watch the video version of this tutorial on YouTube.</a></p>
<p>If you are interested in building a rhythm game, check out <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">my open-source one built in Python on GitHub</a> and my Udemy course in which we will build the game from the ground up.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973">https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973</a></div>
<p> </p>
<p>If you'd like to know whether the course is for you, check out my course promotional video on YouTube and <a target="_blank" href="https://www.udemy.com/course/renpy-minigames/?referralCode=46F88E557D14A0FDD973">free sample lectures on Udemy.</a></p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/_AaUKSjTNY8" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>My YouTube channel also features other fun project tutorials like <a target="_blank" href="https://youtu.be/UBwvFuTC1ZE">building a Discord AI Chatbot</a>, and, <a target="_blank" href="https://youtu.be/H2gnD7Ixeao">a series of coding interview crash courses</a> I'm developing. Hope to see you there!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How My Musical Training Has Helped Me Learn How to Code ]]>
                </title>
                <description>
                    <![CDATA[ You might be thinking to yourself, what does music have to do with learning how to code? Well, as a professional oboist, I have noticed a lot of parallels between studying music and studying programming.  Both require a lot of focus, discipline, and ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-my-musical-training-helped-me-learn-how-to-code/</link>
                <guid isPermaLink="false">66b8d95b98b552b8a8592b20</guid>
                
                    <category>
                        <![CDATA[ learn to code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning to code ]]>
                    </category>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ self-improvement  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Jessica Wilkins ]]>
                </dc:creator>
                <pubDate>Mon, 11 Jan 2021 18:03:18 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5ff9688d75d5f706921cace0.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You might be thinking to yourself, what does music have to do with learning how to code?</p>
<p>Well, as a professional <a target="_blank" href="https://en.wikipedia.org/wiki/Oboe">oboist</a>, I have noticed a lot of parallels between studying music and studying programming. </p>
<p>Both require a lot of focus, discipline, and perseverance to become great.</p>
<p>Here are some examples of how my musical training helps me stay focused in learning how to code.</p>
<h2 id="heading-just-get-it-done">Just Get it Done</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-289.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@gpthree?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;George Pagan III / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>Back in college, there were times I wasn't prepared for my lessons, rehearsals, or exams. </p>
<p>Instead of taking ownership for my lack of preparation I would make excuses like,</p>
<blockquote>
<p>“I was busy studying for a music theory test and that’s why I couldn’t prepare for my lesson.”</p>
</blockquote>
<p>or</p>
<blockquote>
<p>“I have a big audition coming up so I was busy preparing for that.”</p>
</blockquote>
<p>No matter what excuse I had in the moment for being unprepared, my oboe professor never bought it. He always believed there was enough time in the day to practice, study, and have a little bit of a social life.</p>
<p>So, he would often drop that famous NIKE slogan, "Just Do It."</p>
<p>I would always get a little laugh about it, but he was making a valid point.</p>
<p>As a self-taught programmer, there are so many distractions around you all the time. It is very easy for thoughts to come into your head like,</p>
<blockquote>
<p>“I really should do some more algorithm practice, but maybe tomorrow.”</p>
</blockquote>
<p>or</p>
<blockquote>
<p>“I really should get started on that new project but I am really busy.”</p>
</blockquote>
<p>Most people in life do not have 24 hours of uninterrupted free time to do whatever we want. There will always be something that comes up and we just have to deal with it.</p>
<p>The harsh reality is that we have to stay disciplined to get our work done so we can become better programmers.</p>
<p>So when you are about to make another excuse for procrastination remember to get it done anyway! </p>
<h2 id="heading-practice-makes-perfect-or-does-it">Practice Makes Perfect - Or Does it?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-290.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@samthewam24?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;Samuel Sianipar / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>We have all heard the saying, “Practice makes Perfect”.</p>
<p>Well, that is not entirely true. </p>
<p>My oboe professor in college would always dismiss this phrase and instead tell us “Practice Makes Permanent”.</p>
<p>Whenever I was trying to learn a new piece, I would get frustrated that it wasn’t getting any better. No matter how many times I practiced, it was still a mess.</p>
<p>But I learned early on that just showing up and practicing is not enough. You have to practice smart.</p>
<p>So I learned how to break things down into small chunks, slow down the metronome and work on that passage until it was right. </p>
<p>Once I changed my practicing habits, I started to become a much better musician.</p>
<p>In learning how to code, rushing through a course is not going to make you a better programmer. In some cases, you might develop some bad habits that will be tough to break later on.</p>
<p>It is better to tackle one problem at a time and work through it slowly than rush through all of them.</p>
<p>If you practice smart, then the concepts will start to make more sense. </p>
<h2 id="heading-consistency-is-key">Consistency is Key</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-291.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@xps?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;XPS / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>I was one of those kids that started piano at age 7 and was really into it for the first few years. </p>
<p>But once I started working on harder pieces, I just lost the motivation to practice.</p>
<p>I was very sporadic with my practicing habits, and as a result I wasn’t getting any better. My lessons ended up being a complete waste because we were just going over the same things week after week.</p>
<p>With all of the hype around learning how to code at home, a lot of people dive in with an initial excitement. They will go through tutorials and build small projects from class.</p>
<p>But as the months go by, people start to lose motivation. </p>
<p>They start to code less often and eventually lose interest and quit.</p>
<p>In learning how to code, you have to come up with a consistent schedule or else you will never become a programmer. </p>
<p>You will always be stuck struggling with the fundamentals and never be able to build more complex projects.</p>
<p>You don’t have to put in 12 hours a day to be successful. Build a schedule that works for you.</p>
<p>If you only have 2 hours a day to dedicate to learning, then stick with that. </p>
<p>But repeating a cycle of doing 8 hours one day and then taking two weeks off will just lead to lack of progress.</p>
<p>Consistency is the key to learning something well. </p>
<h2 id="heading-practice-what-you-dont-know">Practice what you don’t know</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/04/image-292.png" alt="Image" width="600" height="400" loading="lazy">
_Photo by [Unsplash](https://unsplash.com/@surface?utm_source=ghost&amp;utm_medium=referral&amp;utm_campaign=api-credit"&gt;Surface / &lt;a href="https://unsplash.com/?utm_source=ghost&amp;utm_medium=referral&amp;utm<em>campaign=api-credit)</em></p>
<p>During my junior year of college, our wind ensemble conductor told us something that has stuck with me ever since.</p>
<p>He said, “Have the courage to practice what you don’t know.”</p>
<p>Most of us wanted to practice music that we knew sounded good in the practice room. It was probably a way for us to feel good and stroke our egos a little bit.</p>
<p>But, my conductor was right.</p>
<p>If you don’t challenge yourself and work on your weaknesses then you will never grow as a musician.</p>
<p>There is so much to learn when you are just starting out as a beginner programmer. It is really tempting to only focus on the things you enjoy.</p>
<p>It makes sense that you want to feel good about yourself and show off what you know. But that doesn’t give you the excuse to ignore what you are bad at.</p>
<p>If you know you are bad at algorithms, don’t ignore them. Make extra time to work on them so they become easier to solve.</p>
<p>Or maybe you are struggling with recursion. It is not going to go away just because you don’t like it.</p>
<p>Even though it is difficult in the moment, you have to have the discipline to sit down and learn what you need to learn.</p>
<p>I hope you enjoyed this article. If you want to learn more about me, follow me on twitter @codergirl1991</p>
<p>Happy coding!  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Code Radio: 24/7 music designed for coding ]]>
                </title>
                <description>
                    <![CDATA[ You can go ahead and start listening to Code Radio while you read this Most developers I know listen to music while they code. When the meetings are over, the headphones come out. And devs aren’t alone. According to a study Spotify conducted in 2014,... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/code-radio-24-7/</link>
                <guid isPermaLink="false">66b8d2d40c9c1d363b7c4213</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Thu, 18 Jul 2019 16:26:01 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/code-radio-meta-image-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <h3 id="heading-you-can-go-ahead-and-start-listening-to-code-radio-while-you-read-thishttpscoderadiofreecodecamporg"><strong><a target="_blank" href="https://coderadio.freecodecamp.org">You can go ahead and start listening to Code Radio while you read this</a></strong></h3>
<p>Most developers I know listen to music while they code. When the meetings are over, the headphones come out.</p>
<p>And devs aren’t alone. According to a study Spotify conducted in <a target="_blank" href="https://news.spotify.com/us/2014/04/10/rocking-9-to-5/">2014</a>, 61% of people listen to music while they work — mostly to stay focused, and to drown out surrounding noise.</p>
<p>So in 2018, freeCodeCamp.org created a music live stream on our YouTube channel called Code Radio.</p>
<p>And now we've gone a step further: we've moved Code Radio off of YouTube and onto our own fast, data-efficient internet radio station.</p>
<div class="embed-wrapper">
        <blockquote class="twitter-tweet">
          <a href="https://twitter.com/ossia/status/1150846010412941314"></a>
        </blockquote>
        <script defer="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></div>
<h3 id="heading-choosing-the-tunes"><strong>Choosing the tunes</strong></h3>
<p>We made a couple critical decisions early on:</p>
<ol>
<li>There would be no advertisements. This wasn’t a hard decision, since we don’t have ads anywhere on freeCodeCamp.org or our YouTube channel, anyway.</li>
<li>The genre would be instrumental downtempo music, which is relaxing and non-distracting. My grad school classmates and I had listened to thousands of hours of this type of music while studying and hadn’t gotten bored of it yet. So it seemed like a safe bet for a coding live stream as well.</li>
</ol>
<p>Fortunately, I knew a prolific musician in the freeCodeCamp community.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/W9uzUsVcnsMnihDpdRr45BuAgFlFG3CSYO--" alt="Image" width="800" height="496" loading="lazy">
<em>Lawrence hanging out in LA (photo by Leah Perrino)</em></p>
<p><a target="_blank" href="https://twitter.com/TreblesandBlues">Lawrence Yeo</a> — also known as Trebles and Blues — is a DJ and music producer in Los Angeles. His work encompasses many genres: American soul, jazz, Brazilian funk, and even Korean folk music. The common root that connects all these influences together is hip hop — blending percussive elements and sampling techniques to create a wide variety of instrumental works.</p>
<p>In addition to his work as a musician, Lawrence enjoys programming. Among other things, he built <a target="_blank" href="https://treblesandbluesmusic.com/">his own website for his music</a>.</p>
<p>Lawrence hand-curated Code Radio's more than 1,250 songs.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/B8e3cll6sZKJfcbDDw3UFPeTt1yf1PU01Qmf" alt="Image" width="800" height="533" loading="lazy">
<em>Lawrence working on some new grooves (Photo by Joannza Lo)</em></p>
<h3 id="heading-making-code-radio-fast-and-accessible"><strong>Making Code Radio fast and accessible</strong></h3>
<p>Now you can listen to Code Radio on your phone. It is now extremely data-efficient. The website itself loads fast, and the music starts playing almost immediately.</p>
<p>If you open Code Radio on your phone, you can switch between apps and the music will keep playing. You can even lock your phone and continue listening, and have the ability to stop and start the music from your headphones or lock screen.</p>
<p>And if your phone has limited data, you can kick the bitrate down to 64kbps to cut Code Radio's data usage in half.</p>
<h3 id="heading-again-heres-a-link-to-code-radiohttpscoderadiofreecodecamporg"><strong><a target="_blank" href="https://coderadio.freecodecamp.org">Again, here's a link to Code Radio</a></strong></h3>
<p>Be sure to bookmark Code Radio and come back. We'll continue to expand and refine the playlist, and add additional features. You can tweet about Code Radio using the #CodeRadio hashtag.</p>
<p>Enjoy the tunes, and happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The Fall and Rise of Code Radio ]]>
                </title>
                <description>
                    <![CDATA[ Code Radio is an internet radio station run by the freeCodeCamp community. We play music designed to help you focus while you're coding. Over the past year, Code Radio had grown to be one of the largest music streams on YouTube. People played it in t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/the-fall-and-rise-of-code-radio/</link>
                <guid isPermaLink="false">66b8d5cfd482a18d3e02825a</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Quincy Larson ]]>
                </dc:creator>
                <pubDate>Mon, 08 Jul 2019 17:36:33 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2019/07/maxresdefault--3-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Code Radio is an internet radio station run by the freeCodeCamp community. We play music designed to help you focus while you're coding.</p>
<p>Over the past year, Code Radio had grown to be one of the largest music streams on YouTube. People played it in their coffee shops and co-working spaces. Wherever people coded, the familiar groove of Code Radio could be heard not too far off in the distance.</p>
<p>In the past 28 days alone, developers listened to Code Radio for more than 14 million minutes. (That's the equivalent of a whopping 27 years of jamming out and coding.)</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/Channel_Analytics_-_YouTube_Studio-2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-the-fall">The Fall</h1>
<p>One of the 1,250+ songs on Code Radio contained a short audio sample from an anime that played over a beat at the end of a song.</p>
<p>It turned out that a Japanese media company - through a series of acquisitions - happened to own the rights to that anime. And they used some sort of automated system to trawl YouTube and identify streams that had any samples from their vast catalog of intellectual property.</p>
<p>One of those streams was Code Radio. And on Wednesday morning, their system filed an automated takedown request to YouTube.</p>
<p>Just like that, the stereos in 1,000 cafes, offices, and hackerspaces around the world fell silent. Our Code Radio stream was replaced with this message from YouTube:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/07/_1__Code_Radio_--___--_24_7_concentration_music_for_programmers_--_jazzy_beats_from_freeCodeCamp_org_-_YouTube.png" alt="1__Code_Radio%F0%9F%8E%A7___%F0%9F%92%BB_24_7_concentration_music_for_programmers_%F0%9F%94%A5_jazzy_beats_from_freeCodeCamp_org_-_YouTube|690x422" width="600" height="400" loading="lazy"></p>
<p>We immediately contacted YouTube support. This had to be a mistake.</p>
<p>The customer service reps we talked to were friendly. But they didn't know how to fix it. They didn't even know how we could regain access our channel's streaming controls. Instead they said they "would look into it and get back to us."</p>
<p>(As of Monday afternoon, we have yet to hear back from them.)</p>
<p>So in the depths of this confusion - in a sea of tweets and emails from dedicated Code Radio listeners asking what was happening - I came to see the truth: <strong>Code Radio needed a new home - a home where a single questionable automated takedown request couldn't wipe it from existence.</strong></p>
<h1 id="heading-code-radio-also-rises">Code Radio also rises</h1>
<p><img src="https://media1.tenor.com/images/3637aa31124d333fa35935548ffb7996/tenor.gif" alt="Image" width="480" height="368" loading="lazy"></p>
<blockquote>
<p>"Why do we fall, Mr. Wayne? So we can learn to pick ourselves back up." - Alfred in Batman Begins</p>
</blockquote>
<p>Ah - a self-hosted Code Radio! There would be several benefits:</p>
<ol>
<li>Watching YouTube uses a lot of data. Many people have limited data plans. If we self-host Code Radio, we could just serve the MP3 files themselves, rather than a video stream. We could even offer a data-light version of the music at a lower bitrate.</li>
<li>YouTube is blocked in a lot of countries where freeCodeCamp is popular - including China. A self-hosted version of Code Radio would be available to everyone, anywhere in the world.</li>
<li>With YouTube, you have to keep the YouTube app open or the music will stop playing (unless you pay them US $12 per month for YouTube Premium). A self-hosted Code Radio could continue playing in the background on your phone - even when you switch applications or lock your phone.</li>
<li>With a self-hosted version, we could build Code Radio mobile apps, Alexa skills so you can easily listen to Code Radio on an Amazon Echo - the sky is the limit.</li>
</ol>
<p>But how would we implement a self-hosted version? Wouldn't it be expensive to serve 14 million minutes of audio each month. That's a lot of data.</p>
<h1 id="heading-building-code-radio">Building Code Radio</h1>
<p>It turns out that the internet radio community is quite active. We immediately found an awesome <a target="_blank" href="https://github.com/AzuraCast/AzuraCast">open source self-hosted internet radio project called AzuraCast</a>.</p>
<p>I reached out to the project maintainer through Twitter, and within minutes, we had him on a call with us. He was a former terrestrial radio guy. He brought us up to speed on the internet radio tooling ecosystem.</p>
<p>Yes - streaming digital audio to people around the world is a lot more expensive than just serving our coding curriculum data. But with some additional donations from supporters, we should be able to swing it.</p>
<p>With AzuraCast, plus some additional relay tools, we could run a self-hosted internet radio station at our previous scale for less than US $100 per month.</p>
<p><em>Side note: If you aren't a supporter yet, we would welcome your support. Every little bit helps: <a target="_blank" href="https://donate.freecodecamp.org">https://donate.freecodecamp.org</a> - And yes, we accept one-time donations, crypto, employer donation matching, and more: <a target="_blank" href="https://donate.freecodecamp.org/other-ways-to-donate/">https://donate.freecodecamp.org/other-ways-to-donate/</a>)</em></p>
<h1 id="heading-code-radio-is-live-help-us-load-test-it-and-give-us-feedback">Code Radio is live. Help us load-test it and give us feedback.</h1>
<p>You can start listening to Code Radio right now: <a target="_blank" href="https://coderadio.freecodecamp.org">Listen to Code Radio</a></p>
<p>We are working on a lot of additional features that we'll roll out over the next few days:</p>
<ul>
<li>bitrate controls (so you can save your mobile data by listening at 64 kbps)</li>
<li>some form of chat - preferably with existing forum accounts and forum moderators</li>
<li>a chatbot (maybe Nightbot again)</li>
<li>hotkeys</li>
<li>a better mobile experience</li>
<li>bringing back the classic Saron Yitbarek Code Radio animation</li>
</ul>
<p>I would like to thank @abdolsa, @beaucarnes, @raisedadead, @askmp, @scissorsneedfoodtoo, and of course Code Radio DJ and curator <a target="_blank" href="https://twitter.com/TreblesandBlues">Lawrence Yeo AKA Trebles and Blues</a>. They all pulled together and within 24 hours helped get this prototype up and running.</p>
<h1 id="heading-youtube-da-real-mvp">YouTube da real MVP</h1>
<p>In all seriousness, I would also like to thank YouTube. Through their own bumbling, they inadvertently forced us to take a step back and look into the possibility of self-hosting Code Radio.</p>
<p>We will continue to post in-depth coding tutorials and free programming courses on YouTube. We don't hold their own incompetence against them. We are grateful they exist and provide the infrastructure for nonprofits like ours to serve HD video to 1 Million+ subscribers for free.</p>
<p>This is just the latest chapter in our community's gradual move off of proprietary platforms like Medium and Facebook, and over onto our own tools like Developer News and this forum.</p>
<p>Thanks for reading, thanks for listening, and happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to make realtime SoundCloud Waveforms in React Native ]]>
                </title>
                <description>
                    <![CDATA[ By Pritish Vaidya Introduction SoundCloud is a music and podcast streaming platform for listening to millions of authentic tracks. They have a really interactive interface for playing / listening to the tracks. The most important feature in their int... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-make-realtime-soundcloud-waveforms-in-react-native-4df0f4c6b3cc/</link>
                <guid isPermaLink="false">66c35367b1d4339762339fa9</guid>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React Native ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 05 Nov 2018 21:38:28 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*j5GlB8Rv63-BazDF" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Pritish Vaidya</p>
<h3 id="heading-introduction"><strong>Introduction</strong></h3>
<p>SoundCloud is a music and podcast streaming platform for listening to millions of authentic tracks. They have a really interactive interface for playing / listening to the tracks.</p>
<p>The most important feature in their interface is showing the progress of the track based on its <em>frequency waveform.</em> This helps the users to identify the nature of it.</p>
<p>They also have a <a target="_blank" href="https://developers.soundcloud.com/blog/waveforms-let-s-talk-about-them">blog post</a> which describes how to use the waveform based on its image. It is hard to use the same techniques to generate the waveform in a <em>React Native</em> app. Their <a target="_blank" href="https://github.com/soundcloud/waveformjs"><strong>Waveform.js</strong></a> <strong>SDK</strong> <em>translates a waveform into floating points to render on an HTML5 canvas</em> and is currently <em>no longer operational.</em></p>
<p>In this article we’ll discuss how to use the same waveform for our React Native apps.</p>
<h3 id="heading-why-should-i-use-soundclouds-waveforms">Why Should I use SoundCloud’s Waveforms?</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/kB4RbN318B8fU2hED2jFXFaXDB8G8--O9HfB" alt="Image" width="578" height="438" loading="lazy">
_Image Credits — [KnowYourMeme](https://knowyourmeme.com/photos/1269398-arthurs-headphones" rel="noopener" target="<em>blank" title=")</em></p>
<ul>
<li>The SoundCloud’s waveform looks more impressive than the old boring way of showing the <em>progress bar.</em></li>
<li>The pre-loaded waveform will give the user an idea of the different frequencies present in the song.</li>
<li>It is also much easier to show the <em>buffered track percentage</em> on a waveform rather than showing it on a blank progress bar.</li>
</ul>
<h3 id="heading-lets-learn-more-about-soundclouds-waveforms">Let’s learn more about SoundCloud’s Waveforms</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/LQ0a-DVQtUZJ8LmuJEqNenemVj10ylCPKzxJ" alt="Image" width="500" height="250" loading="lazy">
_Image Credits — [Backstage Blog by SoundCloud Developers](http://img.svbtle.com/inline_leemartin_24131769094098_raw.png" rel="noopener" target="<em>blank" title=")</em></p>
<p>The SoundCloud provides a <code>waveform_url</code> in its tracks API.</p>
<ul>
<li>Each track has its own unique <code>waveform_url</code> .</li>
<li>The <code>waveform_url</code> contains a link to the image hoisted over the cloud.</li>
</ul>
<p><strong>Example —</strong> <a target="_blank" href="https://w1.sndcdn.com/PP3Eb34ToNki_m.png">https://w1.sndcdn.com/PP3Eb34ToNki_m.png</a></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ngCp52SzK2q5vYwSAyokRu5nsw8rs8PZdLAj" alt="Image" width="800" height="124" loading="lazy">
_Image Credits — [SoundCloud’s waveform for the track “Megadeth — Sweating Bullets” by mauriciohaensch](https://w1.sndcdn.com/PP3Eb34ToNki_m.png" rel="noopener" target="<em>blank" title=")</em></p>
<p>As of now, every argument is static hence it is unusable in this current state. Therefore we need to re-create the waveform based on it using <strong>React Native’s</strong> <em>containers</em> in order to have access to the <em>touch events, styles etc.</em></p>
<h3 id="heading-getting-started">Getting Started</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/g9wt8pkrRv-xcPBH8AhI4e-oZcJcUEst0bcc" alt="Image" width="500" height="303" loading="lazy">
_Image Credits — [ImgFlip](https://imgflip.com/memegenerator/4081988/What-if-i-told-you" rel="noopener" target="<em>blank" title=")</em></p>
<p>Here is a list of stuff that you will need:</p>
<ul>
<li><a target="_blank" href="https://github.com/d3/d3-scale">d3-scale</a></li>
<li><a target="_blank" href="https://github.com/d3/d3-array">d3-array</a></li>
</ul>
<p>First, we need the sampling of the waveform. The trick is to replace <code>.png</code> with <code>.json</code> for the <code>waveform_url</code> . A <code>GET</code> call to it would give us a response object that contains</p>
<ul>
<li><strong>width</strong> (Width of the waveform)</li>
<li><strong>height</strong> (Height of the waveform)</li>
<li><strong>samples</strong> (Array)</li>
</ul>
<p>For more info, you can try out the following link <a target="_blank" href="https://w1.sndcdn.com/PP3Eb34ToNki_m.json">https://w1.sndcdn.com/PP3Eb34ToNki_m.json</a>.</p>
<h3 id="heading-dive-into-the-code">Dive into the code</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/bdodPYaqbZeIjm8lEuNdAIXa9BFywxEkmY3C" alt="Image" width="800" height="533" loading="lazy">
_Image Credits — [Unsplash](https://images.unsplash.com/photo-1466477234737-8a3b3faed8c3?ixlib=rb-0.3.5&amp;s=919a7e046d85dc25ac72f4c3070228b6&amp;auto=format&amp;fit=crop&amp;w=800&amp;q=60" rel="noopener" target="<em>blank" title=")</em></p>
<h4 id="heading-add-a-custom-soundcloudwave-component">Add a Custom SoundCloudWave Component</h4>
<pre><code class="lang-jsx"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">percentPlayed</span> (<span class="hljs-params">time, totalDuration</span>) </span>{
 <span class="hljs-keyword">return</span>  <span class="hljs-built_in">Number</span>(time) / (<span class="hljs-built_in">Number</span>(totalDuration) / <span class="hljs-number">1000</span>)
}

&lt;SoundCloudWave
  waveformUrl={waveform_url}
  height={<span class="hljs-number">50</span>}
  width={width}
  percentPlayable={percentPlayed(bufferedTime, totalDuration)} 
  percentPlayed={percentPlayed(currentTime, totalDuration)}
  setTime={<span class="hljs-built_in">this</span>.setTime}
/&gt;
</code></pre>
<p>It would be better to create a custom <em>SoundCloudWave</em> component that can be used in multiple places as required. Here are the required <code>props</code>:</p>
<ul>
<li><strong>waveformUrl</strong> — The URL object to the waveform (accessible through the Tracks API)</li>
<li><strong>height</strong> — Height of the waveform</li>
<li><strong>width</strong> — Width of the waveform component</li>
<li><strong>percentPlayable</strong> — The duration of the track buffered in seconds</li>
<li><strong>percentPlayed</strong> — The duration of the track played in seconds</li>
<li><strong>setTime —</strong> The callback handler to change the current track time.</li>
</ul>
<h4 id="heading-get-the-samples">Get the samples</h4>
<pre><code class="lang-jsx">fetch(waveformUrl.replace(<span class="hljs-string">'png'</span>, <span class="hljs-string">'json'</span>))
  .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> res.json())
  .then(<span class="hljs-function"><span class="hljs-params">json</span> =&gt;</span> {
    <span class="hljs-built_in">this</span>.setState({
      <span class="hljs-attr">waveform</span>: json,
      waveformUrl
    })
  });
</code></pre>
<p>Get the samples by using a simple <code>GET</code> API call and store the result in the <code>state</code>.</p>
<h4 id="heading-create-a-waveform-component">Create a Waveform Component</h4>
<pre><code class="lang-jsx"><span class="hljs-keyword">import</span> { mean } <span class="hljs-keyword">from</span> <span class="hljs-string">'d3-array'</span>;

<span class="hljs-keyword">const</span> ACTIVE = <span class="hljs-string">'#FF1844'</span>,
  INACTIVE = <span class="hljs-string">'#424056'</span>,
  ACTIVE_PLAYABLE = <span class="hljs-string">'#1b1b26'</span>

<span class="hljs-keyword">const</span> ACTIVE_INVERSE = <span class="hljs-string">'#4F1224'</span>,
  ACTIVE_PLAYABLE_INVERSE = <span class="hljs-string">'#131116'</span>,
  INACTIVE_INVERSE = <span class="hljs-string">'#1C1A27'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getColor</span>(<span class="hljs-params">
    bars,
    bar,
    percentPlayed,
    percentPlayable,
    inverse
</span>) </span>{
  <span class="hljs-keyword">if</span>(bar/bars.length &lt; percentPlayed) {
    <span class="hljs-keyword">return</span> inverse ? ACTIVE : ACTIVE_INVERSE
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(bar/bars.length &lt; percentPlayable) {
    <span class="hljs-keyword">return</span> inverse ? ACTIVE_PLAYABLE : ACTIVE_PLAYABLE_INVERSE
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> inverse ? INACTIVE : INACTIVE_INVERSE
  }
}

<span class="hljs-keyword">const</span> Waveform =
  <span class="hljs-function">(<span class="hljs-params">
   {
     waveform,
     height,
     width,
     setTime,
     percentPlayed,
     percentPlayable,
     inverse
   }
  </span>) =&gt;</span> 
  {
    <span class="hljs-keyword">const</span> scaleLinearHeight = scaleLinear().domain([<span class="hljs-number">0</span>, waveform.height]).range([<span class="hljs-number">0</span>, height]);
    <span class="hljs-keyword">const</span> chunks = _.chunk(waveform.samples, waveform.width/((width - <span class="hljs-number">60</span>)/<span class="hljs-number">3</span>))
      <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{[{</span>
          <span class="hljs-attr">height</span>,
          <span class="hljs-attr">width</span>,
          <span class="hljs-attr">justifyContent:</span> '<span class="hljs-attr">center</span>',
          <span class="hljs-attr">flexDirection:</span> '<span class="hljs-attr">row</span>',
         },
         <span class="hljs-attr">inverse</span> &amp;&amp; {
          <span class="hljs-attr">transform:</span> [
            { <span class="hljs-attr">rotateX:</span> '<span class="hljs-attr">180deg</span>' },
            { <span class="hljs-attr">rotateY:</span> '<span class="hljs-attr">0deg</span>'},
          ]
         }
        ]}&gt;</span>
         {chunks.map((chunk, i) =&gt; (
           <span class="hljs-tag">&lt;<span class="hljs-name">TouchableOpacity</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{i}</span> <span class="hljs-attr">onPress</span>=<span class="hljs-string">{()</span> =&gt;</span> {
             setTime(i)
           }}&gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span>
              <span class="hljs-attr">backgroundColor:</span> <span class="hljs-attr">getColor</span>
               (
                 <span class="hljs-attr">chunks</span>,
                 <span class="hljs-attr">i</span>,
                 <span class="hljs-attr">percentPlayed</span>,
                 <span class="hljs-attr">percentPlayable</span>,
                 <span class="hljs-attr">inverse</span>
               ),
              <span class="hljs-attr">width:</span> <span class="hljs-attr">2</span>,
              <span class="hljs-attr">marginRight:</span> <span class="hljs-attr">1</span>,
              <span class="hljs-attr">height:</span> <span class="hljs-attr">scaleLinearHeight</span>(<span class="hljs-attr">mean</span>(<span class="hljs-attr">chunk</span>))
             }}
            /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">TouchableOpacity</span>&gt;</span>
         ))}
        <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
      )
  }
</code></pre>
<p>The <strong>Waveform Component</strong> works as follows:</p>
<ul>
<li>The Chunks split the <code>samples</code> object based on the <code>width</code> that the user wants to render on the screen.</li>
<li>The Chunks are then mapped into a <code>Touchable</code> event. The styles as <code>width:2</code> and <code>height: scaleLinearHeight(mean(chunk))</code>. This generates the <code>mean</code> from the <code>d3-array</code>.</li>
<li>The <code>backgroundColor</code> is being passed as a method with different parameters to the <code>getColor</code> method. This will then determine the color to return based on the conditions set.</li>
<li>The <code>Touchable onPress</code> event will call the custom handler passed into it, to set the new <em>seek time</em> of the track.</li>
</ul>
<p>Now this stateless component can be rendered to your child component as:</p>
<pre><code class="lang-jsx">render() {
  <span class="hljs-keyword">const</span> {height, width} = <span class="hljs-built_in">this</span>.props
  <span class="hljs-keyword">const</span> { waveform } = <span class="hljs-built_in">this</span>.state
  <span class="hljs-keyword">if</span> (!waveform) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  <span class="hljs-keyword">return</span> (
     <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">View</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{flex:</span> <span class="hljs-attr">1</span>, <span class="hljs-attr">justifyContent:</span> '<span class="hljs-attr">center</span>'}}&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">Waveform</span>
         <span class="hljs-attr">waveform</span>=<span class="hljs-string">{waveform}</span>
         <span class="hljs-attr">height</span>=<span class="hljs-string">{height}</span>
         <span class="hljs-attr">width</span>=<span class="hljs-string">{width}</span>
         <span class="hljs-attr">setTime</span>=<span class="hljs-string">{this.setTime}</span>
         <span class="hljs-attr">percentPlayed</span>=<span class="hljs-string">{this.props.percent}</span>
         <span class="hljs-attr">percentPlayable</span>=<span class="hljs-string">{this.props.percentPlayable}</span>
         <span class="hljs-attr">inverse</span>
       /&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">Waveform</span>
         <span class="hljs-attr">waveform</span>=<span class="hljs-string">{waveform}</span>
         <span class="hljs-attr">height</span>=<span class="hljs-string">{height}</span>
         <span class="hljs-attr">width</span>=<span class="hljs-string">{width}</span>
         <span class="hljs-attr">setTime</span>=<span class="hljs-string">{this.setTime}</span>
         <span class="hljs-attr">percentPlayed</span>=<span class="hljs-string">{this.props.percent}</span>
         <span class="hljs-attr">percentPlayable</span>=<span class="hljs-string">{this.props.percentPlayable}</span>
         <span class="hljs-attr">inverse</span>=<span class="hljs-string">{false}</span>
       /&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">View</span>&gt;</span></span>
    )
}
</code></pre>
<p>Here one of the waveform component is original and one inverted as in the SoundCloud’s player.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Here are the links to the <em>react-native-soundcloud-waveform</em></p>
<ul>
<li><a target="_blank" href="https://github.com/pritishvaidya/react-native-soundcloud-waveform">Github</a></li>
<li><a target="_blank" href="https://www.npmjs.com/package/react-native-soundcloud-waveform">npm</a></li>
</ul>
<p>I’ve also made an app in react-native — <strong>MetalCloud</strong> for <strong>Metal Music fans</strong> where you can see the above component at work.</p>
<p>Here are the links:</p>
<ul>
<li><a target="_blank" href="https://itunes.apple.com/us/app/metalcloud/id1319945253?mt=8">IOS</a></li>
<li><a target="_blank" href="https://play.google.com/store/apps/details?id=com.metalcloud">Android</a></li>
</ul>
<p>Thanks for reading. If you liked this article, show your support by clapping to share with other people on Medium.</p>
<p><em>More of the cool stuff can be found on my <a target="_blank" href="https://stackoverflow.com/users/6606831/pritish-vaidya">StackOverflow</a> and <a target="_blank" href="https://github.com/pritishvaidya">GitHub</a> profiles.</em></p>
<p><em>Follow me on <a target="_blank" href="https://www.linkedin.com/in/pritish-vaidya-506686128/">LinkedIn</a>, <a target="_blank" href="https://medium.com/@pritishvaidya94">Medium</a>, <a target="_blank" href="https://twitter.com/PritishVaidya">Twitter</a> for further update new articles.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I built the SiriWaveJS library: a look at the math and the code ]]>
                </title>
                <description>
                    <![CDATA[ By Flavio De Stefano It was 4 years ago when I had the idea to replicate the Apple® Siri wave-form (introduced with the iPhone 4S) in the browser using pure Javascript. During the last month, I updated this library by doing a lot of refactoring using... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-built-siriwavejs-library-maths-and-code-behind-6971497ae5c1/</link>
                <guid isPermaLink="false">66c34d794f7405e6476b01ed</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Math ]]>
                    </category>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Siri ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 26 Oct 2018 13:50:53 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*DxOICjfEReAFqCeC5V0oNA.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Flavio De Stefano</p>
<p>It was 4 years ago when I had the idea to replicate the <strong>Apple® Siri</strong> <strong>wave-form</strong> (introduced with the iPhone 4S) in the browser using pure Javascript.</p>
<p>During the last month, I updated this library by doing a lot of refactoring using ES6 features and reviewed the build process using <strong>RollupJS.</strong> Now I’ve decided to share what I've learned during this process and the math behind this library.</p>
<p>To get an idea what the output will be, visit the <a target="_blank" href="http://kopiro.github.io/siriwave/"><strong>live example</strong></a><strong>;</strong> the whole codebase is <a target="_blank" href="https://github.com/kopiro/siriwave"><strong>here</strong></a>.</p>
<p>Additionally, you can download all plots drawn in this article in GCX (OSX Grapher format): <a target="_blank" href="https://github.com/kopiro/siriwave/raw/master/default.gcx"><strong>default.gcx</strong></a> and <a target="_blank" href="https://github.com/kopiro/siriwave/raw/master/ios9.gcx"><strong>ios9.gcx</strong></a><strong>.</strong></p>
<h3 id="heading-the-classic-wave-style"><strong>The classic wave style</strong></h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/U5DWfdAYQRgGntwYHyJQh2SPSbr2Eals8fD8" alt="Image" width="800" height="400" loading="lazy">
<em>Classic style</em></p>
<p>Initially, this library only had the classic wave-form style that all of you remember using in iOS 7 and iOS 8.</p>
<p>It’s no hard task to replicate this simple wave-form, only a bit of math and basic concepts of the Canvas API.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/HGJeelo1DbenKSl72V423t-q89s3aBWrLet4" alt="Image" width="640" height="308" loading="lazy">
<em>Siri wave-form in iOS 7/8</em></p>
<p>You’re probably thinking that the wave-form is a modification of the <strong>Sine</strong> math equation, and you're right…well, almost right.</p>
<p>Before starting to code, we’ve got to find our linear equation that will be simply applied afterwards. My favourite plot editor is <strong>Grapher;</strong> you can find it in any OSX installation under _Applications &gt; Utilities &gt; Graphe_r.app.</p>
<p>We start by drawing the well known:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/-OIGYrieegxfDZ-rlhtkXmrrgBv6VgxZnb3f" alt="Image" width="70" height="18" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/AyYMYn3BxP7KdVZqlLdJ55gcqZHTHB5PFLVi" alt="Image" width="800" height="382" loading="lazy">
<em>Plot for y = sin(x)</em></p>
<p>Perfecto! Now, let’s add some parameters (Amplitude <strong>[A]</strong>, Time coordinate<strong>[t]</strong> and Spatial frequency <strong>[k]</strong>) that will be useful later (Read more here: <a target="_blank" href="https://en.wikipedia.org/wiki/Wave">https://en.wikipedia.org/wiki/Wave</a>).</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/tDIRSzaKzb3bBDMJpQ2JfuHxwFPirZliPMV2" alt="Image" width="192" height="19" loading="lazy"></p>
<p>Now we have to “attenuate” this function on plot boundaries, so that for <strong>|x| &gt;</strong>; 2, t<strong>he</strong> y values tends to 0. Let’s draw separately an equati<strong>on g(</strong>x) that has these characteristics.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/EGqbusNiAWDyno0CSwWWpjmklbWccDUeypq1" alt="Image" width="103" height="44" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/L68Rd8wjrZV-9X9al6sme2Wi4kt7Z171E6bb" alt="Image" width="538" height="264" loading="lazy"></p>
<p>This seems to be a good equation to start with. Let’s add some parameters here too to smooth the curve for our purposes:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/FWPk14LdAEnYMvGdv-X65IMf8pMPgJx6pO-5" alt="Image" width="139" height="44" loading="lazy"></p>
<p>Now, by multiplying our <strong>f(x, …)</strong> and <strong>g(x, …)<em>,</em></strong> and by setting precise parameters to the other static values, we obtain something like this.</p>
<ul>
<li><strong>A = 0.9</strong> set the amplitude of the wave to max Y = A</li>
<li><strong>k = 8</strong> set the spatial frequency and we obtain “more peaks” in the range [-2, 2]</li>
<li><strong>t = -π/2</strong> set the phase translation so that <strong>f(0, …) = 1</strong></li>
<li><strong>K = 4</strong> set the factor for the “attenuation equation” so that the final equation is y = 0 when <strong>|x| ≥ 2</strong></li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/mI5c-n9vpwQWrtIK2pWz6R3gz6CCrJ0gRQ3s" alt="Image" width="656" height="385" loading="lazy"></p>
<p>It looks good! ?</p>
<p>Now, if you notice on the original wave we have other sub-waves that will give a lower value for the amplitude. Let’s draw them for <strong>A = {0.8, 0.6, 0.4, 0.2, -0.2, -0.4, -0.6, -0.8}</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/73RU94BxLIkS49r4TWBmA5IVuQwJAyTYPpF6" alt="Image" width="781" height="467" loading="lazy"></p>
<p>In the final canvas composition the sub-waves will be drawn with a decreasing opacity tending to 0.</p>
<h4 id="heading-basic-code-concepts">Basic code concepts</h4>
<p>What do we do now with this equation?</p>
<p>We use the equation to obtain the <strong>Y value</strong> for an <strong>input X</strong>.</p>
<p>Basically, by using a simple <strong>for loop</strong> from <strong>-2 to 2,</strong> (the <em>plot boundaries in this case)</em>, we have to draw <strong>point by point</strong> the equation on the canvas using the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/beginPath"><strong>beginPath</strong></a> and <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo">lineTo</a> API.</p>
<pre><code><span class="hljs-keyword">const</span> ctx = canvas.getContext(<span class="hljs-string">'2d'</span>);
</code></pre><pre><code>ctx.beginPath();ctx.strokeStyle = <span class="hljs-string">'white'</span>;
</code></pre><pre><code><span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">-2</span>; i &lt;= <span class="hljs-number">2</span>; i += <span class="hljs-number">0.01</span>) {   <span class="hljs-keyword">const</span> x = _xpos(i);   <span class="hljs-keyword">const</span> y = _ypos(i);   ctx.lineTo(x, y);}
</code></pre><pre><code>ctx.stroke();
</code></pre><p>Probably this pseudo-code will clear up these ideas. We still have to implement our <strong>_xpos</strong> and <strong>_ypos</strong> functions.</p>
<p>But… hey, what is <strong>0.01⁉️</strong> That value represents <strong>how many pixels</strong> you move forward in each iteration before reaching the right plot boundary… but what is the correct value?</p>
<p>If you use a really small value (<strong>&lt;0.</strong>01), you’ll get an insanely precise rendering of the graph but your performance will decrease because you’ll get too many iterations.</p>
<p>Instead, if you use a really big value (<strong>&gt; 0.</strong>1) your graph will lose precision and you’ll notice this instantly.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/3c8OI5O8uiBqD8YUn7bp22xCmxHpCElp8pIh" alt="Image" width="610" height="312" loading="lazy">
<em>Plot drawn with precision = 0.2</em></p>
<p>You can see that the final code is actually similar to the pseudo-code: <a target="_blank" href="https://github.com/kopiro/siriwave/blob/master/src/curve.js#L25">https://github.com/kopiro/siriwave/blob/master/src/curve.js#L25</a></p>
<h4 id="heading-implement-xposi">Implement _xpos(i)</h4>
<p>You may argue that if we’re drawing the plot by incrementing the <strong><em>x</em></strong>, then <strong>_<em>xpos</em></strong> may simply return the input argument.</p>
<p>This is almost correct, but our plot is always drawn from <strong>-B</strong> to <strong>B</strong> <em>(B = Boundary = 2).</em></p>
<p>So, to draw on the canvas via <strong>pixel coordinates</strong>, we must translate <strong>-B to 0,</strong> and <strong>B to 1</strong> (simple transposition of [-B, B] to [0,1]); then multiply [0,1] and the <strong>canvas width (w).</strong></p>
<blockquote>
<p>_xpos(i) = w * [ (i + B) / 2B ]</p>
</blockquote>
<p><a target="_blank" href="https://github.com/kopiro/siriwave/blob/master/src/curve.js#L19">https://github.com/kopiro/siriwave/blob/master/src/curve.js#L19</a></p>
<h4 id="heading-implement-ypos"><strong>Implement _ypos</strong></h4>
<p>To implement <strong>_ypos</strong>, we should simply write our equation obtained before (closely).</p>
<pre><code><span class="hljs-keyword">const</span> K = <span class="hljs-number">4</span>;<span class="hljs-keyword">const</span> FREQ = <span class="hljs-number">6</span>;
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_attFn</span>(<span class="hljs-params">x</span>) </span>{   <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.pow(K / (K + <span class="hljs-built_in">Math</span>.pow(x, K)), K);}
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_ypos</span>(<span class="hljs-params">i</span>) </span>{   <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.sin(FREQ * i - phase) *       _attFn(i) *       canvasHeight *      globalAmplitude *       (<span class="hljs-number">1</span> / attenuation);}
</code></pre><p>Let’s clarify some parameters.</p>
<ul>
<li><strong>canvasHeight</strong> is Canvas height expressed in PX</li>
<li><strong>i</strong> is our input value (the <strong>x</strong>)</li>
<li><strong>phase</strong> is the most important parameter, let’s discuss it later</li>
<li><strong>globalAmplitude</strong> is a static parameter that represents the amplitude of the total wave (composed by sub-waves)</li>
<li><strong>attenuation</strong> is a static parameter that changes for each line and represents the amplitude of a wave</li>
</ul>
<p><a target="_blank" href="https://github.com/kopiro/siriwave/blob/master/src/curve.js#L24">https://github.com/kopiro/siriwave/blob/master/src/curve.js#L24</a></p>
<h4 id="heading-phase"><strong>Phase</strong></h4>
<p>Now let’s discuss about the <strong>phase variable:</strong> it is the <strong>first changing variable</strong> over time, because it simulates the wave movement.</p>
<p>What does it mean? It means that <strong>for each <em>animation frame,</em></strong> our base controller should <strong>increment</strong> this value. But to avoid this value throwing a buffer overflow, let’s modulo it with 2π (since <strong>Math.sin</strong> dominio is already modulo 2π).</p>
<pre><code>phase = (phase + (<span class="hljs-built_in">Math</span>.PI / <span class="hljs-number">2</span>) * speed) % (<span class="hljs-number">2</span> * <span class="hljs-built_in">Math</span>.PI);
</code></pre><p>We multiply <strong>speed</strong> and <strong>Math.PI</strong> so that with <strong>speed = 1</strong> we have the maximum speed (why? because <strong>sin(0) = 0, sin(π/2) = 1, sin(π) = 0, … ?)</strong></p>
<h4 id="heading-finalizing">Finalizing</h4>
<p>Now that we have all code to draw a single line, we define a configuration array to draw all sub-waves, and then cycle over them.</p>
<pre><code><span class="hljs-keyword">return</span> [   { <span class="hljs-attr">attenuation</span>: <span class="hljs-number">-2</span>, <span class="hljs-attr">lineWidth</span>: <span class="hljs-number">1.0</span>, <span class="hljs-attr">opacity</span>: <span class="hljs-number">0.1</span> },   { <span class="hljs-attr">attenuation</span>: <span class="hljs-number">-6</span>, <span class="hljs-attr">lineWidth</span>: <span class="hljs-number">1.0</span>, <span class="hljs-attr">opacity</span>: <span class="hljs-number">0.2</span> },   { <span class="hljs-attr">attenuation</span>: <span class="hljs-number">4</span>, <span class="hljs-attr">lineWidth</span>: <span class="hljs-number">1.0</span>, <span class="hljs-attr">opacity</span>: <span class="hljs-number">0.4</span> },   { <span class="hljs-attr">attenuation</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">lineWidth</span>: <span class="hljs-number">1.0</span>, <span class="hljs-attr">opacity</span>: <span class="hljs-number">0.6</span>},
</code></pre><pre><code>   <span class="hljs-comment">// basic line   { attenuation: 1, lineWidth: 1.5, opacity: 1.0},];</span>
</code></pre><p><a target="_blank" href="https://github.com/kopiro/siriwave/blob/master/src/siriwave.js#L190">https://github.com/kopiro/siriwave/blob/master/src/siriwave.js#L190</a></p>
<h3 id="heading-the-ios-9-style">The iOS 9+ style</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/KAVRuTjxVxZvEQEIyG2xru3yzDpLZWvd8zdO" alt="Image" width="1196" height="594" loading="lazy">
<em>GIF of SiriwaveJS iOS9+</em></p>
<p>Now things start to get complicated. The style introduced with iOS 9 is really complex and the reverse engineering to simulate it <strong>it’s not easy at all</strong>! I’m not fully satisfied of the final result, but I’ll continue to improve it until I get the desired result.</p>
<p>As previously done, let’s start to obtain the linear equations of the waves.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/kxsuU2ovEPmN0mqiOwoWM3dHUYmG4wnRAQpc" alt="Image" width="745" height="195" loading="lazy">
<em>Original Siri iOS 9+ wave-form</em></p>
<p>As you can notice:</p>
<ul>
<li>we have three <strong>different specular equations</strong> with different colours (<strong>green, blue, red</strong>)</li>
<li>a single wave seems to be a <strong>sum of sine equations</strong> with <strong>different parameters</strong></li>
<li>all other colours are a <strong>composition</strong> of these three base colours</li>
<li>there is a <strong>straight line</strong> at the plot boundaries</li>
</ul>
<p>By picking again our previous equations, let’s define a more complex equation that <strong>involves translation.</strong> We start by defining again our attenuation equation:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/PFv-Gz5oeue1rG-Wg06zngdoCsTpTPM83k6c" alt="Image" width="199" height="44" loading="lazy"></p>
<p>Now, define <strong>h(x, A, k, t)</strong> function, that is the <strong>sine function</strong> multiplied for <strong>attenuation function,</strong> in its absolute value:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/77pEjutms8rTTvzBaxAIX0dFAyqp6C5pChFp" alt="Image" width="295" height="38" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/gICWeQIDSMxE5jJMSZ2WQv6Kg5zmRQPT54tl" alt="Image" width="522" height="225" loading="lazy"></p>
<p>We now have a powerful tool.</p>
<p>With <strong>h(x)</strong>, we can now create the final wave-form by summing different <strong>h(x)</strong> with different parameters involving different amplitudes, frequency and translations. For example, let’s define the <strong>red curve</strong> by putting random values.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/hbal1DKzau5IyTSD4DaTdYc8pJpr3xZqd8Si" alt="Image" width="679" height="18" loading="lazy"></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/pLT6aOYpEHowx2xYoKy3Iqve6cqC9z4YADZ9" alt="Image" width="800" height="263" loading="lazy"></p>
<p>If we do the same with a <strong>green</strong> and <strong>blue</strong> curve, this is the result:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/QAB6jCDUoq4uzllkLTbhIhKa0XCecjennMZL" alt="Image" width="800" height="268" loading="lazy"></p>
<p>This is not quite perfect, but it could work.</p>
<p>To obtain the specular version, just multiply everything by <strong>-1.</strong></p>
<p>In the coding side, the approach is the same, we have only a more complex equation for <strong>_ypos.</strong></p>
<pre><code><span class="hljs-keyword">const</span> K = <span class="hljs-number">4</span>;<span class="hljs-keyword">const</span> NO_OF_CURVES = <span class="hljs-number">3</span>;
</code></pre><pre><code><span class="hljs-comment">// This parameters should be generated randomlyconst widths = [ 0.4, 0.6, 0.3 ];const offsets = [ 1, 4, -3 ];const amplitudes = [ 0.5, 0.7, 0.2 ];const phases = [ 0, 0, 0 ];</span>
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_globalAttFn</span>(<span class="hljs-params">x</span>) </span>{   <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.pow(K / (K + <span class="hljs-built_in">Math</span>.pow(x, <span class="hljs-number">2</span>)), K);}
</code></pre><pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">_ypos</span>(<span class="hljs-params">i</span>) </span>{   <span class="hljs-keyword">let</span> y = <span class="hljs-number">0</span>;   <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> ci = <span class="hljs-number">0</span>; ci &lt; NO_OF_CURVES; ci++) {      <span class="hljs-keyword">const</span> t = offsets[ci];      <span class="hljs-keyword">const</span> k = <span class="hljs-number">1</span> / widths[ci];      <span class="hljs-keyword">const</span> x = (i * k) - t;            y += <span class="hljs-built_in">Math</span>.abs(         amplitudes[ci] *          <span class="hljs-built_in">Math</span>.sin(x - phases[ci]) *          _globalAttFn(x)      );   }
</code></pre><pre><code>   y = y / NO_OF_CURVES;   <span class="hljs-keyword">return</span> canvasHeightMax * globalAmplitude * y;}
</code></pre><p>There’s nothing complex here. The only thing that changed is that we cycle <strong>NO_OF_CURVES</strong> times over all pseudo-random parameters and we <strong>sum</strong> all <strong>y values.</strong></p>
<p>Before multiplying it for <strong>canvasHeightMax</strong> and <strong>globalAmplitude</strong> that give us the absolute PX coordinate of the canvas, we divide it for NO_OF_CURVES so that <strong>y is always ≤ 1.</strong></p>
<p><a target="_blank" href="https://github.com/kopiro/siriwave/blob/master/src/ios9curve.js#L103">https://github.com/kopiro/siriwave/blob/master/src/ios9curve.js#L103</a></p>
<h4 id="heading-composite-operation"><strong>Composite operation</strong></h4>
<p>One thing that actually matters here is the <strong>globalCompositeOperation</strong> mode to set in the Canvas. If you notice, in the original controller, when there’s a overlap of 2+ colors, they’re actually mixed in a standard way.</p>
<p>The default is set to <strong>source-over</strong>, but the result is poor, even with an opacity set.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/fR8PeyeFbcJq-8Qycopohv6M1hfIK4Zudjal" alt="Image" width="800" height="388" loading="lazy">
<em>composite operation: source-over</em></p>
<p>You can see all examples of vary <strong>globalCompositeOperation</strong> here: <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation">https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/globalCompositeOperation</a></p>
<p>By setting <strong>globalCompositeOperation</strong> to <strong>“ligther”</strong>, you notice that the intersection of the colours is nearest to the original.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/I5HGeo9b8U3bmt1XquQiRsdohkUZeRzIFE3x" alt="Image" width="800" height="370" loading="lazy">
<em>Composite operation: lighter</em></p>
<h3 id="heading-build-with-rollupjs">Build with RollupJS</h3>
<p>Before refactoring everything, I wasn’t satisfied at all with the codebase: old prototype-like classes, a single Javascript file for everything, no uglify/minify and <strong>no build at all.</strong></p>
<p>Using the new ES6 feature like <strong>native classes, spread operators</strong> and <strong>lambda functions</strong>, I was able to clean everything, split files, and decrease lines of unnecessary code.</p>
<p>Furthermore, I used <a target="_blank" href="https://rollupjs.org/">RollupJS</a> to create a transpiled and minified build in various formats.</p>
<p>Since this is a browser-only library, I decided to create two builds: an <strong>UMD (Universal Module Definition)</strong> build that you can use directly by importing the script or by using CDN, and another one as an <strong>ESM module.</strong></p>
<p>The UMD module is built with this configuration:</p>
<pre><code>{   <span class="hljs-attr">input</span>: <span class="hljs-string">'src/siriwave.js'</span>,   <span class="hljs-attr">output</span>: {      <span class="hljs-attr">file</span>: pkg.unpkg,      <span class="hljs-attr">name</span>: pkg.amdName,      <span class="hljs-attr">format</span>: <span class="hljs-string">'umd'</span>    },    <span class="hljs-attr">plugins</span>: [       resolve(),       commonjs(),       babel({ <span class="hljs-attr">exclude</span>: <span class="hljs-string">'node_modules/**'</span> }),    ]}
</code></pre><p>An additional <strong>minified UMD module</strong> is built with this configuration:</p>
<pre><code>{   <span class="hljs-attr">input</span>: <span class="hljs-string">'src/siriwave.js'</span>,   <span class="hljs-attr">output</span>: {      <span class="hljs-attr">file</span>: pkg.unpkg.replace(<span class="hljs-string">'.js'</span>, <span class="hljs-string">'.min.js'</span>),      <span class="hljs-attr">name</span>: pkg.amdName,      <span class="hljs-attr">format</span>: <span class="hljs-string">'umd'</span>    },    <span class="hljs-attr">plugins</span>: [       resolve(),       commonjs(),       babel({ <span class="hljs-attr">exclude</span>: <span class="hljs-string">'node_modules/**'</span> }),       uglify()]}
</code></pre><p>Benefiting of UnPKG service, you can find the final build on this URL served by a CDN: <a target="_blank" href="https://unpkg.com/siriwave/dist/siriwave.min.js">https://unpkg.com/siriwave/dist/siriwave.min.js</a></p>
<p>This is the “old style Javascript way” — you can just import your script and then refer in your code by using <strong>SiriWave</strong> global object.</p>
<p>To provide a more elegant and modern way, I also built an ESM module with this configuration:</p>
<pre><code>{    <span class="hljs-attr">input</span>: ‘src/siriwave.js’,   <span class="hljs-attr">output</span>: {       <span class="hljs-attr">file</span>: pkg.module,       <span class="hljs-attr">format</span>: ‘esm’   },    <span class="hljs-attr">plugins</span>: [       babel({ <span class="hljs-attr">exclude</span>: ‘node_modules<span class="hljs-comment">/**’ })   ]}</span>
</code></pre><p>We clearly don’t want the <strong>resolve</strong> or <strong>commonjs</strong> RollupJS plugins because the developer transplier will resolve dependencies for us.</p>
<p>You can find the final RollupJS configuration here: <a target="_blank" href="https://github.com/kopiro/siriwave/blob/master/rollup.config.js">https://github.com/kopiro/siriwave/blob/master/rollup.config.js</a></p>
<h4 id="heading-watch-and-hot-code-reload"><strong>Watch and Hot code reload</strong></h4>
<p>Using RollupJS, you can also take advantage of <strong>rollup-plugin-livereload</strong> and <strong>rollup-plugin-serve</strong> plugins to provide a better way to work on scripts.</p>
<p>Basically, you just add these plugins when you’re in “developer” mode:</p>
<pre><code><span class="hljs-keyword">import</span> livereload <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-livereload'</span>;<span class="hljs-keyword">import</span> serve <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup-plugin-serve'</span>;
</code></pre><pre><code><span class="hljs-keyword">if</span> (process.env.NODE_ENV !== <span class="hljs-string">'production'</span>) { additional_plugins.push(  serve({   <span class="hljs-attr">open</span>: <span class="hljs-literal">true</span>,   <span class="hljs-attr">contentBase</span>: <span class="hljs-string">'.'</span>  }) ); additional_plugins.push(  livereload({   <span class="hljs-attr">watch</span>: <span class="hljs-string">'dist'</span>  }) );}
</code></pre><p>We finish by adding these lines into the <strong>package.json:</strong></p>
<pre><code><span class="hljs-string">"module"</span>: <span class="hljs-string">"dist/siriwave.m.js"</span>,<span class="hljs-string">"jsnext:main"</span>: <span class="hljs-string">"dist/siriwave.m.js"</span>,<span class="hljs-string">"unpkg"</span>: <span class="hljs-string">"dist/siriwave.js"</span>,<span class="hljs-string">"amdName"</span>: <span class="hljs-string">"SiriWave"</span>,<span class="hljs-string">"scripts"</span>: {   <span class="hljs-string">"build"</span>: <span class="hljs-string">"NODE_ENV=production rollup -c"</span>,   <span class="hljs-string">"dev"</span>: <span class="hljs-string">"rollup -c -w"</span>},
</code></pre><p>Let’s clarify some parameters:</p>
<ul>
<li><strong>module / jsnext:main:</strong> path of dist ESM module</li>
<li><strong>unpkg:</strong> path of dist UMD module</li>
<li><strong>amdName:</strong> name of the global object in UMD module</li>
</ul>
<p>Thanks a lot <strong>RollupJS!</strong></p>
<p>Hope that you find this article interesting, see you soon! ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Spotify’s “This Is” playlists: the ultimate song analysis for 50 mainstream artists ]]>
                </title>
                <description>
                    <![CDATA[ By James Le Each artist has their own unique musical styles. From Ed Sheeran who devotes his life to the acoustic guitar, to Drake who masters the art of rapping. From Adele who can belt some crazy high notes on her pop ballads, to Kygo who creates E... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/spotifys-this-is-playlists-the-ultimate-song-analysis-for-50-mainstream-artists-491882081819/</link>
                <guid isPermaLink="false">66c35f58c7095d76345eb011</guid>
                
                    <category>
                        <![CDATA[ Data Science ]]>
                    </category>
                
                    <category>
                        <![CDATA[ data visualization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ music ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Spotify ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 27 Jun 2018 15:32:31 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*VoHx2GEJLinEv3AWbxHNOw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By James Le</p>
<p>Each artist has their own unique musical styles. From Ed Sheeran who devotes his life to the acoustic guitar, to Drake who masters the art of rapping. From Adele who can belt some crazy high notes on her pop ballads, to Kygo who creates EDM magic on his DJ set. Music is about creativity, originality, inspiration, and feelings, and it is the perfect gateway to connect people across differences.</p>
<p>Spotify is the largest music streaming service available. With more than 35 million songs and 170 million monthly active users, it is the ideal platform for musicians to reach their audience. On the app, music can be browsed or searched via various parameters — such as artists, album, genre, playlist, or record label. Users can create, edit, and share playlists, share tracks on social media, and make playlists with other users.</p>
<p>Additionally, Spotify launched a variety of interesting playlists tailor-made for its users, of which I most admire these three:</p>
<ul>
<li><strong>Discover Weekly</strong>: a weekly generated playlist (updated on Mondays) that brings users two hours of custom-made music recommendations, mixing a user’s personal taste with songs enjoyed by similar listeners.</li>
<li><strong>Release Radar</strong>: a personalized playlist that allows users to stay up-to-date on new music released by artists they listen to the most.</li>
<li><strong>Daily Mix</strong>: a series of playlists that have “near endless playback” and mixes the user’s favorite tracks with new, recommended songs.</li>
</ul>
<p>I recently discovered the <a target="_blank" href="https://open.spotify.com/search/playlists/this%20is%20">‘This Is”</a> playlist series. One of Spotify’s best original features, “This Is” delivers on a major promise of the streaming revolution — the canonization and preservation of great artists’ repertoires for future generations to discover and appreciate.</p>
<p>Each one is dedicated to a different legendary artist, chronicling the high points of iconic discographies. “This is: Kanye West”. “This is: Maroon 5”. “This is: Elton John”. Spotify has provided a shortcut, giving us curated lists of the greatest songs from the greatest artists.</p>
<h3 id="heading-what-well-cover-here">What we’ll cover here</h3>
<p>The purpose of this project is to analyze the music that different artists on Spotify produce. The focus will be placed on disentangling the musical taste of 50 different artists from a wide range of genres. Throughout the process, I also identify different clusters of artists that share a similar musical style.</p>
<p>For the study, I will access the <a target="_blank" href="https://beta.developer.spotify.com/web-api/">Spotify Web API</a>, which provides data from the Spotify music catalog. This can be accessed via standard HTTPS requests to an API endpoint.</p>
<p>The Spotify API provides, among other things, track information for each song, including audio statistics such as <strong>danceability</strong>, <strong>instrumentalness</strong>, or <strong>tempo</strong>. Each feature measures an aspect of a song. Detailed information on how each feature is calculated can be found in the Spotify API Website. The code snippets in this article might be a bit tricky to understand, especially for data beginners, so bear with me.</p>
<p>Here’s a quick summary of my approach:</p>
<ul>
<li>Get the data from Spotify API.</li>
<li>Process the data to extract audio features for each artist.</li>
<li>Visualize the data using D3.js.</li>
<li>Apply k-means clustering to separate the artists into different groups.</li>
<li>Analyze each feature for all the artists.</li>
</ul>
<p>Let’s now retrieve the audio feature information from “This Is” Playlists of 50 different artists on Spotify.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*BVAvpWWBCjB0xHPM_HC1BQ.png" alt="Image" width="800" height="224" loading="lazy">
_Source: [https://blog.prototypr.io/have-you-heard-about-the-spotify-web-api-8e8d1dac9eaf](https://blog.prototypr.io/have-you-heard-about-the-spotify-web-api-8e8d1dac9eaf" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-getting-data"><strong>Getting data</strong></h3>
<p>The first step was registering my application in the <a target="_blank" href="https://beta.developer.spotify.com/web-api/">API Website</a> and getting the keys (Client ID and Client Secret) for future requests.</p>
<p>The Spotify Web API has different URIs (Uniform Resource Identifiers) to access playlists, artists, or tracks information. Consequently, the process of getting data must be divided into two key steps:</p>
<ul>
<li>get the “This Is” Playlist Series for multiple musicians.</li>
<li>get the audio features for each artist’s Playlist tracks.</li>
</ul>
<h4 id="heading-web-api-credentials"><strong>Web API credentials</strong></h4>
<p>First, I created two variables for the <code>Client ID</code> and the <code>Client Secret</code> credentials.</p>
<pre><code>spotifyKey &lt;- <span class="hljs-string">"YOUR CLIEND ID"</span>spotifySecret &lt;- <span class="hljs-string">"YOUR CLIENT SECRET"</span>
</code></pre><p>After that, I requested an access token in order to authorize my app to retrieve and manage Spotify data.</p>
<pre><code>library(Rspotify)library(httr)library(jsonlite)spotifyEndpoint &lt;- oauth_endpoint(NULL, <span class="hljs-string">"https://accounts.spotify.com/authorize"</span>,<span class="hljs-string">"https://accounts.spotify.com/api/token"</span>)
</code></pre><pre><code>spotifyToken &lt;- spotifyOAuth(<span class="hljs-string">"Spotify Analysis"</span>, spotifyKey, spotifySecret)
</code></pre><h4 id="heading-this-is-playlist-series"><strong>“This Is” Playlist Series</strong></h4>
<p>The first step was to pull the artists’ <a target="_blank" href="https://open.spotify.com/search/playlists/this%20is%20">“This Is” series</a> is to get the URIs for each one. Here are the 50 musicians I have chosen, using their popularity, modernity, and diversity as the main criteria:</p>
<ul>
<li><strong>Pop</strong>: Taylor Swift, Ariana Grande, Shawn Mendes, Maroon 5, Adele, Justin Bieber, Ed Sheeran, Justin Timberlake, Charlie Puth, John Mayer, Lorde, Fifth Harmony, Lana Del Rey, James Arthur, Zara Larsson, Pentatonix.</li>
<li><strong>Hip-Hop / Rap</strong>: Kendrick Lamar, Post Malone, Drake, Kanye West, Eminem, Future, 50 Cent, Lil Wayne, Wiz Khalifa, Snoop Dogg, Macklemore, Jay-Z.</li>
<li><strong>R &amp; B</strong>: Bruno Mars, Beyonce, Enrique Iglesias, Stevie Wonder, John Legend, Alicia Keys, Usher, Rihanna.</li>
<li><strong>EDM / House</strong>: Kygo, The Chainsmokers, Avicii, Marshmello, Calvin Harris, Martin Garrix.</li>
<li><strong>Rock</strong>: Coldplay, Elton John, One Republic, The Script, Jason Mraz.</li>
<li><strong>Jazz</strong>: Frank Sinatra, Michael Buble, Norah Jones.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*-IcwnYFk40BgckbA4uGbyg.png" alt="Image" width="800" height="400" loading="lazy">
_Source: [http://www.thedrum.com/news/2017/11/29/spotify-wraps-up-2017-making-humorous-goals-2018-using-its-data-and-artists](http://www.thedrum.com/news/2017/11/29/spotify-wraps-up-2017-making-humorous-goals-2018-using-its-data-and-artists" rel="noopener" target="<em>blank" title=")</em></p>
<p>I basically went to each musician’s individual playlist, copied the URIs, stored each URI in a .csv file, and imported the .csv files into R.</p>
<pre><code>library(readr)
</code></pre><pre><code>playlistURI &lt;- read.csv(<span class="hljs-string">"this-is-playlist-URI.csv"</span>, header = T, sep = <span class="hljs-string">";"</span>)
</code></pre><p>With each Playlist URI, I applied the <code>getPlaylistSongs</code> from the “RSpotify” package, and stored the Playlist information in an empty data.frame.</p>
<pre><code># Empty dataframePlaylistSongs &lt;- data.frame(PlaylistID = character(),                            Musician = character(),                            tracks = character(),                            id = character(),                            popularity = integer(),                            artist = character(),                            artistId = character(),                            album = character(),                            albumId = character(),                            stringsAsFactors=FALSE)
</code></pre><pre><code># Getting each playlistfor (i <span class="hljs-keyword">in</span> <span class="hljs-number">1</span>:nrow(playlistURI)) {  i &lt;- cbind(PlaylistID = <span class="hljs-keyword">as</span>.factor(playlistURI[i,<span class="hljs-number">2</span>]),             Musician = <span class="hljs-keyword">as</span>.factor(playlistURI[i,<span class="hljs-number">1</span>]),             getPlaylistSongs(<span class="hljs-string">"spotify"</span>,                              playlistid = <span class="hljs-keyword">as</span>.factor(playlistURI[i,<span class="hljs-number">2</span>]),                              token=spotifyToken))  PlaylistSongs &lt;- rbind(PlaylistSongs, i)}
</code></pre><h4 id="heading-audio-features"><strong>Audio features</strong></h4>
<p>First, I wrote a formula (<code>getFeatures</code>) that extracts the audio features for any specific ID stored as a vector.</p>
<pre><code>getFeatures &lt;- <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">vector_id, token</span>) </span>{  link &lt;- httr::GET(paste0(<span class="hljs-string">"https://api.spotify.com/v1/audio-features/?ids="</span>,   vector_id), <span class="hljs-attr">httr</span>::config(token = token))  list &lt;- httr::content(link)  <span class="hljs-keyword">return</span>(list)}
</code></pre><p>Next, I included <code>getFeatures</code> in another formula (<code>get_features</code>). The latter formula extracts the audio features for the track ID’s vector, and returns them in a data.frame.</p>
<pre><code>get_features &lt;- <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">x</span>) </span>{  getFeatures2 &lt;- getFeatures(vector_id = x, token = spotifyToken)  features_output &lt;- <span class="hljs-keyword">do</span>.call(rbind, lapply(getFeatures2$audio_features, data.frame, stringsAsFactors=FALSE))}
</code></pre><p>Using the formula created above, I was able to extract the audio features for each track. In order to do so, I needed a vector containing each track ID. The rate limit for the Spotify API is 100 tracks, so I decided to create a vector with track IDs for each musician.</p>
<p>Next, I applied the <code>get_features</code> formula to each vector, obtaining the audio features for each musician.</p>
<p>After that, I merged each musician’s audio features data.frame into a new one, <code>all_features</code>. It contains the audio features for all the tracks in every musician’s “This Is” Playlist.</p>
<pre><code>library(gdata)
</code></pre><pre><code>all_features &lt;- combine(TaylorSwift,ArianaGrande,KendrickLamar,ShawnMendes,Maroon5,PostMalone,Kygo,TheChainsmokers,Adele,Drake,JustinBieber,Coldplay,KanyeWest,BrunoMars,EdSheeran,Eminem,Beyonce,Avicii,Marshmello,CalvinHarris,JustinTimberlake,FrankSinatra,CharliePuth,MichaelBuble,MartinGarrix,EnriqueIglesias,JohnMayer,Future,EltonJohn,FiftyCent,Lorde,LilWayne,WizKhalifa,FifthHarmony,LanaDelRay,NorahJones,JamesArthur,OneRepublic,TheScript,StevieWonder,JasonMraz,JohnLegend,Pentatonix,AliciaKeys,Usher,SnoopDogg,Macklemore,ZaraLarsson,JayZ,Rihanna)
</code></pre><p>Finally, I computed the mean of each musician’s audio features using the <code>aggregate</code> function. The resulting data.frame contains the audio features for each musician, expressed as the mean of the tracks in their respective playlists.</p>
<pre><code>mean_features &lt;- aggregate(all_features[, c(<span class="hljs-number">1</span>:<span class="hljs-number">11</span>,<span class="hljs-number">17</span>)], list(all_features$source), mean)
</code></pre><pre><code>names(mean_features) &lt;- c(<span class="hljs-string">"Musician"</span>, <span class="hljs-string">"danceability"</span>, <span class="hljs-string">"energy"</span>, <span class="hljs-string">"key"</span>, <span class="hljs-string">"loudness"</span>, <span class="hljs-string">"mode"</span>, <span class="hljs-string">"speechiness"</span>, <span class="hljs-string">"acousticness"</span>, <span class="hljs-string">"instrumentalness"</span>, <span class="hljs-string">"liveness"</span>, <span class="hljs-string">"valence"</span>, <span class="hljs-string">"tempo"</span>, <span class="hljs-string">"duration_ms"</span>)
</code></pre><p>The image below shows a subset of the <code>mean_features</code> data.frame, for your reference.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*wGUAA2LF3YZCDxRR0tkniw.png" alt="Image" width="800" height="391" loading="lazy"></p>
<h4 id="heading-audio-features-description"><strong>Audio features description</strong></h4>
<p>The description of each feature from the <a target="_blank" href="https://beta.developer.spotify.com/web-api/get-audio-features/">Spotify Web API Guidance</a> can be found below:</p>
<ul>
<li><strong>Danceability</strong>: describes the suitability of a track for dancing. This is based on a combination of musical elements including tempo, rhythm stability, beat strength, and overall regularity. A value of 0.0 is least danceable and 1.0 is most danceable.</li>
<li><strong>Energy</strong>: a measure from 0.0 to 1.0, and represents a perceptual measure of intensity and activity. Typically, energetic tracks feel fast, loud, and noisy. For example, death metal has high energy, while a Bach prelude scores low on the scale. Perceptual features contributing to this attribute include dynamic range, perceived loudness, timbre, onset rate, and general entropy.</li>
<li><strong>Key</strong>: the key the track is in. Integers map to pitches using standard Pitch Class notation. E.g. 0 = C, 1 = C♯/D♭, 2 = D, and so on.</li>
<li><strong>Loudness</strong>: the overall loudness of a track in decibels (dB). Loudness values are averaged across the entire track and are useful for comparing the relative loudness of tracks. Loudness is the quality of a sound that is the primary psychological correlate of physical strength (amplitude). Values typical range between -60 and 0 db.</li>
<li><strong>Mode</strong>: indicates the modality (major or minor) of a track, the type of scale from which its melodic content is derived. Major is represented by 1 and minor is 0.</li>
<li><strong>Speechiness</strong>: Speechiness detects the presence of spoken words in a track. The more exclusively speech-like the recording (e.g. talk show, audio book, poetry), the closer to 1.0 the attribute value. Values above 0.66 describe tracks that are probably made entirely of spoken words. Values between 0.33 and 0.66 describe tracks that may contain both music and speech, either in sections or layered, including such cases as rap music. Values below 0.33 most likely represent instrumental music and other non-speech-like tracks.</li>
<li><strong>Acousticness</strong>: a confidence measure from 0.0 to 1.0 of whether the track is acoustic. 1.0 represents high confidence that the track is acoustic.</li>
<li><strong>Instrumentalness</strong>: predicts whether a track contains no vocals. “Ooh” and “aah” sounds are treated as instrumental in this context. Rap or spoken word tracks are clearly “vocal”. The closer the instrumentalness value is to 1.0, the greater likelihood the track contains no vocal content. Values above 0.5 are intended to represent instrumental tracks, but confidence is higher as the value approaches 1.0.</li>
<li><strong>Liveness</strong>: detects the presence of an audience in the recording. Higher liveness values represent an increased probability that the track was performed live. A value above 0.8 provides strong likelihood that the track is live.</li>
<li><strong>Valence</strong>: a measure from 0.0 to 1.0 describing the musical positiveness conveyed by a track. Tracks with high valence sound more positive (for example happy, cheerful, euphoric), while tracks with low valence sound more negative (for example sad, depressed, angry).</li>
<li><strong>Tempo</strong>: the overall estimated tempo of a track in beats per minute (BPM). In musical terminology, tempo is the speed or pace of a given piece, and derives directly from the average beat duration.</li>
<li><strong>Duration_ms</strong>: the duration of the track in milliseconds.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*q2q_HGP0kkDt3XFNvsTjrQ.jpeg" alt="Image" width="800" height="558" loading="lazy">
_Source: [https://www.engadget.com/2018/02/05/spotify-recommendation-tech-nelson-custom-playlists/](https://www.engadget.com/2018/02/05/spotify-recommendation-tech-nelson-custom-playlists/" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-data-visualization"><strong>Data Visualization</strong></h3>
<h4 id="heading-radar-charts"><strong>Radar charts</strong></h4>
<p>A radar chart is useful to compare the musical vibes of these musicians in a more visual way. The first visualization is an R implementation of the radar chart from the <a target="_blank" href="http://www.chartjs.org/">chart.js</a> JavaScript library, and evaluates the audio features for ten selected musicians.</p>
<p>In order to plot, I normalized the key, loudness, tempo, and duration_ms values to be from 0 to 1. This helps to make the chart more clear and readable.</p>
<pre><code>mean_features_norm &lt;- cbind(mean_features[<span class="hljs-number">1</span>], apply(mean_features[<span class="hljs-number">-1</span>],<span class="hljs-number">2</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">x</span>)</span>{(x-min(x))/diff(range(x))}))
</code></pre><p>Okay, let’s plot these interactive radar charts in batches of ten musicians. Each chart displays data set labels when you hover over each radial line, showing the value for the selected feature. The code below details the process of making the radar chart for the first batch of ten musicians. The code for the other four batches has been omitted, but the radar charts are displayed.</p>
<p><strong>Batch 1: Taylor Swift, Ariana Grande, Kendrick Lamar, Shawn Mendes, Maroon 5, Post Malone, Kygo, The Chainsmokers, Adele, Drake</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*dbjyWWWkCpbnz5xIRuDudA.png" alt="Image" width="800" height="437" loading="lazy"></p>
<p><strong>Batch 2: Justin Bieber, Coldplay, Kanye West, Bruno Mars, Ed Sheeran, Eminem, Beyonce, Avicii, Marshmello, Calvin Harris</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*eiqCu3agTApjiJuTDNwnTA.png" alt="Image" width="800" height="485" loading="lazy"></p>
<p><strong>Batch 3: Justin Timberlake, Frank Sinatra, Charlie Puth, Michael Buble, Martin Garrix, Enrique Iglesias, John Mayer, Future, Elton John, 50 Cent</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*tHQk1q8_ND7ltSRSarVrNw.png" alt="Image" width="800" height="501" loading="lazy"></p>
<p><strong>Batch 4: Lorde, Lil Wayne, Wiz Khalifa, Fifth Harmony, Lana Del Rey, Norah Jones, James Arthur, One Republic, The Script, Stevie Wonder</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EtNTSS3fVjDPi0HKAw3ANA.png" alt="Image" width="800" height="483" loading="lazy"></p>
<p><strong>Batch 5: Jason Mraz, John Legend, Pentatonix, Alicia Keys, Usher, Snoop Dogg, Macklemore, Zara Larsson, Jay-Z, Rihanna</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*i2DYW2eSNlKybqXOJRtntg.png" alt="Image" width="800" height="478" loading="lazy"></p>
<h4 id="heading-cluster-analysis"><strong>Cluster Analysis</strong></h4>
<p>Another way to find out the differences between these musicians in their musical repertoire is by grouping them in clusters. The general idea of a clustering algorithm is to divide a given dataset into multiple groups on the basis of similarity in the data.</p>
<p>In this case, musicians will be grouped in different clusters according to their music preferences. Rather than defining groups before looking at the data, clustering allows me to find and analyze the musician groupings that have formed organically.</p>
<p>Prior to clustering data, it is important to rescale the numeric variables of the dataset. Since I have mixed numerical data, where each audio feature is different from another and has different measurements, running the scale function (aka z-standardizing them) is a good practice to give equal weight to them. After that, I kept the musicians as the row names to be able to show them as labels in the plot.</p>
<pre><code>scaled.features &lt;- scale(mean_features[<span class="hljs-number">-1</span>])rownames(scaled.features) &lt;- mean_features$Musician
</code></pre><p>I applied the <strong>K-Means Clustering</strong> method, which is one of the most popular techniques of unsupervised statistical learning methods. It is used for unlabeled data. The algorithm finds groups in the data, with the number of groups represented by the variable <strong>K</strong>. The algorithm works iteratively to assign each data point to one of K groups based on the variables that are provided. Data points are clustered based on similarity.</p>
<p>In this instance, I chose <em>K = 6</em> — the clusters can be formed based on the six different genres I used when choosing the artists (Pop, Hip-Hop, R&amp;B, EDM, Rock, and Jazz).</p>
<p>After I applied the K-Means algorithm for each musician, I can plot a two-dimensional view of the data. In the first plot, the x-axis and y-axis correspond to the first and second principal components respectively. The eigenvectors (represented by red arrows) indicate the directional influence each variable has on the principal components.</p>
<p>Let’s have a look at the clusters that result from applying the K-Means algorithm to my dataset.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*T57D-wOsVH7Suc-FVEUi0Q.png" alt="Image" width="800" height="474" loading="lazy"></p>
<p>As you can see in the graph above, the x-axis is <strong>PC1 (30.24%)</strong> and the y-axis is <strong>PC2 (16.54%)</strong>. These are the first two principal components. The PCA graph shows that PC1 separates artists by loudness/energy vs acoustic/mellowness, while PC2 appears to separate artists on a scale of valence vs key, tempo and instrumentalness.</p>
<p>Because my data is multivariate, it is tedious to inspect all the many bivariate scatterplots. Instead, a single “summarizing” scatterplot is more convenient. The scatterplot of the first two principal components which were derived from the data has been shown in the graph. The percentage, likewise, is the variance explained by each component of the overall variability: the 1st component captured 30.24% and the 2nd component captured 16.54% of the information about the multivariate data.</p>
<p>If you’re interested to learn more about the math behind this algorithm, I recommend that you brush up on <a target="_blank" href="https://stats.stackexchange.com/questions/2691/making-sense-of-principal-component-analysis-eigenvectors-eigenvalues">Principal Component Analysis</a>.</p>
<p>Let’s see which artists belong to which clusters:</p>
<pre><code>k_means$cluster
</code></pre><p><img src="https://cdn-media-1.freecodecamp.org/images/1*qQOP3E-4nHM9eVstkvGD9A.png" alt="Image" width="800" height="161" loading="lazy"></p>
<p>I have also plotted another radar chart containing the features for each cluster. It is useful to compare the attributes of the songs that each cluster creates.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*kAObmmSYnx8A5AOAmxTKMA.png" alt="Image" width="800" height="515" loading="lazy"></p>
<p><strong>Cluster 1</strong> contains four artists: Coldplay, Avicii, Marshmello, and Martin Garrix. Their music is mostly performed live and instrumental, usually loud and full of energy with high tempo. This is not too surprising, as three of the four artists perform EDM / House music, and Coldplay is known for their live concerts.</p>
<p><strong>Cluster 2</strong> contains 2 artists: Frank Sinatra and Norah Jones (any jazz fans out there?). Their music scores high on acousticness and the Major scale mode. However, they score low in all the remaining attributes. Typical Jazz tunes.</p>
<p><strong>Cluster 3</strong> contains ten artists: Post Malone, Kygo, The Chainsmokers, Adele, Lorde, Lana Del Rey, James Arthur, One Republic, John Legend, and Alicia Keys. This cluster scores average in mostly all the attributes. This suggests that this group of artists is well-balanced and versatile with style and creation, hence the diversity of genres presented in this cluster (EDM, Pop, R&amp;B).</p>
<p><strong>Cluster 4</strong> contains 15 artists: Ariana Grande, Maroon 5, Drake, Justin Bieber, Bruno Mars, Calvin Harris, Charlie Puth, Enrique Iglesias, Future, Wiz Khalifa, Fifth Harmony, Usher, Macklemore, Zara Larsson, and Rihanna. Their music is danceable, loud, high-tempo, and energetic. This group has the presence of many young mainstream artists in the Pop and Hip-Hop genres.</p>
<p><strong>Cluster 5</strong> contains 10 artists: Taylor Swift, Shawn Mendes, Ed Sheeran, Michael Buble, John Mayer, Elton John, The Script, Stevie Wonder, Jason Mraz, and Pentatonix. This is my favorite group! Taylor Swift? Ed Sheeran? John Mayer? Jason Mraz? Elton John? I guess I listen to a lot of singer-songwriter artists. Their music is mostly in the Major scale, while achieving perfect balance (average score) in all other attributes.</p>
<p><strong>Cluster 6</strong> contains nine artists: Kendrick Lamar, Kanye West, Eminem, Beyonce, Justin Timberlake, 50 Cent, Lil Wayne, Snoop Dogg, and Jay-Z. You already see the trend here: seven of them are Rappers, and even Beyonce and JT regularly collaborate with rappers. Their songs have high number of spoken words and speech-like sections, are long in duration, and often performed live. Any better description of rap music?</p>
<h4 id="heading-analysis-by-feature"><strong>Analysis by feature</strong></h4>
<p>The following charts show the values for each feature for every musician. The code below details the process for making the <strong>danceability</strong> diverging bar plot. The code for the other features has been omitted, but each feature’s plot is displayed subsequently.</p>
<p><strong>Danceability</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*mmdqIehzJaW53bsZz-r4aw.png" alt="Image" width="800" height="465" loading="lazy"></p>
<p>If you want to bust the moves and impress your crush, try listen to more of Future, Drake, Wiz Khalifa, Snoop Doog, and Eminem. On the other hand, don’t even attempt to dance to Frank Sinatra or Lana Del Rey’s tunes.</p>
<p><strong>Energy</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*nmymrRifwfiQinCuBd4YjQ.png" alt="Image" width="800" height="438" loading="lazy"></p>
<p>You’re a fairly energetic person if you listen to lots of Marhsmello, Calvin Harris, Enrique Iglesias, Martin Garrix, Eminem, Jay-Z. The opposite is true if you’re a fan of Frank Sinatra and Norah Jones.</p>
<p><strong>Loudness</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*CfGqLdEzbLosO_NXOoitiQ.png" alt="Image" width="800" height="438" loading="lazy"></p>
<p>The Loudness ranking is almost the same as the Energy one.</p>
<p><strong>Speechiness</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*0kD8rOdsUlasKAccun4ToA.png" alt="Image" width="800" height="431" loading="lazy"></p>
<p>All the Rap fans out there: what’s your favorite songs from Kendrick Lamar? or 50 Cent? or Jay-Z? Hmm, I’m surprised Eminem does not rank higher, as I personally think that he’s the GOAT of all rappers.</p>
<p><strong>Acousticness</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ljkt02Nbop7eBctlHflmkQ.png" alt="Image" width="800" height="427" loading="lazy"></p>
<p>Acousticness is the exact opposite of Loudness and Energy. Mr. Sinatra and Mrs. Jones released some powerful acoustic tracks throughout their careers.</p>
<p><strong>Instrumentalness</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*YQ5rafbPvyNh-KanFWqNNg.png" alt="Image" width="800" height="424" loading="lazy"></p>
<p>EDM for the win! Martin Garrix, Avicii, and Marshmello produce tracks that contain almost no vocals.</p>
<p><strong>Liveness</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*DnYTLJ4GDEpwz4AYsh6OMw.png" alt="Image" width="800" height="429" loading="lazy"></p>
<p>So who are the 5 artists who performed the most live audio recordings? Jason Mraz, Coldplay, Martin Garrix, Kanye West, and Kendrick Lamar, in that order.</p>
<p><strong>Valence</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*OelJ9gnwDamJPvZxQFIBMg.png" alt="Image" width="800" height="428" loading="lazy"></p>
<p>Valence is the feature that describes musical positiveness conveyed by a track. Music by Bruno Mars, Stevie Wonder, and Enrique Iglesias are very positive, while music by Lana Del Rey, Coldplay, and Martin Garrix sound quite negative.</p>
<p><strong>Tempo</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*sE4BMoJmy8msXXGNWxYYyg.png" alt="Image" width="800" height="428" loading="lazy"></p>
<p>Future, Marshmello, and Wiz Khalifa are kings of speed. They produce tracks with the highest tempo in beats per minute. And Snoop Dogg, lol? He tends to take some time to utter his magic words.</p>
<p><strong>Duration</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*4nZrGRrl94SrqjVLuu6l3g.png" alt="Image" width="800" height="429" loading="lazy"></p>
<p>Last but not least, songs by Justin Timberlake, followed by Elton John and Eminem, are, sometimes excruciatingly, long. In contrast, Frank Sinatra, Zara Larsson, and Pentatonix favor shorter music.</p>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>Whoa, I had a lot of fun doing this analysis and visualization project on Spotify data. Who could have thought that James Arthur and Post Malone are in the same cluster? Or Kendrick Lamar is the speediest rapper in the game? Or that Marshmello would beat Martin Garrix in producing energetic tracks?</p>
<p>Anyway, you can view the complete R Markdown, separate R code for processing and visualizing data, and the original dataset in <a target="_blank" href="https://github.com/khanhnamle1994/spotify-artists-analysis">my GitHub repository here</a>. From my own perspective, R is much better in data visualization than Python, with the likes of libraries such as <a target="_blank" href="https://www.statmethods.net/advgraphs/ggplot2.html">ggplot</a> and <a target="_blank" href="https://plot.ly/r/">plot.ly</a>. I highly encourage you to give R a try!</p>
<p>— —</p>
<p><em>If you enjoyed this piece, I’d love it if you hit the clap button</em> ? s<em>o others might stumble upon it. You can find my own code on</em> G<a target="_blank" href="https://github.com/khanhnamle1994"><em>itHub,</em></a> <em>and more of my writing and projects at</em> h<a target="_blank" href="https://jameskle.com"><em>ttps://jameskle.com/.</em></a> _You can also follow me on T<a target="_blank" href="https://twitter.com/@james_aka_yale">witter,</a> e<a target="_blank" href="mailto:khanhle.1013@gmail.com">mail me directly</a> or f<a target="_blank" href="http://www.linkedin.com/in/khanhnamle94">ind me on LinkedIn.</a> S<a target="_blank" href="http://eepurl.com/deWjzb">ign up for my newsletter</a> to receive my latest thoughts on data science, machine learning, and artificial intelligence right at your inbox!_</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
