Display "Hello World" on framebuffer in linux - linux

I have used the linux 3.14 version on my ARM target and i want to show some line of characters in the display using frame buffer. I can change the colors of the display using the below code.
#include <stdio.h>
unsigned char colours[8][4] = {
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
};
int frames[] = {0,5,10,15,20,25,30};
int columns = 800;
int lines = 480;
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
int frame(int c, int l){
int i;
for(i=0; i < ARRAY_SIZE(frames); i++){
if((c==frames[i])&&((l>=frames[i])&&l<=(lines-frames[i]))){
return 1;
}
if((c==columns-frames[i])&&((l>=frames[i])&&l<=(lines-frames[i]))){
return 1;
}
if((l==frames[i])&&((c>=frames[i])&&c<=(columns-frames[i]))){
return 1;
}
if((l==lines-frames[i])&&((c>=frames[i])&&c<=(columns-frames[i]))){
return 1;
}
}
return 0;
}
int main(int argc, char **argv)
{
unsigned char pixel[3];
int l, c;
char *filename = argv[1];
printf ("Device : %s\n",filename);
FILE *f = fopen(filename,"wb");
if(f){
printf("Device open success \n");
for(l=0; l<lines; l++){
for(c=0; c < columns; c++){
if(frame(c,l)){
fwrite(colours[3], 1, sizeof(colours[3]), f);
}else{
int colour = c/(columns/ARRAY_SIZE(colours));
fwrite(colours[colour], 1, sizeof(colours[colour]), f);
}
}
}
fclose(f);
}
else
printf("Device open failed \n");
return 0;
}
In the same way i want to show some lines of character to the display. Example, I want to show characters "Hello world !!!" in the display using frame buffer.
Could any one help me to work it out.

You can find an elegant piece of code to do this in tslib. tslib is a c library for filtering touchscreen events. Actually, you don't need tslib for your purpose (yes, you don't have to build it). In their tests you can find a utility to access the framebuffer.
They have provided the fbutils.h whose implementation you can find in fbutils-linux.c. This code is very simple in that it directly manipulates the linux framebuffer and does not have any dependencies. Currently it's not even 500 lines, and if you only want to display text, you can remove other irrelevant functionality. It supports two fonts - font_8x8 and font_8x16 - whose definitions you can find in the respective .c files.
I won't go into code details as it is easy to understand. Will just list the current API and provide a simpler code for open and close functionality.
int open_framebuffer(void);
void close_framebuffer(void);
void setcolor(unsigned colidx, unsigned value);
void put_cross(int x, int y, unsigned colidx);
void put_string(int x, int y, char *s, unsigned colidx);
void put_string_center(int x, int y, char *s, unsigned colidx);
void pixel(int x, int y, unsigned colidx);
void line(int x1, int y1, int x2, int y2, unsigned colidx);
void rect(int x1, int y1, int x2, int y2, unsigned colidx);
void fillrect(int x1, int y1, int x2, int y2, unsigned colidx);
To manipulate the linux framebuffer, first you should memory map it into your process address space. After memory mapping you can access it just like an array. Using some ioctl you can get information about the framebuffer such as resolution, bytes-per-pixel etc. See here for details.
In the code below, you can pass the name of the fb device to open it, such as /dev/fb0. You can use the rest of the functions in the original code for drawing.
int open_framebuffer(const char *fbdevice)
{
uint32_t y, addr;
fb_fd = open(fbdevice, O_RDWR);
if (fb_fd == -1) {
perror("open fbdevice");
return -1;
}
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fix) < 0) {
perror("ioctl FBIOGET_FSCREENINFO");
close(fb_fd);
return -1;
}
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &var) < 0) {
perror("ioctl FBIOGET_VSCREENINFO");
close(fb_fd);
return -1;
}
xres_orig = var.xres;
yres_orig = var.yres;
if (rotation & 1) {
/* 1 or 3 */
y = var.yres;
yres = var.xres;
xres = y;
} else {
/* 0 or 2 */
xres = var.xres;
yres = var.yres;
}
fbuffer = mmap(NULL,
fix.smem_len,
PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
fb_fd,
0);
if (fbuffer == (unsigned char *)-1) {
perror("mmap framebuffer");
close(fb_fd);
return -1;
}
memset(fbuffer, 0, fix.smem_len);
bytes_per_pixel = (var.bits_per_pixel + 7) / 8;
transp_mask = ((1 << var.transp.length) - 1) <<
var.transp.offset; /* transp.length unlikely > 32 */
line_addr = malloc(sizeof(*line_addr) * var.yres_virtual);
addr = 0;
for (y = 0; y < var.yres_virtual; y++, addr += fix.line_length)
line_addr[y] = fbuffer + addr;
return 0;
}
void close_framebuffer(void)
{
memset(fbuffer, 0, fix.smem_len);
munmap(fbuffer, fix.smem_len);
close(fb_fd);
free(line_addr);
xres = 0;
yres = 0;
rotation = 0;
}
You can find examples of its usage in test programs in the folder, such as ts_test.c.
You can extend this code to support other fonts, display images etc.
Good luck!

