Solved

acmStreamOpen()

Posted on 2004-04-17
37
2,215 Views
Last Modified: 2010-05-18
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
Comment
Question by:Buffon
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 18
  • 18
37 Comments
 
LVL 22

Expert Comment

by:grg99
ID: 10848940
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
 
LVL 6

Expert Comment

by:WxW
ID: 10852385
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
 
LVL 11

Author Comment

by:Buffon
ID: 10891727
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
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 6

Expert Comment

by:WxW
ID: 10891841
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
 
LVL 11

Author Comment

by:Buffon
ID: 10893866
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
 
LVL 6

Expert Comment

by:WxW
ID: 10896239
Paste the code you are using to compress the stream.
0
 
LVL 11

Author Comment

by:Buffon
ID: 10907159
     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
 
LVL 6

Expert Comment

by:WxW
ID: 10907194
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
 
LVL 11

Author Comment

by:Buffon
ID: 10907226
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
 
LVL 6

Accepted Solution

by:
WxW earned 100 total points
ID: 10907309
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
 
LVL 11

Author Comment

by:Buffon
ID: 10907364
           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
 
LVL 6

Expert Comment

by:WxW
ID: 10907491
(WAVEFORMATEX*) , not (WAVEFORMATEX)
0
 
LVL 11

Author Comment

by:Buffon
ID: 10907597
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
 
LVL 6

Expert Comment

by:WxW
ID: 10907804
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
 
LVL 11

Author Comment

by:Buffon
ID: 10907849
Thanks a lot. I'll check your code later. Do you know where I can find more documentation about codecs?
0
 
LVL 6

Expert Comment

by:WxW
ID: 10907852
Unfortunately, codecs and the whole ACM is not that well documented :)
What do you need to do beside recording tho ?
0
 
LVL 11

Author Comment

by:Buffon
ID: 10907865
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
 
LVL 6

Expert Comment

by:WxW
ID: 10907892
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
 
LVL 11

Author Comment

by:Buffon
ID: 10907944
Where can I get this Lernout && Hauspie Codec? Or do you implement it by yourself?
0
 
LVL 6

Expert Comment

by:WxW
ID: 10907964
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
 
LVL 11

Author Comment

by:Buffon
ID: 10908187
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
 
LVL 11

Author Comment

by:Buffon
ID: 10908198
And I even didnt succeed to convert to this format.
0
 
LVL 6

Expert Comment

by:WxW
ID: 10908293
If you use acmFormatChoose, do you see the codec inside the list,
the CELP method of the codec uses 600b/s
0
 
LVL 11

Author Comment

by:Buffon
ID: 10908986
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
 
LVL 6

Expert Comment

by:WxW
ID: 10909014
cfm->fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT;

You specify to init to the struct you provide, but the structure is empty.
0
 
LVL 11

Author Comment

by:Buffon
ID: 10909940
I tried to put there valid struct, but still I get "access violation" error.
0
 
LVL 6

Expert Comment

by:WxW
ID: 10911056
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
 
LVL 11

Author Comment

by:Buffon
ID: 10929960
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
 
LVL 6

Expert Comment

by:WxW
ID: 10930483
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
 
LVL 11

Author Comment

by:Buffon
ID: 10930539
     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
 
LVL 6

Expert Comment

by:WxW
ID: 10930624
Try this

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

0
 
LVL 11

Author Comment

by:Buffon
ID: 10967628
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
 
LVL 6

Expert Comment

by:WxW
ID: 10967962
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
 
LVL 11

Author Comment

by:Buffon
ID: 10968293
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
 
LVL 6

Expert Comment

by:WxW
ID: 10968345
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
 
LVL 11

Author Comment

by:Buffon
ID: 10968357
Thank you very much WxW, you helped me a lot. Good luck to you in your project.
0
 
LVL 6

Expert Comment

by:WxW
ID: 10968907
You too.
0

Featured Post

[Live Webinar] The Cloud Skills Gap

As Cloud technologies come of age, business leaders grapple with the impact it has on their team's skills and the gap associated with the use of a cloud platform.

Join experts from 451 Research and Concerto Cloud Services on July 27th where we will examine fact and fiction.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Errors will happen. It is a fact of life for the programmer. How and when errors are detected have a great impact on quality and cost of a product. It is better to detect errors at compile time, when possible and practical. Errors that make their wa…
IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn how to user default arguments when defining functions. This method of defining functions will be contrasted with the non-default-argument of defining functions.
Suggested Courses

617 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question