Skip to content

Sound API 2#943

Merged
hiroshihorie merged 57 commits intomainfrom
hiroshi/sound-player-2
Apr 7, 2026
Merged

Sound API 2#943
hiroshihorie merged 57 commits intomainfrom
hiroshi/sound-player-2

Conversation

@hiroshihorie
Copy link
Copy Markdown
Member

@hiroshihorie hiroshihorie commented Mar 10, 2026

Conveniently prepare and play short audio clips locally and/or for remote participants.

// Prepare
let sound = try await SoundPlayer.shared.prepare(
    fileURL: fileURL,
    named: "sample01" // optional
)

// Playback
try await sound.play(
    options: SoundPlaybackOptions(
        mode: .concurrent,
        loop: false,
        destination: .localAndRemote
    )
)

// Stop / release
await sound.stop()
await sound.release()

Remote playback is best-effort and usually requires the microphone/WebRTC input path to be published.

Example: livekit-examples/swift-example#89

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 24, 2026

⚠️ This PR does not contain any files in the .changes directory.

@hiroshihorie hiroshihorie marked this pull request as ready for review March 24, 2026 11:50
@hiroshihorie hiroshihorie mentioned this pull request Mar 24, 2026
@hiroshihorie hiroshihorie requested a review from pblazej March 30, 2026 22:53

/// Plays a prepared sound.
///
/// Remote playback is best-effort and is skipped if remote routing is unavailable.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way to propagate the error atm, right?

public class SoundPlayer: Loggable, @unchecked Sendable {
// MARK: - Public

public static let shared = SoundPlayer()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered an explicit lifecycle here (e.g. coupled with AudioManager, Room etc.)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: if this is a singleton, can leverage https://www.avanderlee.com/concurrency/global-actor/

/// Releases the associated audio session requirement.
///
/// Releasing the same handle multiple times is a no-op.
public func release() throws {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably should be called on deinit.

Copy link
Copy Markdown
Contributor

@pblazej pblazej left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more than enough for V1, good point about gradual evolution.

Trivia I'd address before merging:

  • rename url to fileURL still (just self-documenting)
  • auto-release session on handle's deinit
  • adding globalActor

Follow-ups:

  • playOnce addition (no hard feelings)?
  • lazy pool
  • caching converted audio

For me as a consumer the biggest confusion comes from the fact that .remote cannot be played without mic track - which is not clear just reading the APIs. I get the best-effort tradeoff, maybe we can escalate the log to .warning at least / or rename the enum more aggressively:

    public enum Destination: Sendable {
        case local
        case remoteIfAvailable // ?
        case localAndRemoteIfAvailable

that could be an OptionSet as well e.g.

  public struct Destination: OptionSet, Sendable {
      public static let local  = Destination(rawValue: 1 << 0)
      public static let remoteIfAvailable = Destination(rawValue: 1 << 1)
  }

you pass like this:

  options: .init(destination: [.local, .remoteIfAvailable])
  options: .init(destination: .local)

@hiroshihorie
Copy link
Copy Markdown
Member Author

Thanks @pblazej , will work on a second pass of polishing this up.

@hiroshihorie hiroshihorie merged commit cdda87f into main Apr 7, 2026
25 of 27 checks passed
@hiroshihorie hiroshihorie deleted the hiroshi/sound-player-2 branch April 7, 2026 12:27
@pblazej pblazej mentioned this pull request Apr 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants