Ejemplo n.º 1
0
JNIEXPORT jobject JNICALL Java_com_synthbot_jasiohost_AsioDriver_ASIOGetChannelInfo
(JNIEnv *env, jclass clazz, jint index, jboolean isInput) {

  ASIOChannelInfo channelInfo;
  channelInfo.channel = (long) index;
  channelInfo.isInput = (isInput == JNI_TRUE) ? ASIOTrue : ASIOFalse;
  ASIOError errorCode = ASIOGetChannelInfo(&channelInfo);
  
  switch (errorCode) {
    case ASE_OK: {
      return env->NewObject(
          env->FindClass("com/synthbot/jasiohost/AsioChannel"),
          env->GetMethodID(env->FindClass("com/synthbot/jasiohost/AsioChannel"), "<init>", "(IZZILcom/synthbot/jasiohost/AsioSampleType;Ljava/lang/String;)V"),
          (jint) index,
          isInput,
          (channelInfo.isActive == ASIOTrue) ? JNI_TRUE : JNI_FALSE,
          (jint) channelInfo.channelGroup,
          env->CallStaticObjectMethod(
              env->FindClass("com/synthbot/jasiohost/AsioSampleType"),
              env->GetStaticMethodID(env->FindClass("com/synthbot/jasiohost/AsioSampleType"), "getSampleType", "(I)Lcom/synthbot/jasiohost/AsioSampleType;"),
              channelInfo.type),
          env->NewStringUTF(channelInfo.name));
    }
    case ASE_NotPresent: {
      env->ThrowNew(
          env->FindClass("com/synthbot/jasiohost/AsioException"),
          (isInput == JNI_TRUE) ? "The requested input does not exist." : "The requested output does not exist.");
      return NULL;
    }
    default: {
      env->ThrowNew(
          env->FindClass("com/synthbot/jasiohost/AsioException"),
          "Unknown error code.");
      return NULL;
    }
  }
}
Ejemplo n.º 2
0
SndASIO::SndASIO(int channels, int mode, char* driver, int numbuffs, 
		 SndObj** inputs, 
		 int vecsize, float sr) : 
  SndIO(channels,16,inputs,vecsize, sr){

  int i;
  m_numbuffs = numbuffs;
  m_mode = mode;
  m_running = false;
  m_driver = driver;
  m_ocurrentbuffer = m_icurrentbuffer = 1;
  m_icount = m_ocount = 0; 

  memset(&m_driverinfo, 0, sizeof(ASIODriverInfo));

  m_asiocallbacks.bufferSwitch = &bufferSwitch;
  m_asiocallbacks.sampleRateDidChange = &sampleRateChanged;
  m_asiocallbacks.asioMessage = &asioMessages;
  m_asiocallbacks.bufferSwitchTimeInfo = &bufferSwitchTimeInfo;

  // Allocate the memory for BufferInfos
  if(!(bufferinfos = new ASIOBufferInfo[(m_channels+2)*2])){
    m_error = 21;
    return;
  }


  if(!asioDrivers) asioDrivers = new AsioDrivers;

  if(asioDrivers->loadDriver(m_driver)){

    if(ASIOInit(&m_driverinfo) == ASE_OK){

      if(ASIOCanSampleRate(m_sr) == ASE_OK) 
	ASIOSetSampleRate(m_sr);
      else ASIOGetSampleRate((double *)&m_sr);
      // set buffer size
      long dump1, dump2, dump3;
      ASIOGetBufferSize(&dump1, &dump2, &buffsize, &dump3);
   
      // get number of channels
      ASIOGetChannels(&ichannels, &ochannels);
      if(ichannels < m_channels){
	m_channels = (short) ichannels;
	m_samples = m_vecsize*m_channels;
      }
      else ichannels = m_channels;
      if(ochannels < m_channels){
	m_channels = (short) ochannels;
	m_samples = m_vecsize*m_channels;
      }
      else ochannels = m_channels;
   
      if(m_mode == SND_OUTPUT) ichannels = 0;
      if(m_mode == SND_INPUT) ochannels = 0;
       
  
      // Set the channel infos
      if(!(m_channelinfos = new ASIOChannelInfo[m_channels*2])){
	m_error = 22;
	return;
      }
      
      if((m_mode == SND_IO) || (m_mode == SND_OUTPUT)){           
	outsndbuff = new float*[m_numbuffs];    
   
	for(i = 0; i< m_numbuffs; i++){
	  if(!(outsndbuff[i] = new float[buffsize*m_channels])){
	    m_error =14;
	    return;
	  }
	}

	for(i = 0; i < m_channels; i++){	
	  bufferinfos[i].isInput = ASIOFalse;
	  bufferinfos[i].channelNum = i;
	  bufferinfos[i].buffers[0] =
	    bufferinfos[i].buffers[1] = 0;

	  m_channelinfos[i].channel = bufferinfos[i].channelNum;
	  m_channelinfos[i].isInput = bufferinfos[i].isInput;

	  ASIOGetChannelInfo(&m_channelinfos[i]);

  
	  switch(m_channelinfos[i].type){

	  case ASIOSTInt16LSB:
	    encoding = SHORTSAM;
	    m_bits = 16;
	    break;

	  case ASIOSTInt24LSB:
	    encoding = S24LE;
	    m_bits = 24;
	    break;

	  case ASIOSTInt32LSB:
	    encoding = LONGSAM;
	    m_bits = 32;
	    break;

	  default:
	    encoding = SHORTSAM;
	    break;

	  }
	} 
      }
 	 
      if((m_mode == SND_IO) || (m_mode == SND_INPUT)){
	insndbuff = new float*[m_numbuffs];    
   
	for(i = 0; i< m_numbuffs; i++){
	  if(!(insndbuff[i] = new float[buffsize*m_channels])){
	    m_error =14;
	    return;
	  }
	}
   
	for(i = 0; i < m_channels; i++){
	  bufferinfos[i+ochannels].isInput = ASIOTrue;
	  bufferinfos[i+ochannels].channelNum = i;
	  bufferinfos[i+ochannels].buffers[0] =
	    bufferinfos[i+ochannels].buffers[1] = 0;

	  m_channelinfos[i+ochannels].channel = bufferinfos[i+ochannels].channelNum;
	  m_channelinfos[i+ochannels].isInput = bufferinfos[i+ochannels].isInput;
 
	  ASIOGetChannelInfo(&m_channelinfos[i+ochannels]);
     
	  switch(m_channelinfos[i+ochannels].type){

	  case ASIOSTInt16LSB:
	    encoding = SHORTSAM;
	    m_bits = 16;
	    break;

	  case ASIOSTInt24LSB:
	    encoding = S24LE;
	    m_bits = 24;
	    break;

	  case ASIOSTInt32LSB:
	    encoding = LONGSAM;
	    m_bits = 32;
	    break;

	  default:
	    encoding = SHORTSAM;
	    break;

	  }

	}
      }

      if(!(ASIOCreateBuffers(bufferinfos, ichannels+ochannels, 
			     buffsize, &m_asiocallbacks)== ASE_OK)){
	m_error = 25;
	return;
      }

    
      if(ASIOOutputReady() == ASE_OK) optimise = true;
      else optimise = false;
      // printf("channels: %d\n", m_channels);
      m_outsndbuff = outsndbuff;
      m_insndbuff = insndbuff;
      m_encoding = encoding;
      m_bufferinfos = bufferinfos;
      m_ichannels = ichannels;
      m_ochannels  = ochannels;
      m_buffsize = buffsize;
      currentbuffer = 0;
      m_called_read = false;
      buffs = m_numbuffs;
  
    } 
    else { // could not initialise
      m_error = 24;
      return;
    }
  }   
  else { // if driver could not be loaded

    m_error = 23;
    return;

  }
#ifdef DEBUG
  cout << m_bits;
#endif
}
Ejemplo n.º 3
0
// Main:
int main()
{
    int retval = 0;
    bool inited = false, buffersCreated = false, started = false;
    char *error = NULL;

    drv.sampleRate = 44100.0;

    // Initialize FX parameters:
    fx.f0_gain.init();
    fx.f1_compressor.init();

    // Set our own inputs:
    for (int i = 0; i < icr; ++i)
    {
        fx.f0_gain.input.gain[i] = _mm256_set1_pd(0);   // dB

        fx.f1_compressor.input.threshold[i] = _mm256_set1_pd(-30);  // dBFS
        fx.f1_compressor.input.attack[i]    = _mm256_set1_pd(1.0);  // msec
        fx.f1_compressor.input.release[i]   = _mm256_set1_pd(80);   // msec
        fx.f1_compressor.input.ratio[i]     = _mm256_set1_pd(0.25); // N:1
        fx.f1_compressor.input.gain[i]      = _mm256_set1_pd(6);    // dB
    }

    // Calculate input-dependent values:
    fx.f0_gain.recalc();
    fx.f1_compressor.recalc();

    // FX parameters are all set.

#ifdef NOT_LIVE
    // Test mode:

#if 0
    const auto t0 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(-1.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(-1.0));
    printvec_dB(t0);
    printf("\n\n");
    const auto p0 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(-1.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(1.0));
    printvec_dB(t0);
    printf("\n\n");
    const auto t1 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(0.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(-1.0));
    printvec_dB(t1);
    printf("\n\n");
    const auto p1 = mm256_if_then_else(_mm256_cmp_pd(_mm256_set1_pd(0.0), _mm256_set1_pd(0.0), _CMP_LT_OQ), _mm256_set1_pd(0.0), _mm256_set1_pd(1.0));
    printvec_dB(t1);
    printf("\n\n");
    goto done;
#endif

    vec8_i32 in, out;
    long long c = 0LL;
    for (int i = 0; i < 20; ++i)
    {
        for (int n = 0; n < 48; ++n, ++c)
        {
            double s = sin(2.0 * 3.14159265358979323846 * (double)c / drv.sampleRate);
            int si = (int)(s * INT_MAX / 2);

            in = _mm256_set1_epi32(si);

            processEffects(in, out, 0);
        }

#if 1
        printf("samp:   ");
        printvec_samp(in);
        printf("\n");

        printf("input:  ");
        for (int n = 0; n < icr; ++n)
        {
            printvec_dB(fx.fi_monitor.levels[n]);
            if (n < icr - 1) printf(" ");
        }
        printf("\n");

        printf("gain:   ");
        for (int n = 0; n < icr; ++n)
        {
            printvec_dB(fx.f0_output.levels[n]);
            if (n < icr - 1) printf(" ");
        }
        printf("\n");

        printf("comp:   ");
        for (int n = 0; n < icr; ++n)
        {
            printvec_dB(fx.fo_monitor.levels[n]);
            if (n < icr - 1) printf(" ");
        }
        printf("\n");

        printf("samp:   ");
        printvec_samp(out);
        printf("\n\n");
#endif
    }
#else
    // ASIO live engine mode:
    if (!loadAsioDriver("UA-1000"))
    {
        error = "load failed.";
        goto err;
    }

    if (ASIOInit(&drv.driver) != ASE_OK)
        goto err;

    inited = true;

    if (ASIOGetChannels(&drv.inputChannels, &drv.outputChannels) != ASE_OK)
        goto err;

    printf("in: %d, out %d\n", drv.inputChannels, drv.outputChannels);

    if (ASIOGetBufferSize(&drv.minSize, &drv.maxSize, &drv.preferredSize, &drv.granularity) != ASE_OK)
        goto err;

    printf("min buf size: %d, preferred: %d, max buf size: %d\n", drv.minSize, drv.preferredSize, drv.maxSize);

    if (ASIOGetSampleRate(&drv.sampleRate) != ASE_OK)
        goto err;

    printf("rate: %f\n\n", drv.sampleRate);

    if (ASIOOutputReady() == ASE_OK)
        drv.postOutput = true;
    else
        drv.postOutput = false;

    // fill the bufferInfos from the start without a gap
    ASIOBufferInfo *info = drv.bufferInfos;

    // prepare inputs (Though this is not necessarily required, no opened inputs will work, too
    if (drv.inputChannels > kMaxInputChannels)
        drv.inputBuffers = kMaxInputChannels;
    else
        drv.inputBuffers = drv.inputChannels;
    for (int i = 0; i < drv.inputBuffers; i++, info++)
    {
        info->isInput = ASIOTrue;
        info->channelNum = i;
        info->buffers[0] = info->buffers[1] = 0;
    }

    // prepare outputs
    if (drv.outputChannels > kMaxOutputChannels)
        drv.outputBuffers = kMaxOutputChannels;
    else
        drv.outputBuffers = drv.outputChannels;
    for (int i = 0; i < drv.outputBuffers; i++, info++)
    {
        info->isInput = ASIOFalse;
        info->channelNum = i;
        info->buffers[0] = info->buffers[1] = 0;
    }

    asioCallbacks.asioMessage = asioMessage;
    asioCallbacks.bufferSwitch = bufferSwitch;
    asioCallbacks.bufferSwitchTimeInfo = bufferSwitchTimeInfo;

    // Create the buffers:
    if (ASIOCreateBuffers(drv.bufferInfos, drv.inputBuffers + drv.outputBuffers, drv.preferredSize, &asioCallbacks) != ASE_OK)
        goto err;
    else
        buffersCreated = true;

    // now get all the buffer details, sample word length, name, word clock group and activation
    for (int i = 0; i < drv.inputBuffers + drv.outputBuffers; i++)
    {
        drv.channelInfos[i].channel = drv.bufferInfos[i].channelNum;
        drv.channelInfos[i].isInput = drv.bufferInfos[i].isInput;
        if (ASIOGetChannelInfo(&drv.channelInfos[i]) != ASE_OK)
            goto err;

        //printf("%s[%2d].type = %d\n", drv.channelInfos[i].isInput ? "in " : "out", drv.channelInfos[i].channel, drv.channelInfos[i].type);
        if (drv.channelInfos[i].type != ASIOSTInt32LSB)
        {
            error = "Application assumes sample types of ASIOSTInt32LSB!";
            goto err;
        }
    }

    // get the input and output latencies
    // Latencies often are only valid after ASIOCreateBuffers()
    // (input latency is the age of the first sample in the currently returned audio block)
    // (output latency is the time the first sample in the currently returned audio block requires to get to the output)
    if (ASIOGetLatencies(&drv.inputLatency, &drv.outputLatency) != ASE_OK)
        goto err;

    printf ("latencies: input: %d, output: %d\n", drv.inputLatency, drv.outputLatency);

    // Start the engine:
    if (ASIOStart() != ASE_OK)
        goto err;
    else
        started = true;

    printf("Engine started.\n\n");
    const int total_time = 30;
    for (int i = 0; i < total_time; ++i)
    {
        printf("Engine running %2d.   \r", total_time - i);
        Sleep(1000);
    }
#endif

    goto done;

err:
    if (error == NULL)
        error = drv.driver.errorMessage;

    if (error != NULL)
        fprintf(stderr, "%s\r\n", error);

    retval = -1;

done:
    if (started)
        ASIOStop();
    if (buffersCreated)
        ASIODisposeBuffers();
    if (inited)
        ASIOExit();
    return retval;
}
//----------------------------------------------------------------------------------
ASIOError create_asio_buffers (DriverInfo *asioDriverInfo)
{	// create buffers for all inputs and outputs of the card with the 
	// preferredSize from ASIOGetBufferSize() as buffer size
	long i;
	ASIOError result;

	// fill the bufferInfos from the start without a gap
	ASIOBufferInfo *info = asioDriverInfo->bufferInfos;

	// prepare inputs (Though this is not necessaily required, no opened inputs will work, too
	if (asioDriverInfo->inputChannels > kMaxInputChannels)
		asioDriverInfo->inputBuffers = kMaxInputChannels;
	else
		asioDriverInfo->inputBuffers = asioDriverInfo->inputChannels;
	for(i = 0; i < asioDriverInfo->inputBuffers; i++, info++)
	{
		info->isInput = ASIOTrue;
		info->channelNum = i;
		info->buffers[0] = info->buffers[1] = 0;
	}

	// prepare outputs
	if (asioDriverInfo->outputChannels > kMaxOutputChannels)
		asioDriverInfo->outputBuffers = kMaxOutputChannels;
	else
		asioDriverInfo->outputBuffers = asioDriverInfo->outputChannels;
	//Ilia: disable output
	asioDriverInfo->outputBuffers = 0;
	for(i = 0; i < asioDriverInfo->outputBuffers; i++, info++)
	{
		info->isInput = ASIOFalse;
		info->channelNum = i;
		info->buffers[0] = info->buffers[1] = 0;
	}

	// create and activate buffers
	result = ASIOCreateBuffers(asioDriverInfo->bufferInfos,
		asioDriverInfo->inputBuffers + asioDriverInfo->outputBuffers,
		asioDriverInfo->preferredSize, &asioCallbacks);
	if (result == ASE_OK)
	{
		// now get all the buffer details, sample word length, name, word clock group and activation
		for (i = 0; i < asioDriverInfo->inputBuffers + asioDriverInfo->outputBuffers; i++)
		{
			asioDriverInfo->channelInfos[i].channel = asioDriverInfo->bufferInfos[i].channelNum;
			asioDriverInfo->channelInfos[i].isInput = asioDriverInfo->bufferInfos[i].isInput;
			result = ASIOGetChannelInfo(&asioDriverInfo->channelInfos[i]);
			printf("channel %d name: %s\n",i,asioDriverInfo->channelInfos[i].name);
			if (result != ASE_OK)
				break;
		}

		if (result == ASE_OK)
		{
			// get the input and output latencies
			// Latencies often are only valid after ASIOCreateBuffers()
			// (input latency is the age of the first sample in the currently returned audio block)
			// (output latency is the time the first sample in the currently returned audio block requires to get to the output)
			result = ASIOGetLatencies(&asioDriverInfo->inputLatency, &asioDriverInfo->outputLatency);
			if (result == ASE_OK)
				printf ("ASIOGetLatencies (input: %d, output: %d);\n", asioDriverInfo->inputLatency, asioDriverInfo->outputLatency);
		}
	}
	return result;
}
Ejemplo n.º 5
0
static BOOL CALLBACK cfgproc_asio( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  asio_config_type *cfg;
  if (uMsg == WM_INITDIALOG)
  {
    SetWindowLong(hwndDlg,GWL_USERDATA,(long)(cfg = (asio_config_type *)lParam));
	  if(!asioDrivers)
		  asioDrivers = new AsioDrivers();

    if (asioDrivers)
    {
      int l=asioDrivers->asioGetNumDev();
      int x;
      for (x = 0; x < l; x ++)
      {
        char buf[256];
        asioDrivers->asioGetDriverName(x,buf,sizeof(buf));
        SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)buf);        
      }
      if (!l)
        SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_ADDSTRING,0,(LPARAM)"No ASIO drivers found!");
      int wd=cfg->asio_driver;
      if (wd<0||wd>l) wd=0;
      SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_SETCURSEL,(WPARAM)wd,0);
      SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_COMBO1,CBN_SELCHANGE),0);
    }   
    //delete asioDrivers; // fucko thsi shouldnt be commented
   // asioDrivers=0;
    
    return 1;
  }
  cfg = (asio_config_type *)GetWindowLong(hwndDlg,GWL_USERDATA);
  if (uMsg == WM_COMMAND && wParam == MAKEWPARAM(IDC_COMBO1,CBN_SELCHANGE))
  {
    int wp=SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0);
    SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_RESETCONTENT,0,0);
    SendDlgItemMessage(hwndDlg,IDC_COMBO3,CB_RESETCONTENT,0,0);
    SendDlgItemMessage(hwndDlg,IDC_COMBO4,CB_RESETCONTENT,0,0);
    SendDlgItemMessage(hwndDlg,IDC_COMBO5,CB_RESETCONTENT,0,0);
    if (wp != CB_ERR)
    {
      char buf[256];
      SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETLBTEXT,wp,(LPARAM)buf);
	    if(!asioDrivers)
		    asioDrivers = new AsioDrivers();
      if (asioDrivers && asioDrivers->loadDriver(buf))
      {
      	ASIODriverInfo driverInfo;
        driverInfo.sysRef = (void *)hwndDlg;
        if (ASIOInit(&driverInfo) == ASE_OK)
        {
          long inch=0,outch=0;
	        if (ASIOGetChannels(&inch, &outch) == ASE_OK) 
          {
            long i;
            for (i = 0; i < inch; i ++)
            {
              ASIOChannelInfo c;
              c.channel=i;
              c.isInput=1;
              if (ASIOGetChannelInfo(&c) == ASE_OK) 
              {
		char buf[1024];
		wsprintf(buf,"%d: %s",i+1,c.name);
                SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)buf);
                SendDlgItemMessage(hwndDlg,IDC_COMBO4,CB_ADDSTRING,0,(LPARAM)buf);
              }
            }
            for (i = 0; i < outch; i ++)
            {
              ASIOChannelInfo c;
              c.channel=i;
              c.isInput=0;
              if (ASIOGetChannelInfo(&c) == ASE_OK) 
              {
		char buf[1024];
		wsprintf(buf,"%d: %s",i+1,c.name);
                SendDlgItemMessage(hwndDlg,IDC_COMBO3,CB_ADDSTRING,0,(LPARAM)buf);
                SendDlgItemMessage(hwndDlg,IDC_COMBO5,CB_ADDSTRING,0,(LPARAM)buf);
              }
            }
            int wd=cfg->asio_input[0];
            if (wd<0||wd>inch) wd=0;
            SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_SETCURSEL,(WPARAM)wd,0);
            wd=cfg->asio_input[1];
            if (wd<0||wd>inch) wd=0;
            SendDlgItemMessage(hwndDlg,IDC_COMBO4,CB_SETCURSEL,(WPARAM)wd,0);
            wd=cfg->asio_output[0];
            if (wd<0||wd>outch) wd=0;
            SendDlgItemMessage(hwndDlg,IDC_COMBO3,CB_SETCURSEL,(WPARAM)wd,0);
            wd=cfg->asio_output[1];
            if (wd<0||wd>outch) wd=0;
            SendDlgItemMessage(hwndDlg,IDC_COMBO5,CB_SETCURSEL,(WPARAM)wd,0);
          } 
          ASIOExit();
        }

        asioDrivers->removeCurrentDriver();
      }
      //delete asioDrivers;// fucko thsi shouldnt be commented
     // asioDrivers=0;
    }
    // repopulate channel list
  }

  if (uMsg == WM_COMMAND && LOWORD(wParam) == IDC_BUTTON1)
  {
    char buf[256];
    int wp=SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0);
    if (wp != CB_ERR)
    {
      if (!asioDrivers) asioDrivers = new AsioDrivers();

      SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETLBTEXT,wp,(LPARAM)buf);
      if (asioDrivers && asioDrivers->loadDriver(buf))
      {
        ASIODriverInfo driverInfo;
        driverInfo.sysRef=(HWND)hwndDlg;
        if (ASIOInit(&driverInfo) == ASE_OK)
        {
          ASIOControlPanel();
          ASIOExit();
        }
      }
      asioDrivers->removeCurrentDriver();
     // if (asioDrivers) delete asioDrivers;
   //   asioDrivers=0;// fucko thsi shouldnt be commented
    }
  }

  if (uMsg == WM_COMMAND && LOWORD(wParam) == IDOK)
  {
    int a;
    a=SendDlgItemMessage(hwndDlg,IDC_COMBO1,CB_GETCURSEL,0,0);
    if (a != CB_ERR) cfg->asio_driver = a;
    a=SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCURSEL,0,0);
    if (a != CB_ERR) cfg->asio_input[0] = a;
    a=SendDlgItemMessage(hwndDlg,IDC_COMBO4,CB_GETCURSEL,0,0);
    if (a != CB_ERR) cfg->asio_input[1] = a;
    a=SendDlgItemMessage(hwndDlg,IDC_COMBO3,CB_GETCURSEL,0,0);
    if (a != CB_ERR) cfg->asio_output[0] = a;
    a=SendDlgItemMessage(hwndDlg,IDC_COMBO5,CB_GETCURSEL,0,0);
    if (a != CB_ERR) cfg->asio_output[1] = a;
  }
  return 0;
}