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
33struct CChannel
34{
35 int m_Vol;
36 int m_Pan;
37};
38
39struct 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
58class 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
97public:
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