1 #pragma once
2
3
4 #include <algorithm> // std::swap
5
6 #include "util/types.h"
7
8
9 namespace mixxx {
10
11 // A sample buffer with properly aligned memory to enable SSE optimizations.
12 // After construction the content of the buffer is uninitialized. No resize
13 // operation is provided intentionally because malloc might block! Copying
14 // has intentionally been disabled, because it should not be needed.
15 //
16 // Hint: If the size of an existing sample buffer ever needs to be altered
17 // after construction this can simply be achieved by swapping the contents
18 // with a temporary sample buffer that has been constructed with the desired
19 // size:
20 //
21 // SampleBuffer sampleBuffer(oldSize);
22 // ...
23 // SampleBuffer tempBuffer(newSize)
24 // ... copy data from sampleBuffer to tempBuffer...
25 // sampleBuffer.swap(sampleBuffer);
26 //
27 // The local variable tempBuffer can be omitted if no data needs to be
28 // copied from the existing sampleBuffer:
29 //
30 // SampleBuffer sampleBuffer(oldSize);
31 // ...
32 // SampleBuffer(newSize).swap(sampleBuffer);
33 //
34 class SampleBuffer final {
35 public:
SampleBuffer()36 SampleBuffer()
37 : m_data(nullptr),
38 m_size(0) {
39 }
40 explicit SampleBuffer(SINT size);
41 SampleBuffer(SampleBuffer&) = delete;
SampleBuffer(SampleBuffer && that)42 SampleBuffer(SampleBuffer&& that)
43 : m_data(that.m_data),
44 m_size(that.m_size) {
45 that.m_data = nullptr;
46 that.m_size = 0;
47 }
48 virtual ~SampleBuffer() final;
49
50 SampleBuffer& operator=(SampleBuffer& that) = delete;
51 SampleBuffer& operator=(SampleBuffer&& that) {
52 swap(that);
53 return *this;
54 }
55
size()56 SINT size() const {
57 return m_size;
58 }
59
60 CSAMPLE* data(SINT offset = 0) {
61 DEBUG_ASSERT((m_data != nullptr) || (offset == 0));
62 DEBUG_ASSERT(0 <= offset);
63 // >=: allow access to one element behind allocated memory
64 DEBUG_ASSERT(m_size >= offset);
65 return m_data + offset;
66 }
67 const CSAMPLE* data(SINT offset = 0) const {
68 DEBUG_ASSERT((m_data != nullptr) || (offset == 0));
69 DEBUG_ASSERT(0 <= offset);
70 // >=: allow access to one element behind allocated memory
71 DEBUG_ASSERT(m_size >= offset);
72 return m_data + offset;
73 }
74
75 CSAMPLE& operator[](SINT index) {
76 return *data(index);
77 }
78 const CSAMPLE& operator[](SINT index) const {
79 return *data(index);
80 }
81
82 // Exchanges the members of two buffers in conformance with the
83 // implementation of all STL containers. Required for exception
84 // safe programming and as a workaround for the missing resize
85 // operation.
swap(SampleBuffer & that)86 void swap(SampleBuffer& that) {
87 std::swap(m_data, that.m_data);
88 std::swap(m_size, that.m_size);
89 }
90
91 // Fills the whole buffer with zeroes
92 void clear();
93
94 // Fills the whole buffer with the same value
95 void fill(CSAMPLE value);
96
97 class ReadableSlice {
98 public:
ReadableSlice()99 ReadableSlice()
100 : m_data(nullptr),
101 m_length(0) {
102 }
ReadableSlice(const CSAMPLE * data,SINT length)103 ReadableSlice(const CSAMPLE* data, SINT length)
104 : m_data(data),
105 m_length(length) {
106 DEBUG_ASSERT(m_length >= 0);
107 DEBUG_ASSERT((m_length == 0) || (m_data != nullptr));
108 }
ReadableSlice(const SampleBuffer & buffer,SINT offset,SINT length)109 ReadableSlice(const SampleBuffer& buffer, SINT offset, SINT length)
110 : m_data(buffer.data(offset)),
111 m_length(length) {
112 DEBUG_ASSERT((buffer.size() - offset) >= length);
113 }
114 const CSAMPLE* data(SINT offset = 0) const {
115 DEBUG_ASSERT((m_data != nullptr) || (offset == 0));
116 DEBUG_ASSERT(0 <= offset);
117 // >=: allow access to one element behind allocated memory
118 DEBUG_ASSERT(m_length >= offset);
119 return m_data + offset;
120 }
121 SINT length(SINT offset = 0) const {
122 DEBUG_ASSERT(0 <= offset);
123 // >=: allow access to one element behind allocated memory
124 DEBUG_ASSERT(m_length >= offset);
125 return m_length - offset;
126 }
empty()127 bool empty() const {
128 return (m_data == nullptr) || (m_length <= 0);
129 }
130 const CSAMPLE& operator[](SINT index) const {
131 return *data(index);
132 }
133 private:
134 const CSAMPLE* m_data;
135 SINT m_length;
136 };
137
138 class WritableSlice {
139 public:
WritableSlice()140 WritableSlice()
141 : m_data(nullptr),
142 m_length(0) {
143 }
WritableSlice(CSAMPLE * data,SINT length)144 WritableSlice(CSAMPLE* data, SINT length)
145 : m_data(data),
146 m_length(length) {
147 DEBUG_ASSERT(m_length >= 0);
148 DEBUG_ASSERT((m_length == 0) || (m_data != nullptr));
149 }
WritableSlice(SampleBuffer & buffer)150 explicit WritableSlice(SampleBuffer& buffer)
151 : m_data(buffer.data()),
152 m_length(buffer.size()) {
153 }
WritableSlice(SampleBuffer & buffer,SINT offset,SINT length)154 WritableSlice(SampleBuffer& buffer, SINT offset, SINT length)
155 : m_data(buffer.data(offset)),
156 m_length(length) {
157 DEBUG_ASSERT((buffer.size() - offset) >= length);
158 }
159 CSAMPLE* data(SINT offset = 0) const {
160 DEBUG_ASSERT((m_data != nullptr) || (offset == 0));
161 DEBUG_ASSERT(0 <= offset);
162 // >=: allow access to one element behind allocated memory
163 DEBUG_ASSERT(m_length >= offset);
164 return m_data + offset;
165 }
166 SINT length(SINT offset = 0) const {
167 DEBUG_ASSERT(0 <= offset);
168 // >=: allow access to one element behind allocated memory
169 DEBUG_ASSERT(m_length >= offset);
170 return m_length - offset;
171 }
empty()172 bool empty() const {
173 return (m_data == nullptr) || (m_length <= 0);
174 }
175 CSAMPLE& operator[](SINT index) const {
176 return *data(index);
177 }
178 private:
179 CSAMPLE* m_data;
180 SINT m_length;
181 };
182
183 private:
184 CSAMPLE* m_data;
185 SINT m_size;
186 };
187
188 } // namespace mixxx
189
190 namespace std {
191
192 // Template specialization of std::swap() for SampleBuffer
193 template<>
swap(::mixxx::SampleBuffer & lhs,::mixxx::SampleBuffer & rhs)194 inline void swap(::mixxx::SampleBuffer& lhs, ::mixxx::SampleBuffer& rhs) {
195 lhs.swap(rhs);
196 }
197
198 } // namespace std
199