• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2343
  • Last Modified:

acmStreamOpen()

In MSDN is written:
"The ACM uses stream functions to support data format conversion. Converters in the ACM change the format, but not the data type. For example, a converter module can change 44-kHz, 16-bit data to 44-kHz, 8-bit data. "
Indeed I never succeeded to convert from PCM to MU_LAW for example. So how can I convert from one format to another?
0
Buffon
Asked:
Buffon
  • 18
  • 18
1 Solution
 
grg99Commented:
The whole ACM codec stuff is a big mess.  It was not thought out too clearly from the start, so ther's plenty of cant-get-there-from-here's.

The only saving grace is that you can almost always convert to the lowest-common-denominator, the misaptly named "PCM" fornat.   Then you can use the PCM codec to convert sampling rates and/or number of bits and channels.  Then if you have to, you can use the original codec backwards to restore the original compression format.

Also lookup all the enumeration API's, which sort-of sideways give you some of the info on the capbilities of each codec.  Sort of.

0
 
WxWCommented:
There are formats that cannot be converted. Example, the Microsoft G 723 sound codec. You cannot open the sound device with it and you cannot use ACM to convert to it for unknown reasons.

Try using PCM with codecs known to work like DSP Truespeech, ACELP.NET , Lernout & Hauspie etc.
0
 
BuffonAuthor Commented:
To WxW:
I tried to convert 8000 Khz 8 bit mono WAVE_FORMAT_PCM to  WAVE_FORMAT_DSPGROUP_TRUESPEECH, but acmStreamOpen() failed with "The requested operation cannot be performed." error, the same about ACELP.NET.
0
Free Tool: IP Lookup

Get more info about an IP address or domain name, such as organization, abuse contacts and geolocation.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
WxWCommented:
Try , instead of converting, to open the wave device with waveInOpen, specifying the ACELP.NET codec (or another) in the WAVEFORMATEX structure.
Note that additional bytes might be needed by the structure for the codec to work.

Paste your code.

0
 
BuffonAuthor Commented:
I dont want to record in any other format than PCM, because not on every computer there ACELP.NET codec. What I am trying to do is to compress the voice in order to reduce the bandwidth.
0
 
WxWCommented:
Paste the code you are using to compress the stream.
0
 
BuffonAuthor Commented:
     waveFmt.wFormatTag      = WAVE_FORMAT_PCM;
      waveFmt.nChannels       = 1;
      waveFmt.nSamplesPerSec  = SAMPLES_PER_SEC;
      waveFmt.wBitsPerSample  = BITS_PER_SAMPL;
      waveFmt.nAvgBytesPerSec = waveFmt.nSamplesPerSec * waveFmt.nChannels; // SamplesPerSecond * Channels            
      waveFmt.nBlockAlign     = waveFmt.nChannels * waveFmt.wBitsPerSample / 8; // (Channels * BitsPerSample) / 8      
      waveFmt.cbSize          = 0;

