Cabbage Logo
Back to Cabbage Site

Csound wrapper/plugin for Unreal Engine

Hey there,
mayby you can help me with finding informations about the ue4 wrapper. my goal for now is to control a self made synth with csound in unreal engine. the new audioengine is running fine, i just have no clue how to setup my own csound synth in UE4 :slight_smile: and sadly i cant find any usefull infos about that.

Any tips welcome :slight_smile:

Do you have an C/C++ knowledge? You will need it to use the Csound host API with UE4. My hands are tied with other projects at the moment, but Iā€™m happy to direct you. I wonder is @virtualHC has anything he can share?

yeah, i know a bit c/c++, im no pro though ^^ i am in touch with VirtualHC. thx :slight_smile:

Great. Hope it works out.

Performing actual necromancy here (sorry if thatā€™s frowned upon), but this is pretty much exactly what Iā€™m trying to figure out at the moment,

was wondering @virtualHC or @rorywalsh if you could point me in the direction of Unrealā€™s interface to that, their docs are melting me
Hope everyoneā€™s keeping well!

Do you have any simple examples from thei docs? I could probably let you know what needs to be done. I havenā€™t looked at their API since they fixed it and made it usable. I think that was bout 3 years ago?

So theyā€™re talking about it in this, particularly the Generating sound source bit, and then here are their API docs re sound, I feel like all the stuff there overcomplicates it for me, where I basically just want to get the spout buffer, accumulate until theyre the same size and then copy that to their (very well hidden) output buffer!

Actually, I was hoping for some sample code? Is there there the equivalent of hello world somewhere I can look at?

Of Unreal in general or of the audio side? Iā€™ve been looking for examples of audio all day but canā€™t find anything helpful or simple on it
(Oh wait theres some stuff here which might be of use) https://forums.unrealengine.com/development-discussion/c-gameplay-programming/18425-procedural-audio

That thread discusses the old API, which was a crock of crap. The new one is much better, but Iā€™m wondering what they did with the bloody examples? Have you seen Aron McLeranā€™s talk on YouTube, he goes through some examples there. Itā€™s the one where he first shows the new system. I think itā€™s from GDC? There are code samples there. In fact, i think they are the only code samples Iā€™ve seen. Are there really no sample plugins available with the API?

Iā€™ll check them out while I have lunch for sure, cheers for the tip! I mean youā€™d think were would be wouldnā€™t there, all the audio tutorials and stuff Iā€™ve seen have either been blueprint based or literally like unityā€™s PlayOneShot method, nothing to do with accessing the output buffer

If you have an example of a PlayOneShot in C++ that would be enough Iā€™d sayā€¦

So this is the default player pawn that gets generated in a project, has a fireShot() function with a sound associated if thats any good

Can you use make FireSound a USoundWave instead of the USoundBase. Then you can access a pointer to its PCM data and write to it accordingly? I donā€™t have UE4 set up here at the minute so Iā€™m canā€™t test, But Iā€™ll have my gaming PC out again tomorrow, I will install it there.

Iā€™ll give this a go in a sec and send on the results, basically before that Iā€™d also wanna trigger a score event on an instrument before doing that in the fire shot method right?

Actually, I think this is a better bet:
https://docs.unrealengine.com/en-US/API/Runtime/Engine/Sound/USoundWaveProcedural/index.html
I have to head out for a bit now, but I can have a go at this in the morning for you if you havenā€™t already figured it out :wink:

1 Like

So Iā€™m using that and have it no longer erroring or crashing (but still no noise), hereā€™s some code I wrote to interface:

void AudioProcessor::processNextBlock()
{
auto csoundData = csoundWrapper.getOutputBuffer();
uint8 buffer[instanceData.ksmps];
for(auto i = 0; i < instanceData.ksmps; i++){
auto sample = *csoundData;
sample *= 128;
sample += 128;
buffer[i] = sample;
csoundData++;
}
procedural->QueueAudio(buffer, instanceData.ksmps);
}

and to schedule that:

