From a2a151a78f28ee36c6667590448e2c7aa7fe07cf Mon Sep 17 00:00:00 2001 From: Teetow Date: Mon, 2 Mar 2026 23:26:15 +0100 Subject: [PATCH] Fix capitalization of "Audio.com" in various components and documentation; add UI semaphore utility for managing concurrent access. --- src/assets/data/promotions.ts | 63 +++-- .../img/promo/audio-com-exit-intent-logo.svg | 52 ++++ src/assets/js/cookieConsent.js | 162 ++++++++++++- src/components/banner/ExitIntentPopup.tsx | 222 ++++++++++++------ src/components/homepage/Audiodotcom.astro | 26 +- src/content/blog/audacity-3-5.md | 2 +- .../blog/next-steps-audiocom-audacity.md | 8 +- src/pages/FAQ.md | 78 +++--- src/pages/post-download.astro | 63 +++-- src/utils/uiSemaphore.ts | 113 +++++++++ test-results/.last-run.json | 4 + 11 files changed, 621 insertions(+), 172 deletions(-) create mode 100644 src/assets/img/promo/audio-com-exit-intent-logo.svg create mode 100644 src/utils/uiSemaphore.ts create mode 100644 test-results/.last-run.json diff --git a/src/assets/data/promotions.ts b/src/assets/data/promotions.ts index ccf44c1..e81dd19 100644 --- a/src/assets/data/promotions.ts +++ b/src/assets/data/promotions.ts @@ -1,3 +1,5 @@ +import audioComPromoImage from "../img/promo/audacity-audiocom-promo.png"; + export type PromoType = "banner" | "video" | "exit-popup"; export type ExitPopupPolicy = { @@ -6,10 +8,17 @@ export type ExitPopupPolicy = { minDwellMs?: number; }; -export type ExitPopupCopy = { +export type ExitPopupOptions = { + routeAllowlist: string[]; + displayMode?: "toast" | "modal"; + promoImageSrc?: string; + promoImageAlt?: string; title: string; - body: string; + body?: string; dismissText: string; + policy?: ExitPopupPolicy; + impressionTracking?: TrackingConfig; + dismissTracking?: TrackingConfig; }; export type TrackingConfig = { @@ -18,14 +27,6 @@ export type TrackingConfig = { name: string; }; -export type ExitPopupConfig = { - routeAllowlist: string[]; - copy: ExitPopupCopy; - displayMode?: "toast" | "modal"; - policy?: ExitPopupPolicy; - impressionTracking?: TrackingConfig; -}; - export type PromoData = { type: PromoType; isActive?: boolean; @@ -44,7 +45,7 @@ export type PromoData = { text: string; link: string; }; - exitPopup?: ExitPopupConfig; + popupOptions?: ExitPopupOptions; // Video-specific properties video?: { placeholderImage: string; @@ -77,7 +78,7 @@ export const getFilteredPromos = ( if (type && promo.type !== type) return false; if (path && promo.type === "exit-popup") { - const allowlist = promo.exitPopup?.routeAllowlist ?? []; + const allowlist = promo.popupOptions?.routeAllowlist ?? []; if (!routeMatchesAllowlist(path, allowlist)) return false; } @@ -93,6 +94,11 @@ export const getFilteredPromos = ( }); }; +const AUDIO_COM_EXIT_POPUP_IMAGE_SRC = + typeof audioComPromoImage === "string" + ? audioComPromoImage + : audioComPromoImage.src; + const promoData: Record = { // === BANNER PROMOS === audacity4Alpha: { @@ -316,29 +322,36 @@ const promoData: Record = { isActive: true, priority: 50, message: - "Start with Audio.com to back up and access your projects across devices.", + "Use Audio.com to back up your projects, and share them from anywhere!", cta: { - text: "Start with Audio.com", + text: "Join Audio.com", link: "https://audio.com/", }, - tracking: { - category: "Exit Intent", - action: "exit_intent_cta_click", - name: "Audio.com Exit Intent Popup", - }, - exitPopup: { + popupOptions: { + title: "Keep your audio safe in the cloud", routeAllowlist: ["/download", "/post-download", "/cloud-saving"], displayMode: "modal", - copy: { - title: "Keep your audio safe in the cloud", - body: "Start with Audio.com to back up and access your projects across devices.", - dismissText: "Not now", + promoImageSrc: AUDIO_COM_EXIT_POPUP_IMAGE_SRC, + promoImageAlt: "Audio.com promotion", + dismissText: "Not now", + policy: { + minDwellMs: 3000, }, impressionTracking: { category: "Exit Intent", action: "exit_intent_impression", - name: "Audio.com Exit Intent Popup", + name: "audio.com Exit Intent Popup", }, + dismissTracking: { + category: "Exit Intent", + action: "exit_intent_dismiss", + name: "audio.com Exit Intent Popup", + }, + }, + tracking: { + category: "Exit Intent", + action: "exit_intent_cta_click", + name: "audio.com Exit Intent Popup", }, }, }; diff --git a/src/assets/img/promo/audio-com-exit-intent-logo.svg b/src/assets/img/promo/audio-com-exit-intent-logo.svg new file mode 100644 index 0000000..dfe70be --- /dev/null +++ b/src/assets/img/promo/audio-com-exit-intent-logo.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + diff --git a/src/assets/js/cookieConsent.js b/src/assets/js/cookieConsent.js index 3b945dd..2fca0e3 100644 --- a/src/assets/js/cookieConsent.js +++ b/src/assets/js/cookieConsent.js @@ -8,13 +8,91 @@ const cookieStorage = { }, setItem: (key, value) => { document.cookie = `${key}=${value}; expires=${new Date( - new Date().getTime() + 1000 * 60 * 60 * 24 * 365 + new Date().getTime() + 1000 * 60 * 60 * 24 * 365, ).toGMTString()}; path=/ `; }, }; const storageType = cookieStorage; const consentPropertyName = "audacity_consent"; +const ATTENTION_OVERLAY_CHANNEL = "attention-overlay"; +const COOKIE_CONSENT_OWNER = "cookie-consent"; +const COOKIE_CONSENT_PRIORITY = 10; + +const getUiSemaphore = () => { + if (typeof window === "undefined") { + return null; + } + + if (window.__audacityUiSemaphore) { + return window.__audacityUiSemaphore; + } + + const state = window.__audacityUiSemaphoreState || { + locks: new Map(), + listeners: new Set(), + }; + + window.__audacityUiSemaphoreState = state; + + const notify = (channel) => { + const lock = state.locks.get(channel) || null; + state.listeners.forEach((listener) => listener(channel, lock)); + }; + + const semaphore = { + acquire(channel, owner, options) { + const requestedPriority = options?.priority || 0; + const shouldPreempt = options?.preempt || false; + const currentLock = state.locks.get(channel); + + if (currentLock && currentLock.owner !== owner) { + if (!(shouldPreempt && requestedPriority > currentLock.priority)) { + return false; + } + } + + if ( + currentLock && + currentLock.owner === owner && + currentLock.priority === requestedPriority + ) { + return true; + } + + state.locks.set(channel, { + owner, + priority: requestedPriority, + }); + notify(channel); + return true; + }, + release(channel, owner) { + const currentLock = state.locks.get(channel); + if (!currentLock || currentLock.owner !== owner) { + return false; + } + + state.locks.delete(channel); + notify(channel); + }, + isLocked(channel) { + return state.locks.has(channel); + }, + getLock(channel) { + return state.locks.get(channel) || null; + }, + subscribe(listener) { + state.listeners.add(listener); + return () => { + state.listeners.delete(listener); + }; + }, + }; + + window.__audacityUiSemaphore = semaphore; + return semaphore; +}; const showShowPopup = () => !storageType.getItem(consentPropertyName); const saveAcceptToStorage = () => @@ -26,12 +104,46 @@ window.addEventListener("load", function () { const consentPopup = document.getElementById("consent-popup"); const acceptBtn = document.getElementById("accept"); const rejectBtn = document.getElementById("reject"); + const semaphore = getUiSemaphore(); + let popupRetryTimeoutId = null; + let shouldAttemptPopup = true; + let tryShowPopup = () => {}; + + const releaseOverlayLock = () => { + if (!semaphore) { + return; + } + + semaphore.release(ATTENTION_OVERLAY_CHANNEL, COOKIE_CONSENT_OWNER); + }; + + const stopPopupAttempts = () => { + shouldAttemptPopup = false; + if (popupRetryTimeoutId !== null) { + window.clearTimeout(popupRetryTimeoutId); + popupRetryTimeoutId = null; + } + }; + + const queuePopupRetry = () => { + if (!shouldAttemptPopup || !showShowPopup(storageType)) { + return; + } + + if (popupRetryTimeoutId !== null) { + window.clearTimeout(popupRetryTimeoutId); + } + + popupRetryTimeoutId = window.setTimeout(tryShowPopup, 300); + }; const acceptCookie = (event) => { event.preventDefault(); saveAcceptToStorage(storageType); + stopPopupAttempts(); consentPopup.classList.add("hide"); + releaseOverlayLock(); if (typeof _paq !== "undefined") { _paq.push(["setCookieConsentGiven"]); } @@ -41,15 +153,59 @@ window.addEventListener("load", function () { event.preventDefault(); saveRejectToStorage(storageType); + stopPopupAttempts(); consentPopup.classList.add("hide"); + releaseOverlayLock(); }; acceptBtn.addEventListener("click", acceptCookie); rejectBtn.addEventListener("click", rejectCookie); + if (semaphore) { + semaphore.subscribe((channel, lock) => { + if (channel !== ATTENTION_OVERLAY_CHANNEL || !consentPopup) { + return; + } + + const ownedByCookie = lock?.owner === COOKIE_CONSENT_OWNER; + if (!ownedByCookie && !consentPopup.classList.contains("hide")) { + consentPopup.classList.add("hide"); + } + + if (!lock && shouldAttemptPopup && showShowPopup(storageType)) { + queuePopupRetry(); + } + }); + } + if (showShowPopup(storageType)) { - setTimeout(() => { + tryShowPopup = () => { + if (!shouldAttemptPopup || !showShowPopup(storageType)) { + return; + } + + if (!semaphore) { + consentPopup.classList.remove("hide"); + return; + } + + const acquired = semaphore.acquire( + ATTENTION_OVERLAY_CHANNEL, + COOKIE_CONSENT_OWNER, + { + priority: COOKIE_CONSENT_PRIORITY, + preempt: false, + }, + ); + + if (!acquired) { + queuePopupRetry(); + return; + } + consentPopup.classList.remove("hide"); - }, 2000); + }; + + setTimeout(tryShowPopup, 2000); } }); diff --git a/src/components/banner/ExitIntentPopup.tsx b/src/components/banner/ExitIntentPopup.tsx index 14a9605..4cf45ce 100644 --- a/src/components/banner/ExitIntentPopup.tsx +++ b/src/components/banner/ExitIntentPopup.tsx @@ -6,6 +6,7 @@ import promoData, { } from "../../assets/data/promotions"; import { trackEventIfConsented } from "../../utils/matomo"; import { selectWeightedItem } from "../../utils/selectWeightedItem"; +import { getUiSemaphore } from "../../utils/uiSemaphore"; import { useEffect, useMemo, useRef, useState } from "react"; type ExitIntentPopupProps = { @@ -14,6 +15,9 @@ type ExitIntentPopupProps = { const SESSION_IMPRESSION_KEY = "exit_intent_session_impressions"; const SUPPRESS_UNTIL_KEY = "exit_intent_suppress_until"; +const ATTENTION_OVERLAY_CHANNEL = "attention-overlay"; +const EXIT_INTENT_OVERLAY_OWNER = "exit-intent-popup"; +const EXIT_INTENT_OVERLAY_PRIORITY = 100; const DEFAULT_POLICY: Required = { sessionCap: 1, @@ -28,19 +32,22 @@ type ExitPopupPromo = PromoData & { link: string; }; tracking: TrackingConfig; - exitPopup: { + popupOptions: { routeAllowlist: string[]; displayMode?: "toast" | "modal"; - copy: { - title: string; - body: string; - dismissText: string; - }; + promoImageSrc?: string; + promoImageAlt?: string; + title: string; + body?: string; + dismissText: string; policy?: ExitPopupPolicy; impressionTracking?: TrackingConfig; + dismissTracking?: TrackingConfig; }; }; +type DismissReason = "button" | "backdrop" | "escape"; + const resolvePolicy = (policy: ExitPopupPolicy | undefined) => ({ sessionCap: policy?.sessionCap ?? DEFAULT_POLICY.sessionCap, dismissCooldownDays: @@ -53,7 +60,8 @@ const isExitPopupPromo = (promo: PromoData): promo is ExitPopupPromo => { promo.type === "exit-popup" && Boolean(promo.cta) && Boolean(promo.tracking) && - Boolean(promo.exitPopup?.copy) + Boolean(promo.popupOptions?.title) && + Boolean(promo.popupOptions?.dismissText) ); }; @@ -62,11 +70,10 @@ const isDesktopCapableContext = () => { return false; } - const hasMinWidth = window.matchMedia("(min-width: 1024px)").matches; const hasPointer = window.matchMedia("(pointer: fine)").matches; const hasHover = window.matchMedia("(hover: hover)").matches; - return hasMinWidth && hasPointer && hasHover; + return hasPointer && hasHover; }; const getNumberFromStorage = (storage: Storage, key: string): number => { @@ -118,10 +125,13 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { const [isDwellReady, setIsDwellReady] = useState(false); const [hasEngagement, setHasEngagement] = useState(false); const [hasExitIntent, setHasExitIntent] = useState(false); + const [isAttentionOverlayLocked, setIsAttentionOverlayLocked] = + useState(false); const [selectedPromo, setSelectedPromo] = useState( null, ); const hasShownRef = useRef(false); + const hasOverlayLockRef = useRef(false); const isDebugMode = isExitIntentDebugEnabled(); const resolvedPath = useMemo(() => { @@ -132,13 +142,57 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { }, [requestPath]); const resolvedPolicy = useMemo( - () => resolvePolicy(selectedPromo?.exitPopup?.policy), + () => resolvePolicy(selectedPromo?.popupOptions?.policy), [selectedPromo], ); - const impressionTracking = selectedPromo?.exitPopup?.impressionTracking; + const impressionTracking = selectedPromo?.popupOptions?.impressionTracking; const trackingForImpression = impressionTracking ?? selectedPromo?.tracking; + const releaseOverlayLock = () => { + const semaphore = getUiSemaphore(); + if (!semaphore || !hasOverlayLockRef.current) { + return; + } + + semaphore.release(ATTENTION_OVERLAY_CHANNEL, EXIT_INTENT_OVERLAY_OWNER); + hasOverlayLockRef.current = false; + }; + + useEffect(() => { + const semaphore = getUiSemaphore(); + if (!semaphore) { + return; + } + + const syncOverlayLock = () => { + const lock = semaphore.getLock(ATTENTION_OVERLAY_CHANNEL); + setIsAttentionOverlayLocked( + Boolean(lock) && lock?.owner !== EXIT_INTENT_OVERLAY_OWNER, + ); + }; + + syncOverlayLock(); + const unsubscribe = semaphore.subscribe((channel) => { + if (channel !== ATTENTION_OVERLAY_CHANNEL) { + return; + } + syncOverlayLock(); + }); + + return () => { + unsubscribe(); + releaseOverlayLock(); + }; + }, []); + + useEffect(() => { + if (isAttentionOverlayLocked && isVisible) { + releaseOverlayLock(); + setIsVisible(false); + } + }, [isAttentionOverlayLocked, isVisible]); + useEffect(() => { if (typeof window === "undefined") { return; @@ -149,6 +203,7 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { path: resolvedPath, }).filter(isExitPopupPromo); + releaseOverlayLock(); setSelectedPromo(selectExitPopupPromo(eligiblePopups)); setIsVisible(false); setIsDwellReady(false); @@ -171,6 +226,10 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { return; } + if (isAttentionOverlayLocked) { + return; + } + if (!isRouteEligible || !isDesktopCapableContext()) { return; } @@ -214,7 +273,17 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { return; } + const dismissTracking = selectedPromo?.popupOptions?.dismissTracking; + if (dismissTracking) { + trackEventIfConsented( + dismissTracking.category, + dismissTracking.action, + `${dismissTracking.name} (escape)`, + ); + } + setSuppressCooldown(resolvedPolicy.dismissCooldownDays); + releaseOverlayLock(); setIsVisible(false); }; @@ -232,7 +301,14 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { document.removeEventListener("mouseout", handleMouseOut); window.removeEventListener("keydown", handleEscapeDismiss); }; - }, [isDebugMode, isRouteEligible, isVisible, resolvedPolicy]); + }, [ + isAttentionOverlayLocked, + isDebugMode, + isRouteEligible, + isVisible, + resolvedPolicy, + selectedPromo, + ]); useEffect(() => { if ( @@ -241,12 +317,25 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { !isDwellReady || !hasEngagement || !hasExitIntent || + isAttentionOverlayLocked || hasShownRef.current ) { return; } + const semaphore = getUiSemaphore(); + if ( + semaphore && + !semaphore.acquire(ATTENTION_OVERLAY_CHANNEL, EXIT_INTENT_OVERLAY_OWNER, { + priority: EXIT_INTENT_OVERLAY_PRIORITY, + preempt: true, + }) + ) { + return; + } + hasShownRef.current = true; + hasOverlayLockRef.current = true; incrementSessionImpressions(); setIsVisible(true); @@ -261,13 +350,24 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { hasEngagement, hasExitIntent, isDwellReady, + isAttentionOverlayLocked, isRouteEligible, selectedPromo, trackingForImpression, ]); - const handleDismiss = () => { + const handleDismiss = (reason: DismissReason) => { + const dismissTracking = selectedPromo?.popupOptions?.dismissTracking; + if (dismissTracking) { + trackEventIfConsented( + dismissTracking.category, + dismissTracking.action, + `${dismissTracking.name} (${reason})`, + ); + } + setSuppressCooldown(resolvedPolicy.dismissCooldownDays); + releaseOverlayLock(); setIsVisible(false); }; @@ -277,6 +377,7 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { } setSuppressCooldown(resolvedPolicy.dismissCooldownDays); + releaseOverlayLock(); trackEventIfConsented( selectedPromo.tracking.category, selectedPromo.tracking.action, @@ -288,65 +389,39 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { return null; } - const { copy, displayMode = "toast" } = selectedPromo.exitPopup; + const { + title, + body: popupBody, + dismissText, + displayMode = "toast", + promoImageSrc, + promoImageAlt, + } = selectedPromo.popupOptions; + const body = popupBody ?? selectedPromo.message; + const promoImageClassName = + displayMode === "toast" + ? "mb-3 h-[5rem] w-full rounded-md object-cover object-center" + : "mb-3 h-auto w-full rounded-md"; const content = (
- - - - - - - - - - - - - -

{copy.title}

-

{copy.body}

-
+ {promoImageSrc && ( + {promoImageAlt + )} +

{title}

+

{body}

+
+ = ({ requestPath }) => { > {selectedPromo.cta.text} -
); @@ -369,7 +437,7 @@ const ExitIntentPopup: React.FC = ({ requestPath }) => { return (
handleDismiss("backdrop")} aria-live="polite" > diff --git a/src/components/homepage/Audiodotcom.astro b/src/components/homepage/Audiodotcom.astro index d1c765b..81a57f8 100644 --- a/src/components/homepage/Audiodotcom.astro +++ b/src/components/homepage/Audiodotcom.astro @@ -5,7 +5,7 @@ import { Image } from "astro:assets"; import FeaturedVideo from "../video/FeaturedVideo"; import JoinAudioDotComButton from "../button/JoinAudioDotComButton"; import AudioDotComLogo from "../../assets/img/audiocom_wordmark_offwhite_transparentbg.svg"; -import VideoPlaceholder from '../../assets/img/audiocom-placeholder.webp' +import VideoPlaceholder from "../../assets/img/audiocom-placeholder.webp"; // Optimize the background image const optimizedBg = await getImage({ @@ -26,13 +26,17 @@ const releaseVideo = { title: "Introducing Audio.com", placeholderImage: releaseVideoPlaceholderImage.src, videoURL: "https://www.youtube-nocookie.com/embed/ZDnQgaCoppo?autoplay=1", - imageAltText: "Video thumbnail: 15 reasons why you should use Audio.com" + imageAltText: "Video thumbnail: 15 reasons why you should use Audio.com", }; --- -
-
+
+
@@ -45,16 +49,18 @@ const releaseVideo = { matomoEventName={releaseVideo.title} />
- + -
- audio.com +
+ Audio.com

Level up your Audacity

Audio.com, the online companion to Audacity, lets you collaborate on - projects, create versioned backups, and easily share and publish your - work. + projects, create versioned backups, and easily share and publish + your work.

@@ -64,4 +70,4 @@ const releaseVideo = {
-
\ No newline at end of file +
diff --git a/src/content/blog/audacity-3-5.md b/src/content/blog/audacity-3-5.md index 7691c63..83f3b5b 100644 --- a/src/content/blog/audacity-3-5.md +++ b/src/content/blog/audacity-3-5.md @@ -24,7 +24,7 @@ We are excited to announce Audacity 3.5, which adds the following features: ## Cloud project saving -We've introduced a new cloud-saving feature that allows you to save your Audacity projects to audio.com. This allows you to work from any device, share & collaborate with others and restore previous versions if something went wrong. +We've introduced a new cloud-saving feature that allows you to save your Audacity projects to Audio.com. This allows you to work from any device, share & collaborate with others and restore previous versions if something went wrong. ## Automatic tempo detection diff --git a/src/content/blog/next-steps-audiocom-audacity.md b/src/content/blog/next-steps-audiocom-audacity.md index db8fd8f..711fc2d 100644 --- a/src/content/blog/next-steps-audiocom-audacity.md +++ b/src/content/blog/next-steps-audiocom-audacity.md @@ -10,16 +10,16 @@ draft: false In this update, we wanted to share our future goals for [Audio.com](http://audio.com/), including a focus on building a **creator-first platform**. -As [audio.com](http://audio.com/) has grown, we’ve realized — through in-depth research and discussions with our community — that many creators are interested in sophisticated cloud tools to help collaborate, sell, manage and distribute their work. +As [Audio.com](http://audio.com/) has grown, we’ve realized — through in-depth research and discussions with our community — that many creators are interested in sophisticated cloud tools to help collaborate, sell, manage and distribute their work. -That’s why we’re planning to soon include a cloud-saving feature on [audio.com](http://audio.com/) that allows creators to work on Audacity projects that are saved directly to the cloud. This means that by signing in to Audio.com on any device, you can immediately continue working on your Audacity project. This feature will also provide a convenient way to share your projects with others, and get feedback instantly. It’s a first, exciting step in providing a space that enables audiophiles, podcasters and musicians to collaborate together. +That’s why we’re planning to soon include a cloud-saving feature on [Audio.com](http://audio.com/) that allows creators to work on Audacity projects that are saved directly to the cloud. This means that by signing in to Audio.com on any device, you can immediately continue working on your Audacity project. This feature will also provide a convenient way to share your projects with others, and get feedback instantly. It’s a first, exciting step in providing a space that enables audiophiles, podcasters and musicians to collaborate together. Building this kind of capability is complicated, so we'll soon launch a beta release to let you try it out and provide feedback. We expect to be releasing this Beta in mid-February. -We want to provide the best creator tools and services we can while still growing revenue to invest into our products. And while many audio.com features will remain free, the new cloud storage feature will incur additional costs for our team. So we will offer 5 projects completely free, but charge a small fee for usage beyond this amount. +We want to provide the best creator tools and services we can while still growing revenue to invest into our products. And while many Audio.com features will remain free, the new cloud storage feature will incur additional costs for our team. So we will offer 5 projects completely free, but charge a small fee for usage beyond this amount. Audacity will, of course, **always** remain 100% free and open source. We’d love to hear your opinions about this, including the kinds of additional tools you think we should build. Please let us know what you think [in our forum](https://forum.audacityteam.org/t/the-next-steps-for-audio-com-audacity/97997). Thanks for your continued support, -*Audacity &* [*audio.com*](http://audio.com/) *teams* \ No newline at end of file +_Audacity &_ [_Audio.com_](http://audio.com/) _teams_ diff --git a/src/pages/FAQ.md b/src/pages/FAQ.md index 90b719d..d6b52aa 100644 --- a/src/pages/FAQ.md +++ b/src/pages/FAQ.md @@ -6,70 +6,78 @@ title: Audacity ® | Frequently Asked Questions ## About Audacity ### What is Audacity? + Audacity is the world’s most popular free software for recording and editing audio. So if you're producing music, a podcast, or just playing around with audio, Audacity is for you. It is [available to download](/download) as a desktop app for Windows, macOS and Linux. ### Is Audacity free? + Yes! Audacity has always — and will always — remain free for everyone. It is available to download from the [Audacity website](/). ### Who is Audacity for? + Audacity is for anyone who wants to get creative with sound. It’s also the perfect tool for anyone who needs to quickly edit or export audio, for any reason. -Here are a few of the most common ways Audacity is used every day: +Here are a few of the most common ways Audacity is used every day: - Podcasters recording and editing spoken content. Audacity is the world’s most popular app for podcasters. - Musicians and bedroom producers, who can edit multiple-parts, mix and add simple effects in an app that’s faster and more intuitive than most DAWs. - Field recorders and educators, who can capture, edit or analyze the sounds of environments, ambience, animals and more. ### Is Audacity open source? -Audacity is proudly open source. This means its source code remains open to anyone to view or modify. Audacity is licensed under the GNU General Public License. Details can be found [here](https://github.com/audacity/audacity/blob/master/LICENSE.txt). + +Audacity is proudly open source. This means its source code remains open to anyone to view or modify. Audacity is licensed under the GNU General Public License. Details can be found [here](https://github.com/audacity/audacity/blob/master/LICENSE.txt). A dedicated worldwide community of passionate audio lovers have collaborated to make Audacity the well-loved software it is today. Many third-party plugins have also been developed for Audacity thanks to its open source nature. ### Does Audacity cost money? -No, Audacity is free for everyone. If you found yourself paying for Audacity, one of the following things happened: -* You signed up for additional Audacity Cloud storage, so you can [save more projects in the cloud](/cloud-saving). Contact support@audio.com to ask for a refund. -* You tried to download an Audacity app for iOS or Android. At the time of writing, there doesn't exist an Audacity version for these platforms, so any app you see there was not made by the Audacity team. Contact Apple or Google Play support and ask for a refund. +No, Audacity is free for everyone. If you found yourself paying for Audacity, one of the following things happened: + +- You signed up for additional Audacity Cloud storage, so you can [save more projects in the cloud](/cloud-saving). Contact support@audio.com to ask for a refund. +- You tried to download an Audacity app for iOS or Android. At the time of writing, there doesn't exist an Audacity version for these platforms, so any app you see there was not made by the Audacity team. Contact Apple or Google Play support and ask for a refund. --- ## Download & install ### What devices are compatible with Audacity? + Audacity is tested compatible with the following operating systems: - Windows 10 & 11, with Vista, 7 and 8.1 believed to work - MacOS 12 & 13, with OS X versions since 10.9 believed to still work - Linux: Ubuntu 22.04, with most major distributions believed to work. - ### Is Audacity only available for desktop? + Yes, Audacity is only available for desktop computers or laptops. There is no mobile version of Audacity available. ### Where can I download Audacity? + Audacity is available to download from the [Audacity website](/). -You can either download the app directly, or download Audacity through the free [Muse Hub](https://www.musehub.com/). +You can either download the app directly, or download Audacity through the free [Muse Hub](https://www.musehub.com/). If you download Audacity via the Muse Hub, you'll also get access to a free selection of sounds, loops and effects available to use in your Audacity projects. ### Why should I download Audacity through the Muse Hub? + The [Muse Hub](https://www.musehub.com/) is a gateway to creativity for any audio producer. It's packed with the best free apps, sounds and effects for composing, producing or performing. In the Muse Hub you'll find: - **Muse Sounds:** premium collections of sampled instruments, including keys, strings, brass, woodwind, percussion, choirs and electronics. Hear your music played back in astonishing detail with these moving — and completely free — preset packs. - **Elements:** free looping sound clips. Drop them easily into your Audacity performances or podcasts. - **Muse FX:** effects plugins including reverb, delay, compress and more, for fine-tuning your Audacity projects. -You can download Audacity via the Muse Hub. If you already have Audacity, you can download the Muse Hub and access the free extras [here](https://www.musehub.com/). - + You can download Audacity via the Muse Hub. If you already have Audacity, you can download the Muse Hub and access the free extras [here](https://www.musehub.com/). ### How do I install Audacity or update to the latest version? + You can learn how to install Audacity on Windows, macOS and Linux on our [support page](https://support.audacityteam.org/basics/downloading-and-installing-audacity). - ### Is Audacity safe to download? -Yes. Audacity is entirely safe to download and install on your desktop computer if it has been downloaded directly from the [Audacity website](/). + +Yes. Audacity is entirely safe to download and install on your desktop computer if it has been downloaded directly from the [Audacity website](/). We also publish official versions through the Microsoft Store, via `winget`, via Github and Fosshub. There also are third party Audacity builds around, though we cannot guarantee for the integrity of versions downloaded from places other than mentioned here. @@ -78,7 +86,8 @@ We also publish official versions through the Microsoft Store, via `winget`, via ## Features ### What features come with Audacity? -In the app, every audio creator can: + +In the app, every audio creator can: - Record live with a microphone or mixer. Or digitize imported recordings. - Edit your tracks fast with intuitive tools, including cutting, pasting and smooth volume mixing. @@ -91,43 +100,48 @@ In the app, every audio creator can: - Take your editing to the next level with an extensive selection of third-party effects plugins, designed by the passionate Audacity, open-source community. - Collaborate and backup projects with Cloud saving. - Visualize and analyze your audio clips in Spectrogram view. -- Upload and share your files online to [audio.com](https://audio.com/), instantly. +- Upload and share your files online to [Audio.com](https://audio.com/), instantly. To learn more about specific features, check out the [Audacity manual](https://manual.audacityteam.org/index.html). ### What audio file formats are compatible with Audacity? + Audacity supports import, export and conversion of files in every popular audio format, including mp3, m4a, AIFF, FLAC, WAV and more. In fact, you can just use Audacity to convert files into different formats if you like. -You can even combine clips from multiple formats into the same audio project. +You can even combine clips from multiple formats into the same audio project. ### What audio quality is compatible with Audacity? + Audacity supports 16-bit, 24-bit and 32-bit audio. Sample rates and formats are converted using high-quality resampling and dithering. ### Can I use Audacity to produce music? + Yes! Audacity can be used to record and produce your music tracks. Because it's free and easy-to-use, Audacity is a great DAW for music production beginners or those who want to make quick edits to recordings of live performances. For more information on how to use specific Audacity features, check out the [Audacity manual](https://manual.audacityteam.org/). ### Can I use Audacity to produce podcasts? + Yes! In fact, Audacity is the world's most popular software for recording and producing podcasts — because it's easy-to-use and completely free. For more information on how to use specific Audacity features, check out the [Audacity manual](https://manual.audacityteam.org/). ### Is Audacity a DAW? + Audacity is blurring the line between audio editor and DAW. It's capable of much more than a mere audio editor (for example, non-destructive editing and realtime effects), but it also is lacking some key features which DAWs traditionally have (MIDI editing and virtual instruments). The gap towards being a full DAW is being rapidly closed, with each release bringing new features that make Audacity increasingly capable, while remaining simple to use. ### I'm just getting started with Audacity. Are there any free tutorials available? -We have a beginner tutorial series available on https://support.audacityteam.org/, beginning with [installing Audacity](https://support.audacityteam.org/basics/downloading-and-installing-audacity) and [FFmpeg](https://support.audacityteam.org/basics/installing-ffmpeg), [recording your voice](https://support.audacityteam.org/basics/recording-your-voice-and-microphone), [recording desktop audio](https://support.audacityteam.org/basics/recording-desktop-audio), [editing audio](https://support.audacityteam.org/basics/audacity-editing), [saving and exporting projects](https://support.audacityteam.org/basics/saving-and-exporting-projects) and more! + +We have a beginner tutorial series available on https://support.audacityteam.org/, beginning with [installing Audacity](https://support.audacityteam.org/basics/downloading-and-installing-audacity) and [FFmpeg](https://support.audacityteam.org/basics/installing-ffmpeg), [recording your voice](https://support.audacityteam.org/basics/recording-your-voice-and-microphone), [recording desktop audio](https://support.audacityteam.org/basics/recording-desktop-audio), [editing audio](https://support.audacityteam.org/basics/audacity-editing), [saving and exporting projects](https://support.audacityteam.org/basics/saving-and-exporting-projects) and more! ### Is Audacity compatible with third party plugins? + Yes! As an open-source application many third party plugins have been developed for Audacity. Check out a list of some of our team's favorite Audacity plugins at https://plugins.audacityteam.org/ Audacity supports VST3, LADSPA, LV2, Nyquist, VST and Audio Unit plugins. - - ### Where can I find more information about specific Audacity features? Try the free, online [Audacity manual](https://manual.audacityteam.org/) for in-depth, technical information on specific features. @@ -137,6 +151,7 @@ Try the free, online [Audacity manual](https://manual.audacityteam.org/) for in- ## Legal & privacy ### What is Audacity's privacy policy? + The Audacity app only collects data relevant to error reporting (such as device information) and software updates. The Audacity team will take all the necessary steps to keep your data protected. It is fully GDPR compliant. @@ -144,45 +159,52 @@ The Audacity team will take all the necessary steps to keep your data protected. Read Audacity's privacy policy in full [here](/desktop-privacy-notice/). The cookie policy for this website can be found [here](/cookie-policy/). ### Is Audacity spyware? + No. Audacity is entirely safe to use and doesn't store personal information. Audacity only collects data relevant to error reporting (such as device information) and software updates. The Audacity team is fully GDPR compliant. Read Audacity's privacy policy in full [here](/desktop-privacy-notice/). ### I've shared my Audacity project on Audio.com. What are my legal rights regarding my project? -If you are the sole creator of the audio project, the intellectual property rights and mechanical copyright is retained by you. Similar to other UGC (User Generated Content) platforms, by using [audio.com](https://audio.com/) you grant a license to host and show your work. You are also free to distribute your work on other platforms, if you wish. For more details read the full [Terms & Conditions](https://audio.com/legal/terms-of-use.pdf). + +If you are the sole creator of the audio project, the intellectual property rights and mechanical copyright is retained by you. Similar to other UGC (User Generated Content) platforms, by using [Audio.com](https://audio.com/) you grant a license to host and show your work. You are also free to distribute your work on other platforms, if you wish. For more details read the full [Terms & Conditions](https://audio.com/legal/terms-of-use.pdf). --- ## Team & news ### Who is the Audacity Team? -Audacity is developed by a small team working remotely and supported by the community. + +Audacity is developed by a small team working remotely and supported by the community. Current team members are: -* Product owner: Martin Keary -* Project manager: Yana Larina -* Developers: Matthieu Hodgkinson, Grzegorz Wojciechowski, Dmitry Makarenko, Gabriel Sartori, Paul Martin -* Designers: Dilson's Pickles, Leo Wattenberg -* Testers: Sergey Lapysh, Antons Činakovs -* People from [MuseScore Studio](https://musescore.org) helping with Audacity 4 development: Jessica Williamson, Igor Korsukov, Elnur Ismailzada +- Product owner: Martin Keary +- Project manager: Yana Larina +- Developers: Matthieu Hodgkinson, Grzegorz Wojciechowski, Dmitry Makarenko, Gabriel Sartori, Paul Martin +- Designers: Dilson's Pickles, Leo Wattenberg +- Testers: Sergey Lapysh, Antons Činakovs +- People from [MuseScore Studio](https://musescore.org) helping with Audacity 4 development: Jessica Williamson, Igor Korsukov, Elnur Ismailzada Code contributors can be found on Github in the [commit history](https://github.com/audacity/audacity/commits/master) and [graphs](https://github.com/audacity/audacity/graphs/contributors), [contributors to the manual](https://manual.audacityteam.org/man/credits.html), [contributors to the forum](https://forum.audacityteam.org/u?order=post_count&period=all) and [contributors to the support site](https://support.audacityteam.org/community/contributing/credits-and-license) can be found on the respective pages. ### Where can I stay up to date with the latest Audacity news and updates? + Check out our [blog](/blog), subscribe to Audacity [YouTube Channel](https://www.youtube.com/@audacity), or follow us on [Facebook](https://www.facebook.com/Audacity/) or [Twitter](https://twitter.com/getaudacity). ### I have a question or issue with Audacity. Is there a support team I can contact? + Many frequently asked questions and issues are answered on our support page [here](https://support.audacityteam.org/). For detailed information on Audacity features, please check out the [Audacity manual](https://manual.audacityteam.org/index.html). If you have a question you can't find the answer to, please leave a comment on the [Audacity forum](https://forum.audacityteam.org/), where one of our team (or someone in the Audacity community), will be happy to help! ### I'm an open-source developer working with Audacity. How can I get in touch with the team? -Audacity is currently undergoing major code restructuring, so we unfortunately have very limited capacity to support third parties. That said, feel free to drop by our [dev discord](/devserver). + +Audacity is currently undergoing major code restructuring, so we unfortunately have very limited capacity to support third parties. That said, feel free to drop by our [dev discord](/devserver). ### What is the history of Audacity? + Audacity has transformed the lives of musicians and audio content creators for over two decades. Since launching in 2000, the desktop app has been downloaded over 200 million times. -In 2020, we joined with the makers of [Ultimate Guitar](https://www.ultimate-guitar.com/), [MuseScore](https://musescore.org/) and others to form [Muse Group](https://mu.se/), with a mission to empower millions of creatives all over the globe. Since then, our sister service [audio.com](https://audio.com) launched. +In 2020, we joined with the makers of [Ultimate Guitar](https://www.ultimate-guitar.com/), [MuseScore](https://musescore.org/) and others to form [Muse Group](https://mu.se/), with a mission to empower millions of creatives all over the globe. Since then, our sister service [Audio.com](https://audio.com) launched. -We remain committed to our free, open-source roots — and passionate about helping the next generation create the very best sounds, music and podcasts with Audacity. \ No newline at end of file +We remain committed to our free, open-source roots — and passionate about helping the next generation create the very best sounds, music and podcasts with Audacity. diff --git a/src/pages/post-download.astro b/src/pages/post-download.astro index 5f9e4b5..fcf6260 100644 --- a/src/pages/post-download.astro +++ b/src/pages/post-download.astro @@ -6,42 +6,58 @@ import logo from "../assets/img/promo/audacity-audiocom-promo.png"; import { Image } from "astro:assets"; --- - - +
-
- Audacity logo and audio.com logo overlaying in a playful manner +
+ Audacity logo and Audio.com logo overlaying in a playful manner
-

- Thank you for downloading Audacity! -

+

Thank you for downloading Audacity!

You can now complete Cloud setup

- -
-
-
+
+
+
- - -
-
-
+
+ +
+
+
- - -
-
-
+
+ +
+
+
- +
Download Audacity application
Connect to Cloud storage
-
Never lose your work in Audacity
+
+ Never lose your work in Audacity +
@@ -52,7 +68,6 @@ import { Image } from "astro:assets"; large />
-
diff --git a/src/utils/uiSemaphore.ts b/src/utils/uiSemaphore.ts new file mode 100644 index 0000000..cf7456c --- /dev/null +++ b/src/utils/uiSemaphore.ts @@ -0,0 +1,113 @@ +type UiSemaphoreLock = { + owner: string; + priority: number; +}; + +type UiSemaphoreAcquireOptions = { + priority?: number; + preempt?: boolean; +}; + +type UiSemaphoreListener = ( + channel: string, + lock: UiSemaphoreLock | null, +) => void; + +type UiSemaphore = { + acquire: ( + channel: string, + owner: string, + options?: UiSemaphoreAcquireOptions, + ) => boolean; + release: (channel: string, owner: string) => void; + isLocked: (channel: string) => boolean; + getLock: (channel: string) => UiSemaphoreLock | null; + subscribe: (listener: UiSemaphoreListener) => () => void; +}; + +type UiSemaphoreState = { + locks: Map; + listeners: Set; +}; + +declare global { + interface Window { + __audacityUiSemaphoreState?: UiSemaphoreState; + __audacityUiSemaphore?: UiSemaphore; + } +} + +const notify = (state: UiSemaphoreState, channel: string) => { + const lock = state.locks.get(channel) ?? null; + state.listeners.forEach((listener) => listener(channel, lock)); +}; + +export const getUiSemaphore = (): UiSemaphore | null => { + if (typeof window === "undefined") { + return null; + } + + if (window.__audacityUiSemaphore) { + return window.__audacityUiSemaphore; + } + + const state: UiSemaphoreState = window.__audacityUiSemaphoreState ?? { + locks: new Map(), + listeners: new Set(), + }; + + window.__audacityUiSemaphoreState = state; + + const semaphore: UiSemaphore = { + acquire(channel, owner, options) { + const requestedPriority = options?.priority ?? 0; + const shouldPreempt = options?.preempt ?? false; + const currentLock = state.locks.get(channel); + + if (currentLock && currentLock.owner !== owner) { + if (!(shouldPreempt && requestedPriority > currentLock.priority)) { + return false; + } + } + + if ( + currentLock && + currentLock.owner === owner && + currentLock.priority === requestedPriority + ) { + return true; + } + + state.locks.set(channel, { + owner, + priority: requestedPriority, + }); + notify(state, channel); + return true; + }, + release(channel, owner) { + const currentLock = state.locks.get(channel); + if (currentLock?.owner !== owner) { + return; + } + + state.locks.delete(channel); + notify(state, channel); + }, + isLocked(channel) { + return state.locks.has(channel); + }, + getLock(channel) { + return state.locks.get(channel) ?? null; + }, + subscribe(listener) { + state.listeners.add(listener); + return () => { + state.listeners.delete(listener); + }; + }, + }; + + window.__audacityUiSemaphore = semaphore; + return semaphore; +}; diff --git a/test-results/.last-run.json b/test-results/.last-run.json new file mode 100644 index 0000000..f740f7c --- /dev/null +++ b/test-results/.last-run.json @@ -0,0 +1,4 @@ +{ + "status": "passed", + "failedTests": [] +}