質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

Q&A

解決済

4回答

8580閲覧

Linuxで、マイク入力データをスピーカーからリアルタイムで出力したい

YOshim

総合スコア1085

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Linux

Linuxは、Unixをベースにして開発されたオペレーティングシステムです。日本では「リナックス」と呼ばれています。 主にWebサーバやDNSサーバ、イントラネットなどのサーバ用OSとして利用されています。 上位500のスーパーコンピュータの90%以上はLinuxを使用しています。 携帯端末用のプラットフォームAndroidは、Linuxカーネル上に構築されています。

Ubuntu

Ubuntuは、Debian GNU/Linuxを基盤としたフリーのオペレーティングシステムです。

0グッド

1クリップ

投稿2018/10/31 09:16

編集2018/10/31 11:58

現在、Ubuntuでマイクからの信号をスピーカーから出力しようと四苦八苦しております。
"マイクからの信号"といっても通常のライン入力ではなく、
一度マイコンでAD変換された信号をシリアル通信でPCに送り、
PC側でRAWデータのままスピーカーから出力しようとしています。
*複数のマイクを用途に応じて使い分けるため、
全てのマイクのデータを一旦マイコンで受けれる構成にしています。

現在、シリアル通信で1つのマイクのデータを受信し、そのデータをスピーカーから出力できるようになったのですが、
音が途切れ途切れになってしまいます。

原因は、シリアル信号の受信と音声出力が同時に行われないためと推測しますが、
対策方法がわかりません。

対策方法や、もっと簡単な方法があるのであればご教授願いたく。

ソースコードは以下です。

#include <stdlib.h> #include <stdio.h> #include <sys/wait.h> #define SERIAL_PORT "/dev/ttyUSB2" #define BAUDRATE B115200 int main(int argc, char* argv[]){ unsigned char buf[255]; FILE *fp; fp = fopen("tmpFile.raw","w"); //シリアル通信の設定 int fd; struct termios oldtio, newtio; fd = open(SERIAL_PORT, O_RDWR); ioctl(fd, TCGETS, &oldtio); newtio = oldtio; newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; ioctl(fd, TCSETS, &newtio); while(1){ int pid; pid = fork(); if(pid < 0){ return -1; } if(pid == 0){ execlp("aplay", "aplay", "tmpFile.raw", NULL);//音声を再生 exit(99); }else{ int status; waitpid(pid, &status, 0); fp = fopen("tmpFile.raw", "w");//tmpFileをクリアする int len = read(fd, buf, sizeof(buf)); if(len > 0){ for(int i=0; i<len; i++){ fputc(buf[i],fp);//ファイルに書き込み } } fflush(fp);//ファイルにバッファ分を書き込み } } ioctl(fd, TCSETS, &oldtio); fclose(fp); close(fd); }

追記:
上記のコードは、シリアル通信のデータを毎回ファイルに書き込んでいますが、
シリアル通信の結果をパイプでaplayに接続すれば、高速に処理出来て音飛びが発生しないのでは
と考えています。
*まだそのやりかたがわかっていません・・・

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

aplayが標準入力を読み込むというのであれば、

c

1#include <sys/stat.h> 2#include <sys/ioctl.h> 3#include <fcntl.h> 4#include <termios.h> 5#include <unistd.h> 6#include <stdlib.h> 7#include <stdio.h> 8#include <sys/wait.h> 9#define SERIAL_PORT "/dev/ttyUSB1" 10#define BAUDRATE B115200 11 12int main(int argc, char* argv[]){ 13 int fd; 14 struct termios oldtio, newtio; 15 fd = open(SERIAL_PORT, O_RDWR); 16 ioctl(fd, TCGETS, &oldtio); 17 newtio = oldtio; 18 newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; 19 ioctl(fd, TCSETS, &newtio); 20 21 dup2(fd, 0); 22 execlp("aplay", "aplay", NULL); 23}

でいいと思います。

ちょっと説明。

  • ファイルディスクリプタ0番は、標準入力。
  • dup2は、ファイルディスクリプタのコピー。つまり、上記ではシリアルポートを標準入力に割り当て。
  • forkやexecなんちゃらは、ファイルディスクリプタの状態を引き継ぐ。つまり、aplayの実行時には標準入力はシリアルポートのまま。

こっちの方が読み込みをaplayの方に任せられる分、問題が起きにくいのではないかと。

これで解決するかどうかわかりませんが、というか自分で試していないので動くかどうかすらわかりませんが、まぁそれはそれで置いといて、dup2(もしくは、他にもdupとか)で標準入出力を置き換えるのはunixプログラムでよくやる手段なので、覚えておいて損はないと思います。


