<commit_before>/*
    csound~ : A MaxMSP external interface for the Csound API.
    
    Created by <NAME> on 2/4/06.
    Copyright 2006-2010 <NAME>. All rights reserved.
    
    LICENSE AGREEMENT
    
    This software is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this software; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "CsoundObject.h"
#include "csound~.h"
#include "memory.h" 
#include "util.h"

using namespace dvx;

CsoundObject::CsoundObject(t_csound *partner) :
	m_csound((CSOUND*) csoundCreate(partner)),
	m_x(partner),
	m_obj((t_object*)partner),
	m_lock((char*)"CsoundObject"),
	m_iChanGroup(m_obj, m_csound, IN_),
	m_oChanGroup(m_obj, m_csound, OUT_),
	m_midiBuffer(m_obj),
	m_scripter(m_obj, m_iChanGroup),
	m_sequencer(m_obj, m_csound, &m_iChanGroup, &m_midiBuffer),
	m_msg_buf(m_obj),
	m_sr(sys_getsr()),
	m_chans(2), m_inChans(2), m_outChans(2),
	m_ksmps(32),          // Set to non-zero to avoid modulo by zero (could happen if
	                      // csound_dsp() is called before compilation of a Csound orchestra).
	m_in_index(0),
	m_out_index(1000000), // Must init m_out_index to some impossibly large value.
	m_scale(FL(1.0)),
	m_oneDivScale(FL(1.0)),
	m_compiled(false),
	m_renderingToFile(false),
	m_performanceFinished(true),
	m_defaultPathID(path_getdefault()),
	m_defaultPath(),
	m_path(),
	m_args(m_obj),
	m_textBufferLock(),
	m_textBuffer(new char[MAX_STRING_LENGTH]),
	m_renderThreadExists(false)
{
	m_textBuffer.get()[0] = '\0'; // Must init to empty string for messageCallback().
//	csoundSetInputValueCallback(m_csound, inputValueCallback);
//	csoundSetOutputValueCallback(m_csound, outputValueCallback);
   	csoundSetInputChannelCallback(m_csound, inputValueCallback);
	csoundSetOutputChannelCallback(m_csound, outputValueCallback);
	csoundSetMessageCallback(m_csound, messageCallback);
}

CsoundObject::~CsoundObject()
{
	csoundDestroy(m_csound);
	m_msg_buf.post();
}				

void CsoundObject::Compile()
{
	int result = CSOUND_ERROR;

	// Stop the render thread if it exists.
	if(m_renderThreadExists) 
	{
		m_performanceFinished = false;
		if(0 != pthread_join(m_renderThread, NULL))
			m_msg_buf.add(message::_ERROR_MSG, "pthread_join() with render thread failed.");
		m_renderThreadExists = false;
	}

	/* Stop() clears Csound channel pointers for all ChannelObjects, and then it
	   calls csoundReset(), which kills off Csound channels. If we don't clear
	   ChannelObject Csound pointers, then when csound_outputClockCallback() calls
	   ProcessDirtyChannels, or user sends a "control" message, access to freed
	   memory will occur.*/
	Stop(); 

	result = Compile(true);

	if(result == COMPILATION_SUCCESS_MISMATCHED_SR && m_x->matchMaxSR && m_x->sr > 0)
	{
		Stop(); // We compiled above and got a new set of Csound channel pointers. Clear them.
		if(m_x->messageOutputEnabled)
			m_msg_buf.addv(message::_NORMAL_MSG, "Recompiling with sr = %d and ksmps = %d.", m_x->sr, m_ksmps);

		ScopedLock k(m_lock);
		m_args.RemoveSRandKR();
		m_args.AddSRandKR(m_x->sr,m_ksmps); // Add "-r" and "-k" flags to override Csound sr and kr.
		result = Compile(false);
	}
	
	if(result != COMPILATION_FAILURE) 
		outlet_bang(m_x->compiled_bang_outlet); // Send success bang outside any locked zones.
}

