Skip to content

Commit 1e32ef9

Browse files
authored
PhaseLockedVocoderDSP::reset() memory leak (#39)
* Failing test * hound * Destroy mincer before init Failing test passes now
1 parent 0ad9a8d commit 1e32ef9

File tree

3 files changed

+65
-0
lines changed

3 files changed

+65
-0
lines changed

Sources/CSoundpipeAudioKit/Generators/PhaseLockedVocoderDSP.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ void deinit() override {
5454
void reset() override {
5555
SoundpipeDSPBase::reset();
5656
if (!isInitialized) return;
57+
sp_mincer_destroy(&mincer);
58+
sp_mincer_create(&mincer);
5759
sp_mincer_init(sp, mincer, ftbl, 2048);
5860
}
5961

Tests/SoundpipeAudioKitTests/Generator Tests/PhaseLockedVocoderTests.swift

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,66 @@ class PhaseLockedVocoderTests: XCTestCase {
4444

4545
testMD5(audio)
4646
}
47+
48+
func testReset() {
49+
let url = generateTestFile()
50+
XCTAssertNotNil(url)
51+
52+
guard let file = try? AVAudioFile(forReading: url) else {
53+
XCTFail("Couldn't load test file")
54+
return
55+
}
56+
57+
let engine = AudioEngine()
58+
let vocoder = PhaseLockedVocoder(file: file)
59+
engine.output = vocoder
60+
61+
// Start the engine and render some audio
62+
let audio = engine.startTest(totalDuration: 2.0)
63+
vocoder.$position.ramp(to: 0.5, duration: 0.5)
64+
audio.append(engine.render(duration: 1.0))
65+
66+
// Get initial memory usage
67+
var info = mach_task_basic_info()
68+
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
69+
var kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
70+
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
71+
task_info(mach_task_self_,
72+
task_flavor_t(MACH_TASK_BASIC_INFO),
73+
$0,
74+
&count)
75+
}
76+
}
77+
XCTAssertEqual(kerr, KERN_SUCCESS)
78+
let initialMemory = info.resident_size
79+
80+
// Reset the vocoder multiple times to stress test memory management
81+
for _ in 0 ... 1000 {
82+
vocoder.reset()
83+
}
84+
85+
// Get final memory usage
86+
kerr = withUnsafeMutablePointer(to: &info) {
87+
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
88+
task_info(mach_task_self_,
89+
task_flavor_t(MACH_TASK_BASIC_INFO),
90+
$0,
91+
&count)
92+
}
93+
}
94+
XCTAssertEqual(kerr, KERN_SUCCESS)
95+
let finalMemory = info.resident_size
96+
97+
// Calculate memory growth
98+
let memoryGrowth = finalMemory - initialMemory
99+
XCTAssertLessThan(memoryGrowth, 1_000_000, "Memory leak detected: Memory grew by \(memoryGrowth) bytes")
100+
101+
// Continue rendering after reset
102+
vocoder.$position.ramp(to: 0, duration: 0.5)
103+
audio.append(engine.render(duration: 1.0))
104+
105+
engine.stop()
106+
107+
testMD5(audio)
108+
}
47109
}

Tests/SoundpipeAudioKitTests/ValidatedMD5s.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ let validatedMD5s: [String: String] = [
4848
"-[OscillatorAutomationTests testAutomationAfterDelayedConnection]": "93d96731b7ca3dc9bf1e4209bd0b65ec",
4949
"-[OscillatorAutomationTests testDelayedAutomation]": "640265895a27587289d65a29ce129804",
5050
"-[PhaseLockedVocoderTests testDefault]": "eb9fe2d8ee2e3b3d6527a4e139c2686e",
51+
"-[PhaseLockedVocoderTests testReset]": "814d0574a9d98c76e2dd557050f55f37",
5152
"-[PitchTapTests testBasic]": "5b6ae6252df77df298996a7367a00a9e",
5253
"-[PluckedStringTests testDefault]": "3f13907e6e916b7a4bf6046a4cbf0764",
5354
"-[TalkboxTests testTalkbox]": "316ef6638793f5fb6ec43fae1919ccff",

0 commit comments

Comments
 (0)