First, I strongly suggest to avoid use of fopen/fwrite function to access devices. These function handle internal buffers that can be troublesome. Prefers functions open and write.
Next, you can't continue with series of if .. then .. else .. to render a true graphic. You need to allocate a buffer that represent your framebuffer. Its size will, be columns * lines * 4 (you need 1 byte per primary color). To write a pixel, you have to use something like:
buf[l * columns + c * 4 + 0] = red_value;
buf[l * columns + c * 4 + 1] = green_value;
buf[l * columns + c * 4 + 2] = blue_value;
buf[l * columns + c * 4 + 3] = alpha_value;
Once you buffer is fully filled, write it with:
write(fd, buf, sizeof(buf));
(where fd is file descriptor return by fd = open("/dev/fbdev0", O_WRONLY);)
Check that you are now able to set arbitrary pixels on our framebuffer.
Finally, you need a database of rendered characters. You could create it yourself, but I suggest to use https://github.com/dhepper/font8x8.
Fonts are monochrome so each bit represent one pixel. On your framebuffer, you need 4bytes for one pixel. So you will have to do some conversion.
This is a really basic way to access framebuffer, there are plenty of improvements to do:
columns, lines and pixel representation should negotiated/retrieved using FBIO*ET_*SCREENINFO ioctl.
using write to access framebuffer is not the preferred method. It is slow and does not allow to updating framebuffer easily. The preferred method use mmap.
if you want to to animate framebuffer, you to use a double buffer: allocate a buffer twice larger than necessary, write alternatively first part or second part and update shown buffer with FBIOPAN_DISPLAY
font8x8 is not ideal. You may want to use any other font available on web. You need a library to decode font format (libfreetype) and a library to render a glyph (= a letter) in a particular size to a buffer (aka rasterize step) that you can copy to your screen (libpango)
you may want to accelerate buffer copy between your glyph database and your screen framebuffer (aka compose step), but it is a far longer story that involve true GPU drivers

Related

How to get physical and virtual address bits with C/C++ by CPUID command