void AudioProcessor::processBlockScheduler(std::future exitStatus)
{
auto waitTime = instanceData.ksmps / instanceData.sr;
waitTime *= 1000;
while(exitStatus.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout)
{
UE_LOG(LogTemp, Warning, TEXT(ā€œProcessing bufferā€¦ā€));
std::this_thread::sleep_for(std::chrono::milliseconds(waitTime - 1));
processNextBlock();
}
}

which gets called on an std::thread, and the value of exitStatus gets set in the destructor of the actor that launches that thread, does that look like Iā€™m in the right ballpark or am I overcomplicating this?
Edit, think I should be getting a pointer to SPOUT instead of outputBuffer

Is your getOutputBuffer() method accessing csoundGetOutputBuffer()? It should be accessing the audio buffers using csoundGetSpin() and csoundGetSpout(). But generally Iā€™d say this look like a valid approachā€¦

1 Like

Yeah, just clocked that, with SPOUT, say Iā€™m holding a pointer to that, can I just increment that pointer every time I want a new sample and then dereference it to get the sampleā€™s value?
hereā€™s the CsoundWrapper class:

CsoundWrapper::CsoundWrapper()
{
}

CsoundWrapper::~CsoundWrapper()
{

}

bool CsoundWrapper::loadCsd(std::string csdPath)
{
std::lock_guard< std::mutex > guard(csIns._mutex);
csIns.cs = new Csound();

csIns.cs->SetHostImplementedAudioIO(true, 1024);
if(csIns.cs->CompileCsd(csdPath.c_str()) == 0)
{
    csIns.cs->Start();
    csIns.csPerfThread = new CsoundPerformanceThread(csIns.cs);
    csIns.csPerfThread->Play();
    return true;
}
else{
    return false;
}

}

void CsoundWrapper::passParameter(std::string channel, MYFLT value)
{
std::lock_guard< std::mutex > guard(csIns._mutex);
csIns.cs->SetChannel(channel.c_str(), value);
}

void CsoundWrapper::passParameter(std::string channel, std::string value)
{
std::lock_guard< std::mutex > guard(csIns._mutex);
char *cVal = new char[value.size() + 1];
std::copy(value.begin(), value.end(), cVal);
cVal[value.size()] = ā€˜\0ā€™;
csIns.cs->SetStringChannel(channel.c_str(), cVal);
delete[] cVal;
}

void CsoundWrapper::sendScoreEvent(std::vector p)
{
std::lock_guard< std::mutex > guard(csIns._mutex);
MYFLT pFields[p.size()];
std::copy(p.begin(), p.end(), pFields);
csIns.csPerfThread->ScoreEvent(ā€˜iā€™, 0, p.size(), pFields);
std::vector().swapĀ§;
}

MYFLT* CsoundWrapper::getOutputBuffer()
{
return csIns.cs->GetOutputBuffer();
}

MYFLT* CsoundWrapper::getSpoutBuffer()
{
return csIns.cs->GetSpout();
}

InstanceData CsoundWrapper::getInstanceData(){
std::lock_guard< std::mutex > guard(csIns._mutex);
auto sr = csIns.cs->GetSr();
auto ksmps = csIns.cs->GetKsmps();
auto nchnls = csIns.cs->GetNchnls();
return InstanceData(ksmps, sr, nchnls);
}

No idea whats going on with the formatting there hahaha, csIns is just a struct with a Csound*, a CsoundPerformanceThread* and a mutex
Edit: Swapping to SPOUT seems promising, promising in that I got a lovely new crash, saying my call to QueueAudio wasnā€™t in the game thread (Surely it shouldnā€™t be lol)

Oh hold on. You shouldnā€™t be using your own performance thread for Csound. Thatā€™s a no-no, or at least I canā€™t think any any good reason to do so. Unless you and pain have are on extremely good terms, I would stay clear of that approach. You need to tap into Unrealā€™s audio processing methods. Surely there is a simple synth example out there somewhere? I mean a recent one? Btw, did you find that youtube talk? Imā€™ curious because I couldnā€™t find it earlier when I lookedā€¦