1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 
21 #ifndef __DSP_PitchChanger_H__
22 #define __DSP_PitchChanger_H__
23 
24 #include "../../config/common.h"
25 
26 #ifdef HAVE_LIBSOUNDTOUCH
27 
28 /* NOTE:
29  *   - The template is called TPitchChanger which used the libSoundTouch library.  And
30  *     this is not to be confused with the preexisting TSoundStretcher template which
31  *     changes the rate (speed and pitch).
32  */
33 
34 
35 #include <TAutoBuffer.h>
36 
37 #include "../CSound_defs.h"
38 
39 #include <soundtouch/SoundTouch.h>
40 using namespace soundtouch;
41 
42 template<class src_type> class TPitchChanger
43 {
44 public:
45 	/*
46 	 * src is the data to read
47 	 * srcOffset is the first sample that this class will look at
48   	 * length is the length of samples beyond the srcOffset that should be read and is the number of output samples
49 	 * deltaSemitones is the how much + or - to change the pitch
50 	 * frameSize can be passed if the src is actually interlaced data of more than 1 channel of audio
51 	 * frameOffset should be passed when frameSize is given other than one to say which channel in the interlaced data should be processed
52 	 */
53 	TPitchChanger(const src_type &_src,const sample_pos_t _srcOffset,const sample_pos_t _length,const float _deltaSemitones,unsigned srcSampleRate,unsigned _frameSize=1,unsigned _frameOffset=0) :
src(_src)54 		src(_src),
55 		srcOffset(_srcOffset),
56 		length(_length),
57 		deltaSemitones(_deltaSemitones),
58 		frameSize(_frameSize),
59 		frameOffset(_frameOffset),
60 
61 		pos(0),
62 		srcEnd(srcOffset+length),
63 		flushed(false),
64 
65 		outputBuffer(1024),
66 		outputBufferOffset(0),
67 		outputBufferSize(0),
68 
69 		inputBuffer(outputBuffer.getSize())
70 	{
71 		if(frameSize==0)
72 			throw(runtime_error(string(__func__)+" -- frameSize is 0"));
73 		if(frameOffset>=frameSize)
74 			throw(runtime_error(string(__func__)+" -- frameOffset is >= frameSize: "+istring(frameOffset)+">="+istring(frameSize)));
75 
76 		changer.setChannels(1); // ??? need to have a way of allowing more than one channel so that phase across channels is preserved
77 		changer.setSampleRate(srcSampleRate);
78 		changer.setPitchSemiTones(deltaSemitones);
79 	}
80 
~TPitchChanger()81 	virtual ~TPitchChanger()
82 	{
83 	}
84 
getSample()85 	const sample_t getSample()
86 	{
87 		if(outputBufferOffset<outputBufferSize)
88 			// data available to read
89 			return convert_sample<soundtouch::SAMPLETYPE,sample_t>(outputBuffer[outputBufferOffset++]);
90 		else if(changer.numSamples() > 0)
91 		{	// read more data from changer
92 			outputBufferSize=changer.receiveSamples(outputBuffer,outputBuffer.getSize());
93 			outputBufferOffset=0;
94 		}
95 		else /*if(changer.numSamples() <= 0)*/
96 		{	// write more data to changer
97 			if(pos<srcEnd)
98 			{
99 				const size_t chunkSize=min((sample_pos_t)inputBuffer.getSize(),srcEnd-pos);
100 				size_t t;
101 				// unfortunately I'm having to make a copy into inputBuffer before giving it to changer
102 				for(t=0;t<chunkSize;t++)
103 					inputBuffer[t]=convert_sample<sample_t,soundtouch::SAMPLETYPE>(src[((pos++)*frameSize)+frameOffset]);
104 				changer.putSamples(inputBuffer,t);
105 			}
106 			else
107 			{	// no input to give to changer
108 				if(!flushed)
109 				{
110 					changer.flush();
111 					flushed=true;
112 				}
113 				else
114 					return 0;
115 			}
116 		}
117 
118 		// recur
119 		return getSample();
120 	}
121 
122 	// algorithm tuning (libSoundTouch specific.. see SoundTouch.h)
setSetting(uint settingId,uint value)123 	bool setSetting(uint settingId,uint value)
124 	{
125 		return changer.setSetting(settingId,value);
126 	}
127 
128 private:
129 	const src_type src;
130 	const sample_pos_t srcOffset;
131 	const sample_pos_t length;
132 	const float deltaSemitones;
133 	const sample_pos_t frameSize;
134 	const sample_pos_t frameOffset;
135 
136 	sample_pos_t pos;
137 	sample_pos_t srcEnd;
138 	bool flushed;
139 
140 	TAutoBuffer<soundtouch::SAMPLETYPE> outputBuffer;
141 	size_t outputBufferOffset;
142 	size_t outputBufferSize;
143 
144 	TAutoBuffer<soundtouch::SAMPLETYPE> inputBuffer;
145 
146 	SoundTouch changer;
147 };
148 
149 #endif // HAVE_LIBSOUNDTOUCH
150 
151 #endif
152