1 /** 2 Implementation of types that can be played on a VoiceClient 3 */ 4 module dscord.voice.playable; 5 6 import dcad.types : DCAFile; 7 8 /** 9 An interface representing a type which can be played over a VoiceClient. 10 */ 11 interface Playable { 12 /// Duration of the frame in milliseconds 13 const short getFrameDuration(); 14 15 /// Size of the frame in bytes 16 const short getFrameSize(); 17 18 /// Returns the next frame to be played 19 ubyte[] nextFrame(); 20 21 /// Returns true while there are more frames to be played 22 bool hasMoreFrames(); 23 24 /// Called when the Playable begins to be played 25 void start(); 26 } 27 28 /** 29 Playable implementation for DCAFiles 30 */ 31 class DCAPlayable : Playable { 32 private { 33 DCAFile file; 34 35 size_t frameIndex; 36 } 37 38 this(DCAFile file) { 39 this.file = file; 40 } 41 42 // TODO: Don't hardcode this 43 const short getFrameDuration() { 44 return 20; 45 } 46 47 const short getFrameSize() { 48 return 960; 49 } 50 51 bool hasMoreFrames() { 52 return this.frameIndex + 1 < this.file.frames.length; 53 } 54 55 ubyte[] nextFrame() { 56 this.frameIndex++; 57 return this.file.frames[this.frameIndex - 1].data; 58 } 59 60 void start() {} 61 } 62 63 interface PlaylistProvider { 64 bool hasNext(); 65 Playable getNext(); 66 } 67 68 class Playlist : Playable { 69 PlaylistProvider provider; 70 Playable current; 71 72 this(PlaylistProvider provider) { 73 this.provider = provider; 74 } 75 76 const short getFrameDuration() { 77 return this.current.getFrameDuration(); 78 } 79 80 const short getFrameSize() { 81 return this.current.getFrameSize(); 82 } 83 84 bool hasMoreFrames() { 85 if (!this.current) return false; 86 if (this.current.hasMoreFrames()) return true; 87 if (this.provider.hasNext()) return true; 88 return false; 89 } 90 91 ubyte[] nextFrame() { 92 if (!this.current.hasMoreFrames()) { 93 if (this.provider.hasNext()) { 94 this.current = this.provider.getNext(); 95 } else{ 96 this.current = null; 97 } 98 } 99 100 return this.current.nextFrame(); 101 } 102 103 void start() { 104 this.next(); 105 } 106 107 void next() { 108 if (this.provider.hasNext()) { 109 this.current = this.provider.getNext(); 110 } else { 111 this.current = null; 112 } 113 } 114 } 115 116 /** 117 Simple Playlist provider. 118 */ 119 class SimplePlaylistProvider : PlaylistProvider { 120 private { 121 Playable[] playlist; 122 } 123 124 this(Playable[] playlist) { 125 this.playlist = playlist; 126 } 127 128 bool hasNext() { 129 return (this.playlist.length > 0); 130 } 131 132 Playable getNext() { 133 assert(this.hasNext(), "No next Playable for SimplePlaylistProvider"); 134 Playable next = this.playlist[0]; 135 this.playlist = this.playlist[1..$]; 136 return next; 137 } 138 139 @property size_t length() { 140 return this.playlist.length; 141 } 142 143 void add(Playable p) { 144 this.playlist ~= p; 145 } 146 147 void empty() { 148 this.playlist = []; 149 } 150 }