I'm getting physical and virtual address bits size with C by using CPUID command in windows.
I can get the processor information this way, but I'm confused by getting the address bits.
Looks like I should you the 80000008 instruction but I do this way, only 7-8 digits change continuously are displayed.
I want to learn how this command works and solve this problem
#include <stdio.h>
void getcpuid(int T, int* val) {
int reg_ax;
int reg_bx;
int reg_cx;
int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
int val[5]; val[4] = 0;
getcpuid(0x80000002, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000003, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000004, val);
printf("%s\r\n", &val[0]);
return 0;
}
when operate this code with putting EAX = 80000002, 80000003, 80000004, Intel processor brand string was displayed.
And I put 80000008 To getting physical and virtual address bits but random numbers changing constantly was displayed.
I want to know how to use this cpuid commend with 80000008 to get those address bits
i'm programming and operating system beginner.
Please let me know what I have to do.
The inline assembly you're using may be right; but this depends on which compiler it is. I think it is right for Microsoft's MSVC (but I've never used it and can't be sure). For GCC (and CLANG) you'd have to inform the compiler that you're modifying the contents of registers and memory (via. a clobber list), and it would be more efficient to tell the compiler that you're outputting 4 values in 4 registers.
The main problem is that you're trying to treat the output as a (null terminated) string; and the data returned by CPUID is never a null terminated string (even for "get vendor string" and "get brand name string", it's a whitespace padded string with no zero terminator).
To fix that you could:
void getcpuid(int T, int* val) {
unsigned int reg_ax;
unsigned int reg_bx;
unsigned int reg_cx;
unsigned int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
uint32_t val[5]; val[4] = 0;
getcpuid(0x80000002U, val);
printf("0x%08X\r\n", val[0]);
getcpuid(0x80000003U, val);
printf("0x%08X\r\n", val[1]);
getcpuid(0x80000004U, val);
printf("0x%08X\r\n", val[2]);
return 0;
}
The next problem is extracting the virtual address size and physical address size values. These are 8-bit values packed into the first and second byte of eax; so:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
That should work on most recent CPUs; which means that it's still awful and broken on older CPUs.
To start fixing that you want to check that the CPU supports "CPUID leaf 0x80000008" before you assume it exists:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
physicalAddressSize = -1;
virtualAddressSize = -1;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
You can return correct results when "CPUID leaf 0x80000008" doesn't exist. For all CPUs that don't support "CPUID leaf 0x80000008"; virtual address size is 32 bits, and the physical address size is either 36 bits (if PAE is supported) or 32 bits (if PAE is not supported). You can use CPUID to determine if the CPU supports PAE, so it ends up a bit like this:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
getcpuid(0x00000000U, val);
if(val[0] == 0) {
physicalAddressSize = 32; // "CPUID leaf 0x00000001" not supported
} else {
getcpuid(0x00000001U, val);
if( val[3] & (1 << 6) != 0) {
physicalAddressSize = 36; // PAE is supported
} else {
physicalAddressSize = 32; // PAE not supported
}
}
virtualAddressSize = 32;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
The other problem is that sometimes CPUID is buggy; which means that you have to trawl through every single errata sheet for every CPU (from Intel, AMD, VIA, etc) to be sure the results from CPUID are actually correct. For example, there are 3 models of "Intel Pentium 4 Processor on 90 nm Process" where "CPUID leaf 0x800000008" is wrong and says "physical addresses are 40 bits" when they are actually 36 bits.
For all of these cases you need to implement work-arounds (e.g. get the CPU vendor/family/model/stepping information from CPUID and if it matches one of the 3 buggy models of Pentium 4, do an "if(physicalAddressSize == 40) physicalAddressSize = 36;" to fix the CPU's bug).

UART bluetooth communication problem What is the proper format to send data to the UART (integer values)

I have created my functions to send and receive from the UART, and sending the data does not seem to be a problem. In the data visualizer we can see the values and even plot them.
However when sending these data through the bluetooth, we cannot get the values to plot them in any of many available apps.
I believe there is a problem with the way we are sending data through the UART and to the bluetooth and that is why we cannot then get the values to be plotted.
Being a starter at all this, I would like to someone please advice us if the code below is ok, if there is a mistake and if there is a better way to send the data through the UART so as to make the Bluetooth work properly. Target is to be able to plot (graph) the values on the phone.
Many thanks
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h>
#define BAUDRATE 9600
#define BAUD_PRESCALLER (((F_CPU / (BAUDRATE * 16UL))) - 1)
//----------VARIABLES
float V_n,V_nm1,V_measure=0;
volatile int Velo_pulse;
float Exp_fltr_Coeff=0.2;
unsigned int Counter_ADC=0b0001;
unsigned int Value1;
char String[]="";
//----------Functions Definition
//---timers
void Timer1_Control();
void AttachInterrupt();
//---AnalogueRead
void Set_Ports();
void AnalogRead_Setup();
unsigned int AnalogRead();
//---UART
void USART_init(void);
unsigned char USART_receive(void);
void USART_send( unsigned char data);
void USART_putstring(char* StringPtr, unsigned int Value1);
int main(void){
USART_init(); //Call the USART initialization code
Set_Ports();
AnalogRead_Setup();
AttachInterrupt();
Timer1_Control();
sei();
while(1){
_delay_ms(1);
}
return 0;
}
void USART_init(void){
UBRR0H = (unsigned char)(BAUD_PRESCALLER>>8); //UBRR0H = (uint8_t)(BAUD_PRESCALLER>>8);
UBRR0L = (unsigned char)(BAUD_PRESCALLER);
UCSR0B = (1<<RXEN0)|(1<<TXEN0); //Enable receiver / transmitter
UCSR0C = (1<<USBS0)|(3<<UCSZ00); //Set frame format: 8data, 2stop bit
}
unsigned char USART_receive(void){
while(!(UCSR0A & (1<<RXC0))); //Wait for data to be received (buffer RXCn in the UCSRnA register)
return UDR0;
}
void USART_send( unsigned char data){
while(!(UCSR0A & (1<<UDRE0))); //Waiting for empty transmit buffer (buffer UDREn in the UCSRnA register)
UDR0 = data; //Loading Data on the transmit buffer
}
void USART_putstring(char* String, unsigned int Value1){
sprintf(String,"%d\r\n",Value1);
while(*String != 0x00){
USART_send(*String);
String++;}
}
void Set_Ports()
{
DDRD = 0b11111111; //All port is output
DDRD ^= (1 << DDD5); // PD5 is now input
}
ISR(ADC_vect)
{
//ADMUX ^= Counter_ADC; //Swapping between ADC0 an ADC1
}
void AnalogRead_Setup()
{
ADCSRA |= (1 << ADPS2) | (0 << ADPS1) | (0 << ADPS0); // Set ADC prescaler to 16 - 1 MHz sample rate # 16MHz
ADMUX |= (1 << REFS0); // Set ADC reference to AVCC
ADMUX |= (1 << ADLAR); // Left adjust ADC result to allow easy 8 bit reading
ADCSRA |= (1 << ADATE); // Set ADC to Free-Running Mode
ADCSRA |= (1 << ADIE); // Interrupt in Conversion Complete
ADCSRA |= (1 << ADEN); // Enable ADC
}
unsigned int AnalogRead(unsigned int PortVal)
{
if (PortVal==5){
ADMUX |= (0 << MUX3) | (1 << MUX2) | (0 << MUX1) | (1 << MUX0); //sets the pin 0101 sets pin5
} else if (PortVal==4){
ADMUX |= (0 << MUX3) | (1 << MUX2) | (0 << MUX1) | (0 << MUX0); //sets the pin 0101 sets pin4
}
ADCSRA |= (1 << ADSC); // Start A2D Conversions
//while(ADCSRA & (1 << ADSC));
return ADCH;
}
//----------Timer Functions
ISR (TIMER1_COMPA_vect) // Timer1 ISR (compare A vector - Compare Interrupt Mode)
{
cli();
V_measure=(Velo_pulse*60/0.250);
//USART_putstring(String,Velo_pulse);
Velo_pulse=0;
V_n=Exp_fltr_Coeff*V_measure+(1-Exp_fltr_Coeff)*V_nm1;
V_nm1=V_n;
USART_putstring(String,(int)V_n);
sei();
}
ISR (INT0_vect)
{
Velo_pulse++;
//USART_putstring(String,Velo_pulse);
}
void Timer1_Control()
{
TCCR1A=0b00000000; //Clear the timer1 registers
TCCR1B=0b00000000;
TCNT1=0b00000000;
TCCR1B=0b00001101; //Sets prescaler (1024) & Compare mode
OCR1A=2604; // 160ms - 6 Hz
TIMSK1=0b00000010;
}
void AttachInterrupt()
{
DDRD ^= (1 << DDD2); // PD2 (PCINT0 pin) is now an input
PORTD |= (1 << PORTD2); // turn On the Pull-up // PD2 is now an input with pull-up enabled
EICRA = 0b00000011; // set INT0 to trigger on rising edge change
EIMSK = 0b00000001; // Turns on INT0
}
Look at string initialization:
char String[]="";
this allocates an array of chars with a size of 1 item (which is terminating zero).
Then you make a call, passing this array reference as the first parameter:
USART_putstring(String,(int)V_n);
And the USART_putstring is as follows:
void USART_putstring(char* String, unsigned int Value1){
sprintf(String,"%d\r\n",Value1);
while(*String != 0x00){
USART_send(*String);
String++;}
}
Note sprintf(String,"%d\r\n",Value1); it converts numeric value into the char buffer. I.e. the buffer should be large enough to contain the text representation of the number, line feeds \r\n\ and zero - the string terminator.
But since your string buffer has size for only 1 char, it totally depends on luck, what will happen after sprintf: maybe there is some unused memory area, so the whole thing will look as if it working. Maybe there are some other variables, and their value will be overwritten, which makes the program behavior unexpected in the future. Or maybe there is some essential data, and your app will be crashing. Behavior may change after you adding several lines and recompile the code.
The point is: be careful with your buffers. Instead of using constants for initialization, set the exact size for the buffer. The number length is maximum 6 symbols (1 possible sign and 5 digits, assuming you're using AVR-GCC, which has the int 16-bits wide, thus has -32768 as the minimum) + 2 for \r\n\ + 1 for terminating zero. I.e. size of the buffer should be 9 at least.
char String[9];

ALSA playback interrupted without snd_pcm_hw_params_get_* calls

I'm finding that a simple ALSA playback program behaves differently when I put in some calls to snd_pcm_hw_params_get_* functions. My program plays a sine wave from a buffer repeatedly. When I include the calls, I get a pure tone as I expected. When I remove the calls, however, I get a series of beeps. This worries me, because I would not expect calls that retrieve data to have anything to do with how the sound is played. I get this behavior both on a cheap USB sound card and my (presumably nicer) internal sound card.
Here is the code:
#define GETPARAMS
int main() {
snd_pcm_t *handle;
snd_pcm_hw_params_t *params;
const char name[] = "hw:0,0";
int dir;
snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
snd_pcm_access_t access = SND_PCM_ACCESS_RW_INTERLEAVED;
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE;
unsigned int rate = 48000;
unsigned int channels = 2;
unsigned int periods = 4;
snd_pcm_uframes_t periodsize = 2048;
int num_frames = 2*periodsize;
snd_pcm_hw_params_alloca(&params);
snd_pcm_open(&handle, name, stream, 0);
snd_pcm_hw_params_any(handle, params);
#ifdef GETPARAMS
printf("\nparameters before setting:\n");
snd_pcm_hw_params_get_rate(params, &rate, &dir);
printf(" rate = %d, dir = %d\n", rate, dir);
snd_pcm_hw_params_get_channels(params, &channels);
printf(" channels = %d\n", channels);
snd_pcm_hw_params_get_periods(params, &periods, &dir);
printf(" periods = %d, dir = %d\n", periods, dir);
snd_pcm_hw_params_get_buffer_size(params, &periodsize);
printf(" periodsize = %ld\n", periodsize);
#endif
snd_pcm_hw_params_set_access(handle, params, access);
snd_pcm_hw_params_set_format(handle, params, format);
snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);
snd_pcm_hw_params_set_channels(handle, params, 2);
snd_pcm_hw_params_set_periods(handle, params, periods, 0);
snd_pcm_hw_params_set_buffer_size(handle, params, num_frames);
snd_pcm_hw_params(handle, params);
#ifdef GETPARAMS
printf("\nparameters after setting:\n");
snd_pcm_hw_params_get_rate(params, &rate, &dir);
printf(" rate = %d, dir = %d\n", rate, dir);
snd_pcm_hw_params_get_channels(params, &channels);
printf(" channels = %d\n", channels);
snd_pcm_hw_params_get_periods(params, &periods, &dir);
printf(" periods = %d, dir = %d\n", periods, dir);
snd_pcm_hw_params_get_buffer_size(params, &periodsize);
printf(" periodsize = %ld\n\n", periodsize);
#endif
int16_t *data = (int16_t*)calloc(2*periodsize, sizeof(int16_t));
loadpage(data, 2*periodsize);
snd_pcm_sframes_t frames;
snd_pcm_prepare(handle);
for (int i=0; i<8; i++) {
frames = snd_pcm_writei(handle, data, num_frames);
if (frames < 0)
frames = snd_pcm_recover(handle, frames, 0);
if (frames < 0) {
printf("snd_pcm_writei failed: %s\n", snd_strerror(frames));
}
if (frames > 0 && frames < num_frames)
printf("short write (expected %d, write %li)\n", num_frames, frames);
}
snd_pcm_close(handle);
free(data);
}
loadpage() fills the buffer. When I comment out the #define GETPARAMS I get a series of short beeps. When I include it I get a pure tone.
Here is the output when GETPARAMS is defined:
parameters before setting:
rate = 48000, dir = 32766
channels = 2
periods = 4, dir = 32766
periodsize = 2048
parameters after setting:
rate = 48000, dir = 0
channels = 2
periods = 4, dir = 0
periodsize = 4096
You must not call the snd_pcm_hw_param_get_*() functions if the parameters have not yet been set because at that time, the configuration space contains multiple potential values for the parameters.
To print the current state of the hw_params container, use snd_pcm_hw_params_dump():
snd_output_t *output;
snd_output_stdio_attach(&output, stdout, 0);
...
snd_pcm_hw_params_dump(params, output);
...
snd_output_close(output);
Anyway, the problem is that the initial values of periods, periodsize, and num_frames are inconsistent, and that the _get_ calls overwrite these variables with other values that happen to be consistent.
I do not know what values you actually want to use, but note that the period size and the buffer size are measured in frames, and that one frame contains all samples of all channels, i.e., in this case, one frame has four bytes.

Non collective write using in file view

When trying to write blocks to a file, with my blocks being unevenly distributed across my processes, one can use MPI_File_write_at with the good offset. As this function is not a collective operation, this works well.
Exemple :
#include <cstdio>
#include <cstdlib>
#include <string>
#include <mpi.h>
int main(int argc, char* argv[])
{
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int global = 7; // prime helps have unbalanced procs
int local = (global/size) + (global%size>rank?1:0);
int strsize = 5;
MPI_File fh;
MPI_File_open(MPI_COMM_WORLD, "output.txt", MPI_MODE_CREATE|MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
for (int i=0; i<local; ++i)
{
size_t idx = i * size + rank;
std::string buffer = std::string(strsize, 'a' + idx);
size_t offset = buffer.size() * idx;
MPI_File_write_at(fh, offset, buffer.c_str(), buffer.size(), MPI_CHAR, MPI_STATUS_IGNORE);
}
MPI_File_close(&fh);
MPI_Finalize();
return 0;
}
However for more complexe write, particularly when writting multi dimensional data like raw images, one may want to create a view at the file with MPI_Type_create_subarray. However, when using this methods with simple MPI_File_write (which is suppose to be non collective) I run in deadlocks. Exemple :
#include <cstdio>
#include <cstdlib>
#include <string>
#include <mpi.h>
int main(int argc, char* argv[])
{
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int global = 7; // prime helps have unbalanced procs
int local = (global/size) + (global%size>rank?1:0);
int strsize = 5;
MPI_File fh;
MPI_File_open(MPI_COMM_WORLD, "output.txt", MPI_MODE_CREATE|MPI_MODE_WRONLY, MPI_INFO_NULL, &fh);
for (int i=0; i<local; ++i)
{
size_t idx = i * size + rank;
std::string buffer = std::string(strsize, 'a' + idx);
int dim = 2;
int gsizes[2] = { buffer.size(), global };
int lsizes[2] = { buffer.size(), 1 };
int offset[2] = { 0, idx };
MPI_Datatype filetype;
MPI_Type_create_subarray(dim, gsizes, lsizes, offset, MPI_ORDER_C, MPI_CHAR, &filetype);
MPI_Type_commit(&filetype);
MPI_File_set_view(fh, 0, MPI_CHAR, filetype, "native", MPI_INFO_NULL);
MPI_File_write(fh, buffer.c_str(), buffer.size(), MPI_CHAR, MPI_STATUS_IGNORE);
}
MPI_File_close(&fh);
MPI_Finalize();
return 0;
}
How to avoid such a code to lock ? Keep in mind that by real code will really use the multidimensional capabilities of MPI_Type_create_subarray and cannot just use MPI_File_write_at
Also, it is difficult for me to know the maximum number of block in a process, so I'd like to avoid doing a reduce_all and then loop on the max number of block with empty writes when localnb <= id < maxnb
You don't use MPI_REDUCE when you have a variable number of blocks per node. You use MPI_SCAN or MPI_EXSCAN: MPI IO Writing a file when offset is not known
MPI_File_set_view is collective, so if 'local' is different on each processor, you'll find yourself calling a collective routine from less than all processors in the communicator. If you really really need to do so, open the file with MPI_COMM_SELF.
the MPI_SCAN approach means each process can set the file view as needed, and then blammo you can call the collective MPI_File_write_at_all (even if some processes have zero work -- they still need to participate) and take advantage of whatever clever optimizations your MPI-IO implementation provides.

Using Lame function hip_decode in Android NDK to decode mp3 return 0

I am using Lame's mpglib to decode mp3 to PCM in Android NDK for playing. But when I called hip_decode(), it returen 0 meaning that "need more data before we can complete the decode". I had no idea how to solve it. Can someone helps me? Here is my code:
void CBufferWrapper::ConvertMp3toPCM (AAssetManager* mgr, const char *filename){
Print ("ConvertMp3toPCM:file:%s", filename);
AAsset* asset = AAssetManager_open (mgr, filename, AASSET_MODE_UNKNOWN);
// the asset might not be found
assert (asset != NULL);
// open asset as file descriptor
off_t start, length;
int fd = AAsset_openFileDescriptor (asset, &start, &length);
assert (0 <= fd);
long size = AAsset_getLength (asset);
char* buffer = (char*)malloc (sizeof(char)*size);
memset (buffer, 0, size*sizeof(char));
AAsset_read (asset, buffer, size);
AAsset_close (asset);
hip_t ht = hip_decode_init ();
int count = hip_decode (ht, (unsigned char*)buffer, size, pcm_l, pcm_r);
free (buffer);
Print ("ConvertMp3toPCM: length:%ld,pcmcount=%d",length, count);
}
I used MACRO "HAVE_MPGLIB" to compile Lame in NDK. So I think it should work for decoding literally.
Yesterday I had the same problem. Is the same problem but using lame_enc.dll. I did not know how to resolve this 0 returned, this is the reason to this post.
Create a buffer to put mp3 data: unsigned char mp3Data[4096]
Create two buffers for pcm data, but bigger than mp3 one:
unsigned short[4096 * 100];
Open mp3 file and initialize hip.
Now, enter in a do while loop until read bytes are 0 (the end of file).
Inside the loop read 4096 bytes into mp3Data and call hip_decode with
hip_decode(ht, mp3Data, bytesRead, lpcm, rpcm);
You are right, it returns 0. It is asking you for more data.
You need to repeat the reading of 4096 bytes and the call to hip_decode until it returns a valid samples number.
Here is the important part of my program:
int total = 0;
int hecho = 0;
int leido = 0;
int lon = 0;
int x;
do
{
total = fread(mp3b, 1, MAXIMO, fich);
leido += total;
x = hip_decode(hgf, mp3b, total, izquierda, derecha);
if(x > 0)
{
int tamanio;
int y;
tamanio = 1.45 * x + 9200;
unsigned char * bu = (unsigned char *) malloc(tamanio);
y = lame_encode_buffer(lamglofla, izquierda, derecha, x, bu, tamanio);
fwrite(bu, 1, y, fichs);
free(bu);
}
}while(total > 0);
My program decodes a mp3 file and encodes the output into another mp3 file.
I expect that this could be useful.

Resources