會有大量使用ChatGpt 幫忙寫Code 的部分
靈感
https://github.com/marty1885/magic-qoa/tree/master
https://www.facebook.com/groups/1686432621633958/posts/3783463605264172
https://yunosuke-official.bandcamp.com/
能夠使用Pulse Audio 播放1 Khz Sine Wave
sudo apt-get install libpulse-dev
> CMake:Quick Start
cmake_minimum_required(VERSION 3.5.0)
project(01_PulseAudioPlayAudio VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${PULSE_INCLUDE_DIRS})
link_directories(${PULSE_LIBRARY_DIRS})
add_executable(01_PulseAudioPlayAudio main.c)
target_link_libraries(01_PulseAudioPlayAudio ${PULSE_LIBRARIES})
target_link_libraries(01_PulseAudioPlayAudio m)
target_link_libraries(01_PulseAudioPlayAudio pulse-simple)
include(CTest)
enable_testing()
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#define SAMPLE_RATE 48000
#define CHANNELS 2
#define DURATION 5 // 正弦波播放時長(秒)
#define FREQ 1000 // 正弦波頻率(Hz)
int main(int, char**){
pa_simple *s = NULL;
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = SAMPLE_RATE,
.channels = CHANNELS
};
int error;
s = pa_simple_new(NULL, "Sine Wave Generator", PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error);
if (!s) {
fprintf(stderr, "Failed to create PulseAudio connection: %s\n", pa_strerror(error));
return 1;
}
size_t buffer_size = SAMPLE_RATE * CHANNELS * sizeof(short) / 10; // 0.1秒的緩衝區
short *buffer = (short *)malloc(buffer_size);
if (!buffer) {
perror("Failed to allocate buffer");
pa_simple_free(s);
return 1;
}
size_t num_samples = buffer_size / sizeof(short);
double amplitude = 30000; // 16-bit 音頻範圍內的幅度
double phase = 0;
double phase_increment = 2 * M_PI * FREQ / SAMPLE_RATE;
for (int i = 0; i < DURATION * SAMPLE_RATE; i++) {
short sample = (short)(amplitude * sin(phase));
for (int ch = 0; ch < CHANNELS; ch++) {
buffer[i % num_samples + ch * (num_samples / CHANNELS)] = sample;
}
phase += phase_increment;
if (phase >= 2 * M_PI) {
phase -= 2 * M_PI;
}
if ((i % num_samples) == (num_samples - 1)) {
if (pa_simple_write(s, buffer, buffer_size, &error) < 0) {
fprintf(stderr, "Failed to write to PulseAudio: %s\n", pa_strerror(error));
free(buffer);
pa_simple_free(s);
return 1;
}
}
}
if (pa_simple_drain(s, &error) < 0) {
fprintf(stderr, "Failed to drain PulseAudio: %s\n", pa_strerror(error));
}
free(buffer);
pa_simple_free(s);
return 0;
}
.
├── mount-origin
│ └── index.html
└── your_http_server.c
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimal HTTP Server</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>This is a minimal HTTP server using libwebsockets.</p>
</body>
</html>
你需要找到正確的mount-origin的相對位置
. out/build/GCC 12.3.0 x86_64-linux-gnu/02_WebsocketPlayAudio
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/def.h>
#include <sndfile.h>
#define BUFFER_SIZE 1024
int main(int, char**){
// 打開 WAV 檔案
SNDFILE *sf;
SF_INFO sfinfo;
sf = sf_open("audio.wav", SFM_READ, &sfinfo);
if (!sf) {
fprintf(stderr, "Failed to open WAV file: %s\n", sf_strerror(NULL));
return 1;
}
pa_simple *s = NULL;
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = sfinfo.samplerate,
.channels = sfinfo.channels
};
int error;
s = pa_simple_new(NULL, "WAV Player", PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error);
if (!s) {
fprintf(stderr, "Failed to create PulseAudio connection: %s\n", pa_strerror(error));
return 1;
}
// 播放 WAV 檔案
size_t buffer_size = BUFFER_SIZE * sfinfo.channels * sizeof(short);
short *buffer = (short *)malloc(buffer_size);
if (!buffer) {
perror("Failed to allocate buffer");
pa_simple_free(s);
sf_close(sf);
return 1;
}
sf_count_t read_count;
while ((read_count = sf_read_short(sf, buffer, BUFFER_SIZE * sfinfo.channels)) > 0) {
if (pa_simple_write(s, buffer, read_count * sizeof(short), &error) < 0) {
fprintf(stderr, "Failed to write to PulseAudio: %s\n", pa_strerror(error));
free(buffer);
pa_simple_free(s);
sf_close(sf);
return 1;
}
}
if (pa_simple_drain(s, &error) < 0) {
fprintf(stderr, "Failed to drain PulseAudio: %s\n", pa_strerror(error));
}
free(buffer);
pa_simple_free(s);
sf_close(sf);
return 0;
}
cmake_minimum_required(VERSION 3.5.0)
project(01_PulseAudioPlayAudio VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${SNDFILE_INCLUDE_DIRS})
link_directories(${SNDFILE_LIBRARY_DIRS})
add_executable(01_PulseAudioPlayAudio main.c)
target_link_libraries(01_PulseAudioPlayAudio ${PULSE_LIBRARIES})
target_link_libraries(01_PulseAudioPlayAudio m)
target_link_libraries(01_PulseAudioPlayAudio pulse-simple)
target_link_libraries(01_PulseAudioPlayAudio sndfile)
include(CTest)
enable_testing()
因為看懂程式碼之後發現送到Pulse Audio 的資料不能包含Header
需要先使用sndfile 把header 移除
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/def.h>
#include <sys/socket.h>
#define PORT 1234
#define BUFFER_SIZE 1024
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
ssize_t bytes_received;
// PulseAudio 設定
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 48000, // 假設音頻的採樣率為 48kHz
.channels = 2 // 假設音頻為單聲道
};
int error;
pa_simple *s = pa_simple_new(NULL, "WAV Streamer", PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error);
if (!s) {
fprintf(stderr, "Failed to create PulseAudio connection: %s\n", pa_strerror(error));
return 1;
}
// 創建 socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
pa_simple_free(s);
exit(EXIT_FAILURE);
}
// 設定 server 地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 綁定 socket
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
close(server_fd);
pa_simple_free(s);
exit(EXIT_FAILURE);
}
// 監聽 client 連接
if (listen(server_fd, 1) < 0) {
perror("listen");
close(server_fd);
pa_simple_free(s);
exit(EXIT_FAILURE);
}
printf("Waiting for a connection...\n");
// 接受 client 連接
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept");
close(server_fd);
pa_simple_free(s);
exit(EXIT_FAILURE);
}
printf("Client connected.\n");
// 實時接收並播放音頻數據
while ((bytes_received = recv(client_fd, buffer, sizeof(buffer), 0)) > 0) {
// 確認收到的數據符合 PulseAudio 設定
if (bytes_received % (ss.channels * sizeof(short)) != 0) {
fprintf(stderr, "Received data size mismatch. Expected multiple of %zu bytes.\n", ss.channels * sizeof(short));
break;
}
if (pa_simple_write(s, buffer, (size_t)bytes_received, &error) < 0) {
fprintf(stderr, "Failed to write to PulseAudio: %s\n", pa_strerror(error));
break;
}
}
if (bytes_received < 0) {
perror("recv");
}
// 關閉 socket 和 PulseAudio
close(client_fd);
close(server_fd);
if (pa_simple_drain(s, &error) < 0) {
fprintf(stderr, "Failed to drain PulseAudio: %s\n", pa_strerror(error));
}
pa_simple_free(s);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sndfile.h>
#define SERVER_ADDR "127.0.0.1"
#define PORT 1234
#define BUFFER_SIZE 1024
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <wav-file>\n", argv[0]);
return 1;
}
// 打開 WAV 檔案
SNDFILE *sf;
SF_INFO sfinfo;
sf = sf_open(argv[1], SFM_READ, &sfinfo);
if (!sf) {
fprintf(stderr, "Failed to open WAV file: %s\n", sf_strerror(NULL));
return 1;
}
// 確認音頻格式
if (sfinfo.format != (SF_FORMAT_WAV | SF_FORMAT_PCM_16)) {
fprintf(stderr, "Unsupported audio format. Only 16-bit PCM WAV files are supported.\n");
sf_close(sf);
return 1;
}
// 創建 socket
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd < 0) {
perror("socket");
sf_close(sf);
return 1;
}
// 設定 server 地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
server_addr.sin_port = htons(PORT);
// 連接到 server
if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
close(client_fd);
sf_close(sf);
return 1;
}
// 讀取並發送音頻數據
char buffer[BUFFER_SIZE];
sf_count_t read_count;
while ((read_count = sf_read_raw(sf, buffer, sizeof(buffer))) > 0) {
ssize_t bytes_sent = send(client_fd, buffer, read_count, 0);
if (bytes_sent < 0) {
perror("send");
break;
}
}
if (read_count < 0) {
perror("sf_read_raw");
}
// 關閉 socket 和檔案
close(client_fd);
sf_close(sf);
return 0;
}
cmake_minimum_required(VERSION 3.5.0)
project(server VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${SNDFILE_INCLUDE_DIRS})
link_directories(${SNDFILE_LIBRARY_DIRS})
add_executable(server server.c)
add_executable(client client.c)
target_link_libraries(server ${PULSE_LIBRARIES})
target_link_libraries(server pulse-simple)
target_link_libraries(client sndfile)
include(CTest)
enable_testing()
windows 是設定chaneel 1,16bit 48000Khz
pactl list short sources # 列出音頻輸入設備
pactl list short sinks # 列出音頻輸出設備
pactl load-module module-loopback source=RDPSource sink=RDPSink
cmake_minimum_required(VERSION 3.5.0)
project(server VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${SNDFILE_INCLUDE_DIRS})
link_directories(${SNDFILE_LIBRARY_DIRS})
add_executable(client client.c)
target_link_libraries(client ${PULSE_LIBRARIES})
target_link_libraries(client pulse-simple)
include(CTest)
enable_testing()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <signal.h>
#define BUFFER_SIZE 1024
volatile sig_atomic_t running = 1; // 用於控制迴圈是否繼續的全域變數
// 信號處理函式
void handle_sigint(int sig) {
(void)sig; // 防止未使用參數的警告
running = 0; // 設置 running 為 0,告訴迴圈應該結束
printf("\n收到 Ctrl+C,程式結束。\n");
}
int main() {
signal(SIGINT, handle_sigint);
pa_simple *record_stream = NULL;
pa_simple *playback_stream = NULL;
int error;
pa_sample_spec record_ss = {
.format = PA_SAMPLE_S16LE, // 16-bit PCM, little-endian
.rate = 48000, // 麥克風的採樣率
.channels = 1 // 麥克風是單聲道
};
pa_sample_spec playback_ss = {
.format = PA_SAMPLE_S16LE, // 16-bit PCM, little-endian
.rate = 48000, // 喇叭的採樣率
.channels = 2 // 喇叭是立體聲
};
// 創建錄音 stream (從默認麥克風錄音)
record_stream = pa_simple_new(NULL, "Mic-to-Speaker", PA_STREAM_RECORD, NULL, "record", &record_ss, NULL, NULL, &error);
if (!record_stream) {
fprintf(stderr, "Failed to create PulseAudio record stream: %s\n", pa_strerror(error));
return 1;
}
// 創建播放 stream (播放錄音內容)
playback_stream = pa_simple_new(NULL, "Mic-to-Speaker", PA_STREAM_PLAYBACK, NULL, "playback", &playback_ss, NULL, NULL, &error);
if (!playback_stream) {
fprintf(stderr, "Failed to create PulseAudio playback stream: %s\n", pa_strerror(error));
pa_simple_free(record_stream);
return 1;
}
// 從麥克風錄音並播放
short buffer[BUFFER_SIZE];
short playback_buffer[BUFFER_SIZE * 2]; // 2 channels, so double the size
memset(buffer, 0, sizeof(buffer));
memset(playback_buffer, 0, sizeof(playback_buffer));
do {
if (pa_simple_read(record_stream, buffer, sizeof(buffer), &error) < 0) {
fprintf(stderr, "Failed to read from PulseAudio: %s\n", pa_strerror(error));
break;
}
// 將單聲道複製到立體聲
for (int i = 0; i < sizeof(buffer) / sizeof(short); i++) {
playback_buffer[i * 2] = buffer[i]; // 左聲道
playback_buffer[i * 2 + 1] = buffer[i]; // 右聲道
}
// 播放音頻
if (pa_simple_write(playback_stream, playback_buffer, sizeof(playback_buffer), &error) < 0) {
fprintf(stderr, "Failed to play audio: %s\n", pa_strerror(error));
break;
}
} while (running);
// 清空並釋放 PulseAudio stream
pa_simple_drain(playback_stream, &error);
pa_simple_free(record_stream);
pa_simple_free(playback_stream);
return 0;
}
cmake_minimum_required(VERSION 3.5.0)
project(server VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${SNDFILE_INCLUDE_DIRS})
link_directories(${SNDFILE_LIBRARY_DIRS})
add_executable(server server.c)
add_executable(client client.c)
target_link_libraries(server ${PULSE_LIBRARIES})
target_link_libraries(server pulse-simple)
target_link_libraries(client ${PULSE_LIBRARIES})
target_link_libraries(client pulse-simple)
include(CTest)
enable_testing()
Server 的可以沿用上面的Server (PulseAudio Play Wav Through Socket)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#define SERVER_ADDR "127.0.0.1"
#define PORT 1234
#define BUFFER_SIZE 1024
volatile sig_atomic_t running = 1; // 用於控制迴圈是否繼續的全域變數
// 信號處理函式
void handle_sigint(int sig) {
(void)sig; // 防止未使用參數的警告
running = 0; // 設置 running 為 0,告訴迴圈應該結束
printf("\n收到 Ctrl+C,程式結束。\n");
}
int main() {
signal(SIGINT, handle_sigint);
// 創建 socket
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd < 0) {
perror("socket");
return 1;
}
// 設定 server 地址
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
server_addr.sin_port = htons(PORT);
// 連接到 server
if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("connect");
close(client_fd);
return 1;
}
pa_simple *record_stream = NULL;
int error;
pa_sample_spec record_ss = {
.format = PA_SAMPLE_S16LE, // 16-bit PCM, little-endian
.rate = 48000, // 麥克風的採樣率
.channels = 1 // 麥克風是單聲道
};
// 創建錄音 stream (從默認麥克風錄音)
record_stream = pa_simple_new(NULL, "Mic-to-Speaker", PA_STREAM_RECORD, NULL, "record", &record_ss, NULL, NULL, &error);
if (!record_stream) {
fprintf(stderr, "Failed to create PulseAudio record stream: %s\n", pa_strerror(error));
return 1;
}
// 從麥克風錄音並播放
short buffer[BUFFER_SIZE];
short playback_buffer[BUFFER_SIZE * 2]; // 2 channels, so double the size
memset(buffer, 0, sizeof(buffer));
memset(playback_buffer, 0, sizeof(playback_buffer));
do {
if (pa_simple_read(record_stream, buffer, sizeof(buffer), &error) < 0) {
fprintf(stderr, "Failed to read from PulseAudio: %s\n", pa_strerror(error));
break;
}
// 將單聲道複製到立體聲
for (int i = 0; i < sizeof(buffer) / sizeof(short); i++) {
playback_buffer[i * 2] = buffer[i]; // 左聲道
playback_buffer[i * 2 + 1] = buffer[i]; // 右聲道
}
ssize_t bytes_sent = send(client_fd, playback_buffer, sizeof(playback_buffer), 0);
if (bytes_sent < 0) {
perror("send");
break;
}
} while (running);
// 清空並釋放 PulseAudio stream
pa_simple_free(record_stream);
// 關閉 socket
close(client_fd);
return 0;
}
cmake_minimum_required(VERSION 3.5.0)
project(server VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${SNDFILE_INCLUDE_DIRS})
link_directories(${SNDFILE_LIBRARY_DIRS})
add_executable(server server.c)
add_executable(client client.c)
target_link_libraries(server ${PULSE_LIBRARIES})
target_link_libraries(server pulse-simple)
target_link_libraries(client ${PULSE_LIBRARIES})
target_link_libraries(client pulse-simple)
include(CTest)
enable_testing()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <signal.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#define QOA_IMPLEMENTATION
#define QOA_RECORD_TOTAL_ERROR
#include "qoa.h"
#define SERVER_ADDR "127.0.0.1"
#define PORT 1234
#define BUFFER_SIZE 1024000 //5 secs
volatile sig_atomic_t running = 1; // 用於控制迴圈是否繼續的全域變數
// 信號處理函式
void handle_sigint(int sig) {
(void)sig; // 防止未使用參數的警告
running = 0; // 設置 running 為 0,告訴迴圈應該結束
printf("\n收到 Ctrl+C,程式結束。\n");
}
int main() {
signal(SIGINT, handle_sigint);
// 創建 socket
int client_fd = socket(AF_INET, SOCK_STREAM, 0);
if (client_fd < 0) {
perror("socket");
return 1;
}
// 設定 server 地址
// struct sockaddr_in server_addr;
// memset(&server_addr, 0, sizeof(server_addr));
// server_addr.sin_family = AF_INET;
// server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);
// server_addr.sin_port = htons(PORT);
// 連接到 server
// if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
// perror("connect");
// close(client_fd);
// return 1;
// }
pa_simple *record_stream = NULL;
int error;
pa_sample_spec record_ss = {
.format = PA_SAMPLE_S16LE, // 16-bit PCM, little-endian
.rate = 48000, // 麥克風的採樣率
.channels = 1 // 麥克風是單聲道
};
// 創建錄音 stream (從默認麥克風錄音)
record_stream = pa_simple_new(NULL, "Mic-to-Speaker", PA_STREAM_RECORD, NULL, "record", &record_ss, NULL, NULL, &error);
if (!record_stream) {
fprintf(stderr, "Failed to create PulseAudio record stream: %s\n", pa_strerror(error));
return 1;
}
// 從麥克風錄音並播放
short buffer[BUFFER_SIZE];
short playback_buffer[BUFFER_SIZE * 2]; // 2 channels, so double the size
memset(buffer, 0, sizeof(buffer));
memset(playback_buffer, 0, sizeof(playback_buffer));
do {
if (pa_simple_read(record_stream, buffer, sizeof(buffer), &error) < 0) {
fprintf(stderr, "Failed to read from PulseAudio: %s\n", pa_strerror(error));
break;
}
// 將單聲道複製到立體聲
for (int i = 0; i < sizeof(buffer) / sizeof(short); i++) {
playback_buffer[i * 2] = buffer[i]; // 左聲道
playback_buffer[i * 2 + 1] = buffer[i]; // 右聲道
}
// ssize_t bytes_sent = send(client_fd, playback_buffer, sizeof(playback_buffer), 0);
// if (bytes_sent < 0) {
// perror("send");
// break;
// }
} while (running);
//Save the QOA File
int bytes_written = 0;
qoa_desc desc;
desc.channels = 2;
desc.samplerate = 48000;
desc.samples = (sizeof(buffer) / sizeof(short)) / (desc.channels * (16/8));
bytes_written = qoa_write("sample.qoa", playback_buffer, &desc);
// 清空並釋放 PulseAudio stream
pa_simple_free(record_stream);
// 關閉 socket
close(client_fd);
return 0;
}
cat sample.qoa | nc -q 1 localhost 1234
cmake_minimum_required(VERSION 3.5.0)
project(server VERSION 0.1.0 LANGUAGES C)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED True)
find_package(PkgConfig REQUIRED)
pkg_check_modules(PULSE REQUIRED libpulse)
include_directories(${SNDFILE_INCLUDE_DIRS})
link_directories(${SNDFILE_LIBRARY_DIRS})
add_executable(server server.c)
add_executable(client client.c)
target_link_libraries(server ${PULSE_LIBRARIES})
target_link_libraries(server pulse-simple)
target_link_libraries(client ${PULSE_LIBRARIES})
target_link_libraries(client pulse-simple)
include(CTest)
enable_testing()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pulse/simple.h>
#include <pulse/error.h>
#include <pulse/def.h>
#include <sys/socket.h>
#define QOA_IMPLEMENTATION
#define QOA_RECORD_TOTAL_ERROR
#include "qoa.h"
#define PORT 1234
#define BUFFER_SIZE 1024000
int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
char buffer[BUFFER_SIZE];
ssize_t bytes_received;
// PulseAudio 設定
pa_sample_spec ss = {
.format = PA_SAMPLE_S16LE,
.rate = 48000, // 假設音頻的採樣率為 48kHz
.channels = 2 // 假設音頻為雙聲道
};
int error;
pa_simple *s = pa_simple_new(NULL, "WAV Streamer", PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error);
if (!s) {
fprintf(stderr, "Failed to create PulseAudio connection: %s\n", pa_strerror(error));
return 1;
}
// 創建 socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("socket");
pa_simple_free(s);
exit(EXIT_FAILURE);
}
// 設定 server 地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);
// 綁定 socket
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("bind");
close(server_fd);
pa_simple_free(s);
exit(EXIT_FAILURE);
}
// 監聽 client 連接
if (listen(server_fd, 1) < 0) {
perror("listen");
close(server_fd);
pa_simple_free(s);
exit(EXIT_FAILURE);
}
printf("Waiting for a connection...\n");
// 接受 client 連接
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd < 0) {
perror("accept");
close(server_fd);
pa_simple_free(s);
exit(EXIT_FAILURE);
}
printf("Client connected.\n");
short *sample_data;
bytes_received = 0;
char *buffer_pointer = NULL;
buffer_pointer = buffer;
// 實時接收並播放音頻數據
while ((bytes_received = recv(client_fd, buffer_pointer, 1024, 0)) > 0) {
buffer_pointer += bytes_received;
}
qoa_desc desc;
sample_data = qoa_decode(buffer, sizeof(buffer), &desc);
fprintf(stderr, "desc.samples: %d\n", desc.samples);
fprintf(stderr, "real data: %ld\n", desc.samples * desc.channels * sizeof(short));
if (pa_simple_write(s, (void*)sample_data, desc.samples * desc.channels * sizeof(short), &error) < 0) {
fprintf(stderr, "Failed to write to PulseAudio: %s\n", pa_strerror(error));
}
if (bytes_received < 0) {
perror("recv");
}
// 關閉 socket 和 PulseAudio
close(client_fd);
close(server_fd);
if (pa_simple_drain(s, &error) < 0) {
fprintf(stderr, "Failed to drain PulseAudio: %s\n", pa_strerror(error));
}
pa_simple_free(s);
return 0;
}
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up