Skip to content

Audio mix recorder #632

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Mar 25, 2025
Merged

Audio mix recorder #632

merged 17 commits into from
Mar 25, 2025

Conversation

hiroshihorie
Copy link
Member

@hiroshihorie hiroshihorie commented Mar 13, 2025

Easily mix and record multiple audio sources (AudioRenderer protocol).
Can add additional source while recording.

TODO:

  • Customize recording format
  • Optimize code

Example usage

let audioSettings: [String: Any] = [
    AVFormatIDKey: kAudioFormatMPEG4AAC,
    AVSampleRateKey: 48_000,
    AVNumberOfChannelsKey: 1,
    AVLinearPCMBitDepthKey: 32,
    AVLinearPCMIsFloatKey: true,
    AVLinearPCMIsNonInterleaved: false,
    AVLinearPCMIsBigEndianKey: false,
]

let recordFilePath = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent(UUID().uuidString + ".aac")
print("Recording to \(recordFilePath)...")

let recorder = try AudioMixRecorder(filePath: recordFilePath, audioSettings: audioSettings)
try recorder.start()

// Record local audio
AudioManager.shared.add(localAudioRenderer: recorder.addSource())
// And all remote audio
AudioManager.shared.add(remoteAudioRenderer: recorder.addSource())
// Or individual remote tracks
// RemoteAudioTrack.add(audioRenderer:)

Copy link

ilo-nanpa bot commented Mar 13, 2025

it seems like you haven't added any nanpa changeset files to this PR.

if this pull request includes changes to code, make sure to add a changeset, by writing a file to .nanpa/<unique-name>.kdl:

minor type="added" "Introduce frobnication algorithm"

refer to the manpage for more information.

@hiroshihorie hiroshihorie requested a review from ladvoc March 14, 2025 13:42
@hiroshihorie hiroshihorie marked this pull request as ready for review March 14, 2025 13:42
@hiroshihorie hiroshihorie requested a review from bcherry March 14, 2025 14:06
@hiroshihorie hiroshihorie requested a review from pblazej March 14, 2025 14:19
Copy link
Contributor

@bcherry bcherry left a comment

Choose a reason for hiding this comment

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

this is awesome and it works!

I found one bug, not sure what the cause is, but if I do (pseudo-code)

room.connect()
audioRecorder = audiomixrecorder(filePath:"../..")
audiomanager.shared.add(localRenderer: audioRecorder.newSource())
audiomanager.shared.add(remoteRendered: audioRecorder.newSource())

then at disconnect

room.disconnect()
audioRecorder.stop()
audioRecorder = nil

then if I try to connect again, i get a crash. I can fix the crash by manually removing the old sources from the audiomanager delegates. so it suggests that the sources (which should be stored weakly in audiomanager) aren't getting deallocated and maybe there is a retain cycle inside AudioMixRecorder

otherwise this is perfect

@hiroshihorie
Copy link
Member Author

Thanks for testing, will take a look 🙂

@bcherry
Copy link
Contributor

bcherry commented Mar 14, 2025

@hiroshihorie update: I missed the "removeAllSources" method. when i call that too at disconnect then the crash no longer happens when the next session starts but instead when it ends. which doesn't really make sense...

@hiroshihorie
Copy link
Member Author

@bcherry Can you try to reproduce the crash again ?

@ladvoc
Copy link
Contributor

ladvoc commented Mar 24, 2025

I was unable to reproduce the crash following the steps Ben described.

@ladvoc
Copy link
Contributor

ladvoc commented Mar 24, 2025

This might be out of the scope of this PR, but maybe we could define an AudioSettings struct and related types to modernize the API a bit:

var settings = AudioSettings()
settings.formatID = .mpeg4aac
settings.sampleRate = 48_000
settings.channelCount = 1
settings.pcm.bitDepth = 32
settings.pcm.isFloat = true
settings.pcm.isInterleaved = false
settings.pcm.isBigEndian = false

An additional overload of AudioMixRecorder’s initializer will accept this structure instead of a dictionary, but we would keep the existing dictionary-based overload for Objective-C compatibility.

@hiroshihorie
Copy link
Member Author

@ladvoc I agree we can improve the AudioSettings but maybe we can add that later. I think it's a bit hard to properly cover all settings.

@hiroshihorie hiroshihorie merged commit 23094a5 into main Mar 25, 2025
19 of 20 checks passed
@hiroshihorie hiroshihorie deleted the hiroshi/audio-mix-recorder branch March 25, 2025 09:44
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.

3 participants