//pwfDrv I get from FormatEnumProc


            HACMSTREAM hstr = NULL;            
            MMRESULT mmr = acmStreamOpen(&hstr,
                  NULL, // Driver handle
                  &waveFmt, // Source format
                  &pwfDrv, // Destination format
                  NULL, // No filter
                  NULL, // No callback
                  0, // Instance data (not used)
                  ACM_STREAMOPENF_NONREALTIME); // Flags
            if (mmr) {
                  switch(mmr) {
                  case ACMERR_NOTPOSSIBLE:
                        printf("acmStreamOpen - The requested operation cannot be performed.\n");
                        break;
                  case MMSYSERR_INVALFLAG:
                        printf("acmStreamOpen - At least one flag is invalid.\n");
                        break;
                  case MMSYSERR_INVALHANDLE:
                        printf("acmStreamOpen - The specified handle is invalid.\n");
                        break;
                  case MMSYSERR_INVALPARAM:
                        printf("acmStreamOpen - At least one parameter is invalid.\n");
                        break;
                  case MMSYSERR_NOMEM:
                        printf("acmStreamOpen - The system is unable to allocate resources.\n");
                        break;
                  default:
                        printf("Failed to open a stream to do PCM to driver format conversion\n");                        
                  }
                  exit(1);
            }
            
            // Allocate a buffer for the result of the conversion.
            // Compute the output buffer size based on the average byte rate
            // and add a bit for randomness.
            // The IMA_ADPCM driver fails the conversion without this extra space.
            float p1 = (float)pwfDrv.nSamplesPerSec, p2 = (float)waveFmt.nSamplesPerSec, p3 = (float)pwfDrv.wBitsPerSample, p4 = (float)waveFmt.wBitsPerSample;
            DWORD dwDst2Bytes = (DWORD) (p1 / p2 * DataSize * p3 / p4);
            dwDst2Bytes = dwDst2Bytes * 3 / 2; // add a little room
            BYTE* pDst2Data = new BYTE [dwDst2Bytes];
            
            // Fill in the conversion info.
            ACMSTREAMHEADER strhdr2;
            memset(&strhdr2, 0, sizeof(strhdr2));
            strhdr2.cbStruct = sizeof(strhdr2);
            strhdr2.pbSrc = (unsigned char *)WaveData[j]; // the source data to convert
            strhdr2.cbSrcLength = DataSize;
            strhdr2.pbDst = pDst2Data;
            strhdr2.cbDstLength = dwDst2Bytes;
            
            // Prep the header.
            mmr = acmStreamPrepareHeader(hstr, &strhdr2, 0);
            
            // Convert the data.
            mmr = acmStreamConvert(hstr, &strhdr2, 0);
            if (mmr) {
                  printf("Failed to do PCM to driver format conversion\n");
                  exit(1);
            }
            if (strhdr2.fdwStatus == ACMSTREAMHEADER_STATUSF_DONE)
                  printf("done\n");
            u = strhdr2.cbDstLengthUsed;            
            printf("Converted OK: %d\n", u);
            // Close the stream and driver.
            mmr = acmStreamClose(hstr, 0);
0
 
WxWCommented:
pwfDrv may not be a simple WAVEFORMATEX.
It could be a larget structure, depending on the codec. Some codes want additional bytes to be allocated.
When acmFormatEnum returns u the structure, you probably copy only the WAVEFORMATEX from it. Do you look the cbSize member to see if there are more bytes that should be appended to the structure ?


0
 
BuffonAuthor Commented:
I checked the pwfDrv.cbSize is 32. So what should I do? Thats what I do now callback function:

BOOL CALLBACK FormatEnumProc(HACMDRIVERID hadid, LPACMFORMATDETAILS pafd, DWORD dwInstance, DWORD fdwSupport)
{
    //printf("    %4.4lXH, %s\n", pafd->dwFormatTag, pafd->szFormat);
      char *tmp = (char*)dwInstance;
      if (/*strcmp(tmp, "ACELP.net") == 0 && */pafd->pwfx->wFormatTag == WAVE_FORMAT_DSPGROUP_TRUESPEECH)
      {
            printf("found format\n");
            pwfDrv = *(pafd->pwfx);
            had_id = hadid;
            pause = false;
      }
      printf("    %s, %s\n", getCodecDriverName(pafd->dwFormatTag), pafd->szFormat);
      
    return TRUE; // Continue enumerating.
}
0
 
WxWCommented:
in the ACMFORMATDETAILS buffer, there is a WAVEFORMATEX* pointer. This doesn't always point in a native WAVEFORMATEX structure, it points to a structure that BEGINS wth WAVEFORMATEX data but the additional data follows immediately. You see that from the cbwfx member, which is usually bigger than sizeof(WAVEFORMATEX).

Instead of
WAVEFORMATEX* pwfDrv;

you should use
WAVEFORMATEX_EX* pwfDrv;

and define WAVEFORMATEX_EX  as
 typedef struct WAVEFORMATEX_EX
 {
WAVEFORMATEX wfx;
char data[1000];
 }

This way , you will have access to the WAVEFORMATEX members (through wfx), AND have the additional data required. Then, pass that structure into acmStreamOpen, using a cast from WAVEFORMATEX_EX to WAVEFORMATEX.

Also, in the way you have the enumeration, pwfDrv points to padf->pwfx which gets invalid after the enumeration finishes. You should copy the data, not keep a pointer to padf->pwfx:

WAVEFORMATEX_EX wZ;
memcpy(&wZ,padf->pwfx,padf->cbwfx);
Then use wZ to acmStreamOpen.


0
 
BuffonAuthor Commented:
           MMRESULT mmr = acmStreamOpen(&hstr,
                  NULL, // Driver handle
                  &waveFmt, // Source format
                  (WAVEFORMATEX)pwfDrv, // Destination format
                  NULL, // No filter
                  NULL, // No callback
                  0, // Instance data (not used)
                  ACM_STREAMOPENF_NONREALTIME); // Flags

