0

So I'm recording audio with Delphi using MCISendString. The code works fine with one exception. I'm letting the user set the parameters, channels, bits, freq, and on Windows 7 this worked fine. Now on Windows 10 I'm getting mcierr_wave_inputsinuse.

If I reduce the parameters to 1 channel, 8 bits, 8khz, it records fine. Anything above that and MCI refuses it. According to my device it should go 1 channel, 16 bit, 48k.

I'm guessing that the mic is being shared. Does anyone know how to get a Delphi program to take exclusive control so that it can use the full abilities of the device?

I did a good bit of digging and came up with nothing useful.

Thanks

Here's the code I'm using.

 MRet := mciSendString(PChar('RECORD mysound'), NIL, 0, Handle);

It's returning a 322 result code. I've tried it with and without converting to a PChar.

It appears to be related to Cortana. But since there is no way to fully disable Cortana, gaining exclusive control appears to be the only possible solution.

Johan
  • 74,508
  • 24
  • 191
  • 319
uPrompt
  • 159
  • 1
  • 10
  • My guess is it is Cortana. Try turning it off. You can't take exclusive control if something else already has control. – Dsm Mar 15 '16 at 10:52
  • That... was one amazing guess. It seems you're at least partially correct. I killed Cortana via task manager and I got no complaints when I increased the settings and tried to record. However, it seems I'm getting another mci error elsewhere that I'll have to investigate. Clicking to record works but it's only saving a 44 byte file, which is what it was doing prior to me adding in the initial mci error checks. – uPrompt Mar 15 '16 at 11:29
  • Thank you. I would turn Cortana off by clicking on Cortana and turning it off in the options. Killing it with the task manager may only be temporary - you know what Microsoft is like! – Dsm Mar 15 '16 at 11:45
  • Again, you are correct. That worked briefly it seems. As soon as you kill Cortana, it comes right back. And turning off doesn't prevent it from reloading. I can only guess that I caught it in the middle of restarting. I'm back to the same error. – uPrompt Mar 15 '16 at 13:54
  • It's very naughty of Cortana to hog the microphone like that when it is turned off. You can't even uninstall Cortana - Windows 10 crashes if you do that it seems. I don't know what else to suggest. – Dsm Mar 15 '16 at 14:09
  • You might find this link useful http://www.askvg.com/tip-disable-cortana-and-bing-search-results-in-windows-10/ (windows registry option - option 5) – Dsm Mar 15 '16 at 15:44
  • I disabled Cortana complete once in the past and Win 10 had fits. The problem with any of those methods is... I wouldn't expect my customers to go through that process just to use my software. I'll need to find another way around it or coincide with it. Thanks for the help so far, you've put me on the right track which is a lot more than I had. – uPrompt Mar 15 '16 at 17:34
  • Yes, I expected that response on both counts. My thinking was that if the registry operation had worked, though, it could be achieved through software using the TRegistry component (with suitable priviledges of course...). Another thought I had - and I don't know if it gets you anywhere, was the Cortana sets the microphone options to to 1 channel, 8 bits, 8khz and that is why you can't get better. – Dsm Mar 16 '16 at 16:06
  • You should not be gaining exclusive control in a commercial application. I would replace MCI with Wasapi (or MMSystem for XP). These APIs will allow you to get audio with requested quality even if sharing devices with another application (eg. Cortana). – quasoft Mar 16 '16 at 18:24
  • Yea, just stumbled onto Wasabi today. Looks pretty daunting and not much out there that explains it for Delphi. Thanks though, it seems I'm out of options. – uPrompt Mar 16 '16 at 22:57

1 Answers1

1

You can find an example of a simple Delphi app using WasAPI here:

https://web.archive.org/web/20130403023149/http://4coder.org/delphi-source-code/547/

Note that the original website has long since gone down, but wayback still has all the code.

The code to do the actual recording is here:

// http://msdn.microsoft.com/en-us/library/ms678709(VS.85).aspx
procedure TInputRecordThread.Execute;
const
  REFTIMES_PER_SEC = 10000000;
  REFTIMES_PER_MILLISEC = 10000;
var
  MMDev: IMMDevice;
  MMDevEnum: IMMDeviceEnumerator;
  AudioClient: IAudioClient;
  CaptureClient: IAudioCaptureClient;
  PropVar: ^tag_inner_PROPVARIANT;
  hnsRequestedDuration, hnsActualDuration: Int64;
  pWfx, pCloseWfx: PWaveFormatEx;
  BufferFrameCount, NumFramesAvailable, Flags, StreamFlags, PacketLength, FrameSize: Cardinal;
  pData: PByte;
  uDummy: UInt64;
  Returned: HRESULT;
  Wave: TWaveImage;
  Empty: array of byte;
  pEx: PWaveFormatExtensible;
