Search
  • Tim Cave

Next Steps!

Updated: Feb 19, 2021

I've added some more features including saving the state of the VSTi as an FXB file; encoding the tracks to WAV files and mixing them together.


Saving the VSTi State

Arcade has "kits" and I've used the BASS_VST_GetChunk and BASS_VST_SetChunk to dump everything as a binary file and then read back in. So this is the simplest type of implementation.


I added a menu to the second form that shows the Arcade VSTi plugin in all it's glory. once the file save name is picked I've called this function to write to file:



//pass path and filename
void getVSTStateAndWriteToFile(System::String^ fp, System::String^ initD, System::String^ fn) {

	//Gets the VST plug-in state as a plain byte array (memory chunk storage). 	
	void* chunk;
	DWORD len;

	chunk = BASS_VST_GetChunk(vststream, 0, &len);

	String^ managedStr = gcnew String(fp);
	pin_ptr<const wchar_t> wch = PtrToStringChars(managedStr);
	std::wstring nativeWstr(wch);
	std::ofstream f(wch, std::ios::binary | std::ios::trunc);	
	f.write(static_cast<char*>(chunk), len);	
	
}

And then to read the chosen file use this:



void readFileAndSetVSTState(System::String^ fp)
{
	//Sets the VST plug-in state with a plain byte array (memory chunk storage). 
	String^ managedStr = gcnew String(fp);
	pin_ptr<const wchar_t> wch = PtrToStringChars(managedStr);
	std::wstring nativeWstr(wch);	
	std::ifstream f(wch, std::ios::binary);
	// get length of file
	f.seekg(0, std::ios::end);
	auto len = f.tellg();
	f.seekg(0, std::ios::beg);

	std::vector<char> chunk(len);
	f.read(chunk.data(), len);
	BASS_VST_SetChunk(vststream, 0, chunk.data(), len);

}


Encoding

I've decided to write out each individual track to a WAV file and then mix tracks together.


If you want to do this them you'll need the bassenc package and set up the lib, header file and dll like all the other bass add-ons.


Once set up you can save a track to WAV by adding the encoding to bassasio play so you can hear the track playing and record it to disk at the same time:



void playVSTi(int trackNumb) {

	//pick the ASIO driver you want to use
	if (!BASS_ASIO_Init(1, BASS_ASIO_THREAD)) // initialize ASIO device
		MessageBox::Show("Can't initialize ASIO device");
	outstream = BASS_StreamCreate(44100, 2, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE, VstStreamProc, NULL);
	String^ trackPath = "C:\\Users\\timca\\Documents\\ArcadePlayer\\AudioOut\\track" + trackNumb.ToString() + ".wav";
	char* tPath = (char*)Marshal::StringToHGlobalAnsi(trackPath).ToPointer();
	//save to wav file
	BASS_Encode_Start(outstream, tPath  , BASS_ENCODE_PCM, 0, 0); // set a WAV writer on it

	if (!BASS_ASIO_ChannelEnableBASS(FALSE, 0, outstream, TRUE))
		MessageBox::Show("Can't enable ASIO channel(s)");
	BASS_ChannelGetInfo(vststream, &i);
	if (i.chans == 1) BASS_ASIO_ChannelEnableMirror(1, FALSE, 0); // mirror mono channel to form stereo output
	BASS_ASIO_SetRate(i.freq); // try to set the device rate to avoid resampling

	outstream = BASS_StreamCreate(44100, 2, BASS_SAMPLE_FLOAT, VstStreamProc, NULL); // create output	
	outpos = eventpos = 0; // playback positions

	QWORD len = BASS_ChannelGetLength(midistream, BASS_POS_BYTE); // the length in bytes
	time1 = BASS_ChannelBytes2Seconds(midistream, len); // the length in seconds
	BASS_StreamFree(midistream);

	// start the device using default buffer/latency
	BASS_ASIO_Start(0, 0);
		
	Application::UseWaitCursor = true;
	System::Windows::Forms::Cursor::Current = Cursors::WaitCursor;
	Sleep(time1 * 1000);
	Application::UseWaitCursor = false;
	System::Windows::Forms::Cursor::Current = Cursors::Default;

	BASS_Encode_Stop(outstream); // stop the encoder	
	BASS_ASIO_Stop();
}

Finally if you want to add tracks into one you need to set up the mixer using bassmix (another add-on to load up!)


Simple set up a mixer and add the tracks to it, then encode to WAV file:



void playWavs() {	

	BASS_Init(-1, 44100, 0, 0, NULL);
	HSTREAM mixer = BASS_Mixer_StreamCreate(44100, 2, BASS_SAMPLE_FLOAT);
	BASS_ChannelPlay(mixer, 0);
	// create decoders for each of the files	
	DWORD decoder1 = BASS_StreamCreateFile(FALSE, "C:\\Users\\timca\\Documents\\ArcadePlayer\\AudioOut\\track1.wav", 0, 0, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE);
	DWORD decoder2 = BASS_StreamCreateFile(FALSE, "C:\\Users\\timca\\Documents\\ArcadePlayer\\AudioOut\\track2.wav", 0, 0, BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE);

	bool ok2 = BASS_Mixer_StreamAddChannel(mixer, decoder1, BASS_SAMPLE_FLOAT);
	bool ok12 = BASS_Mixer_StreamAddChannel(mixer, decoder2, BASS_SAMPLE_FLOAT);

	BASS_Encode_Start(mixer, "C:\\Users\\timca\\Documents\\ArcadePlayer\\AudioOut\\OutputMix.wav", BASS_ENCODE_PCM, NULL, 0); // set a WAV writer on the mixer

	/// Get longest midi file length so recroding covers longest///////////////////////////
	if (timeTrack1 < timeTrack2) {time1 = timeTrack2;}
	else {time1 = timeTrack1;}

	Application::UseWaitCursor = true;
	System::Windows::Forms::Cursor::Current = Cursors::WaitCursor;
	Sleep(time1 * 1000);
	Application::UseWaitCursor = false;
	System::Windows::Forms::Cursor::Current = Cursors::Default;

	BASS_Encode_Stop(mixer); // close the WAV writer

	FreeVSTChannelStream();
	CleanupAndShutdown();
}


More next steps...

  • Save whole song to file (XML)

  • Read keys on the keyboard to play plugin

  • Add the macro and volume sliders

  • Add VU meters

  • Add more tracks and more rows to a track



Demo playing 2 tracks!








23 views0 comments

Recent Posts

See All