1 module opus.encoder;
2 
3 const int OPUS_OK = 0;
4 
5 enum Application {
6   VOIP = 2048,
7   AUDIO = 2049,
8   LOWDELAY = 2051,
9 }
10 
11 enum Bandwidth {
12   NARROWBAND = 1101,
13   MEDIUMBAND = 1102,
14   WIDEBAND = 1103,
15   SUPERWIDEBAND = 1104,
16   FULLBAND = 1105,
17 }
18 
19 enum CTL {
20   SET_BITRATE_REQUEST = 4002,
21   SET_BANDWIDTH_REQUEST = 4008,
22   SET_INBAND_FEC_REQUEST = 4012,
23   SET_PACKET_LOSS_PERC_REQUEST = 4014,
24 }
25 
26 extern (C) {
27   struct OpusEncoder {};
28 
29   OpusEncoder* opus_encoder_create(int, int, int, int*);
30   void opus_encoder_destroy(OpusEncoder*);
31   int opus_encode(OpusEncoder*, const short*, int, ubyte*, int);
32   int opus_encoder_ctl(OpusEncoder*, int request, ...);
33 }
34 
35 /// Encoder is a wrapper around opus encoding
36 class Encoder {
37   OpusEncoder* encoder;
38 
39   private {
40     int sampleRate;
41     int channels;
42     Application app;
43   }
44 
45   this(int sampleRate=48000, int channels=2, Application app = Application.VOIP) {
46     this.sampleRate = sampleRate;
47     this.channels = channels;
48     this.app = app;
49 
50     int error;
51     this.encoder = opus_encoder_create(sampleRate, channels, app, &error);
52     assert(error == OPUS_OK);
53   }
54 
55   ~this() {
56     if (this.encoder) {
57       opus_encoder_destroy(this.encoder);
58       this.encoder = null;
59     }
60   }
61 
62   /// Encodes an array of PCM data into raw opus
63   ubyte[] encode(immutable short[] pcm, int frameSize) {
64     ubyte[] data = new ubyte[]((frameSize * this.channels) * 2);
65     int size = opus_encode(this.encoder, &pcm[0], frameSize, &data[0], cast(int)data.length);
66     return data[0..size];
67   }
68 
69   /// Sets the bitrate for this encoder (in kbps)
70   void setBitrate(int kbps) {
71     assert(opus_encoder_ctl(this.encoder, CTL.SET_BITRATE_REQUEST, kbps * 1024) == OPUS_OK);
72   }
73 
74   /// Sets the bandwidth for this encoder
75   void setBandwidth(Bandwidth bw) {
76     assert(opus_encoder_ctl(this.encoder, CTL.SET_BANDWIDTH_REQUEST, cast(int)bw) == OPUS_OK);
77   }
78 
79   /// Sets whether inband FEC (forward error correction) is enabled for this encoder
80   void setInbandFEC(bool enabled) {
81     assert(opus_encoder_ctl(this.encoder, CTL.SET_INBAND_FEC_REQUEST, cast(int)enabled) == OPUS_OK);
82   }
83 
84   /// Sets the predicted packet loss percent for FEC (if enabled)
85   void setPacketLossPercent(int percent) {
86     assert(opus_encoder_ctl(this.encoder, CTL.SET_PACKET_LOSS_PERC_REQUEST, percent) == OPUS_OK);
87   }
88 }
89 
90 unittest {
91   Encoder enc = new Encoder();
92   enc.setInbandFEC(true);
93   enc.setPacketLossPercent(30);
94   enc.setBandwidth(Bandwidth.FULLBAND);
95   enc.setBitrate(128);
96   enc.destroy();
97 }