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
14struct 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 bool IsLoaded() const
33 {
34 return m_pData != nullptr;
35 }
36};
37
38struct CChannel
39{
40 int m_Vol;
41 int m_Pan;
42};
43
44struct CVoice
45{
46 CSample *m_pSample;
47 CChannel *m_pChannel;
48 int m_Age; // increases when reused
49 int m_Tick;
50 int m_Vol; // 0 - 255
51 int m_Flags;
52 vec2 m_Position;
53 float m_Falloff; // [0.0, 1.0]
54
55 int m_Shape;
56 union
57 {
58 ISound::CVoiceShapeCircle m_Circle;
59 ISound::CVoiceShapeRectangle m_Rectangle;
60 };
61};
62
63class CSound : public IEngineSound
64{
65 enum
66 {
67 NUM_SAMPLES = 512,
68 NUM_VOICES = 256,
69 NUM_CHANNELS = 16,
70 };
71
72 bool m_SoundEnabled = false;
73 SDL_AudioDeviceID m_Device = 0;
74 CLock m_SoundLock;
75
76 CSample m_aSamples[NUM_SAMPLES] GUARDED_BY(m_SoundLock) = {{.m_Index: 0}};
77 int m_FirstFreeSampleIndex GUARDED_BY(m_SoundLock) = 0;
78
79 CVoice m_aVoices[NUM_VOICES] GUARDED_BY(m_SoundLock) = {{.m_pSample: nullptr}};
80 CChannel m_aChannels[NUM_CHANNELS] GUARDED_BY(m_SoundLock) = {{.m_Vol: 255, .m_Pan: 0}};
81 int m_NextVoice GUARDED_BY(m_SoundLock) = 0;
82 uint32_t m_MaxFrames = 0;
83
84 // This is not an std::atomic<vec2> as this would require linking with
85 // libatomic with clang x86 as there is no native support for this.
86 std::atomic<float> m_ListenerPositionX = 0.0f;
87 std::atomic<float> m_ListenerPositionY = 0.0f;
88 std::atomic<int> m_SoundVolume = 100;
89 int m_MixingRate = 48000;
90
91 class IEngineGraphics *m_pGraphics = nullptr;
92 IStorage *m_pStorage = nullptr;
93
94 int *m_pMixBuffer = nullptr;
95
96 CSample *AllocSample() REQUIRES(!m_SoundLock);
97 void RateConvert(CSample &Sample) const;
98
99 // pContextName used for error
100 bool DecodeOpus(CSample &Sample, const void *pData, unsigned DataSize, const char *pContextName) const;
101 bool DecodeWV(CSample &Sample, const void *pData, unsigned DataSize, const char *pContextName) const;
102
103 void UpdateVolume();
104
105public:
106 int Init() override REQUIRES(!m_SoundLock);
107 int Update() override;
108 void Shutdown() override REQUIRES(!m_SoundLock);
109
110 bool IsSoundEnabled() override { return m_SoundEnabled; }
111
112 int LoadOpus(const char *pFilename, int StorageType = IStorage::TYPE_ALL) override REQUIRES(!m_SoundLock);
113 int LoadWV(const char *pFilename, int StorageType = IStorage::TYPE_ALL) override REQUIRES(!m_SoundLock);
114 int LoadOpusFromMem(const void *pData, unsigned DataSize, bool ForceLoad, const char *pContextName) override REQUIRES(!m_SoundLock);
115 int LoadWVFromMem(const void *pData, unsigned DataSize, bool ForceLoad, const char *pContextName) override REQUIRES(!m_SoundLock);
116 void UnloadSample(int SampleId) override REQUIRES(!m_SoundLock);
117
118 float GetSampleTotalTime(int SampleId) override REQUIRES(!m_SoundLock); // in s
119 float GetSampleCurrentTime(int SampleId) override REQUIRES(!m_SoundLock); // in s
120 void SetSampleCurrentTime(int SampleId, float Time) override REQUIRES(!m_SoundLock);
121
122 void SetChannel(int ChannelId, float Vol, float Pan) override REQUIRES(!m_SoundLock);
123 void SetListenerPosition(vec2 Position) override;
124
125 void SetVoiceVolume(CVoiceHandle Voice, float Volume) override REQUIRES(!m_SoundLock);
126 void SetVoiceFalloff(CVoiceHandle Voice, float Falloff) override REQUIRES(!m_SoundLock);
127 void SetVoicePosition(CVoiceHandle Voice, vec2 Position) override REQUIRES(!m_SoundLock);
128 void SetVoiceTimeOffset(CVoiceHandle Voice, float TimeOffset) override REQUIRES(!m_SoundLock); // in s
129
130 void SetVoiceCircle(CVoiceHandle Voice, float Radius) override REQUIRES(!m_SoundLock);
131 void SetVoiceRectangle(CVoiceHandle Voice, float Width, float Height) override REQUIRES(!m_SoundLock);
132
133 CVoiceHandle Play(int ChannelId, int SampleId, int Flags, float Volume, vec2 Position) REQUIRES(!m_SoundLock);
134 CVoiceHandle PlayAt(int ChannelId, int SampleId, int Flags, float Volume, vec2 Position) override REQUIRES(!m_SoundLock);
135 CVoiceHandle Play(int ChannelId, int SampleId, int Flags, float Volume) override REQUIRES(!m_SoundLock);
136 void Pause(int SampleId) override REQUIRES(!m_SoundLock);
137 void Stop(int SampleId) override REQUIRES(!m_SoundLock);
138 void StopAll() override REQUIRES(!m_SoundLock);
139 void StopVoice(CVoiceHandle Voice) override REQUIRES(!m_SoundLock);
140 bool IsPlaying(int SampleId) override REQUIRES(!m_SoundLock);
141
142 int MixingRate() const override { return m_MixingRate; }
143 void Mix(short *pFinalOut, unsigned Frames) override REQUIRES(!m_SoundLock);
144
145 void PauseAudioDevice() override;
146 void UnpauseAudioDevice() override;
147};
148
149#endif
150