I get:
'type cast' : cannot convert from 'struct WAVEFORMATEX_EXT *' to 'struct tWAVEFORMATEX'
        No constructor could take the source type, or constructor overload resolution was ambiguous
0
 
WxWCommented:
(WAVEFORMATEX*) , not (WAVEFORMATEX)
0
 
BuffonAuthor Commented:
Its finally worked!!! I just needed to replace:
memcpy(&pwfDrv,pafd->pwfx,pafd->cbwfx);
with:
memcpy(&(pwfDrv->wfx),pafd->pwfx,pafd->cbwfx);
because the first one didnt copy well the data.
But when I tried to do the same with ACELP.net, it didnt work?
0
 
WxWCommented:
Because many codecs do not support acmStreamOpen, they only support direct opening with waveInOpen with the WAVEFORMATEX parameter set to use the ACELP codec.
I suggest you use my V.CPP sample in http://www.experts-exchange.com/Programming/Programming_Languages/Cplusplus/Q_20679904.html


Also, there are many codecs that do not work at all with ACM, for unknown reasons, as I said , like MS G 723.
0
 
BuffonAuthor Commented:
Thanks a lot. I'll check your code later. Do you know where I can find more documentation about codecs?
0
 
WxWCommented:
Unfortunately, codecs and the whole ACM is not that well documented :)
What do you need to do beside recording tho ?
0
 
BuffonAuthor Commented:
I am working on my final project in VoIP. Can you suggest me in what formats should I record and compress the voice. And are there some quality loss in this kind of compression and decompression?
0
 
