framework2
623 строки · 18.7 Кб
1/*
2* ofSoundBuffer.cpp
3*
4* Created on: 25/07/2012
5* Author: arturo
6*/
7
8#include "ofSoundBuffer.h"
9#include "ofSoundUtils.h"
10#include "ofLog.h"
11#define GLM_FORCE_CTOR_INIT
12#include "glm/trigonometric.hpp"
13#include <limits>
14
15using std::vector;
16using std::string;
17
18
19#if !defined(TARGET_ANDROID) && !defined(TARGET_IPHONE) && !defined(TARGET_LINUX_ARM)
20ofSoundBuffer::InterpolationAlgorithm ofSoundBuffer::defaultAlgorithm = ofSoundBuffer::Hermite;
21#else
22ofSoundBuffer::InterpolationAlgorithm ofSoundBuffer::defaultAlgorithm = ofSoundBuffer::Linear;
23#endif
24
25ofSoundBuffer::ofSoundBuffer()
26:channels(1)
27,samplerate(44100)
28,tickCount(0)
29,soundStreamDeviceID(0){
30
31}
32
33ofSoundBuffer::ofSoundBuffer(short * shortBuffer, std::size_t numFrames, std::size_t numChannels, unsigned int sampleRate)
34:tickCount(0)
35,soundStreamDeviceID(0) {
36copyFrom(shortBuffer, numFrames, numChannels, sampleRate);
37checkSizeAndChannelsConsistency("constructor");
38}
39
40void ofSoundBuffer::copyFrom(const short * shortBuffer, std::size_t numFrames, std::size_t numChannels, unsigned int _sampleRate) {
41this->channels = numChannels;
42setSampleRate(_sampleRate);
43buffer.resize(numFrames * numChannels);
44for(std::size_t i = 0; i < size(); i++){
45buffer[i] = shortBuffer[i]/float(std::numeric_limits<short>::max());
46}
47checkSizeAndChannelsConsistency("copyFrom");
48}
49
50void ofSoundBuffer::copyFrom(const float * floatBuffer, std::size_t numFrames, std::size_t numChannels, unsigned int _sampleRate) {
51this->channels = numChannels;
52setSampleRate(_sampleRate);
53buffer.assign(floatBuffer, floatBuffer + (numFrames * numChannels));
54checkSizeAndChannelsConsistency("copyFrom");
55}
56
57void ofSoundBuffer::copyFrom(const vector<short> & shortBuffer, std::size_t numChannels, unsigned int sampleRate){
58copyFrom(&shortBuffer[0],shortBuffer.size()/numChannels,numChannels,sampleRate);
59}
60
61void ofSoundBuffer::copyFrom(const vector<float> & floatBuffer, std::size_t numChannels, unsigned int sampleRate){
62copyFrom(&floatBuffer[0],floatBuffer.size()/numChannels,numChannels,sampleRate);
63}
64
65void ofSoundBuffer::toShortPCM(vector<short> & dst) const{
66dst.resize(size());
67for(std::size_t i = 0; i < size(); i++){
68dst[i] = buffer[i]*float(std::numeric_limits<short>::max());
69}
70}
71
72void ofSoundBuffer::toShortPCM(short * dst) const{
73for(std::size_t i = 0; i < size(); i++){
74dst[i] = buffer[i]*float(std::numeric_limits<short>::max());
75}
76}
77
78vector<float> & ofSoundBuffer::getBuffer(){
79return buffer;
80}
81
82const vector<float> & ofSoundBuffer::getBuffer() const{
83return buffer;
84}
85
86uint64_t ofSoundBuffer::getDurationMS() const{
87return uint64_t(getNumFrames()) * uint64_t(1000) / uint64_t(samplerate);
88}
89
90uint64_t ofSoundBuffer::getDurationMicros() const{
91return uint64_t(getNumFrames()) * uint64_t(1000000) / uint64_t(samplerate);
92}
93
94uint64_t ofSoundBuffer::getDurationNanos() const{
95return uint64_t(getNumFrames()) * uint64_t(1000000000) / uint64_t(samplerate);
96}
97
98void ofSoundBuffer::setNumChannels(int channels){
99this->channels = channels;
100checkSizeAndChannelsConsistency("setNumChannels");
101}
102
103void ofSoundBuffer::setSampleRate(unsigned int rate){
104samplerate = rate;
105}
106
107void ofSoundBuffer::allocate(size_t numSamples, size_t numChannels){
108resize(numSamples*numChannels);
109channels = numChannels;
110}
111
112void ofSoundBuffer::resize(std::size_t samples, float val){
113buffer.resize(samples, val);
114checkSizeAndChannelsConsistency("resize(samples,val)");
115}
116
117void ofSoundBuffer::clear(){
118buffer.clear();
119}
120
121void ofSoundBuffer::set(float value){
122buffer.assign(buffer.size(), value);
123checkSizeAndChannelsConsistency("set");
124}
125
126bool ofSoundBuffer::checkSizeAndChannelsConsistency(const std::string& _function ) {
127std::string function = _function;
128
129if ( function.size()!= 0 ){
130function += ": ";
131}
132if ( (size()%channels) != 0 ){
133ofLogWarning("ofSoundBuffer") << function << "channel count " << channels << " is not consistent with sample count " << size() << " (non-zero remainder)";
134return false;
135}
136return true;
137}
138
139float & ofSoundBuffer::operator[](std::size_t pos){
140return buffer[pos];
141}
142
143const float & ofSoundBuffer::operator[](std::size_t pos) const{
144return buffer[pos];
145}
146
147float & ofSoundBuffer::getSample(std::size_t frameIndex, std::size_t channel){
148return buffer[(frameIndex * channels) + channel];
149}
150
151const float & ofSoundBuffer::getSample(std::size_t frameIndex, std::size_t channel) const {
152return buffer[(frameIndex * channels) + channel];
153}
154
155void ofSoundBuffer::swap(ofSoundBuffer & buffer){
156std::swap(this->channels, buffer.channels);
157std::swap(this->samplerate, buffer.samplerate);
158std::swap(this->tickCount, buffer.tickCount);
159std::swap(this->soundStreamDeviceID, buffer.soundStreamDeviceID);
160std::swap(this->buffer, buffer.buffer);
161}
162
163ofSoundBuffer ofSoundBuffer::operator*(float value){
164ofSoundBuffer ret = *this;
165ret *= value;
166return ret;
167}
168
169ofSoundBuffer & ofSoundBuffer::operator*=(float value){
170for(std::size_t i=0;i<buffer.size();i++){
171buffer[i] *= value;
172}
173return *this;
174}
175
176void ofSoundBuffer::stereoPan(float left, float right){
177if(channels!=2){
178ofLogWarning("ofSoundBuffer") << "stereoPan called on a buffer with " << channels << " channels, only works with 2 channels";
179return;
180}
181float * bufferPtr = &buffer[0];
182for(std::size_t i=0;i<getNumFrames();i++){
183*bufferPtr++ *= left;
184*bufferPtr++ *= right;
185}
186}
187
188void ofSoundBuffer::copyTo(ofSoundBuffer & soundBuffer, std::size_t nFrames, std::size_t outChannels,std::size_t fromFrame,bool loop) const{
189soundBuffer.resize(nFrames*outChannels);
190soundBuffer.setNumChannels(outChannels);
191soundBuffer.setSampleRate(samplerate);
192soundBuffer.setTickCount(this->getTickCount());
193soundBuffer.setDeviceID(this->getDeviceID());
194copyTo(&soundBuffer[0], nFrames, outChannels, fromFrame, loop);
195}
196
197void ofSoundBuffer::copyTo(ofSoundBuffer & outBuffer, std::size_t fromFrame, bool loop) const{
198outBuffer.setTickCount(this->getTickCount());
199outBuffer.setDeviceID(this->getDeviceID());
200copyTo(&outBuffer[0], outBuffer.getNumFrames(), outBuffer.getNumChannels(), fromFrame, loop);
201}
202
203void ofSoundBuffer::addTo(ofSoundBuffer & soundBuffer, std::size_t nFrames, std::size_t outChannels,std::size_t fromFrame, bool loop) const {
204soundBuffer.resize(nFrames*outChannels);
205soundBuffer.setNumChannels(outChannels);
206soundBuffer.setSampleRate(samplerate);
207addTo(&soundBuffer.getBuffer()[0], nFrames, outChannels, fromFrame, loop);
208}
209
210void ofSoundBuffer::addTo(ofSoundBuffer & outBuffer, std::size_t fromFrame, bool loop) const{
211addTo(&outBuffer[0], outBuffer.getNumFrames(), outBuffer.getNumChannels(), fromFrame, loop);
212}
213
214void ofSoundBuffer::copyTo(float * outBuffer, std::size_t nFrames, std::size_t outChannels, std::size_t fromFrame, bool loop) const{
215// figure out how many frames we can copy before we need to stop or loop
216std::size_t nFramesToCopy = nFrames;
217if ((fromFrame + nFrames) >= this->getNumFrames()){
218nFramesToCopy = this->getNumFrames() - fromFrame;
219}
220
221const float * buffPtr = &buffer[fromFrame * channels];
222// if channels count matches we can just memcpy
223if(channels == outChannels){
224memcpy(outBuffer, buffPtr, nFramesToCopy * channels * sizeof(float));
225outBuffer += nFramesToCopy * outChannels;
226} else if(channels > outChannels){
227// otherwise, if we have more channels than the output is requesting,
228// we copy the first outChannels channels
229for(std::size_t i = 0; i < nFramesToCopy; i++){
230for(std::size_t j = 0; j < outChannels; j++){
231*outBuffer++ = *buffPtr++;
232}
233// and skip the rest
234buffPtr += channels - outChannels;
235}
236} else {
237// we have fewer channels than output is requesting. so replicate as many channels as possible then loop.
238// if we have 2 channels and output wants 5, data is copied from our channels in the following in order:
239// 1 2 1 2 1
240for(std::size_t i = 0; i < nFramesToCopy; i++){
241for(std::size_t j = 0; j < outChannels; j++){
242*outBuffer++ = buffPtr[(j%channels)];
243}
244buffPtr += channels;
245}
246}
247
248// do we have anything left?
249int framesRemaining = nFrames - (int)nFramesToCopy;
250if (framesRemaining > 0){
251if(!loop || size() == 0){
252// fill with 0s
253for(std::size_t i = 0; i < framesRemaining * outChannels; i++){
254outBuffer[i] = 0;
255}
256}else{
257// loop
258copyTo(outBuffer, framesRemaining, outChannels, 0, loop);
259}
260}
261}
262
263void ofSoundBuffer::addTo(float * outBuffer, std::size_t nFrames, std::size_t outChannels, std::size_t fromFrame, bool loop) const{
264// figure out how many frames we can copy before we need to stop or loop
265std::size_t nFramesToCopy = nFrames;
266if ((fromFrame + nFrames) >= this->getNumFrames()){
267nFramesToCopy = this->getNumFrames() - fromFrame;
268}
269
270const float * buffPtr = &buffer[fromFrame * channels];
271// if channels count matches it is easy
272if(channels == outChannels){
273for(std::size_t i = 0; i < (nFramesToCopy * outChannels); i++){
274outBuffer[i] += buffPtr[i];
275}
276outBuffer += nFramesToCopy * outChannels;
277} else if(channels > outChannels){
278// otherwise, if we have more channels than the output is requesting,
279// we copy the first outChannels channels
280for(std::size_t i = 0; i < nFramesToCopy; i++){
281for(std::size_t j = 0; j < outChannels; j++){
282*outBuffer++ += *buffPtr++;
283}
284// and skip the rest
285buffPtr += channels - outChannels;
286}
287} else {
288// we have fewer channels than output is requesting. so replicate as many channels as possible then loop.
289// if we have 2 channels and output wants 5, data is copied from our channels in the following in order:
290// 1 2 1 2 1
291for(std::size_t i = 0; i < nFramesToCopy; i++){
292for(std::size_t j = 0; j < outChannels; j++){
293*outBuffer++ += buffPtr[(j%channels)];
294}
295buffPtr += channels;
296}
297}
298
299// do we have anything left?
300int framesRemaining = nFrames - (int)nFramesToCopy;
301if (framesRemaining > 0 && loop){
302// loop
303addTo(outBuffer, framesRemaining, outChannels, 0, loop);
304}
305}
306
307
308void ofSoundBuffer::append(ofSoundBuffer & other){
309if(other.getNumChannels() != getNumChannels()){
310ofLogError() << "can't append sound buffers with different num channels";
311return;
312}
313buffer.insert(buffer.end(),other.buffer.begin(),other.buffer.end());
314}
315
316static bool prepareBufferForResampling(const ofSoundBuffer &in, ofSoundBuffer &out, std::size_t numFrames) {
317std::size_t totalOutBufferSize = numFrames * in.getNumChannels();
318
319if(totalOutBufferSize < out.getBuffer().max_size()) {
320out.resize(totalOutBufferSize,0);
321} else {
322ofLogError("ofSoundUtils") << "resampling would create a buffer size of " << totalOutBufferSize << " (too large for std::vector)";
323return false;
324}
325
326out.setNumChannels(in.getNumChannels());
327out.setSampleRate(in.getSampleRate());
328return true;
329}
330
331// based on maximilian optimized for performance.
332// might lose 1 or 2 samples when it reaches the end of the buffer
333void ofSoundBuffer::linearResampleTo(ofSoundBuffer &outBuffer, std::size_t fromFrame, std::size_t numFrames, float speed, bool loop) const {
334
335std::size_t inChannels = getNumChannels();
336std::size_t inFrames = getNumFrames();
337bool bufferReady = prepareBufferForResampling(*this, outBuffer, numFrames);
338
339if(!bufferReady) {
340outBuffer = *this;
341return;
342}
343
344std::size_t start = fromFrame;
345std::size_t end = start*inChannels + double(numFrames*inChannels)*speed;
346double position = start;
347std::size_t intPosition = position;
348float increment = speed;
349std::size_t copySize = inChannels*sizeof(float);
350std::size_t to;
351
352if(end<size()-2*inChannels){
353to = numFrames;
354}else if(fromFrame+2>inFrames){
355to = 0;
356}else{
357to = ceil(float(inFrames-2-fromFrame)/speed);
358}
359
360float remainder = position - intPosition;
361float * resBufferPtr = &outBuffer[0];
362float a, b;
363
364for(std::size_t i=0;i<to;i++){
365intPosition *= inChannels;
366for(std::size_t j=0;j<inChannels;j++){
367a = buffer[intPosition+j];
368b = buffer[intPosition+inChannels+j];
369*resBufferPtr++ = ofLerp(a,b,remainder);
370}
371position += increment;
372intPosition = position;
373remainder = position - intPosition;
374}
375if(end>=size()-2*inChannels){
376to = numFrames-to;
377if(loop){
378intPosition %= inFrames;
379for(std::size_t i=0;i<to;i++){
380intPosition *= inChannels;
381for(std::size_t j=0;j<inChannels;j++){
382a = buffer[intPosition+j];
383b = buffer[intPosition+inChannels+j];
384*resBufferPtr++ = ofLerp(a,b,remainder);
385}
386position += increment;
387intPosition = position;
388}
389}else{
390memset(resBufferPtr,0,to*copySize);
391}
392}
393}
394
395// based on maximilian optimized for performance.
396// might lose 1 to 3 samples when it reaches the end of the buffer
397void ofSoundBuffer::hermiteResampleTo(ofSoundBuffer &outBuffer, std::size_t fromFrame, std::size_t numFrames, float speed, bool loop) const {
398
399std::size_t inChannels = getNumChannels();
400std::size_t inFrames = getNumFrames();
401bool bufferReady = prepareBufferForResampling(*this, outBuffer, numFrames);
402
403if(!bufferReady) {
404outBuffer = *this;
405return;
406}
407
408std::size_t start = fromFrame;
409std::size_t end = start*inChannels + double(numFrames*inChannels)*speed;
410double position = start;
411std::size_t intPosition = position;
412float remainder = position - intPosition;
413float increment = speed;
414std::size_t copySize = inChannels*sizeof(float);
415std::size_t to;
416
417if(end<size()-3*inChannels){
418to = numFrames;
419}else if(fromFrame+3>inFrames){
420to = 0;
421}else{
422to = double(inFrames-3-fromFrame)/speed;
423}
424
425float * resBufferPtr = &outBuffer[0];
426float a,b,c,d;
427std::size_t from = 0;
428
429while(intPosition==0){
430intPosition *= inChannels;
431for(std::size_t j=0;j<inChannels;++j){
432a=loop?buffer[j]:0;
433b=buffer[intPosition+j];
434c=buffer[intPosition+j+inChannels];
435d=buffer[intPosition+j+inChannels*2];
436*resBufferPtr++ = ofInterpolateHermite(a, b, c, d, remainder);
437}
438position += increment;
439intPosition = position;
440remainder = position - intPosition;
441from++;
442}
443
444for(std::size_t i=from;i<to;++i){
445intPosition *= inChannels;
446for(std::size_t j=0;j<inChannels;++j){
447a=buffer[intPosition+j-inChannels];
448b=buffer[intPosition+j];
449c=buffer[intPosition+j+inChannels];
450d=buffer[intPosition+j+inChannels*2];
451*resBufferPtr++ = ofInterpolateHermite(a, b, c, d, remainder);
452}
453position += increment;
454intPosition = position;
455remainder = position - intPosition;
456}
457
458if(end>=size()-3*inChannels){
459to = numFrames-to;
460if(loop){
461intPosition %= size();
462for(std::size_t i=0;i<to;++i){
463for(std::size_t j=0;j<inChannels;++j){
464a=buffer[intPosition+j-inChannels];
465b=buffer[intPosition+j];
466c=buffer[intPosition+j+inChannels];
467d=buffer[intPosition+j+inChannels*2];
468*resBufferPtr++ = ofInterpolateHermite(a, b, c, d, remainder);
469}
470position += increment;
471intPosition = position;
472remainder = position - intPosition;
473intPosition *= inChannels;
474}
475}else{
476memset(resBufferPtr,0,to*copySize);
477}
478}
479}
480
481void ofSoundBuffer::resampleTo(ofSoundBuffer & buffer, std::size_t fromFrame, std::size_t numFrames, float speed, bool loop, InterpolationAlgorithm algorithm) const {
482switch(algorithm){
483case Linear:
484linearResampleTo(buffer, fromFrame, numFrames, speed, loop);
485break;
486case Hermite:
487hermiteResampleTo(buffer, fromFrame, numFrames, speed, loop);
488break;
489}
490}
491
492void ofSoundBuffer::resample(float speed, InterpolationAlgorithm algorithm){
493ofSoundBuffer resampled;
494resampleTo(resampled, 0, ceilf(getNumFrames() / speed), speed, false, algorithm);
495*this = resampled;
496}
497
498void ofSoundBuffer::getChannel(ofSoundBuffer & targetBuffer, std::size_t sourceChannel) const {
499if(channels == 0) {
500ofLogWarning("ofSoundBuffer") << "getChannel requested on empty buffer";
501return;
502}
503if (sourceChannel >= channels){
504ofLogWarning("ofSoundBuffer") << "getChannel requested channel " << sourceChannel << " but we only have " << channels << " channels. clamping channel to " << channels-1;
505sourceChannel = channels-1;
506}
507targetBuffer.setNumChannels(1);
508targetBuffer.setSampleRate(samplerate);
509if(channels == 1){
510copyTo(targetBuffer, getNumFrames(), 1, 0);
511}else{
512// fetch samples from only one channel
513targetBuffer.resize(getNumFrames());
514const float * bufferPtr = &this->buffer[sourceChannel];
515for(std::size_t i = 0; i < targetBuffer.getNumFrames(); i++){
516targetBuffer[i] = *bufferPtr;
517bufferPtr += channels;
518}
519}
520}
521
522void ofSoundBuffer::setChannel(const ofSoundBuffer & inBuffer, std::size_t targetChannel){
523// resize ourself to match inBuffer
524resize(inBuffer.getNumFrames() * channels);
525// copy from inBuffer to targetChannel
526float * bufferPtr = &this->buffer[targetChannel];
527const float * inBufferPtr = &(inBuffer[0]);
528for(std::size_t i = 0; i < getNumFrames(); i++){
529*bufferPtr = *inBufferPtr;
530bufferPtr += channels;
531// inBuffer.getNumChannels() is probably 1 but let's be safe
532inBufferPtr += inBuffer.getNumChannels();
533}
534}
535
536float ofSoundBuffer::getRMSAmplitude() const {
537double acc = 0;
538for(size_t i = 0; i < buffer.size(); i++){
539acc += buffer[i] * buffer[i];
540}
541return sqrt(acc / (double)buffer.size());
542}
543
544float ofSoundBuffer::getRMSAmplitudeChannel(std::size_t channel) const {
545if(channel > channels - 1) {
546return 0;
547}
548
549double acc = 0;
550for(size_t i = 0; i < getNumFrames(); i++) {
551float sample = getSample(i, channel);
552acc += sample * sample;
553}
554return sqrt(acc / (double)getNumFrames());
555}
556
557void ofSoundBuffer::normalize(float level){
558float maxAmplitude = 0;
559for(std::size_t i = 0; i < size(); i++) {
560maxAmplitude = std::max(maxAmplitude, std::abs(buffer[i]));
561}
562float normalizationFactor = level/maxAmplitude;
563for(std::size_t i = 0; i < size(); i++) {
564buffer[i] *= normalizationFactor;
565}
566}
567
568bool ofSoundBuffer::trimSilence(float threshold, bool trimStart, bool trimEnd) {
569if(buffer.empty()) {
570ofLogVerbose("ofSoundBuffer") << "attempted to trim empty buffer";
571return true;
572}
573std::size_t firstNonSilence = 0;
574std::size_t lastNonSilence = buffer.size() - 1;
575if(trimStart) {
576for(std::size_t i = 0; i < buffer.size(); ++i) {
577if(std::abs(buffer[i]) > threshold) {
578firstNonSilence = i;
579break;
580}
581}
582}
583if(trimEnd) {
584for(std::size_t i = lastNonSilence; i > firstNonSilence; --i) {
585if(std::abs(buffer[i]) > threshold) {
586lastNonSilence = i;
587break;
588}
589}
590}
591firstNonSilence -= firstNonSilence % getNumChannels();
592lastNonSilence -= lastNonSilence % getNumChannels();
593if(trimEnd) {
594buffer.erase(buffer.begin() + lastNonSilence, buffer.end());
595}
596if(trimStart) {
597buffer.erase(buffer.begin(), buffer.begin() + firstNonSilence);
598}
599return checkSizeAndChannelsConsistency("trimSilence");
600}
601
602void ofSoundBuffer::fillWithNoise(float amplitude){
603for (std::size_t i=0; i<size(); i++ ) {
604buffer[i] = ofRandom(-amplitude, amplitude);
605}
606}
607
608float ofSoundBuffer::fillWithTone( float pitchHz, float phase ){
609float step = glm::two_pi<float>()*(pitchHz/samplerate);
610for (std::size_t i=0; i<size()/channels; i++ ) {
611std::size_t base = i*channels;
612for (std::size_t j=0; j<channels; j++)
613buffer[base+j] = sinf(phase);
614phase += step;
615}
616return phase;
617}
618
619namespace std{
620void swap(ofSoundBuffer & src, ofSoundBuffer & dst){
621src.swap(dst);
622}
623}
624
625