前面文章已经讲过分布式麦克风linux下的开发,以及在ROS中的应用,也提到了实现多线程的方法,本文主要简单补充一下多个单通道麦克风并行收发数据的实现。
由于ros中多进程一般是指通过多个ros节点收发信息的方式,而多线程的实现仅在一个进程(一个节点)中就可以实现。前面博文已经说明多进程的工作机制,这里不再说明。
由于分布式麦克风是通过ip地址及端口号发送数据的,这里仅拓展到实现两个麦克风的情景,不过原理都是一样的,需要更多的话类似书写即可。
这里传输数据为了保证各个端口之间不会出现串扰的现象(其实不会发生),只是为了容易理解,我们将端口号和传输数据绑定在一起发送(publish),这是就需要创建自己的msg数据类型,以前的博文中有专门的讲述如何创建自己的msg,
如下所示:
float32[] data
uint16 size
Header header
int64 port
data用来存储float类型的音频数据,size是传输的长度,一般为帧长,port就是我们创建的ip地址的端口号。
主要实现步骤如下:
创建两个端口号相匹配的线程(调用函数必须为(void *)类型):
pthread_t tid1,tid2;
pthread_create(&tid1,NULL, rx_x,& 需要传的参数,可为NULL);
pthread_create(&tid1,NULL, rx_x,& 需要传的参数,可为NULL );
while(1);
能够看到上面调用的函数是同一个函数,对的,我们需要实现的就是这种,多线程调用一个函数时的操作。
线程中调用的函数会再次调用函数,看你需要的地方了。具体实现如下:
if(port == 8888){
pcm_file1 = fopen ("/home/yf415/a8888.pcm","a+");
// if(flag%2 == 1){
// r = play_one_frame(packet, r, decoder, snd, channels , pcm);
r = play_one_frame(packet, r, decoder, snd, channels , pcm,port);
// for (int j = 1; j <= r ;j++){
msg.size = r ;
for(int ii = 0 ; ii< r ; ii++ ){
msg.data.resize(r);
msg.data[ii] = pcm[ii] ;
msg.port = port;
// printf("%lf\n",pcm[ii]);
}
Pub.publish (msg);
fwrite (pcm ,sizeof(float) , r , pcm_file1);
fclose(pcm_file1);
// DATA_LOCK; //加锁
// temp[0][j] = pcm[j];
// DATA_UNLOCK; //解锁
// }
}
else if(port == 7777){
// if(flag%2 == 0){
pcm_file2 = fopen ("/home/yf415/a7777.pcm","a+");
r = play_one_frame(packet, r, decoder, snd, channels , pcm ,port);
msg.size = r ;
for(int ii = 0; ii < r ; ii++){
msg.data.resize(r);
msg.data[ii] = pcm[ii] ;
msg.port = port;
// printf("%lf\n",pcm[ii]);
}
Pub.publish(msg);
fwrite (pcm ,sizeof(float) , r , pcm_file2);
fclose(pcm_file2);
}
若是不使用绑定port的形式写音频数据的话会出现音频传输帧之间会有错乱的现象,这时候可以通过文件或存储空间加锁解锁的方式实现(以前博文有指出)。但是我们采用绑定port的目的在于ros的另一个接受端可以很清楚的知道这些数据是从哪一路传送过来的,也有助于我们再次利用这些音频数据。
顺便直接贴出接受端的订阅topic的callback函数如下:
void Callback(const jack_msgs::JackAudio::ConstPtr& msg)
{
float *pcm = NULL;
pcm = (float *)alloca(sizeof(float) * 1920 * 1);
if(msg->port == 8888){
int data_length = msg->size;
int k=0;
short in_shorts[data_length];
jack_mtx.lock();
for(int i=0; i<data_length; i++){
in_shorts[k] = (short)(msg->data[i] * 65535);
// pcm[k] = (msg->data[i]);
k++;
printf ("8888 asr == %d\n", in_shorts[k]);
}
fwrite(in_shorts , sizeof(short) , data_length , pcm_file);
// fwrite(pcm , sizeof(float) , data_length , pcm_file);
jack_mtx.unlock();
}
else if( msg->port == 7777 ){
int data_length = msg->size;
int k=0;
short in_shorts1[data_length];
jack_mtx.lock();
for(int i=0; i< data_length; i++){
in_shorts1[k] = (short)(msg->data[i] * 65535);
k++;
printf ("7777 asr == %d\n", in_shorts1[k]);
}
fwrite(in_shorts1 , sizeof(short) , data_length , pcm_file1);
jack_mtx.unlock();
}
}
开发示例的流程就是这样,当然我们在接受端也可以实现多线程的方式接受,这个看个人了。
另一种想法是:ros中一般能采用多进程的话最好是多进程,实在不行采用多线程(学习一下很有帮助)。我们也可以将上面实现的方式换一下:发送端用多进程的方式发布多个topic,接受端用多线程的方式接受多个topic。既能保证同步性,还非常清晰明了。