1 /* 2 * Copyright 2012, Gerasim Troeglazov (3dEyes**), 3dEyes@gmail.com. 3 * All rights reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <image.h> 10 11 #include <Application.h> 12 13 #include "VSTHost.h" 14 15 static int32 VHostCallback(VSTEffect* effect, int32 opcode, int32 index, 16 int32 value, void* ptr, float opt); 17 18 //Trim string 19 static void 20 TrimString(BString *string) { 21 char* str = string->LockBuffer(256); 22 uint32 k = 0; 23 uint32 i = 0; 24 for(i=0; str[i]!='\0';) { 25 if (isspace(str[i])) { 26 k = i; 27 for(uint32 j = i; j < strlen(str) - 1; j++) 28 str[j] = str[j + 1]; 29 str[strlen(str) - 1] = '\0'; 30 i = k; 31 } else 32 i++; 33 } 34 string->UnlockBuffer(); 35 } 36 37 //VST Parameter class 38 VSTParameter::VSTParameter(VSTPlugin* plugin, int index) 39 { 40 fIndex = index; 41 fEffect = plugin->Effect(); 42 fDropList.MakeEmpty(); 43 44 char temp[256]; 45 //get parameter name 46 temp[0] = 0; 47 fEffect->dispatcher(fEffect, VST_GET_PARAM_NAME, index, 0, temp, 0); 48 fName.SetTo(temp); 49 TrimString(&fName); 50 //get parameter label (unit) 51 temp[0] = 0; 52 fEffect->dispatcher(fEffect, VST_GET_PARAM_UNIT, index, 0, temp, 0); 53 fUnit.SetTo(temp); 54 ValidateValues(&fUnit); 55 //store current value 56 float val = fEffect->getParameter(fEffect, index); 57 //test for minimum value 58 fEffect->setParameter(fEffect, index, 0); 59 temp[0] = 0; 60 fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, temp, 0); 61 fMinValue.SetTo(temp); 62 ValidateValues(&fMinValue); 63 //test for maximum value 64 temp[0] = 0; 65 fEffect->setParameter(fEffect, index, 1.0); 66 fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 0, temp, 0); 67 fMaxValue.SetTo(temp); 68 ValidateValues(&fMaxValue); 69 //test for discrete values 70 char test_disp[VST_PARAM_TEST_COUNT][256]; 71 float test_values[VST_PARAM_TEST_COUNT]; 72 float delta = 1.0 / (float)VST_PARAM_TEST_COUNT; 73 int test_cnt = 0; 74 for(int tst_val = 0; tst_val < VST_PARAM_TEST_COUNT; tst_val++) { 75 float v = (float)tst_val / (float)VST_PARAM_TEST_COUNT; 76 77 if (tst_val >= VST_PARAM_TEST_COUNT - 1) 78 v = 1.0; 79 80 fEffect->setParameter(fEffect, index, v); 81 82 float new_value = fEffect->getParameter(fEffect, index); 83 bool valtest = false; 84 for(int i = 0; i < test_cnt; i++) { 85 if (fabs(test_values[i] - new_value) < delta) { 86 valtest = true; 87 break; 88 } 89 } 90 if (valtest == false) { 91 test_values[test_cnt] = new_value; 92 fEffect->dispatcher(fEffect, VST_GET_PARAM_STR, index, 93 0, test_disp[test_cnt], 0); 94 test_cnt++; 95 } 96 } 97 98 //restore value 99 fEffect->setParameter(fEffect, index, val); 100 101 //detect param type 102 if (test_cnt == 2) { 103 fType = VST_PARAM_CHECKBOX; 104 105 DropListValue* min_item = new DropListValue(); 106 min_item->Value = 0.0; 107 min_item->Index = 0; 108 min_item->Name = fMinValue; 109 fDropList.AddItem(min_item); 110 111 DropListValue* max_item = new DropListValue(); 112 max_item->Value = 1.0; 113 max_item->Index = 1; 114 max_item->Name = fMaxValue; 115 fDropList.AddItem(max_item); 116 } else if (test_cnt > 2 && test_cnt < VST_PARAM_TEST_COUNT / 2) { 117 fType = VST_PARAM_DROPLIST; 118 119 for(int i = 0; i < test_cnt; i++) { 120 DropListValue* item = new DropListValue(); 121 item->Value = test_values[i]; 122 item->Index = i; 123 item->Name = test_disp[i]; 124 fDropList.AddItem(item); 125 } 126 } else { 127 fType = VST_PARAM_SLIDER; 128 } 129 fChanged = 0LL; 130 } 131 132 VSTParameter::~VSTParameter() 133 { 134 } 135 136 BString* 137 VSTParameter::ValidateValues(BString* string) 138 { 139 if (string->Length() == 0) 140 return string; 141 142 bool isNum = true; 143 144 const char *ptr = string->String(); 145 for(; *ptr!=0; ptr++) { 146 char ch = *ptr; 147 if (!((ch >= '0' && ch <= '9') || ch == '.' || ch == '-')) { 148 isNum = false; 149 break; 150 } 151 } 152 153 if (isNum) { 154 float val = atof(string->String()); 155 156 if (val <= -pow(2, 31)) { 157 string->SetTo("-∞"); 158 } else if (val >= pow(2, 31)) { 159 string->SetTo("∞"); 160 } else { 161 char temp[256]; 162 sprintf(temp, "%g", val); 163 string->SetTo(temp); 164 } 165 } else { 166 TrimString(string); 167 if (*string == "oo" || *string == "inf") 168 string->SetTo("∞"); 169 if (*string == "-oo" || *string == "-inf") 170 string->SetTo("-∞"); 171 172 } 173 return string; 174 } 175 176 int 177 VSTParameter::ListCount(void) 178 { 179 return fDropList.CountItems(); 180 } 181 182 DropListValue* 183 VSTParameter::ListItemAt(int index) 184 { 185 DropListValue* item = NULL; 186 if (index >= 0 && index < fDropList.CountItems()) 187 item = (DropListValue*)fDropList.ItemAt(index); 188 return item; 189 } 190 191 192 float 193 VSTParameter::Value() 194 { 195 float value = fEffect->getParameter(fEffect, fIndex); 196 if (fType == VST_PARAM_DROPLIST) { 197 //scan for near value 198 int min_index = 0; 199 float min_delta = 1.0; 200 for(int i = 0; i < fDropList.CountItems(); i++) { 201 DropListValue* item = (DropListValue*)fDropList.ItemAt(i); 202 float delta = fabs(item->Value - value); 203 if (delta <= min_delta) { 204 min_delta = delta; 205 min_index = i; 206 } 207 } 208 value = min_index; 209 } 210 fLastValue = value; 211 return value; 212 } 213 214 void 215 VSTParameter::SetValue(float value) 216 { 217 if (value == fLastValue) 218 return; 219 220 if (fType == VST_PARAM_DROPLIST) { 221 //take value by index 222 int index = (int)vstround(value); 223 if (index >= 0 && index < fDropList.CountItems()) { 224 DropListValue *item = (DropListValue*)fDropList.ItemAt(index); 225 value = item->Value; 226 fLastValue = index; 227 } else { 228 return; 229 } 230 } else { 231 fLastValue = value; 232 } 233 fChanged = system_time(); 234 fEffect->setParameter(fEffect, fIndex, value); 235 } 236 237 bigtime_t 238 VSTParameter::LastChangeTime(void) 239 { 240 return fChanged; 241 } 242 243 const char* 244 VSTParameter::MinimumValue(void) 245 { 246 return fMinValue.String(); 247 } 248 249 const char* 250 VSTParameter::MaximumValue(void) 251 { 252 return fMaxValue.String(); 253 } 254 255 const char* 256 VSTParameter::Unit(void) 257 { 258 return fUnit.String(); 259 } 260 261 int 262 VSTParameter::Index(void) 263 { 264 return fIndex; 265 } 266 267 int 268 VSTParameter::Type(void) 269 { 270 return fType; 271 } 272 273 const char* 274 VSTParameter::Name(void) 275 { 276 return fName.String(); 277 } 278 279 //VST Plugin class 280 VSTPlugin::VSTPlugin() 281 { 282 fActive = false; 283 fEffect = NULL; 284 VSTMainProc = NULL; 285 fInputChannels = 0; 286 fOutputChannels = 0; 287 fSampleRate = 44100.f; 288 fBlockSize = 0; 289 inputs = NULL; 290 outputs = NULL; 291 fParameters.MakeEmpty(); 292 } 293 294 VSTPlugin::~VSTPlugin() 295 { 296 fParameters.MakeEmpty(); 297 UnLoadModule(); 298 } 299 300 int 301 VSTPlugin::LoadModule(const char *path) 302 { 303 char effectName[256] = {0}; 304 char vendorString[256] = {0}; 305 char productString[256] = {0}; 306 307 if (fActive) 308 return VST_ERR_ALREADY_LOADED; 309 310 fPath = BPath(path); 311 312 fModule = load_add_on(path); 313 if (fModule <= 0) 314 return VST_ERR_NOT_LOADED; 315 316 if (get_image_symbol(fModule, "main_plugin", B_SYMBOL_TYPE_TEXT, 317 (void**)&VSTMainProc) != B_OK) { 318 unload_add_on(fModule); 319 return VST_ERR_NO_MAINPROC; 320 } 321 322 fEffect = VSTMainProc(VHostCallback); 323 if (fEffect==NULL) { 324 unload_add_on(fModule); 325 return VST_ERR_NOT_LOADED; 326 } 327 328 fEffect->dispatcher(fEffect, VST_OPEN, 0, 0, 0, 0); 329 330 fEffect->dispatcher(fEffect, VST_GET_EFFECT_NAME, 0, 0, effectName, 0); 331 fEffectName.SetTo(effectName); 332 TrimString(&fEffectName); 333 334 fModuleName.SetTo("VST:"); 335 fModuleName.Append(fPath.Leaf()); 336 337 fEffect->dispatcher(fEffect, VST_GET_VENDOR_STR, 0, 0, vendorString, 0); 338 fVendorString.SetTo(vendorString); 339 TrimString(&fVendorString); 340 341 fEffect->dispatcher(fEffect, VST_GET_PRODUCT_STR, 0, 0, productString, 0); 342 fProductString.SetTo(productString); 343 TrimString(&fProductString); 344 345 fInputChannels = fEffect->numInputs; 346 fOutputChannels = fEffect->numOutputs; 347 348 for(int i=0; i < fEffect->numParams; i++) { 349 VSTParameter *param = new VSTParameter(this, i); 350 fParameters.AddItem(param); 351 } 352 353 fEffect->dispatcher(fEffect, VST_STATE_CHANGED, 0, 1, 0, 0); 354 355 ReAllocBuffers(); 356 357 fActive = true; 358 return B_OK; 359 } 360 361 int 362 VSTPlugin::UnLoadModule(void) 363 { 364 if (!fActive || fModule <= 0) 365 return VST_ERR_NOT_LOADED; 366 367 fEffect->dispatcher(fEffect, VST_STATE_CHANGED, 0, 0, 0, 0); 368 fEffect->dispatcher(fEffect, VST_CLOSE, 0, 0, 0, 0); 369 370 unload_add_on(fModule); 371 372 return B_OK; 373 } 374 375 int 376 VSTPlugin::Channels(int mode) 377 { 378 switch(mode) { 379 case VST_INPUT_CHANNELS: 380 return fInputChannels; 381 case VST_OUTPUT_CHANNELS: 382 return fOutputChannels; 383 default: 384 return 0; 385 } 386 } 387 388 int 389 VSTPlugin::SetSampleRate(float rate) 390 { 391 fSampleRate = rate; 392 fEffect->dispatcher(fEffect, VST_SET_SAMPLE_RATE, 0, 0, 0, rate); 393 return B_OK; 394 } 395 396 float 397 VSTPlugin::SampleRate(void) 398 { 399 return fSampleRate; 400 } 401 402 int 403 VSTPlugin::SetBlockSize(size_t size) 404 { 405 fBlockSize = size; 406 fEffect->dispatcher(fEffect, VST_SET_BLOCK_SIZE, 0, size, 0, 0); 407 ReAllocBuffers(); 408 return B_OK; 409 } 410 411 const char* 412 VSTPlugin::Path(void) 413 { 414 return fPath.Path(); 415 } 416 417 int 418 VSTPlugin::ReAllocBuffers(void) 419 { 420 if (inputs != NULL) { 421 for(int32 i = 0; i < fInputChannels; i++) 422 delete inputs[i]; 423 } 424 425 if (outputs != NULL) { 426 for(int32 i = 0; i < fOutputChannels; i++) 427 delete outputs[i]; 428 } 429 430 if (fInputChannels > 0) { 431 inputs = new float*[fInputChannels]; 432 for(int32 i = 0; i < fInputChannels; i++) { 433 inputs[i] = new float[fBlockSize]; 434 memset(inputs[i], 0, fBlockSize * sizeof(float)); 435 } 436 } 437 438 if (fOutputChannels > 0) { 439 outputs = new float*[fOutputChannels]; 440 for(int32_t i = 0; i < fOutputChannels; i++) { 441 outputs[i] = new float[fBlockSize]; 442 memset (outputs[i], 0, fBlockSize * sizeof(float)); 443 } 444 } 445 return B_OK; 446 } 447 448 size_t 449 VSTPlugin::BlockSize(void) 450 { 451 return fBlockSize; 452 } 453 454 int 455 VSTPlugin::ParametersCount(void) 456 { 457 return fParameters.CountItems(); 458 } 459 460 VSTParameter* 461 VSTPlugin::Parameter(int index) 462 { 463 VSTParameter* param = NULL; 464 465 if (index >= 0 && index < fParameters.CountItems()) 466 param = (VSTParameter*)fParameters.ItemAt(index); 467 468 return param; 469 } 470 471 VSTEffect* 472 VSTPlugin::Effect(void) 473 { 474 return fEffect; 475 } 476 477 const char* 478 VSTPlugin::EffectName(void) 479 { 480 return fEffectName.String(); 481 } 482 483 const char* 484 VSTPlugin::ModuleName(void) 485 { 486 return fModuleName.String(); 487 } 488 489 const char* 490 VSTPlugin::Vendor(void) 491 { 492 return fVendorString.String(); 493 } 494 495 const char* 496 VSTPlugin::Product(void) 497 { 498 return fProductString.String(); 499 } 500 501 502 void 503 VSTPlugin::Process(float *buffer, int samples, int channels) 504 { 505 //todo: full channels remapping needed 506 float* src = buffer; 507 508 if (channels == fInputChannels) { //channel to channel 509 for(int j = 0; j < samples; j++) { 510 for(int c = 0; c < fInputChannels; c++) 511 inputs[c][j] = *src++; 512 } 513 } else if ( channels == 1) { //from mone to multichannel 514 for(int j = 0; j < samples; j++, src++) { 515 for(int c = 0; c < fInputChannels; c++) 516 inputs[c][j] = *src; 517 } 518 } 519 520 fEffect->processReplacing(fEffect, inputs, outputs, fBlockSize); 521 522 float* dst = buffer; 523 524 if (channels == fOutputChannels) { //channel to channel 525 for(int j = 0; j < samples; j++) { 526 for(int c = 0; c < fOutputChannels; c++) 527 *dst++ = outputs[c][j]; 528 } 529 } else if (channels == 1) { //from multichannel to mono 530 for(int j = 0; j < samples; j++, dst++) { 531 float mix = 0; 532 for(int c = 0; c < fOutputChannels; c++) 533 mix += outputs[c][j]; 534 *dst = mix / (float)fOutputChannels; 535 } 536 } 537 } 538 539 static int32 540 VHostCallback(VSTEffect* effect, int32 opcode, int32 index, int32 value, 541 void* ptr, float opt) 542 { 543 intptr_t result = 0; 544 545 switch(opcode) 546 { 547 case VST_MASTER_PRODUCT: 548 if (ptr) { 549 strcpy((char*)ptr, "VSTHost Media AddOn"); 550 result = 1; 551 } 552 break; 553 case VST_MASTER_VERSION : 554 result = 2300; 555 break; 556 } 557 558 return result; 559 } 560