ALSA using pcm_s24le - alsa

Using Linux Mint 17.1 and ALSA. I have two wav files producing identical sound: one using pcm_s16le and the other using pcm_s24le. Each is played correctly by Totem/videos. My code to set hardware parameters and to playback using pcm_s16le works fine. However, when I attempt to adjust these parameters to accommodate pcm_s24le as follows:
snd_pcm_hw_params_set_format(audioHandle, audioParams, SND_PCM_FORMAT_S24_LE);
[I have simply substituted 'SND_PCM_FORMAT_S24LE' for 'SND_PCM_FORMAT_S16_LE']. The call to snd_pcm_writei is
snd_pcm_writei(m_audioHandle, *m_pAudioFrameData, *m_pAudioFrameSize / (m_nChannels * m_bitsPerSample / 8);
I get mostly garbage sound (hissing, choppiness) with a hint of the correct sound.
Essentially my question is, how do I convert code that works for SND_PCM_FORMAT_S16_LE to work for SND_PCM_FORMAT_S24_LE?

There are three possible ways of storing 24-bit samples in memory:
LSB MSB
1st byte 2nd byte 3rd byte 4th byte alignment
S32_LE: 00000000 xxxxxxxx xxxxxxxx xxxxxxxx 32 bits
S24_LE: xxxxxxxx xxxxxxxx xxxxxxxx 00000000 32 bits
S24_3LE: xxxxxxxx xxxxxxxx xxxxxxxx 24 bits
Most hardware uses S32_LE, except for USB, which uses S24_3LE. There is no hardware that uses S24_LE.
ALSA can automatically convert the sample format, but you have to describe your own sample format correctly.

Related

How to send scan code >255 from hid ble keyboard from esp32 over gatt?

I'm using esp-32 esp-idf HID library (https://github.com/espressif/esp-idf/tree/master/examples/bluetooth/ble_hid_device_demo) to make a custom keyboard that sends scan codes to an android device. I need to send scan code = 310, that contains two bytes of data.
I have a device that requires ble hid keyboard's button's scan code = 310[dec]. As I tried to send this code as uint8_t key_vaule[], as it's used in ble_hid_demo_main.c in ble_hid_device_demo project, the device recieved another scan code, it was truncated from 000 0001 0011 0110 [310dec] to 0011 0110 [155dec]. I suppose it happens because of the 8 bit size of the transferred variables instead of 16 bit. Modyfying the libraries from uint8_t size to uint16_t gave nothing, the result was still truncated. Is there a way to send a two-byte code instead of 1 byte?
HID scan codes are always 8 bit. Key combinations such as left-CTRL+<, in this case, are a sequence of a "key modifier" (0x01 for left-CTRL) and a key code (0x36 for <,).
Whist 0x0136 happens to be 31010, it is a mistake to think of multi-byte scan codes sequences as a single integer rather that a byte sequence for a number of reasons:
the integer byte for the machine architecture may not match that defined for HID code sequences,
In an HID keyboard report, it there is a single key modifier byte and up to six key codes - for combinations of up-to 6 regular keys and eight modifier bits for shift, alt, ctrl etc. combinations pressed simultaneously,
In an HID keyboard report there is a "reserved" byte between the modifier and the first key code in any case, so the 0x01 and 0x36 are not contiguous in any case regardless of the machine byte order.
In the case of HID scan codes, your 31010 is in fact two bytes 0x01 and 0x36 (in hexadecimal). When talking about byte sequences it is more natural to use hexadecimal notation - especially in teh case ofthe modifier which is a bit-mask for multiple shift/ctrl etc. keys. The 0x36 represents the key <,, and the 0x01 is a key modifier for left-CTRL.
If your value 310 was truncated when you assigned it to a 16 bit integer most likely you passed it as a single value to an interface that expected a uint8_t. But as explained above, sending a 16bit integer is not correct in any case.
Rather then sending 0x0136 or 31010 you need to send a byte sequence to form a valid keyboard report as described by your device's keyboard report descriptor. In an HID keyboard report, the first byte is the "modifier mask" (0x01/left-CTRL), the second byte is reserved, then there are up-to 6 key codes (allowing multi-key combinations) the actual number of keys supported and therefore length of the report is defined by the report descriptor.
Looking at the API in the HID demo you linked however it is clear that all that is abstracted away and it seems that what you actually need to do is something like:
uint8_t key = HID_KEY_COMMA ;
esp_hidd_send_keyboard_value( hid_conn_id, LEFT_CONTROL_KEY_MASK, &key, 1 ) ;
Note that the modifier is a bit-mask allowing any combination of modifier keys, such as LEFT_CONTROL_KEY_MASK|RIGHT_CONTROL_KEY_MASK. The HID would use this to indicate multiple shifts, but a receiver might use it to allow either the left or right keys without distinction.

MS-XCA decompression metadata points outside of the compressed byte array

I need to decompress a data model file embedded in xlsx file. The file is supposed to use the MS-XLDM file format and should consist of 3 sections (Spreadsheet Data Model Header, Files and Virtual Directory) and only the middle one is compressed. The first and last section are xml with unicode/utf-16 encoding presumably (every other byte is 0x00 and the content is preceded by 0xFF and 0xFE ). The middle file is preceded by a small chunk of xml. More detail about the file structure.
Now according to the documentation the file should be compressed using Xpress compression specified here which uses LZ77 compression and DIRECT2 encoding.
Now to get to the point. From my understanding, there should always be a 4 byte bitmask which indicates if byte in corresponding position should be a 1:1 data or metadata.
For example, given a hypothetical 8-bit bitmask, the string "ABCABCDEF" is compressed as (0,0)A(0,0)B(0,0)C(3,3)D(0,0)E(0,0)F. Its bitmask would be b'00010001' (0x11).
If given position is supposed to be metadata, at least 2 bytes should be read. Out of the 16 bits the first 13 is offset and the last 3 are the length (unless the last bit is 1, then another byte must be read).
So now onto the concrete example that I struggle with. The first 2 chunks are easy.
First one is:
....<Load xmlns="http://schemas.micr
The first 4 bytes (the dots) are 0x00 thus the 32 bytes that follow are uncompressed. Next chunk is similar:
....osoft.com/analysisservices/2003/
Now the 3rd chunk is where I get lost
w±engine":ddl27/"/2G_W?%g100gO8eðg_‡_)§è.Õ®]›‡o
I'm not sure where does the chunk exactly end because when I started counting every 36 bytes after those first ones after a while I would reach a portion of the byte stream which should be uncompressed and it didn't line up.
So back to the 3rd chunk. The bitmask for this one is 0x77 0xB1 0x04 0x01.
Or in binary 01110111 10110001 00000100 00000001. I tried to line it up with the bytes and it didn't make any sense. Clearly the word engine" is uncompressed and it fits to the previous chunks because a quick google search revealed to me a result with namespace "http://schemas.microsoft.com/analysisservices/2003/engine".
01110111 10110001 00000100 00000001
engine" :ddl27 /"/2G_W ?%g100gO8eðg_‡_)
This made me think that maybe the bytes if the bitmask are in reverse order. This made more sense to me.
00000001
engine"
If this was true, then the metadata should be 0x0B 0x02.
Or in binary 00001011 00000010. So if I split it up, the first 13 bits make up the offset of the metadata. And the length is 010 + constant offset 3 = 2+3=5.
Before 0000101100000
Invert 1111010011111
Decimal -353
But looking 353 bytes back it lands in the uncompressed partition xml section and should return the characters in parentheses (a.m.e). This doesn't make sense to me and is probably wrong.
Here is the file I tried to decompress.