begin
  FreeOnTerminate := True;
  pCloseWfx := nil;
  uDummy := 0;
  PropVar := nil;

  CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
  CoCreateInstance(CLASS_MMDeviceEnumerator,
    nil,
    CLSCTX_ALL,
    IID_IMMDeviceEnumerator,
    MMDevEnum);

  if FLoopback then
    Returned := MMDevEnum.GetDefaultAudioEndpoint(eRender, eConsole, MMDev)
  else
    Returned := MMDevEnum.GetDefaultAudioEndpoint(eCapture, eConsole, MMDev);

  if Returned <> S_OK then
  begin
    OleCheck(Returned);
    Exit;
  end;

  Returned := MMDev.Activate(IID_IAudioClient, CLSCTX_ALL, PropVar^, Pointer(AudioClient));
  if Returned <> S_OK then
  begin
    OleCheck(Returned);
    Exit;
  end;

  AudioClient.GetMixFormat(pWfx);

  // http://www.ambisonic.net/mulchaud.html
  case pWfx.wFormatTag of
    WAVE_FORMAT_IEEE_FLOAT:
      begin
        pWfx.wFormatTag := WAVE_FORMAT_PCM;
        pWfx.wBitsPerSample := 16;
        pWfx.nBlockAlign := pWfx.nChannels * pWfx.wBitsPerSample div 8;
        pWfx.nAvgBytesPerSec := pWfx.nBlockAlign * pWfx.nSamplesPerSec;
      end;
    WAVE_FORMAT_EXTENSIBLE:
      begin
        pEx := PWaveFormatExtensible(pWfx);
        if not IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx.SubFormat) then
        begin
          Exit;
        end;

        pEx.SubFormat := KSDATAFORMAT_SUBTYPE_PCM;
        pEx.ValidBitsPerSample := 16;
        pWfx.wBitsPerSample := 16;
        pWfx.nBlockAlign := pWfx.nChannels * pWfx.wBitsPerSample div 8;
        pWfx.nAvgBytesPerSec := pWfx.nBlockAlign * pWfx.nSamplesPerSec;
      end;
    else Exit;
  end;

  if AudioClient.IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, pWfx, pCloseWfx) <> S_OK then
  begin
    Exit;
  end;

  // Äŕçěĺđ ôđýéěŕ.
  FrameSize := pWfx.wBitsPerSample * pWfx.nChannels div 8;

  hnsRequestedDuration := REFTIMES_PER_SEC;
  if FLoopback then
    StreamFlags := AUDCLNT_STREAMFLAGS_LOOPBACK
  else
    StreamFlags := 0;
  Returned := AudioClient.Initialize(AUDCLNT_SHAREMODE_SHARED,
    StreamFlags,
    hnsRequestedDuration,
    0,
    pWfx,
    nil);
  if Returned <> S_OK then
  begin
    Exit;
  end;

  AudioClient.GetBufferSize(BufferFrameCount);

  Returned := AudioClient.GetService(IID_IAudioCaptureClient, Pointer(CaptureClient));
  if Returned <> S_OK then
  begin
    Exit;
  end;

  // Calculate the actual duration of the allocated buffer.
  hnsActualDuration := REFTIMES_PER_SEC * BufferFrameCount div pWfx.nSamplesPerSec;

  // Start recording.
  AudioClient.Start();

  Wave := TWaveImage.Create(FData);
  try
    Wave.InitHeader(pWfx^);

    // Each loop fills about half of the shared buffer.
    while not Terminated do
    begin
      // Sleep for half the buffer duration.
      Sleep(hnsActualDuration div REFTIMES_PER_MILLISEC div 2);

      CaptureClient.GetNextPacketSize(PacketLength);

      while PacketLength <> 0 do
      begin
        // Get the available data in the shared buffer.
        pData := nil;
        Returned := CaptureClient.GetBuffer(pData,
          NumFramesAvailable,
          Flags,
          uDummy,
          uDummy);

        if Returned <> S_OK then
        begin
          Exit;
        end;

        if (Flags or Cardinal(AUDCLNT_BUFFERFLAGS_SILENT)) = Flags then
        begin
          pData := nil;  // Tell CopyData to write silence.
        end;

        if pData = nil then
        begin
          SetLength(Empty, NumFramesAvailable * FrameSize);
          FillChar(Empty[0], Length(Empty), 0);
          FData.Write(Empty[0], Length(Empty));
        end
        else
        begin
          // Ĺîőđŕí˙ĺě äŕííűĺ.
          FData.Write(pData^, NumFramesAvailable * FrameSize);
        end;

        CaptureClient.ReleaseBuffer(NumFramesAvailable);
        CaptureClient.GetNextPacketSize(PacketLength);
      end;
    end;

    // ĂŽĹ„Ĺŕíŕâëčâŕĺě çŕďčńü.
    AudioClient.Stop();

    // ĂŽĹęîđđĺĹčđóĺě çŕÄîëîâîę.
    Wave.CorretHeader;
    FData.Position := 0;
  finally
    Wave.Free;

    if pWfx <> nil then
      CoTaskMemFree(pWfx);
  end;
end;

You might also want to check out: http://torry.net/pages.php?id=167&sort=ID

Johan
  • 74,508
  • 24
  • 191
  • 319