あと、dup2(fd,1)ではないですか。

0で間違いないですよ。
以下のようなプログラムで確認しました。

c

1#include <stdio.h> 2#include <sys/types.h> 3#include <sys/stat.h> 4#include <fcntl.h> 5#include <unistd.h> 6 7int main() 8{ 9 int fd; 10 11 fd = open("test01.c", O_RDONLY); 12 dup2(fd, 0); 13 // これでもいいと思うが、 14 //execlp("cat", "cat", NULL); 15 // こちらのほうがわかりやすいかと。 16 execlp("sed", "sed", "-e", "s/test01/aaaa01/", NULL); 17}

投稿2018/11/02 14:51

編集2018/11/05 00:19
katsuko

総合スコア3469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YOshim

2018/11/02 15:01

ありがとうございます。最近dup2とかシステムコールとかの勉強を始めたところです。 現在手元に確認するための環境がないので、すぐ試せないのが辛いところです。 ちょっと気になりましたが、 execlp("aplay", "aplay", NULL);だとaplayへの入力が無いと思います。 標準入力をaplayに渡す記述が必要なのではと思いますが、いかがでしょうか。
katsuko

2018/11/02 15:23

Livengaさんの回答にある「cat /etc/urandom | aplay」のやり方や、軽くググった限りだと、入力を指定しなければ標準入力から読み込むようなので、これで大丈夫だと思います。 とは言うものの、aplayは使ったことがないので、間違っていたらすいません。
YOshim

2018/11/02 15:28

なるほど。今までaplay単体で実行したことがなかったので、把握していませんでした。 来週月曜にでも結果をおしらせします。
YOshim

2018/11/05 01:26 編集

残念ながら、aplayは入力を指定する必要があるようでした。 あと、dup2(fd,1)ではないですか。 編集:aplayの入力指定は、必要かどうかまだ不明です。オプションコマンドつければ、入力データを記述しなくともエラーは発生しませんでした。 音も出ませんでしたが・・・
YOshim

2018/11/05 01:44

dup2の使用方法について理解不足なため、うまくいきませんでした・・・ 結果的に、異なる方法で実施できました。 お手数おかけしましたが、 この度は回答・コメント頂きありがとうございました。
guest

0

追記の方法ですと, シリアルから読み込んだデータを直接標準出力に書き込みを行い
ターミナル上で作成したプログラムと aplay をパイプで組み合わせてみるのはどうでしょうか.

例えば以下を実行すると砂嵐のような音が流れます.

cat /etc/urandom | aplay

記述されているコードですと, プロセスの終了を待っているため連続的に音を流すのは難しいと思います.
なので, aplay を簡易的なコードを実装してシングルプロセスで動作させるのが最善だと思います.

Introduction to Sound Programming with ALSA
このページに ALSA の使用方法が記載されています.

ALSA project - the C library reference
ALSA を使用したコードのサンプルと関数の詳細が記載されています.

投稿2018/11/01 08:16

Livenga

総合スコア85

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YOshim

2018/11/01 08:57 編集

回答ありがとうございます。 シリアル通信の受信信号を標準出力とし、 ./a.out | aplay(./a.out > aplayではなく) とすることに致しました。 アドバイス頂きありがとうございます。
guest

0

そうすると次は転送ですね。PCの性能や同時に動作しているプロセスの影響も考えられますが、それは一旦置いといて、オーディオ転送用のUSBプロトコルはUSBスピーカーの様に、リアルタイム的動作が保証されるIsochronous転送を使うべきです。通常はこの様な目的のためにある、USB Audio Classという方式をマイコンとPCの通信で使用します。USBオーディオミキサー等の機器では通常、そういった通信をしています。

今回使われているUSBシリアル転送は本当のシリアル転送とは違い、4種類のUSB転送方式の中で一番優先順位が低い「Bulk転送」です。つまり「いつ終わってもいいから、CPUやUSBコントローラーが空いている時に転送してね」という方式です。例えばUSBキーボードやマウスは、より優先順位が高いInperrupt転送を使用します。

正直言って、この事が現在発生している音飛びに影響しているかどうかはわかりません。しかし問題を解決するならば、このような問題を一つずつ潰して行くしか無いと思います。可能であればまず、本当のシリアル(RS-232C)で接続して音飛びが変わるかどうかを確認してみるのはいかがでしょうか。実際のところ本来のMIDIは、本当のシリアル(RS-232C)で転送しています。通常的な利用でリアルタイム性に問題があるUSB Bulk転送方式は使用しません。

投稿2018/11/02 14:03

ahidaka

