1 | /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ |
2 | /* If you are missing that file, acquire a complete release at teeworlds.com. */ |
3 | #ifndef ENGINE_CLIENT_SOUND_H |
4 | #define ENGINE_CLIENT_SOUND_H |
5 | |
6 | #include <base/lock.h> |
7 | |
8 | #include <engine/sound.h> |
9 | |
10 | #include <SDL_audio.h> |
11 | |
12 | #include <atomic> |
13 | |
14 | struct CSample |
15 | { |
16 | int m_Index; |
17 | int m_NextFreeSampleIndex; |
18 | |
19 | short *m_pData; |
20 | int m_NumFrames; |
21 | int m_Rate; |
22 | int m_Channels; |
23 | int m_LoopStart; |
24 | int m_LoopEnd; |
25 | int m_PausedAt; |
26 | |
27 | float TotalTime() const |
28 | { |
29 | return m_NumFrames / (float)m_Rate; |
30 | } |
31 | }; |
32 | |
33 | struct CChannel |
34 | { |
35 | int m_Vol; |
36 | int m_Pan; |
37 | }; |
38 | |
39 | struct CVoice |
40 | { |
41 | CSample *m_pSample; |
42 | CChannel *m_pChannel; |
43 | int m_Age; // increases when reused |
44 | int m_Tick; |
45 | int m_Vol; // 0 - 255 |
46 | int m_Flags; |
47 | int m_X, m_Y; |
48 | float m_Falloff; // [0.0, 1.0] |
49 | |
50 | int m_Shape; |
51 | union |
52 | { |
53 | ISound::CVoiceShapeCircle m_Circle; |
54 | ISound::CVoiceShapeRectangle m_Rectangle; |
55 | }; |
56 | }; |
57 | |
58 | class CSound : public IEngineSound |
59 | { |
60 | enum |
61 | { |
62 | NUM_SAMPLES = 512, |
63 | NUM_VOICES = 256, |
64 | NUM_CHANNELS = 16, |
65 | }; |
66 | |
67 | bool m_SoundEnabled = false; |
68 | SDL_AudioDeviceID m_Device = 0; |
69 | CLock m_SoundLock; |
70 | |
71 | CSample m_aSamples[NUM_SAMPLES] = {{.m_Index: 0}}; |
72 | int m_FirstFreeSampleIndex = 0; |
73 | |
74 | CVoice m_aVoices[NUM_VOICES] = {{.m_pSample: 0}}; |
75 | CChannel m_aChannels[NUM_CHANNELS] = {{.m_Vol: 255, .m_Pan: 0}}; |
76 | int m_NextVoice = 0; |
77 | uint32_t m_MaxFrames = 0; |
78 | |
79 | std::atomic<int> m_CenterX = 0; |
80 | std::atomic<int> m_CenterY = 0; |
81 | std::atomic<int> m_SoundVolume = 100; |
82 | int m_MixingRate = 48000; |
83 | |
84 | class IEngineGraphics *m_pGraphics = nullptr; |
85 | IStorage *m_pStorage = nullptr; |
86 | |
87 | int *m_pMixBuffer = nullptr; |
88 | |
89 | CSample *AllocSample(); |
90 | void RateConvert(CSample &Sample) const; |
91 | |
92 | bool DecodeOpus(CSample &Sample, const void *pData, unsigned DataSize) const; |
93 | bool DecodeWV(CSample &Sample, const void *pData, unsigned DataSize) const; |
94 | |
95 | void UpdateVolume(); |
96 | |
97 | public: |
98 | int Init() override; |
99 | int Update() override; |
100 | void Shutdown() override REQUIRES(!m_SoundLock); |
101 | |
102 | bool IsSoundEnabled() override { return m_SoundEnabled; } |
103 | |
104 | int LoadOpus(const char *pFilename, int StorageType = IStorage::TYPE_ALL) override REQUIRES(!m_SoundLock); |
105 | int LoadWV(const char *pFilename, int StorageType = IStorage::TYPE_ALL) override REQUIRES(!m_SoundLock); |
106 | int LoadOpusFromMem(const void *pData, unsigned DataSize, bool FromEditor) override REQUIRES(!m_SoundLock); |
107 | int LoadWVFromMem(const void *pData, unsigned DataSize, bool FromEditor) override REQUIRES(!m_SoundLock); |
108 | void UnloadSample(int SampleId) override REQUIRES(!m_SoundLock); |
109 | |
110 | float GetSampleTotalTime(int SampleId) override; // in s |
111 | float GetSampleCurrentTime(int SampleId) override REQUIRES(!m_SoundLock); // in s |
112 | void SetSampleCurrentTime(int SampleId, float Time) override REQUIRES(!m_SoundLock); |
113 | |
114 | void SetChannel(int ChannelId, float Vol, float Pan) override; |
115 | void SetListenerPos(float x, float y) override; |
116 | |
117 | void SetVoiceVolume(CVoiceHandle Voice, float Volume) override REQUIRES(!m_SoundLock); |
118 | void SetVoiceFalloff(CVoiceHandle Voice, float Falloff) override REQUIRES(!m_SoundLock); |
119 | void SetVoiceLocation(CVoiceHandle Voice, float x, float y) override REQUIRES(!m_SoundLock); |
120 | void SetVoiceTimeOffset(CVoiceHandle Voice, float TimeOffset) override REQUIRES(!m_SoundLock); // in s |
121 | |
122 | void SetVoiceCircle(CVoiceHandle Voice, float Radius) override REQUIRES(!m_SoundLock); |
123 | void SetVoiceRectangle(CVoiceHandle Voice, float Width, float Height) override REQUIRES(!m_SoundLock); |
124 | |
125 | CVoiceHandle Play(int ChannelId, int SampleId, int Flags, float x, float y) REQUIRES(!m_SoundLock); |
126 | CVoiceHandle PlayAt(int ChannelId, int SampleId, int Flags, float x, float y) override REQUIRES(!m_SoundLock); |
127 | CVoiceHandle Play(int ChannelId, int SampleId, int Flags) override REQUIRES(!m_SoundLock); |
128 | void Pause(int SampleId) override REQUIRES(!m_SoundLock); |
129 | void Stop(int SampleId) override REQUIRES(!m_SoundLock); |
130 | void StopAll() override REQUIRES(!m_SoundLock); |
131 | void StopVoice(CVoiceHandle Voice) override REQUIRES(!m_SoundLock); |
132 | bool IsPlaying(int SampleId) override REQUIRES(!m_SoundLock); |
133 | |
134 | int MixingRate() const override { return m_MixingRate; } |
135 | void Mix(short *pFinalOut, unsigned Frames) override REQUIRES(!m_SoundLock); |
136 | |
137 | void PauseAudioDevice() override; |
138 | void UnpauseAudioDevice() override; |
139 | }; |
140 | |
141 | #endif |
142 | |