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