1111 * with this source code in the LICENSE.md file.
1212 */
1313
14- namespace Discord \Voice \ Processes ;
14+ namespace Discord \Voice ;
1515
1616use FFI ;
1717
1818/**
1919 * Handles the decoding of Opus audio data using FFI (Foreign Function Interface).
2020 *
21- * @since 10.19.0
21+ * @todo
22+ *
23+ * @property FFI $ffi
24+ * @method int opus_packet_get_nb_frames(mixed $packet, int $len)
25+ * @method int opus_packet_get_samples_per_frame(mixed $data, int $Fs)
26+ * @method mixed opus_decoder_create(int $Fs, int $channels, mixed $error)
27+ * @method int opus_decode(mixed $st, mixed $data, int $len, mixed $pcm, int $frame_size, int $decode_fec)
28+ * @method void opus_decoder_destroy(mixed $st)
2229 */
23- final class OpusFfi
30+ class OpusFFI
2431{
25- /**
26- * Creates a FFI instance (code in C) to decode Opus audio data.
27- * By using the libopus library, this function decodes Opus-encoded audio data
28- * into PCM samples.
29- * This is useful for processing audio data in Discord voice channels.
30- * @param string|mixed $data
31- *
32- * @return string Returns the decoded PCM audio data as a string/binary.
33- */
34- public static function decode ($ data ): string
32+ protected FFI $ ffi ;
33+
34+ public function __construct ()
3535 {
3636 // Load libopus and define needed functions/types
37- // TODO: Move this to a separate file or class if needed.
38- $ ffi = FFI ::cdef ('
37+ $ this ->ffi = FFI ::cdef ('
3938 typedef struct OpusDecoder OpusDecoder;
4039 typedef short opus_int16;
4140 typedef int opus_int32;
@@ -47,51 +46,62 @@ public static function decode($data): string
4746 int opus_decode(OpusDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec);
4847 void opus_decoder_destroy(OpusDecoder *st);
4948 ' , 'libopus.so.0 ' );
49+ }
5050
51- // Parameters
52- $ sampleRate = 48000 ;
53- $ channels = 2 ;
54-
51+ /**
52+ * Creates a FFI instance (code in C) to decode Opus audio data.
53+ * By using the libopus library, this function decodes Opus-encoded audio data
54+ * into PCM samples.
55+ *
56+ * @param string|mixed $data The Opus-encoded audio data to decode.
57+ *
58+ * @return string The decoded PCM audio data as a string/binary.
59+ */
60+ public function decode ($ data , int $ channels = 2 , int $ audioRate = 48000 ): string
61+ {
5562 $ dataLength = strlen ($ data );
63+ if ($ dataLength < 0 ) {
64+ return '' ;
65+ }
5666
57- $ dataBuffer = $ ffi ->new ("const unsigned char[ $ dataLength] " , false );
67+ $ dataBuffer = $ this -> ffi ->new ("const unsigned char[ $ dataLength] " , false );
5868 FFI ::memcpy ($ dataBuffer , $ data , $ dataLength );
5969
60- $ frames = $ ffi ->opus_packet_get_nb_frames ($ dataBuffer , $ dataLength );
61- $ samplesPerFrame = $ ffi ->opus_packet_get_samples_per_frame ($ dataBuffer , $ sampleRate );
70+ $ frames = $ this ->opus_packet_get_nb_frames ($ dataBuffer , $ dataLength );
71+ $ samplesPerFrame = $ this ->opus_packet_get_samples_per_frame ($ dataBuffer , $ audioRate );
6272 $ frameSize = $ frames * $ samplesPerFrame ;
6373
6474 // Create decoder
65- $ error = $ ffi ->new ('int ' );
66- $ decoder = $ ffi ->opus_decoder_create ($ sampleRate , $ channels , FFI ::addr ($ error ));
67-
68- // Prepare input data (Opus-encoded)
69-
70- if ($ dataLength < 0 ) {
71- $ ffi ->opus_decoder_destroy ($ decoder );
72-
73- return '' ;
74- }
75+ $ error = $ this ->ffi ->new ('int ' );
76+ $ decoder = $ this ->opus_decoder_create ($ audioRate , $ channels , FFI ::addr ($ error ));
7577
7678 // Prepare output buffer for PCM samples
77- $ pcm = $ ffi ->new ('opus_int16[ ' .$ frameSize * $ channels * 2 .'] ' , false );
79+ $ pcm = $ this -> ffi ->new ('opus_int16[ ' .$ frameSize * $ channels * 2 .'] ' , false );
7880
7981 // Decode
80- $ ret = $ ffi ->opus_decode ($ decoder , $ dataBuffer , $ dataLength , $ pcm , $ frameSize , 0 );
82+ $ ret = $ this ->opus_decode ($ decoder , $ dataBuffer , $ dataLength , $ pcm , $ frameSize , 0 );
8183
82- if ( $ ret < 0 ) {
83- $ ffi ->opus_decoder_destroy ($ decoder );
84+ // Clean up
85+ $ this ->opus_decoder_destroy ($ decoder );
8486
85- // TODO: Handle decoding error
86- return '' ; // Or handle error
87+ if ($ ret < 0 ) {
88+ /** @todo Handle decoding error */
89+ return '' ;
8790 }
8891
89- // Get PCM bytes
90- $ pcm_bytes = FFI ::string ($ pcm , $ ret * $ channels * 2 ); // 2 bytes per sample
91-
92- // Clean up
93- $ ffi ->opus_decoder_destroy ($ decoder );
92+ // 2 bytes per sample
93+ return FFI ::string ($ pcm , $ ret * $ channels * 2 );
94+ }
9495
95- return $ pcm_bytes ;
96+ /**
97+ * Magic method to redirect method calls to the FFI instance.
98+ *
99+ * @param string $name
100+ * @param array $arguments
101+ * @return mixed
102+ */
103+ public function __call (string $ name , array $ arguments )
104+ {
105+ return $ this ->ffi ->$ name (...$ arguments );
96106 }
97107}
0 commit comments