2017-03-12 7 views
1

libasoundを使用してALSA再生デバイスにランダムバイトを書き込む際に問題が発生しています。最終的に私の目標は、再生ストリームをネットワーク経由でルーティングし、リモートデバイスで再生できるようにすることです。ALSA再生デバイスへのバイトストリーミング

this questionのコードは、WAVファイルをメモリに読み込んで、snd_pcm_writei経由でドライバに書き込みます。しかし、このコードが行うことと私がやろうとしていることとの間に重大な違いは、すべて私にすぐに利用可能なデータがないということです。私はそれが利用可能になるとデータをストリーミングするために探しています。私のニーズに合うように上記のサンプルコードを適応さ

は、私はこれで終わる:

#include <stdio.h> 
#include <unistd.h> 
#include <alsa/asoundlib.h> 

static snd_pcm_t *PlaybackHandle; 

int init_playback(const char *device, int samplerate, int channels) { 
    int err; 

    printf("Init parameters: %s %d %d\n", device, samplerate, channels); 

    if((err = snd_pcm_open(&PlaybackHandle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { 
     printf("Can't open audio %s: %s\n", device, snd_strerror(err)); 
     return -1; 
    } 

    if ((err = snd_pcm_set_params(PlaybackHandle, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, channels, samplerate, 1, 500000)) < 0) { 
     printf("Can't set sound parameters: %s\n", snd_strerror(err)); 
     return -1; 
    } 
    return 0; 
} 

int play_bytes(const void *bytes, int len) { 
    snd_pcm_uframes_t frames, count; 
    snd_pcm_uframes_t bufsize, period_size; 
    frames = 0; 
    count = 0; 

    snd_pcm_prepare(PlaybackHandle); 

    snd_pcm_get_params(PlaybackHandle, &bufsize, &period_size); 
    printf("bufsize=%d\n", (int) bufsize); 

    do { 
     int remaining = len - count; 
     int buflen = remaining < bufsize ? remaining : bufsize; 
     frames = snd_pcm_writei(PlaybackHandle, bytes + count, buflen); 
     // If an error, try to recover from it 
     if (frames == -EPIPE) { 
      printf("EPIPE\n"); 
      snd_pcm_prepare(PlaybackHandle); 
     } 
     if (frames < 0) { 
      printf("Recovering\n"); 
      frames = snd_pcm_recover(PlaybackHandle, frames, 0); 
     } 
     if (frames < 0) 
     { 
      printf("Error playing wave: %s\n", snd_strerror(frames)); 
      break; 
     } 

     // Update our pointer 
     count += frames; 
     //printf("count=%d len=%d\n", (int)count, len); 

    } while (count < len); 

    // Wait for playback to completely finish 
    if (count == len) 
     snd_pcm_drain(PlaybackHandle); 
    return 0; 
} 

int close_playback() { 
    snd_pcm_close(PlaybackHandle); 
    return 0; 
} 

int main(int argc, char **argv) { 
    if(argc < 1) { 
     printf("Usage: %s <WAV-file>\n", argv[0]); 
     return -1; 
    } 

    int fd; 
    unsigned long long len; 

    fd = open(argv[1], O_RDONLY); 
    // Find the length 
    len = lseek(fd, 0, SEEK_END); 

    // Skip the first 44 bytes (header) 
    lseek(fd, 44, SEEK_SET); 
    len -= 44; 

    char *data = malloc(len); 
    read(fd, data, len); 

    init_playback("default", 44100, 2); 
    play_bytes(data, len); 
    close_playback(); 
    return 0; 
} 

このコードはgcc playback.c -o playback -lasoundしてコンパイルすることができます。私が使用しているWAVファイルはhereです。

このコードスニペットを実行すると、bufsizeに基づいて着信データをチャンクすると、チャンクサイズに応じて再生中に繰り返されるオーディオの断片があります。チャンクサイズが大きいと、チャンクサイズが小さい場合よりも繰り返し回数が少なくなります。これをオーディオの仕組みと組み合わせると、各チャンクの最後に小さな断片が繰り返されていると思います。

私が使用するパラメータは以下の通りであった。

  • サンプルレート:44100
  • チャンネル:2
それの送信チャンクが動作しないのに対し、

はなぜワンショット作業全体WAVファイルを送信するのですか?ドライバにオーディオデータのチャンクを送信し、正しく再生させるにはどうすればよいですか?

+1

'snd_pcm_drain()'は、ストリームの最後でのみ使用します。 –

+0

お元気ですか?私がやっていることのほとんどは、動作するサンプルコードと同じです。違いは、私は内部的にファイルの内容をチャンクして 'snd_pcm_write'を呼び出すのに対して、サンプルコードはファイルの内容全体を' snd_pcm_writei'に提示する点です。私のスニペットで 'snd_pcm_drain'をどこで呼び出すべきですか? –

+0

質問を更新し、完全に動作するコードサンプルと、テストするために使用しているWAVファイルの1つを追加しました。私は "反復"を記述することは難しいと思うので、うまくいけば、これは助けになるだろう –

答えて

0
frames = snd_pcm_writei(PlaybackHandle, bytes + count, buflen); 
    count += frames; 

snd_pcm_writei()対策フレームでのサイズがありますが、バイトとして扱います。したがって、再生されたデータのわずか4分の1をスキップします。

+0

私は参照してください。そのため、リンクされたSO質問のコードスニペットが動作するのは、do {...} whileループの1回の反復ですべてのデータをフィードし、 'frames'戻り値を無関係にするためです。 –

+0

'WaveSize'はフレーム数でカウントされます。 'count> 0'の場合、' WavePtr + count'は間違っています。 –

関連する問題