WxWCommented:
Yes, always a codec has quality loss. However, there are some codecs which are specific for Voice compression, and not for any sound type conversion. For example, in my project Not Only Two (http://www.turboirc.com/not) , I use the Lernout && Hauspie Codec which provides 600 bytes/second voice sample, good to be sent over internet and good for voice only.

Since you use VoIP, you will find NotOnlyTwo a useful project.

0
 
BuffonAuthor Commented:
Where can I get this Lernout && Hauspie Codec? Or do you implement it by yourself?
0
 
WxWCommented:
Codec packs have it, you can take LHACM.ACM from my Not Only Two setup file and install it.

installation involves running a file named codec.inf with those inside.

[Version]
Signature="$CHICAGO$"
Provider=%Provider%

[DefaultInstall]
CopyFiles  = Copy.Sys
UpdateInis = Links.Add
AddReg     = Reg.Add

; File copying sections (where they go to)
[DestinationDirs]
Copy.Sys=11

; What files are copied where
[Copy.Sys]
lhacm.acm

; Where the files come from
[SourceDisksNames]
1="Audio codec","",1

[SourceDisksFiles]
lhacm.acm

; Links / Shortcuts - Installation
[Links.Add]
system.ini,drivers32,,"MSACM.LHACM=LHACM.ACM"
0
 
BuffonAuthor Commented:
Hi. I installed the codec, but when I get it's formats I see this:

 id: 00146a70H  supports:
   different format conversions
   Short name: L&H Codecs
   Long name:  Lernout & Hauspie CODECs
   Copyright:  Copyright (c) 1996 Lernout & Hauspie Speech Software Codecs NV and Microsoft Co
   Licensing:
   Features:   Lernout & Hauspie (TM) Speech Products Voice Compression. Compresses and decompresses Lernout & Hauspie audio data.
   Supports 5 formats
   Supports 0 filter formats
    Unknown format (id: 0x70), 8.000 kHz, 16 Bit, Mono
    Unknown format (id: 0x71), 8.000 kHz, 16 Bit, Mono
    Unknown format (id: 0x72), 8.000 kHz, 16 Bit, Mono
    Unknown format (id: 0x73), 8.000 kHz, 16 Bit, Mono
    WAVE_FORMAT_PCM, 8.000 kHz, 16 Bit, Mono

But you talked about 600 bytes/sec compression?
0
 
BuffonAuthor Commented:
And I even didnt succeed to convert to this format.
0
 
WxWCommented:
If you use acmFormatChoose, do you see the codec inside the list,
the CELP method of the codec uses 600b/s
0
 
BuffonAuthor Commented:
Can I run acmFormatChoose in comsole application project? If yes, what is wrong here:

      ACMFORMATCHOOSE* cfm = new ACMFORMATCHOOSE;
      WAVEFORMATEX* wfmx = new WAVEFORMATEX;
      cfm->cbStruct = sizeof(ACMFORMATCHOOSE) + sizeof(WAVEFORMATEX);
      cfm->pwfx = wfmx;
      cfm->cbwfx = sizeof(WAVEFORMATEX);
      cfm->fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
      cfm->hwndOwner = NULL;
      cfm->pszTitle = "test";
      mmr = acmFormatChoose(cfm);
      if (mmr)
      {
            switch(mmr) {
            case ACMERR_CANCELED:
                  printf("acmFormatChoose - The user chose the Cancel button or the Close command on the System menu to close the dialog box.\n");
                  break;
            case ACMERR_NOTPOSSIBLE:
                  printf("acmFormatChoose - The buffer identified by the pwfx member of the ACMFORMATCHOOSE structure is too small to contain the selected format.\n");
                  break;
            case MMSYSERR_INVALFLAG:
                  printf("acmFormatChoose - At least one flag is invalid.\n");
                  break;
            case MMSYSERR_INVALHANDLE:
                  printf("acmFormatChoose - The specified handle is invalid.\n");
                  break;
            case MMSYSERR_INVALPARAM:
                  printf("acmFormatChoose - At least one parameter is invalid.\n");
                  break;
            case MMSYSERR_NODRIVER:
                  printf("acmFormatChoose - A suitable driver is not available to provide valid format selections.\n");
                  break;
            default:
                  printf("acmFormatChoose error.\n");                        
            }
            exit(0);
      }
0
 
WxWCommented:
cfm->fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;

You specify to init to the struct you provide, but the structure is empty.
0
 
BuffonAuthor Commented:
I tried to put there valid struct, but still I get "access violation" error.
0
 
WxWCommented:
Remove that style.
Also, I told u that WAVEFORMATEX is not enough for many codecs, you should use WAVEFORMATEX_EX as you used before.
0
 
BuffonAuthor Commented:
I did as you said:

      ACMFORMATCHOOSE* cfm = new ACMFORMATCHOOSE;
      WAVEFORMATEX_EX* wfmx = new WAVEFORMATEX_EX;
      cfm->cbStruct = sizeof(ACMFORMATCHOOSE) + sizeof(WAVEFORMATEX_EX);
      cfm->pwfx = &waveFmt;
      cfm->cbwfx = sizeof(WAVEFORMATEX_EX);
      //cfm->fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
      cfm->fdwStyle = 0;
      cfm->hwndOwner = NULL;
      cfm->pszTitle = "test";
      mmr = acmFormatChoose(cfm);

but still it throws "access violation" error at acmFormatChoose(cfm).
0
 
WxWCommented:
MSDN:

>>
cbStruct

Size, in bytes, of the ACMFORMATCHOOSE structure. This member must be initialized before an application calls the acmFormatChoose function. The size specified in this member must be large enough to contain the base ACMFORMATCHOOSE structure.
>>

so you need only sizeof(ACMFORMATCHOOSE);

Also
pwfx = &waveFmt;
what is waveFmt? You need to pass wfmx with a cast.





0
 
BuffonAuthor Commented:
     ACMFORMATCHOOSE* cfm = new ACMFORMATCHOOSE;
      WAVEFORMATEX_EX* wfmx = new WAVEFORMATEX_EX;
      cfm->cbStruct = sizeof(ACMFORMATCHOOSE);
      cfm->pwfx = (WAVEFORMATEX*)wfmx;
      cfm->cbwfx = sizeof(WAVEFORMATEX_EX);
      //cfm->fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;
      cfm->fdwStyle = 0;
      cfm->hwndOwner = NULL;
      cfm->pszTitle = "test";
      mmr = acmFormatChoose(cfm);

the same effect.
0
 
WxWCommented:
Try this

ACMFORMATCHOOSE a = {sizeof(a),0,0,(WAVEFORMATEX*)wfmx,sizeof(WAVEFORMATEX_EX),"Source Format",{0},{0},0,0};
acmFormatChoose(&a);

0
 
BuffonAuthor Commented:
Hi. I did as you said and it succeeded, but the conversion didnt:


      WAVEFORMATEX_EX* wfmx = new WAVEFORMATEX_EX;
      ACMFORMATCHOOSE a = {sizeof(a),0,0,(WAVEFORMATEX*)wfmx,sizeof(WAVEFORMATEX_EX),"Source Format",{0},{0},0,0};
      mmr = acmFormatChoose(&a);      
      if (mmr)
      {
            switch(mmr) {
            case ACMERR_CANCELED:
                  printf("acmFormatChoose - The user chose the Cancel button or the Close command on the System menu to close the dialog box.\n");
                  break;
            case ACMERR_NOTPOSSIBLE:
                  printf("acmFormatChoose - The buffer identified by the pwfx member of the ACMFORMATCHOOSE structure is too small to contain the selected format.\n");
                  break;
            case MMSYSERR_INVALFLAG:
                  printf("acmFormatChoose - At least one flag is invalid.\n");
                  break;
            case MMSYSERR_INVALHANDLE:
                  printf("acmFormatChoose - The specified handle is invalid.\n");
                  break;
            case MMSYSERR_INVALPARAM:
                  printf("acmFormatChoose - At least one parameter is invalid.\n");
                  break;
            case MMSYSERR_NODRIVER:
                  printf("acmFormatChoose - A suitable driver is not available to provide valid format selections.\n");
                  break;
            default:
                  printf("acmFormatChoose error.\n");                        
            }
            exit(0);
      }
      cin>>tmp;
      
      printf("got format %s\n", a.szFormatTag);
      memcpy(&(pwfDrv->wfx), a.pwfx, a.cbwfx);

      waveFmt.wFormatTag      = WAVE_FORMAT_PCM;
      waveFmt.nChannels       = 1;
      waveFmt.nSamplesPerSec  = 8000;
      waveFmt.wBitsPerSample  = 16;
      waveFmt.nAvgBytesPerSec = waveFmt.nSamplesPerSec * waveFmt.nChannels; // SamplesPerSecond * Channels            
      waveFmt.nBlockAlign     = waveFmt.nChannels * waveFmt.wBitsPerSample / 8; // (Channels * BitsPerSample) / 8      
      waveFmt.cbSize          = 0;

            HACMSTREAM hstr = NULL;            
            MMRESULT mmr = acmStreamOpen(&hstr,
                  NULL, // Driver handle
                  &waveFmt, // Source format
                  (WAVEFORMATEX*)pwfDrv, // Destination format
                  NULL, // No filter
                  NULL, // No callback
                  0, // Instance data (not used)
                  ACM_STREAMOPENF_NONREALTIME); // Flags
            if (mmr) {
                  switch(mmr) {
                  case ACMERR_NOTPOSSIBLE:
                        printf("acmStreamOpen - The requested operation cannot be performed.\n");
                        break;
                  case MMSYSERR_INVALFLAG:
                        printf("acmStreamOpen - At least one flag is invalid.\n");
                        break;
                  case MMSYSERR_INVALHANDLE:
                        printf("acmStreamOpen - The specified handle is invalid.\n");
                        break;
                  case MMSYSERR_INVALPARAM:
                        printf("acmStreamOpen - At least one parameter is invalid.\n");
                        break;
                  case MMSYSERR_NOMEM:
                        printf("acmStreamOpen - The system is unable to allocate resources.\n");
                        break;
                  default:
                        printf("Failed to open a stream to do PCM to driver format conversion\n");                        
                  }
                  exit(1);
            }


output:
got format Lernout & Hauspie CELP 4.8kbit/s
acmStreamOpen - The requested operation cannot be performed.
0
 
WxWCommented:
As I told you, some autio codecs do not function through acmStream* . You must open the audio codec directly with waveInOpen, and record, and the output buffer is compressed directly.
0
 
BuffonAuthor Commented:
I did as you said, it actually working. But the quality isnt the best :(
Do you know some codecs that give 8Kbits/sec and better quality, and work with waveInOpen()?
0
 
WxWCommented:
You can try GSM, ACELP, MP3 etc.
'Good quality' is sometimes different to indicate.
Experiment with various formats and you will find the codec that matches you. I use Lernout because it is the smallest 600b/sec and i need such a small conversion for the project.
0
 
BuffonAuthor Commented:
Thank you very much WxW, you helped me a lot. Good luck to you in your project.
0
 
WxWCommented:
You too.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Cloud Class® Course: MCSA MCSE Windows Server 2012

This course teaches how to install and configure Windows Server 2012 R2.  It is the first step on your path to becoming a Microsoft Certified Solutions Expert (MCSE).

  • 18
  • 18
Tackle projects and never again get stuck behind a technical roadblock.
Join Now