Skip to content

Commit

Permalink
PCM capture added
Browse files Browse the repository at this point in the history
  • Loading branch information
NebiyouTen committed Mar 20, 2019
1 parent 04243c8 commit 0024164
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 0 deletions.
Binary file added PCM_Capture/PCM_Capture
Binary file not shown.
139 changes: 139 additions & 0 deletions PCM_Capture/PCM_Capture.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* A simple progrma that records a sound.
*
* Adopted from the linux journal
* https://www.linuxjournal.com/article/6735
*
* */

// Tell alsa to include the new library
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>
#include "wave.h"

#define SAMPLE_RATE 8000
#define SECOND 1000000
#define NUMSECONDS 20

int main(){

// PCM device handle
snd_pcm_t *handle;
// Structure used to configure the PCM device
snd_pcm_hw_params_t *params;
// name of the device, default for now
char * device_name = "plug:default";
// Variable to store return values of functions
int return_val;
// frames used to determine size of a period
snd_pcm_uframes_t frames;
// Buffer to store data to be captured or played
char *buffer;
//char * file_name = "sample_wav.wav";
unsigned int size, sample_rate, period_time, loop_time, num_loops;

// open pcm for capturing
if ( (return_val = snd_pcm_open(&handle, "plug:default",
SND_PCM_STREAM_CAPTURE,0)) < 0 ){
fprintf(stderr,
"unable to open pcm device %s : %s\n", device_name \
, snd_strerror(return_val));
exit(1);

}

printf("Devie '%s' opened successfully \n", device_name);

// Allocate params
snd_pcm_hw_params_alloca(&params);
// Initialize with default values
snd_pcm_hw_params_any(handle, params);


// Set Configuration values in params
// set access type to RW interleaved and
// check function callback
if (snd_pcm_hw_params_set_access(handle, params,
SND_PCM_ACCESS_RW_INTERLEAVED) < 0 ){
fprintf(stderr,
"unable to set access type to RW Interleaved");
exit(1);
}
// Set the data format to Signed 16-bit little-endian
if (snd_pcm_hw_params_set_format(handle, params,
SND_PCM_FORMAT_S16_LE) < 0 ){
fprintf(stderr,
"unable to set format to Signed 16-bit little-endian");
exit(1);
}
// set setrio channels
if (snd_pcm_hw_params_set_channels(handle, params, 2) < 0 ){
fprintf(stderr,
"unable to set channel to sterio");
exit(1);
}
// sample rate of sample wav file is 256000 bits/second
// Invalid argument error if Sample rate is higher than 200,000
sample_rate = SAMPLE_RATE;
if (snd_pcm_hw_params_set_rate_near(handle, params,
&sample_rate, 0) < 0){
fprintf(stderr,
"unable to set sample rate to %d ",sample_rate);
exit(1);
}

fprintf(stdout,"sample rate is = %d \n", sample_rate);


// set period size to 32 frames. A frame has 2 samples, left and
// right samples. A sample has 2 bytes, most and least significant.
frames = 64;
fprintf(stdout,"Set number of frames is %d \n", frames);
if (snd_pcm_hw_params_set_period_size_near(handle,
params, &frames, 0) < 0){
fprintf(stderr,
"unable to set period size to %d frames",frames);
exit(1);
}
// Finally, write the params to the actual hardware device
if ((return_val = snd_pcm_hw_params(handle, params)) < 0) {
fprintf(stderr,
"unable to set hw parameters: %s\n",
snd_strerror(return_val));
exit(1);
}


// get buffer size for one period
snd_pcm_hw_params_get_period_size(params,&frames,0);
fprintf(stdout,"Got 1 a period size of %d \n", frames);


//size will be 4 * period size.
size = frames * 4;
// allocate a buffer with size of 'size'
fprintf(stdout, "Malloc with size %d \n", size);

buffer = (char *) malloc(size);

// get one period time
snd_pcm_hw_params_get_period_time(params, &loop_time, 0);

num_loops = NUMSECONDS * SECOND / loop_time;

return_val = write_wave(buffer, handle,params, frames, num_loops);

// Drain and close the PCM handle
snd_pcm_drain(handle);
snd_pcm_close(handle);

if (return_val < 0){
fprintf(stderr, "error in writing a file \n", return_val);
exit(1);
}


return 0;

}
1 change: 1 addition & 0 deletions PCM_Capture/gcc_compile_script
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
gcc -I. PCM_Capture.c wave.c -o PCM_Capture -lasound
Binary file added PCM_Capture/test.wav
Binary file not shown.
137 changes: 137 additions & 0 deletions PCM_Capture/wave.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
*
* Wave.c
* A simple program to parse wav and play files .
*
* Adopted from
* http://truelogic.org/wordpress/2015/09/04/parsing-a-wav-file-in-c/
*
* */

