Skip to content

Commit 5a895b3

Browse files
committed
Started Vocoder
1 parent 697a552 commit 5a895b3

File tree

4 files changed

+223
-0
lines changed

4 files changed

+223
-0
lines changed
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
// Copyright AudioKit. All Rights Reserved.
2+
3+
#include "SoundpipeDSPBase.h"
4+
#include "ParameterRamper.h"
5+
#include "Soundpipe.h"
6+
7+
enum VocoderParameter : AUParameterAddress {
8+
VocoderParameterAttackTime,
9+
VocoderParameterReleaseTime,
10+
VocoderParameterBandwidthRatio
11+
};
12+
13+
class VocoderDSP : public SoundpipeDSPBase {
14+
private:
15+
sp_vocoder *vocoderL;
16+
sp_vocoder *vocoderR;
17+
ParameterRamper attackRamp{0.1};
18+
ParameterRamper releaseRamp{0.1};
19+
ParameterRamper bwRatioRamp{0.5};
20+
21+
float attackTimeL = 0.1;
22+
float releaseTimeL = 0.1;
23+
float bwRatioL = 0.5;
24+
float attackTimeR = 0.1;
25+
float releaseTimeR = 0.1;
26+
float bwRatioR = 0.5;
27+
28+
public:
29+
VocoderDSP() {
30+
inputBufferLists.resize(2); // Set up for two input streams
31+
parameters[VocoderParameterAttackTime] = &attackRamp;
32+
parameters[VocoderParameterReleaseTime] = &releaseRamp;
33+
parameters[VocoderParameterBandwidthRatio] = &bwRatioRamp;
34+
}
35+
36+
void init(int channelCount, double sampleRate) override {
37+
SoundpipeDSPBase::init(channelCount, sampleRate);
38+
sp_vocoder_create(&vocoderL);
39+
sp_vocoder_init(sp, vocoderL);
40+
sp_vocoder_create(&vocoderR);
41+
sp_vocoder_init(sp, vocoderR);
42+
}
43+
44+
void deinit() override {
45+
SoundpipeDSPBase::deinit();
46+
sp_vocoder_destroy(&vocoderL);
47+
sp_vocoder_destroy(&vocoderR);
48+
}
49+
50+
void reset() override {
51+
SoundpipeDSPBase::reset();
52+
if (!isInitialized) return;
53+
sp_vocoder_init(sp, vocoderL);
54+
sp_vocoder_init(sp, vocoderR);
55+
}
56+
57+
void process(FrameRange range) override {
58+
for (int i : range) {
59+
float sourceInL = inputSample(0, i); // carrier input left
60+
float sourceInR = inputSample(1, i); // carrier input right
61+
float excitationInL = input2Sample(0, i); // modulator input left
62+
float excitationInR = input2Sample(1, i); // modulator input right
63+
float outSampleL;
64+
float outSampleR;
65+
66+
attackTimeL = attackRamp.getAndStep();
67+
releaseTimeL = releaseRamp.getAndStep();
68+
bwRatioL = bwRatioRamp.getAndStep();
69+
70+
attackTimeR = attackTimeL;
71+
releaseTimeR = releaseTimeL;
72+
bwRatioR = bwRatioL;
73+
74+
vocoderL->atk = &attackTimeL;
75+
vocoderL->rel = &releaseTimeL;
76+
vocoderL->bwratio = &bwRatioL;
77+
vocoderR->atk = &attackTimeR;
78+
vocoderR->rel = &releaseTimeR;
79+
vocoderR->bwratio = &bwRatioR;
80+
81+
sp_vocoder_compute(sp, vocoderL, &sourceInL, &excitationInL, &outSampleL);
82+
sp_vocoder_compute(sp, vocoderR, &sourceInR, &excitationInR, &outSampleR);
83+
84+
outputSample(0, i) = outSampleL;
85+
outputSample(1, i) = outSampleR;
86+
}
87+
}
88+
};
89+
90+
AK_REGISTER_DSP(VocoderDSP, "vcdr")
91+
AK_REGISTER_PARAMETER(VocoderParameterAttackTime)
92+
AK_REGISTER_PARAMETER(VocoderParameterReleaseTime)
93+
AK_REGISTER_PARAMETER(VocoderParameterBandwidthRatio)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
2+
3+
// Copyright AudioKit. All Rights Reserved.
4+
5+
import AudioKit
6+
import AudioKitEX
7+
import AVFoundation
8+
import CAudioKitEX
9+
10+
/// 16-band channel vocoder
11+
///
12+
public class Vocoder: Node {
13+
let input: Node
14+
let excitation: Node
15+
16+
/// Connected nodes
17+
public var connections: [Node] { [input, excitation] }
18+
19+
/// Underlying AVAudioNode
20+
public var avAudioNode = instantiate(effect: "vcdr")
21+
22+
// MARK: - Parameters
23+
24+
/// Attack time (0.001 to 0.5 seconds)
25+
public static let attackTimeDef = NodeParameterDef(
26+
identifier: "atk",
27+
name: "Attack Time",
28+
address: akGetParameterAddress("VocoderParameterAttackTime"),
29+
defaultValue: 0.1,
30+
range: 0.001 ... 0.5,
31+
unit: .seconds
32+
)
33+
34+
/// Release time
35+
public static let releaseTimeDef = NodeParameterDef(
36+
identifier: "rel",
37+
name: "Release Time",
38+
address: akGetParameterAddress("VocoderParameterReleaseTime"),
39+
defaultValue: 0.1,
40+
range: 0.001 ... 0.5,
41+
unit: .seconds
42+
)
43+
44+
/// Bandwidth ratio (0.1 to 2.0)
45+
public static let bandwidthRatioDef = NodeParameterDef(
46+
identifier: "bwratio",
47+
name: "Bandwidth Ratio",
48+
address: akGetParameterAddress("VocoderParameterBandwidthRatio"),
49+
defaultValue: 0.5,
50+
range: 0.1 ... 2.0,
51+
unit: .generic
52+
)
53+
54+
/// Attack time (seconds)
55+
@Parameter(attackTimeDef) public var attackTime: AUValue
56+
57+
/// Release time (seconds)
58+
@Parameter(releaseTimeDef) public var releaseTime: AUValue
59+
60+
/// Bandwidth ratio
61+
@Parameter(bandwidthRatioDef) public var bandwidthRatio: AUValue
62+
63+
// MARK: - Initialization
64+
65+
/// Initialize this vocoder node
66+
///
67+
/// - Parameters:
68+
/// - input: Source signal (carrier)
69+
/// - excitation: Excitation signal (modulator)
70+
/// - attackTime: Attack time (0.001 to 0.5 seconds)
71+
/// - releaseTime: Release time (0.001 to 0.5 seconds)
72+
/// - bandwidthRatio: Bandwidth ratio (0.1 to 2.0)
73+
///
74+
public init(
75+
_ input: Node,
76+
excitation: Node,
77+
attackTime: AUValue = attackTimeDef.defaultValue,
78+
releaseTime: AUValue = releaseTimeDef.defaultValue,
79+
bandwidthRatio: AUValue = bandwidthRatioDef.defaultValue
80+
) {
81+
self.input = input
82+
self.excitation = excitation
83+
84+
setupParameters()
85+
86+
self.attackTime = attackTime
87+
self.releaseTime = releaseTime
88+
self.bandwidthRatio = bandwidthRatio
89+
}
90+
}
91+
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright AudioKit. All Rights Reserved. Revision History at http://github.com/AudioKit/AudioKit/
2+
3+
import AudioKit
4+
import SoundpipeAudioKit
5+
import XCTest
6+
7+
class VocoderTests: XCTestCase {
8+
func testVocoder() {
9+
let engine = AudioEngine()
10+
11+
let url = Bundle.module.url(forResource: "12345", withExtension: "wav", subdirectory: "TestResources")!
12+
let source = AudioPlayer(url: url)!
13+
14+
// Excitation/carrier (harmonically rich signal)
15+
let excitation = DynamicOscillator(waveform: Table(.sawtooth), frequency: 110, amplitude: 1.0)
16+
excitation.$frequency.ramp(to: 440, duration: 5.0)
17+
18+
// Create vocoder with both signals
19+
let vocoder = Vocoder(source, excitation: excitation, attackTime: 0.123, releaseTime: 0.24, bandwidthRatio: 0.324)
20+
vocoder.$attackTime.ramp(from: 0.01, to: 0.5, duration: 5.0)
21+
vocoder.$releaseTime.ramp(from: 0.01, to: 0.5, duration: 5.0)
22+
vocoder.$bandwidthRatio.ramp(from: 0.1, to: 2.0, duration: 5.0)
23+
24+
engine.output = vocoder
25+
26+
let audio = engine.startTest(totalDuration: 5.0)
27+
28+
// Start both signals
29+
source.play()
30+
excitation.play()
31+
32+
audio.append(engine.render(duration: 5.0))
33+
34+
testMD5(audio)
35+
// audio.audition()
36+
}
37+
}
38+

Tests/SoundpipeAudioKitTests/ValidatedMD5s.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,5 @@ let validatedMD5s: [String: String] = [
5151
"-[PitchTapTests testBasic]": "5b6ae6252df77df298996a7367a00a9e",
5252
"-[PluckedStringTests testDefault]": "3f13907e6e916b7a4bf6046a4cbf0764",
5353
"-[TalkboxTests testTalkbox]": "316ef6638793f5fb6ec43fae1919ccff",
54+
"-[VocoderTests testVocoder]": "1e084a4d0399923b923ff6a07dc8a320",
5455
]

0 commit comments

Comments
 (0)