int CsoundObject::Compile(bool lock)
{
	int threadCreateResult, result = COMPILATION_FAILURE;
//	char *opcodedir = NULL;
//	string default_opcodedir;
	MYFLT *csIn = NULL;

	if(!m_args.ArgListValid())
	{
		m_msg_buf.add(message::_ERROR_MSG, "Can't compile. Please provide a valid csound message first.");
		return COMPILATION_FAILURE;
	}
	
	try
	{
		m_midiBuffer.Clear();
	
        // FIXME - look into setting this again, at least for Windows
//		#ifdef MACOSX
//			if(sizeof(MYFLT) == sizeof(float))
//				default_opcodedir = "/Library/Frameworks/CsoundLib.framework/Resources/Opcodes";
//			else
//				default_opcodedir = "/Library/Frameworks/CsoundLib64.framework/Versions/6.0/Resources/Opcodes64";
//		#elif _WINDOWS
//			if(sizeof(MYFLT) == sizeof(float))
//				default_opcodedir = "C:\\Program Files\\Csound\\plugins";
//			else
//				default_opcodedir = "C:\\Program Files\\Csound\\plugins64";
//		#endif
	
		{	
			// Begin locked section.
			ScopedLock k(m_lock,lock);

			m_compiled = false;

//			if(sizeof(MYFLT) == sizeof(float))
//			{
//				opcodedir = (char*) csoundGetEnv(NULL, "OPCODE6DIR");
//				if(!opcodedir && 0 != csoundSetGlobalEnv("OPCODE6DIR", default_opcodedir.c_str()))
//					m_msg_buf.addv(message::_ERROR_MSG, "csoundSetGlobalEnv(OPCODE6DIR,%s) failed.", default_opcodedir.c_str());
//			}
//			else
//			{
//				opcodedir = (char*) csoundGetEnv(NULL, "OPCODE6DIR64");
//				if(!opcodedir && 0 != csoundSetGlobalEnv("OPCODE6DIR64", default_opcodedir.c_str()))
//					m_msg_buf.addv(message::_ERROR_MSG, "csoundSetGlobalEnv(OPCODE6DIR64,%s) failed.", default_opcodedir.c_str());
//			}
		
			// Let the user know what arguments are being passed to csoundCompile().
			if(m_x->messageOutputEnabled) 
				m_msg_buf.add(message::_NORMAL_MSG, m_args.GetArgumentsAsString());
			
            #ifdef _WINDOWS
                Sleep(10); // Multiple compiles in quick successsion may crash without sleeping for a bit.
            #else
                usleep(10000);
            #endif

            // Cannot render to file if csoundSetHostImplementedAudioIO() is called.
            if(!m_renderingToFile) {
                csoundSetHostImplementedAudioIO(m_csound, 1, 0);
                csoundSetHostImplementedMIDIIO(m_csound, 1);
            }

            // These functions cannot be called until after csoundPreCompile()
            // has executed.  csoundPreCompile() allocates memory for an MGLOBAL
            // struct which contains the pointers to these functions.
            csoundSetExternalMidiInOpenCallback(m_csound, midiInOpenCallback);
            csoundSetExternalMidiInCloseCallback(m_csound, midiInCloseCallback);
            csoundSetExternalMidiReadCallback(m_csound, midiReadCallback);			
            csoundSetExternalMidiOutOpenCallback(m_csound, midiOutOpenCallback);
            csoundSetExternalMidiOutCloseCallback(m_csound, midiOutCloseCallback);
            csoundSetExternalMidiWriteCallback(m_csound, midiWriteCallback);

            // Set the current directory again (in case it was changed).
            SetCurDir();

            if(CSOUND_SUCCESS != csoundCompile(m_csound, m_args.NumArgs(), m_args.GetArray())) {
                m_msg_buf.add(message::_ERROR_MSG, "csoundCompile() failed.");
            } else {
                result = COMPILATION_SUCCESS;
                csoundSetHostData(m_csound, m_x);
                m_chans = csoundGetNchnls(m_csound);
                m_ksmps = csoundGetKsmps(m_csound);
                m_x->evenlyDivisible = (m_x->vectorSize % m_ksmps == 0);
    //            MaxChannelStringLength = csoundGetStrVarMaxLen(m_csound);
                MaxChannelStringLength = 256; //FIXME - not sure this is correct...
                m_compiled = true;
                m_in_index = 0;
                m_out_index = 1000000; // Must init to something larger than the largest possible Max vector size.
                m_performanceFinished = false;
                m_sr = csoundGetSr(m_csound);
                m_sequencer.SetSR(m_sr);
              
                if(m_x->sr != m_sr)
                {
                    result = COMPILATION_SUCCESS_MISMATCHED_SR;
                    if(!m_x->matchMaxSR && m_x->messageOutputEnabled)
                        m_msg_buf.addv(message::_WARNING_MSG, "Max sr (%d) != Csound sr (%d)", m_x->sr, m_sr);
                }

                m_scale = csoundGet0dBFS(m_csound);
                m_oneDivScale = 1.0 / m_scale;
            
                if(m_chans != m_x->numInSignals)
                    m_msg_buf.addv(message::_WARNING_MSG, "# of Csound audio channels (%d) != # of signal inlets (%d).", m_chans, m_x->numInSignals);
            
                if(m_chans != m_x->numOutSignals)
                    m_msg_buf.addv(message::_WARNING_MSG, "# of Csound audio channels (%d) != # of signal outlets (%d).", m_chans, m_x->numOutSignals);

                csoundStart(m_csound);
                // If DSP is not active and bypass == 0, perform one k-cycle of the newly compiled
                // Csound instance to force table loading.
                if(!sys_getdspstate() && !m_x->bypass)
                {<commit_msg>removed call to csoundStart() which was necessary with Csound 5 but extraneous in Csound 6 [fixes #492]<commit_after>/*
    csound~ : A MaxMSP external interface for the Csound API.
    
    Created by <NAME> on 2/4/06.
    Copyright 2006-2010 <NAME>. All rights reserved.
    
    LICENSE AGREEMENT
    
    This software is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    
    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.
    
    You should have received a copy of the GNU Lesser General Public
    License along with this software; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "CsoundObject.h"
#include "csound~.h"
#include "memory.h" 
#include "util.h"

using namespace dvx;

CsoundObject::CsoundObject(t_csound *partner) :
	m_csound((CSOUND*) csoundCreate(partner)),
	m_x(partner),
	m_obj((t_object*)partner),
	m_lock((char*)"CsoundObject"),
	m_iChanGroup(m_obj, m_csound, IN_),
	m_oChanGroup(m_obj, m_csound, OUT_),
	m_midiBuffer(m_obj),
	m_scripter(m_obj, m_iChanGroup),
	m_sequencer(m_obj, m_csound, &m_iChanGroup, &m_midiBuffer),
	m_msg_buf(m_obj),
	m_sr(sys_getsr()),
	m_chans(2), m_inChans(2), m_outChans(2),
	m_ksmps(32),          // Set to non-zero to avoid modulo by zero (could happen if
	                      // csound_dsp() is called before compilation of a Csound orchestra).
	m_in_index(0),
	m_out_index(1000000), // Must init m_out_index to some impossibly large value.
	m_scale(FL(1.0)),
	m_oneDivScale(FL(1.0)),
	m_compiled(false),
	m_renderingToFile(false),
	m_performanceFinished(true),
	m_defaultPathID(path_getdefault()),
	m_defaultPath(),
	m_path(),
	m_args(m_obj),
	m_textBufferLock(),
	m_textBuffer(new char[MAX_STRING_LENGTH]),
	m_renderThreadExists(false)
{
	m_textBuffer.get()[0] = '\0'; // Must init to empty string for messageCallback().
//	csoundSetInputValueCallback(m_csound, inputValueCallback);
//	csoundSetOutputValueCallback(m_csound, outputValueCallback);
   	csoundSetInputChannelCallback(m_csound, inputValueCallback);
	csoundSetOutputChannelCallback(m_csound, outputValueCallback);
	csoundSetMessageCallback(m_csound, messageCallback);
}

CsoundObject::~CsoundObject()
{
	csoundDestroy(m_csound);
	m_msg_buf.post();
}				

void CsoundObject::Compile()
{
	int result = CSOUND_ERROR;

	// Stop the render thread if it exists.
	if(m_renderThreadExists) 
	{
		m_performanceFinished = false;
		if(0 != pthread_join(m_renderThread, NULL))
			m_msg_buf.add(message::_ERROR_MSG, "pthread_join() with render thread failed.");
		m_renderThreadExists = false;
	}

	/* Stop() clears Csound channel pointers for all ChannelObjects, and then it
	   calls csoundReset(), which kills off Csound channels. If we don't clear
	   ChannelObject Csound pointers, then when csound_outputClockCallback() calls
	   ProcessDirtyChannels, or user sends a "control" message, access to freed
	   memory will occur.*/
	Stop(); 

	result = Compile(true);

	if(result == COMPILATION_SUCCESS_MISMATCHED_SR && m_x->matchMaxSR && m_x->sr > 0)
	{
		Stop(); // We compiled above and got a new set of Csound channel pointers. Clear them.
		if(m_x->messageOutputEnabled)
			m_msg_buf.addv(message::_NORMAL_MSG, "Recompiling with sr = %d and ksmps = %d.", m_x->sr, m_ksmps);

		ScopedLock k(m_lock);
		m_args.RemoveSRandKR();
		m_args.AddSRandKR(m_x->sr,m_ksmps); // Add "-r" and "-k" flags to override Csound sr and kr.
		result = Compile(false);
	}
	
	if(result != COMPILATION_FAILURE) 
		outlet_bang(m_x->compiled_bang_outlet); // Send success bang outside any locked zones.
}

int CsoundObject::Compile(bool lock)
{
	int threadCreateResult, result = COMPILATION_FAILURE;
//	char *opcodedir = NULL;
//	string default_opcodedir;
	MYFLT *csIn = NULL;

	if(!m_args.ArgListValid())
	{
		m_msg_buf.add(message::_ERROR_MSG, "Can't compile. Please provide a valid csound message first.");
		return COMPILATION_FAILURE;
	}
	
	try
	{
		m_midiBuffer.Clear();
	
        // FIXME - look into setting this again, at least for Windows
//		#ifdef MACOSX
//			if(sizeof(MYFLT) == sizeof(float))
//				default_opcodedir = "/Library/Frameworks/CsoundLib.framework/Resources/Opcodes";
//			else
//				default_opcodedir = "/Library/Frameworks/CsoundLib64.framework/Versions/6.0/Resources/Opcodes64";
//		#elif _WINDOWS
//			if(sizeof(MYFLT) == sizeof(float))
//				default_opcodedir = "C:\\Program Files\\Csound\\plugins";
//			else
//				default_opcodedir = "C:\\Program Files\\Csound\\plugins64";
//		#endif
	
		{	
			// Begin locked section.
			ScopedLock k(m_lock,lock);

			m_compiled = false;

//			if(sizeof(MYFLT) == sizeof(float))
//			{
//				opcodedir = (char*) csoundGetEnv(NULL, "OPCODE6DIR");
//				if(!opcodedir && 0 != csoundSetGlobalEnv("OPCODE6DIR", default_opcodedir.c_str()))
//					m_msg_buf.addv(message::_ERROR_MSG, "csoundSetGlobalEnv(OPCODE6DIR,%s) failed.", default_opcodedir.c_str());
//			}
//			else
//			{
//				opcodedir = (char*) csoundGetEnv(NULL, "OPCODE6DIR64");
//				if(!opcodedir && 0 != csoundSetGlobalEnv("OPCODE6DIR64", default_opcodedir.c_str()))
//					m_msg_buf.addv(message::_ERROR_MSG, "csoundSetGlobalEnv(OPCODE6DIR64,%s) failed.", default_opcodedir.c_str());
//			}
		
			// Let the user know what arguments are being passed to csoundCompile().
			if(m_x->messageOutputEnabled) 
				m_msg_buf.add(message::_NORMAL_MSG, m_args.GetArgumentsAsString());
			
            #ifdef _WINDOWS
                Sleep(10); // Multiple compiles in quick successsion may crash without sleeping for a bit.
            #else
                usleep(10000);
            #endif

            // Cannot render to file if csoundSetHostImplementedAudioIO() is called.
            if(!m_renderingToFile) {
                csoundSetHostImplementedAudioIO(m_csound, 1, 0);
                csoundSetHostImplementedMIDIIO(m_csound, 1);
            }

            // These functions cannot be called until after csoundPreCompile()
            // has executed.  csoundPreCompile() allocates memory for an MGLOBAL
            // struct which contains the pointers to these functions.
            csoundSetExternalMidiInOpenCallback(m_csound, midiInOpenCallback);
            csoundSetExternalMidiInCloseCallback(m_csound, midiInCloseCallback);
            csoundSetExternalMidiReadCallback(m_csound, midiReadCallback);			
            csoundSetExternalMidiOutOpenCallback(m_csound, midiOutOpenCallback);
            csoundSetExternalMidiOutCloseCallback(m_csound, midiOutCloseCallback);
            csoundSetExternalMidiWriteCallback(m_csound, midiWriteCallback);

            // Set the current directory again (in case it was changed).
            SetCurDir();

            if(CSOUND_SUCCESS != csoundCompile(m_csound, m_args.NumArgs(), m_args.GetArray())) {
                m_msg_buf.add(message::_ERROR_MSG, "csoundCompile() failed.");
            } else {
                result = COMPILATION_SUCCESS;
                csoundSetHostData(m_csound, m_x);
                m_chans = csoundGetNchnls(m_csound);
                m_ksmps = csoundGetKsmps(m_csound);
                m_x->evenlyDivisible = (m_x->vectorSize % m_ksmps == 0);
    //            MaxChannelStringLength = csoundGetStrVarMaxLen(m_csound);
                MaxChannelStringLength = 256; //FIXME - not sure this is correct...
                m_compiled = true;
                m_in_index = 0;
                m_out_index = 1000000; // Must init to something larger than the largest possible Max vector size.
                m_performanceFinished = false;
                m_sr = csoundGetSr(m_csound);
                m_sequencer.SetSR(m_sr);
              
                if(m_x->sr != m_sr)
                {
                    result = COMPILATION_SUCCESS_MISMATCHED_SR;
                    if(!m_x->matchMaxSR && m_x->messageOutputEnabled)
                        m_msg_buf.addv(message::_WARNING_MSG, "Max sr (%d) != Csound sr (%d)", m_x->sr, m_sr);
                }

                m_scale = csoundGet0dBFS(m_csound);
                m_oneDivScale = 1.0 / m_scale;
            
                if(m_chans != m_x->numInSignals)
                    m_msg_buf.addv(message::_WARNING_MSG, "# of Csound audio channels (%d) != # of signal inlets (%d).", m_chans, m_x->numInSignals);
            
                if(m_chans != m_x->numOutSignals)
                    m_msg_buf.addv(message::_WARNING_MSG, "# of Csound audio channels (%d) != # of signal outlets (%d).", m_chans, m_x->numOutSignals);

                // If DSP is not active and bypass == 0, perform one k-cycle of the newly compiled
                // Csound instance to force table loading.
                if(!sys_getdspstate() && !m_x->bypass)
                {
{'fraction_non_alphanumeric': 0.10823918434830532, 'fraction_numerical': 0.010967208597409754, 'mean_word_length': 3.0075088339222615, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3099396', 'n_tokens_mistral': 6356, 'n_tokens_neox': 5887, 'n_words': 1385}