// Tell Alsa to use newer version of its API

#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>

#include <stdio.h>
#include "wave.h"
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>

#define TRUE 1
#define FALSE 0
#define READ_in_CHUNKS 1
#define HEADER_SIZE 44
#define NUM_BYTES 4

// struct for the
struct WAV_HEADER wave_header;
// file ptr
FILE *file_ptr;
#define FILENAME "test.wav";

/*
* @Function to write a wav file and play it using
* the PCM handle passed.
*
* Arguments: file_name: name of the wav file to be played
* handle : Pointer to a PCM
* frames : Number of frames to be written in the buffer
* , if chunk mode is used
*
* Returns: integer:
*
* */
int write_wave(char *buffer, snd_pcm_t *handle,
snd_pcm_hw_params_t *params,
int frames, int length){

int return_val, sample_rate, data_length;
char *file_name = FILENAME;

fprintf(stdout,"Wave file writing started %lu \n"
, sizeof(wave_header));

file_ptr = fopen(file_name , "w+");

/*
* Write header first
*
* */

fprintf(stdout, "Allocated wave header %lu \n", sizeof(wave_header));

// riff header: hex encoded string for riff
wave_header.riff = htonl(0X52494646); // riff header
// write chunk size, zero for now will be updated once file written
wave_header.file_size = 0;
// write WAVE to format, encoded string
wave_header.wave = htonl(0X57415645);
// fmt chunk marker encoded string "fmt"
wave_header.fmt_chunk_marker = htonl(0X666d7420);
wave_header.length_of_fmt = 16;
wave_header.format_type = 1; // PCM
wave_header.channels = 2;
wave_header.sample_rate = 8000;
wave_header.byterate = 4 * 8000;
wave_header.block_align = 4;
wave_header.bits_per_sample = 16;
wave_header.data_chunk_header = htonl(0x64617461); // "data"
wave_header.data_size = 0; // fill it at the end

fwrite(&wave_header, sizeof(wave_header), 1, file_ptr);


/*
* Write data now
*
* */
data_length = length;
while (length > 0 ){

length -= 1;
if ( (return_val = snd_pcm_readi(handle, buffer, frames))
== -EPIPE ){
// overrun occurs
fprintf(stderr, "overrun occured \n");
snd_pcm_prepare(handle);
}else if(return_val < 0){
fprintf(stderr, "error from read: %s \n", snd_strerror(return_val));
}else if( return_val != (int)frames ){
fprintf(stderr, "short write %d != %d bytes \n. ",return_val,(int)frames);
}

return_val = fwrite(buffer, frames , 4, file_ptr);

if (return_val != 4){

printf(" Write to file error %d != %d \n", return_val, frames);
fclose(file_ptr);
return -1;

}

}

// move pointer to beggning of file with offset of 4
// meaning points to file size
fseek(file_ptr, NUM_BYTES , SEEK_SET);
// set file size
data_length = data_length * frames * NUM_BYTES
+ (HEADER_SIZE - 2 * NUM_BYTES);
fwrite(&data_length, 1, sizeof(uint32_t), file_ptr);

// move pointer to point to data size header
fseek(file_ptr, HEADER_SIZE - NUM_BYTES, SEEK_SET);
data_length -= + (HEADER_SIZE - 2 * NUM_BYTES);
fwrite(&data_length, 1, sizeof(uint32_t), file_ptr);

fprintf(stdout, "Final datasize is %d \n",data_length);

fclose(file_ptr);
return 0;
}
40 changes: 40 additions & 0 deletions PCM_Capture/wave.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

/*
*
* Wave.h
* A header file for a simple wav file parser
* Adopted from
* http://truelogic.org/wordpress/2015/09/04/parsing-a-wav-file-in-c/
*
* */


// A structure to hold header of a wave file
struct WAV_HEADER{

uint32_t riff; //Bytes for RIFF file format
uint32_t file_size; // An int (4 bytes) for file size
uint32_t wave; // Chars for file headers
uint32_t fmt_chunk_marker; // fmt string data
uint32_t length_of_fmt; // length of format data
uint16_t format_type; // format type
uint16_t channels;
uint32_t sample_rate;
uint32_t byterate;
uint16_t block_align;
uint16_t bits_per_sample;
uint32_t data_chunk_header ;
uint32_t data_size;

};


int read_wave_and_play(char *file_name, snd_pcm_t *handle,
snd_pcm_uframes_t frames);



int write_wave(char *buffer, snd_pcm_t *handle,
snd_pcm_hw_params_t *params, int frames, int length);


0 comments on commit 0024164

Please sign in to comment.