総合スコア391

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YOshim

2018/11/02 15:49 編集

USBの技術仕様は重要なことですが、 実際に参考となるコードで回答を頂きたいです。
ahidaka

2018/11/02 15:34

意味が伝わらなくて残念です。ALSAやLinuxのUSBドライバ、マイコン側のUSB Audioの使いこなしは大変でしょうから、まずは比較的簡単にできる/dev/ttyUSB2とPCのハードウエアをケーブル変えるだけで可能なRS-232Cを試してみてはいかがでしょうかと言っているだけです。 RS-232C自体は直接USBの技術や仕様とは関係ないし、既存のコードもそのまま動くはずです。もしRS-232Cの意味がわからないのでしたら調べてください。
ahidaka

2018/11/02 15:36

誤字修正
guest

0

自己解決

シリアル通信を標準出力としてaplayで音声を再生することに致しました。

#include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h> #define SERIAL_PORT "/dev/ttyUSB1" #define BAUDRATE B115200 int main(int argc, char* argv[]){ unsigned char buf[100]; int fd; struct termios oldtio, newtio; fd = open(SERIAL_PORT, O_RDWR); ioctl(fd, TCGETS, &oldtio); newtio = oldtio; newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; ioctl(fd, TCSETS, &newtio); while(1){ int len = read(fd, buf, sizeof(buf)); if(len > 0){ for(int i = 0;i<len; i++){ fputc(buf[i],stdout); } } fflush(stdout); } ioctl(fd, TCSETS, &oldtio); fclose(fp); close(fd); }

まだ正常な音声が出力されてはいませんが、
その原因はLinux、PCではなくマイコン側のソースコードの問題と考えています。

正常な音声が出力されていない以上、より良い方法を模索しようと思います。

##追記:
下記のコードで音飛びなく動作するようになりました。

#include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <fcntl.h> #include <termios.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <sys/wait.h> #define SERIAL_PORT "/dev/ttyUSB0" #define BAUDRATE B115200 #define BUFSIZE 100 int main(int argc, char* argv[]){ unsigned char buf[BUFSIZE]; int fd; struct termios oldtio, newtio; FILE *fp; fd = open(SERIAL_PORT, O_RDWR); ioctl(fd, TCGETS, &oldtio); newtio = oldtio; newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; ioctl(fd, TCSETS, &newtio); while(1){ int len = read(fd, buf,BUFSIZE); if(len > 0){ fwrite(buf,BUFSIZE,1,stdout); } } ioctl(fd, TCSETS, &oldtio); fclose(fp); close(fd); }

主な変更点はfputcfwriteに変えたくらいですが、これはLivengaさんが最初におっしゃったことでしたね。

投稿2018/11/01 08:37

編集2018/11/05 01:49
YOshim

総合スコア1085

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Livenga

2018/11/01 08:57

read(fd, buf, sizeof(buf)) で遅延が発生しているのではないでしょうか. 良いか悪いかわかりませんが, 1バイト読み込んだら, 1バイト書き込みで尤もらしい音声の出力されると思います. read の返り値で読み込んだバイト数が取得されているので, fwrite(buf, sizeof(unsigned char), len, stdout) もしくは write(STDOUT_FILENO, buf, sizeof(unsinged char) * len) のほうが無駄がないと思います.
YOshim

2018/11/01 09:17 編集

>fwrite(buf, sizeof(unsigned char), len, stdout) >もしくは >write(STDOUT_FILENO, buf, sizeof(unsinged char) * len) これは、 if(len>0){ for(...){} } の代わりに置き換えたほうが良いという理解でよいでしょうか。 その場合もreadは実装する必要があるということでしょうか。 やはり音が途切れてしまうので、FIFOを使ってはどうかと検討中です。 イメージとしては、シリアル通信データを直接stdoutにパイプするイメージですが 簡単にできないものでしょうか。
ahidaka

2018/11/01 13:30

まずは、"tmpFile.raw"をマイコンでどのように録音したのか、サンプリング周波数がどうなっているのかすごく気になります。
YOshim

2018/11/01 21:31

マイコンのタイマー機能で5kHzで8bitデータを取得しています。 Aplayから音を出す際は、-r5000のオプションを記載して5kHzでサンプリングしています。 チャンネル数は録音も再生も1です。
ahidaka

2018/11/02 01:22

なるほど。それで直接再生では正しい音が出るのですね。
YOshim

2018/11/02 03:55

"直接再生"というのかわかりませんが マイクからの信号をファイルに保存し、保存したファイルをaplayで再生すると 音は飛び飛びにはならず、正常に再生されているように聞こえます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問