WAV format audio control

I have tried to change the volume of a PCM wave file in C: 2 channel,
Sample Rate: 22050 Hz. 24 bits per sample.
But when I do, I get lots of distortion, I tried a way that I have found among the answers to Reduce the volume of a Wav audio file using C.
I also did a simple divide by 2 of the 24 bit data, but all the things I tried resulted in a very distorted signal (though volume goes down).
When I reduce volume by -6dB in audacity and stream the new wav to my I2S amplifier, then it plays the wav in perfect quality and at a low volume. So audacity converts the samples perfectly.. But how do they do this?
I must be doing something wrong in my software but I can't figure out what.
Following code fills a DMA buffer with 100% volume data:
buffer[i] = ((uint32_t)b2 << 16) | ((uint32_t)b1 << 8) | ((uint32_t)b0);
With this code I would expect volume will be less, but it only gets very distorted:
int32_t temp;
temp = (int32_t)((uint32_t)b2 << 16) | ((uint32_t)b1 << 8) | ((uint32_t)b0);
buffer[i] = (uint32_t)(temp /2);
Any idea why the distortion happens?
I also have tried to figure out how the data is stored, therefore I have exported the first 2 samples out of audacity, and 10 samples printed by my firmware (firmware bytes are verified as correctly with viewing the wav file with a hex viewer.
WAV data:
000000h,FFFEFEh,FFFF01h,000002h,FFFEFEh,FFFEFEh,000002h,000002h,FFFFFDh,FFFFFFh,FFFF04h,000000h,FFFFFDh,000000h,000002h,FFFFFFh,FFFF02h,FFFEFFh,FFFFFEh,000003h,000002h,FFFFFDh,FFFEFEh,000002h,FFFF01h,FFFFFFh,FFFFFFh,000000h,000002h,000002h,FFFFFDh,000002h,000003h,FFFFFEh,FFFFFDh,FFFF01h,000003h,000001h,FFFEFDh,FFFFFEh,FFFF03h,000002h,FFFFFDh,FFFFFFh,FFFF03h,FFFFFEh
I think it's strange to see such different values like 000000h,FFFEFEh while the first sample shows 0 in audacity sample graph.
This is a sample export in audacity for the first 2 samples:
0.00000 0.00000
-0.00002 -0.00002
What did I get wrong?
In order to listen to the new content of "buffer" it must be either converted back from 32 bit to 24 bit or the file format must be changed to 32bit wav. this step is not described in your question.
The conversion to 32 bit should be shifted 8 bits left in order to get the sign bit in right position and use the full dynamic of 32 bit. if your host is little endian (as wave) the endianness should not be a problem.
Test the conversions in both direction before trying to alter the gain!
More fine tuned gain can be obtained by using float instead of 32 bit integer.

Writing number as raw binary to file

So I'm trying to write the value of a long as raw binary to a file with fwrite(), here's a test file:
#include<stdio.h>
int main() {
FILE* stream;
stream = fopen("write", "wb");
long number = 0xfff;
fwrite(&number, sizeof(long), 1, stream);
}
When opening the written file in a text editor, this is the output:
ff0f 0000 0000 0000
while I was expecting something like 0000 0000 0000 0fff.
How do I write the desired result correctly as raw binary?
Read about Endianness, which states how a bytes are arranged in a word (or double/quad word, et cetera) in a computer system.
I'm assuming you've coded and compiled this example on a X86 system, which is little-endian, so, the least significant bits COME FIRST. The opposite of that arrangement is called big-endian.
Now, it is clear that your objective in this exercise is to marshall (or pickle, depending on how your prefer your jargon) some bytes to be later retrieved, possibly by another program.
If you develop a program that uses fread() and and reads the data in the same way (using sizeof(long) so you don't read too much data) and in a machine with the same endianness, it will magically work, and the number you expect is gonna be back. But, if you compile and run the "read" tool in a machine with the opposite endianness, reading the same input file, your number will be garbled.
If your objective is to marshall data, you should be better off with a tool to help you marshall your bytes in a way that is endianness-agnostic, that is, a library that helps you get the data in the correct order. There are libraries out there that take care of that for you.
There's no problem. You're seeing it as ff0f 0000 0000 0000 cause of the endian of the machine!
Try using fread() instead!
as other have pointed out in the comments, this is an endianness "issue". That it, it is only an issue if you are going to run your software on systems with an other endianness.
This very useful resource is the first result for the "c endian" google search, at least for me. I hope it helps you.
Edit: I will dig a bit more into the details below.
To determine what is the endianness of the machine you are currently running on, write a known value (for example, 0xAABBCCDD) into memory, then take a pointer to that value and cast it to a char* or other 1-byte data type. Read the same value again, byte by byte, and compare the ordering with what you wrote. If they are the same, you are on a big endian machine. If not... Then you could be on a little (more probable) or middle (less probable) endian machine. You can in either case generate a "swap map" for reordering the bytes; which is the reason why I chose four different byte values in the above example.
But, how to swap those values? An easy way, as indicated here, is to use bit shifts with masks. You can also use tables and swap their values around.

How is a character text in a file with ascii encoding stored on disk as file?

Let us say I have a 32-bit machine running a 32-bit OS with an application program like Notepad (assume). Assume I create a .txt file with that program which contains just a single character 'A' in it and save the file with ANSI coding (or ASCII) on disk. With 32 bits making up a single addressable memory block called a word, how would the 4 bytes in the word be used to store 'A' (i.e., number 65 in ASCII)? Now, 65 translates to 0100 0001 in binary.
ASCII means, that you are just using one byte per character. many encodings just use one byte per character, but there are some like utf16, which use constantly two bytes for each character.
the 32 bits get just relevant, if your are processing these characters in your CPU in a register, and you load them as an integer. then the single byte is converted to a 32 - bit integer and processed by the cpu, when you save it its again one byte long
how one byte is converted into a 32 bit integer, thats described for example here: http://en.wikipedia.org/wiki/Endianness

Resources