aboutsummaryrefslogtreecommitdiffstats
path: root/maximilian.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'maximilian.cpp')
-rw-r--r--maximilian.cpp522
1 files changed, 516 insertions, 6 deletions
diff --git a/maximilian.cpp b/maximilian.cpp
index 8884e52..b0c0b5e 100644
--- a/maximilian.cpp
+++ b/maximilian.cpp
@@ -364,6 +364,8 @@ double maxiOsc::triangle(double frequency) {
}
+// don't use this nonsense. Use ramps instead.
+// ..er... I mean "This method is deprecated"
double maxiEnvelope::line(int numberofsegments,double segments[1000]) {
//This is a basic multi-segment ramp generator that you can use for more or less anything.
//However, it's not that intuitive.
@@ -372,14 +374,14 @@ double maxiEnvelope::line(int numberofsegments,double segments[1000]) {
nextval=segments[valindex+2];
currentval=segments[valindex];
if (currentval-amplitude > 0.0000001 && valindex < numberofsegments) {
- amplitude += ((currentval-startval)/(maxiSettings::sampleRate/period));
+ amplitude += ((currentval-startVal)/(maxiSettings::sampleRate/period));
} else if (currentval-amplitude < -0.0000001 && valindex < numberofsegments) {
- amplitude -= (((currentval-startval)*(-1))/(maxiSettings::sampleRate/period));
+ amplitude -= (((currentval-startVal)*(-1))/(maxiSettings::sampleRate/period));
} else if (valindex >numberofsegments-1) {
valindex=numberofsegments-2;
} else {
valindex=valindex+2;
- startval=currentval;
+ startVal=currentval;
}
output=amplitude;
@@ -391,7 +393,7 @@ double maxiEnvelope::line(int numberofsegments,double segments[1000]) {
return(output);
}
-//and this
+//and this is also deprecated
void maxiEnvelope::trigger(int index, double amp) {
isPlaying=1;//ok the envelope is being used now.
valindex=index;
@@ -399,6 +401,195 @@ void maxiEnvelope::trigger(int index, double amp) {
}
+void maxiEnvelope::trigger(bool noteOn) {
+
+ if (noteOn) trig=1;
+ if (noteOn==false) trig=0;
+
+}
+
+double maxiEnvelope::ramp(double startVal, double endVal, double duration){
+
+ if (trig!=0) {
+ phase=startVal;
+ isPlaying=true;
+ trig=0;
+ }
+
+ if (isPlaying) {
+
+ if (startVal<endVal) {
+ phase += ((endVal-startVal)/(maxiSettings::sampleRate/(1./duration)));
+ if ( phase >= endVal ) phase = endVal;
+ }
+
+ if (startVal > endVal) {
+ phase += ((endVal-startVal)/(maxiSettings::sampleRate/(1./duration)));
+ if ( phase <= endVal ) phase = endVal;
+ }
+ return(phase);
+ } else {
+
+ return(0);
+
+ }
+
+
+}
+
+
+double maxiEnvelope::ramps(std::vector<double> rampsArray){
+
+ if (trig!=0) {
+ valindex=0;
+ endVal=rampsArray[valindex+1];
+ isPlaying=true;
+ trig=0;
+
+ }
+
+ if (isPlaying) {
+
+ if (valindex>0 && rampsArray[valindex-1]==rampsArray[valindex+1]) {
+ period += (1/(maxiSettings::sampleRate/(1./rampsArray[valindex])));
+ if (period>=1) {
+ phase=endVal;
+ startVal=phase;
+ if (valindex+2<rampsArray.size()){
+ valindex+=2;
+ endVal=rampsArray[valindex+1];
+ period=0;
+ }
+ } output=phase;
+ }
+
+ if (valindex==0 && output==endVal) {
+ period += (1/(maxiSettings::sampleRate/(1./rampsArray[valindex])));
+ if (period>=1) {
+ phase=endVal;
+ startVal=phase;
+ if (valindex+2<rampsArray.size()){
+ valindex+=2;
+ endVal=rampsArray[valindex+1];
+ period=0;
+ }
+ } output=phase;
+ }
+
+ if (phase<endVal) {
+ phase += ((endVal-startVal)/(maxiSettings::sampleRate/(1./rampsArray[valindex])));
+ if ( phase >= endVal ) {
+ phase=endVal;
+ startVal=phase;
+ if (valindex+2<rampsArray.size()){
+ valindex+=2;
+ endVal=rampsArray[valindex+1];
+
+ }
+ }
+ output=phase;
+ }
+
+ if (phase > endVal) {
+ phase += ((endVal-startVal)/(maxiSettings::sampleRate/(1./rampsArray[valindex])));
+ if ( phase <= endVal ) {
+ phase=endVal;
+ startVal=phase;
+ if (valindex+2<rampsArray.size()){
+ valindex+=2;
+ endVal=rampsArray[valindex+1];
+
+ }
+ } output=phase;
+ }
+
+ return(output);
+
+ } else {
+
+ return(0);
+ }
+}
+
+double maxiEnvelope::ar(double attack, double release) {
+
+ if (trig!=0) {
+ //phase=0;
+ releaseMode=false;
+ trig=0;
+ }
+
+ if (phase<1 && releaseMode==false) {
+ phase += ((1)/(maxiSettings::sampleRate/(1./attack)));
+ if ( phase >= 1 ) {
+ phase=1;
+ releaseMode=true;
+ };
+ }
+
+ if (releaseMode==true) {
+ phase += ((-1)/(maxiSettings::sampleRate/(1./release)));
+ if ( phase <= 0 ) phase = 0;
+ }
+
+ return phase;
+}
+
+
+double maxiEnvelope::adsr(double attack, double decay, double sustain, double release) {
+
+ if (trig!=0 && !attackMode) {
+// phase=0.;
+ releaseMode=false;
+ decayMode=false;
+ sustainMode=false;
+ attackMode=true;
+ trig=0;
+ }
+
+ if (attackMode) {
+ phase += ((1)/(maxiSettings::sampleRate/(1./attack)));
+
+ if ( phase >= 1 ) {
+ phase=1;
+ attackMode=false;
+ decayMode=true;
+ };
+ }
+
+ if (decayMode) {
+ phase += ((-1)/(maxiSettings::sampleRate/(1./decay)));
+ if ( phase <= sustain ) {
+ phase=sustain;
+ decayMode=false;
+ sustainMode=true;
+ };
+ }
+
+ if (sustainMode) {
+
+ if (noteOn) {
+ phase=sustain;
+ }
+
+ if (!noteOn) {
+ sustainMode=false;
+ releaseMode=true;
+ }
+ }
+
+ if (releaseMode) {
+ phase += ((-sustain)/(maxiSettings::sampleRate/(1./release)));
+ if ( phase <= 0 ) {
+ phase = 0;
+ releaseMode=false;
+ }
+ }
+
+ return phase;
+}
+
+
//Delay with feedback
maxiDelayline::maxiDelayline() {
memset( memory, 0, 88200*sizeof (double) );
@@ -1084,8 +1275,8 @@ double maxiSample::bufferPlay4(unsigned char &bufferin,double frequency, double
}
-void maxiSample::getLength() {
- length=myDataSize*0.5;
+long maxiSample::getLength() {
+ return (length=myDataSize*0.5);
}
void maxiSample::setLength(unsigned long numSamples) {
@@ -2005,6 +2196,325 @@ void maxiSampler::trigger() {
}
+///*************************************************************
+///
+/// Init most variables
+///
+///*************************************************************
+maxiRecorder::maxiRecorder() :
+bufferSize(maxiSettings::sampleRate * 2),
+bufferQueueSize(3),
+bufferIndex(0),
+recordedAmountFrames(0),
+threadRunning(false)
+{
+}
+///*************************************************************
+///
+/// Free resources in RAII manner and save to wav when this
+/// object lifetime is up!
+///
+///*************************************************************
+maxiRecorder::~maxiRecorder()
+{
+ if (isRecording()) saveToWav();
+ freeResources();
+}
+
+///*************************************************************
+///
+/// Free resources only if they exist; may be called multiple
+/// times in it's lifetime becasuse of destructor.
+///
+///*************************************************************
+void maxiRecorder::freeResources()
+{
+ if (savedBuffers.size() > 0)
+ {
+ for (int i = 0; i < savedBuffers.size(); ++i)
+ {
+ delete[] savedBuffers.front();
+ savedBuffers.pop();
+ }
+ }
+}
+
+///*************************************************************
+///
+/// Simply return if we are recording or not
+///
+///*************************************************************
+bool maxiRecorder::isRecording() const
+{
+ return doRecord;
+}
+
+///*************************************************************
+///
+/// Set the filename (and path)
+///
+///*************************************************************
+void maxiRecorder::setup(std::string _filename)
+{
+ filename = _filename;
+}
+
+///*************************************************************
+///
+/// You don't need to worry calling about this, this runs in a
+/// detached thread after calling startRecording() and means
+/// we can allocate new memory without interupting the real
+/// time audio callback. Rather than have multiple ifdefs to
+/// split up the same function to use different OS's threading
+/// services I just redefined the thing because it looked
+/// cleaner.
+///
+/// A pointer to the instanciated object from this class is
+/// passed as '_context'. We do this because we are calling this
+/// function from a static helper func and need to access object
+/// specific variables.
+///
+///*************************************************************
+#if defined(OS_IS_UNIX)
+void* maxiRecorder::update(void* _context)
+{
+ pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+ maxiRecorder* _this = static_cast<maxiRecorder*>(_context);
+ _this->threadRunning = true;
+ while (_this->doRecord)
+ {
+ usleep((long)(10000. / bufferSize / maxiSettings::sampleRate));
+ while (_this->bufferQueueSize > _this->bufferQueue.size())
+ {
+ _this->enqueueBuffer();
+ }
+ }
+ std::cout << "\nFinishing Recording..." << std::endl;
+ return 0;
+}
+#elif defined(OS_IS_WIN)
+void* maxiRecorder::update(void* _context)
+{
+ maxiRecorder* _this = static_cast<maxiRecorder*>(_context);
+ _this->threadRunning = true;
+ long time = (long)(1000. / bufferSize / maxiSettings::sampleRate);
+ while (_this->doRecord)
+ {
+ Sleep(time);
+ while (_this->bufferQueueSize > _this->bufferQueue.size())
+ {
+ _this->enqueueBuffer();
+ }
+ }
+ std::cout << "\nFinishing Recording..." << std::endl;
+ return 0;
+}
+#endif
+
+///*************************************************************
+///
+/// This whacks the update method into a detached thread which
+/// will ensure there are enough buffers available in the queue.
+///
+///*************************************************************
+void maxiRecorder::startRecording()
+{
+ doRecord = true;
+ for (int i = 0; i < bufferQueueSize; ++i)
+ {
+ enqueueBuffer();
+ }
+
+#if defined(OS_IS_UNIX)
+ if (pthread_create(&daemon, NULL, &maxiRecorder::update_pthread_helper, this) != 0)
+ std::cout << "pthread created incorrectly" << std::endl;
+ if (pthread_detach(daemon) != 0)
+ std::cout << "pthread detached incorrectly" << std::endl;
+#elif defined(OS_IS_WIN)
+ unsigned threadID;
+ daemonHandle = (HANDLE) _beginthreadex(NULL, 0, &maxiRecorder::update_pthread_helper, (void *) this, 0, &threadID);
+#endif
+
+
+}
+
+///*************************************************************
+///
+/// stops the recording thread by allowing the stack to unwind
+/// in update() and stops the Windows thread if necessary
+///
+///*************************************************************
+void maxiRecorder::stopRecording()
+{
+#if defined(OS_IS_WIN)
+ CloseHandle(daemonHandle);
+#endif
+ doRecord = false;
+}
+
+///*************************************************************
+///
+/// Shamelessly hacked together largely using this link
+/// http://stackoverflow.com/questions/22226872/two-problems-
+/// when-writing-to-wav-c
+///
+/// I haven't bothered to change the syntax much here and the
+/// templated write function was too convenient during my
+/// struggle to get my head around wav headers so I've kept it
+/// very similar to the attached code.
+///
+/// A clear optimisation would be to make getProcessedData()
+/// return a vector of shorts to prevent another loop
+///
+///*************************************************************
+template <typename T>
+void maxiRecorder::write(std::ofstream& _stream, const T& _t) {
+ _stream.write((const char*)&_t, sizeof(T));
+}
+
+void maxiRecorder::saveToWav()
+{
+ if (isRecording()) stopRecording();
+ if (threadRunning)
+ {
+#if defined(OS_IS_UNIX)
+ pthread_cancel(daemon);
+#endif
+ threadRunning = false;
+ }
+ std::vector<double> pcmData = getProcessedData();
+ std::vector<short> pcmDataInt;
+
+ pcmDataInt.resize(pcmData.size());
+
+ for (int i = 0; i < pcmData.size(); ++i)
+ pcmDataInt[i] = (short) (pcmData[i] * 3276.7);
+
+ int sampleRate = maxiSettings::sampleRate;
+ short channels = maxiSettings::channels;
+ int buffSize = pcmDataInt.size() * 2;
+
+ std::ofstream stream(filename.c_str(), std::ios::binary);
+
+ if (stream.is_open())
+ {
+ /* Header */
+ stream.write("RIFF", 4);
+ write<int>(stream, 36 + buffSize);
+ stream.write("WAVE", 4);
+
+ /* Format Chunk */
+ stream.write("fmt ", 4);
+ write<int>(stream, 16);
+ write<short>(stream, 1);
+ write<short>(stream, channels);
+ write<int>(stream, sampleRate);
+ write<int>(stream, sampleRate * channels * sizeof(short));
+ write<short>(stream, channels * sizeof(short));
+ write<short>(stream, 8 * sizeof(short));
+
+ /* Data Chunk */
+ stream.write("data", 4);
+ stream.write((const char*) &buffSize, 4);
+ stream.write((const char*) pcmDataInt.data(), buffSize);
+
+ stream.close();
+
+ std::cout << "Wrote " << buffSize
+ << " bytes to " << filename.c_str()
+ << std::endl;
+ }
+ else
+ {
+ std::cerr << "Failed to open file: " << strerror(errno) << std::endl;
+ }
+}
+
+///*************************************************************
+///
+/// This takes the queue of user generated arrays and whacks
+/// it into a vector of doubles which is easier to transverse
+/// and work with.
+///
+///*************************************************************
+std::vector<double> maxiRecorder::getProcessedData()
+{
+ std::vector<double> userData;
+ int dataSize = savedBuffers.size() * bufferSize;
+ userData.resize(dataSize);
+
+ int savedIndex = 0;
+ for (int i = 0; i < dataSize; ++i)
+ {
+ if (i > 0 && i % bufferSize == 0)
+ {
+ savedIndex = 0;
+ savedBuffers.pop();
+ }
+
+ userData[i] = savedBuffers.front()[savedIndex];
+ ++savedIndex;
+ }
+
+ double lastFrame = userData[userData.size() - 1];
+ while (lastFrame == 0)
+ {
+ userData.pop_back();
+ lastFrame = userData[userData.size() - 1];
+ }
+
+ return userData;
+}
+
+///*************************************************************
+///
+/// Pass the buffer of audio to this method and it will write
+/// it to the right place. This method is real-time safe and
+/// is overriden for ofx's / port's floats array or maximilian's
+/// / rtaudio's double
+///
+///*************************************************************
+void maxiRecorder::passData(double* _in, int _inBufferSize)
+{
+ for (int i = 0; i < _inBufferSize; ++i)
+ {
+ if (bufferIndex >= bufferSize)
+ {
+ bufferQueue.pop();
+ bufferIndex = 0;
+ }
+ bufferQueue.front()[bufferIndex] = _in[i];
+ ++bufferIndex;
+ ++recordedAmountFrames;
+ }
+}
+void maxiRecorder::passData(float* _in, int _inBufferSize)
+{
+ for (int i = 0; i < _inBufferSize; ++i)
+ {
+ if (bufferIndex >= bufferSize)
+ {
+ bufferQueue.pop();
+ bufferIndex = 0;
+ }
+ bufferQueue.front()[bufferIndex] = (double) _in[i];
+ ++bufferIndex;
+ ++recordedAmountFrames;
+ }
+}
+
+///*************************************************************
+///
+/// The method to instanciate new memory and ensure the correct
+/// structures have the correct addresses.
+///
+///*************************************************************
+void maxiRecorder::enqueueBuffer()
+{
+ double* b = new double[bufferSize];
+ bufferQueue.push(b);
+ savedBuffers.push(b);
+}