Solved

acmStreamOpen()

Posted on 2004-04-17
37
2,157 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
  • 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
 
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
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
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

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Written by John Humphreys C++ Threading and the POSIX Library This article will cover the basic information that you need to know in order to make use of the POSIX threading library available for C and C++ on UNIX and most Linux systems.   [s…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

705 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

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now