下載功能實現了,不過懶得多寫,乾脆用 <- 按鍵代替預設下載一首歌。
調節音量madplay做不到,需要自己開啟音效卡驅動,沒做。進度條是擺設。
完成的功能:
播放,上/下一首,雙擊歌名播放,歌詞同步顯示。
運行環境:ARM92t0 s3c2440開發板,linux無介面,qt
開發環境:qt arm-linux-gcc
先在PC機上開發,後移殖到ARM板上。
將就將就吧,做的爛,也有收穫。
:
PC機上開發ARM板上效果
關鍵代碼(QT工程不發了,標頭檔也不發,man一下就知道的,也就看下怎麼實現):
#include "mp3.h"#include "ui_mp3.h"class mp3 *pthis;mp3::mp3(QWidget *parent) : QWidget(parent), ui(new Ui::mp3){ ui->setupUi(this);/************************* TO DO **************************/ this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | Qt::CustomizeWindowHint | Qt::FramelessWindowHint); my_init(); // 自訂的一些初始化動作}mp3::~mp3(){ delete ui;}/************************************TO DO****************************//* 自訂初始化 */void mp3::my_init(){ pid = 0; // 播放進程ID m_mtime = 0; // 上句歌曲時間,從0開始 m_state = 0; pthis = this; fp = NULL; // 歌詞檔案 memset(m_lyric,0,sizeof(m_lyric)); // 上一句歌詞為空白 init_music_list(); // 播放清單 connect(&m_lyric_timer,SIGNAL(timeout()),this,SLOT(show_lyric())); // connect(this,SIGNAL(child_exit()),this,SLOT(next_music())); struct sigaction act; act.sa_handler = sig_fun; sigaction(SIGCHLD,&act,NULL); // 播放進程退出 訊號處理函數}void mp3::next_music(){ on_xyspushButton_clicked();}void mp3::init_music_list(){ int row = ui->namelistWidget->currentRow(); // 儲存當前選中的歌 ui->namelistWidget->clear(); char cwd_buf[80]; /* 擷取目前的目錄 */ getcwd(cwd_buf,sizeof(cwd_buf)); DIR *dir; if((dir = opendir(cwd_buf)) == NULL) { perror("opendir"); QString msg("開啟目錄失敗!"); ui->bbslistWidget->addItem(msg.left(11)); ui->bbslistWidget->scrollToBottom(); } struct dirent *ptr; QString name; int i = 0; /* 遍曆整個目錄 */ while((ptr = readdir(dir)) != NULL) { /*尋找尾碼名為mp3的普通檔案 */ if((ptr->d_type == 8) && strstr(ptr->d_name,".mp3") != NULL) { name = QString(ptr->d_name); ui->namelistWidget->addItem(name); ui->namelistWidget->scrollToBottom(); i++; } } sprintf(cwd_buf,"共有歌曲 %d 首",i); name = cwd_buf; ui->bbslistWidget->clear(); ui->bbslistWidget->addItem(name.left(name.length())); ui->bbslistWidget->scrollToBottom(); if (row<0) row = 0; if (ui->namelistWidget->count()) ui->namelistWidget->setCurrentRow(row); closedir(dir);}/* 播放mp3 */void mp3::play_fun(){ if (pid) { // 播放進程正在運行,先殺掉 kill(pid,SIGKILL); pid = 0; m_mtime = 0; // 恢複定時器為初始狀態 m_lyric_timer.stop(); } // 先獲得要播放歌曲的全路徑 int row = ui->namelistWidget->currentRow(); if (row == -1) { QString qstr = "未選中歌曲"; ui->bbslistWidget->addItem(qstr.left(qstr.length())); ui->bbslistWidget->scrollToBottom(); return; } QString str1 = ui->namelistWidget->item(row)->text(); QByteArray ba = str1.toLocal8Bit(); char *c_str2 = ba.data(); // 調用播放器播放 /* QStringList arguments; arguments<<c_str2; m_qprocess.start("madplay",arguments); sleep(3); execlp("madplay","madplay",c_str2,NULL); pid = m_qprocess.pid(); cout << "ssssssss"<<endl; if (pid == -1) { perror("Qprocess"); return; }*/ pid = fork(); if (pid == -1) { perror("fork"); return; } if (!pid) { execlp("madplay","madplay",c_str2,NULL); perror("execlp madplay"); return; } else { ui->bfpushButton->setText("#"); init_lyric(c_str2); // 準備播放歌詞 }}void mp3::init_lyric(char *lrc_name){ ui->lrclistWidget->clear(); strcpy(&lrc_name[strlen(lrc_name)-4],".lrc"); fp = fopen(lrc_name,"r"); if (fp == NULL) { QString qstr = "當前播放歌曲 "; qstr += lrc_name; qstr += "\n未找到歌詞\n\n\n\n\n"; ui->lrclistWidget->addItem(qstr.left(qstr.length())); ui->lrclistWidget->scrollToBottom(); return; } show_lyric();}/* * 播放鍵按下 */void mp3::on_bfpushButton_clicked(){ if (pid) m_state = 1; if (ui->bfpushButton->text() == "#") { // 表示播放中,則發訊號暫停 ui->bfpushButton->setText("D"); kill(pid,SIGSTOP); return; } else if (pid) { // 如果按鍵符號為D,pid不為0,則表示播放歌曲暫停中,則發訊號播放它 ui->bfpushButton->setText("#"); // 播放後將 D 改為 # 符號 kill(pid,SIGCONT); return; } play_fun(); // 播放}// 歌曲列表雙擊void mp3::on_namelistWidget_doubleClicked(QModelIndex index){ if (pid) m_state = 1; play_fun();}// 下一首歌按下 >>void mp3::on_xyspushButton_clicked(){ if (pid) m_state = 1; int row = ui->namelistWidget->currentRow(); if (row == -1) { QString qstr = "列表空"; ui->bbslistWidget->addItem(qstr.left(qstr.length())); ui->bbslistWidget->scrollToBottom(); return; } else if(row == ui->namelistWidget->count()-1)//最後一首 row = 0; //移動到第一首 else row++; ui->namelistWidget->setCurrentRow(row); play_fun();}// 上一首歌按下 <<void mp3::on_syspushButton_clicked(){ if (pid) m_state = 1; int row = ui->namelistWidget->currentRow(); if (row == -1) { QString qstr = "列表空"; ui->bbslistWidget->addItem(qstr.left(qstr.length())); ui->bbslistWidget->scrollToBottom(); return; } else if (row == 0) row = ui->namelistWidget->count() -1; else row--; ui->namelistWidget->setCurrentRow(row); play_fun();}/* * 顯示歌詞 */void mp3::show_lyric(){ QString lyric; if (m_mtime) { // 時間間隔不為0說明有內容可以輸出 lyric = QString(m_lyric); ui->lrclistWidget->addItem(lyric.left(lyric.length())); ui->lrclistWidget->scrollToBottom(); } char tmp[80] = {}; if (!fgets(tmp,80,fp)) { // 得到檔案的一行,結尾到則退出 m_mtime = 0; m_lyric_timer.stop(); return; } else { int m,s,mm; if (tmp[1] == '0') { // 得到間隔時間 sscanf(tmp,"[%d:%d.%d]",&m,&s,&mm); m_lyric_timer.start(m*60*1000+s*1000+mm*10 - m_mtime); // 設定定時 m_mtime = m*60*1000 + s*1000 +mm*10; } else { m_lyric_timer.start(100); // 說明是開頭部分,定時為1(直接輸出) m_mtime = 1; } } strcpy(m_lyric,read_lyric(tmp)); // 將內容存在 m_lyric中,供定時響應後顯示}/* * 返回真正歌詞 */char* mp3::read_lyric(char *gc){ int begsub = 0; if (strstr(gc,"[ti:") || strstr(gc,"[ar:") || strstr(gc,"[by:")) { begsub = 4; gc[strlen(gc)-3] = '\0'; // 去年前後部分 return &gc[begsub]; } else if (strstr(gc,"[al:]")) return " "; else begsub = 10; // 去掉時間部分 gc[strlen(gc)-2] = '\0'; // 最後的擴號部分去掉 return &gc[begsub];}/* * 播放進程退出,返回SIGCHLD訊號,調用此處理函數 */void sig_fun(int signum){ cout<<"SIGCHLD"<<endl; if (!pthis->pid) return; if (pthis->m_state) { // 判斷是否正常退出 pthis->m_state = 0; return; } pthis->next_music(); // 正常退出則播放下一首}/********************************* 網路 *********************************/char* acp_fun(char *name,char *buf,int fds){ int fd; if ((fd = open(name,O_CREAT | O_RDWR,777)) == -1) { cout<<"建立 mp3 檔案失敗"<<endl; perror("open"); return NULL; } while (1) { write(fd,&buf[4],*(int*)buf); printf("%d\n",*(int*)buf); if (!recv(fds,buf,BUF_SIZE,0)) { cout<<"與伺服器中斷連線"<<endl; perror("recv"); close(fd); return NULL; } if (!strcmp("#end",buf)) { cout<<"歌曲下載完畢"<<endl; close(fd); system("chmod 777 笑紅塵.mp3"); return NULL; } }}void* net_fun(void *arg){ //pthread_detach(pthread_self()); char name[] = "笑紅塵.mp3"; int fds = socket(AF_INET,SOCK_STREAM,0); // 建立通訊端 struct sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(1988); saddr.sin_addr.s_addr = inet_addr("192.168.1.200"); if (!connect(fds,(struct sockaddr*)&saddr,sizeof(saddr))) { cout<<"串連網路成功!"<<endl; // 串連 } else { cout<<"串連網路失敗!"<<endl; perror("connect"); return NULL; } char buf[BUF_SIZE]; strcpy(buf,(char*)name); if (send(fds,buf,BUF_SIZE,0) == -1) { cout<<"發送歌曲名失敗"<<endl; perror("send"); return NULL; } if (!recv(fds,buf,sizeof(buf),0)) { cout<<"與伺服器中斷連線"<<endl; return NULL; } if (!strcmp("#NULL",buf)) { cout<<"未找到歌曲"<<endl; return NULL; } acp_fun((char*)name,buf,fds); close(fds); return NULL;}void mp3::on_yljpushButton_clicked(){ if (pid) m_state = 1; pthread_t tid; // 從伺服器下載歌曲 pthread_create(&tid,NULL,net_fun,NULL); // 這裡設定引一個參數為NULL會出段錯誤}void mp3::on_yljpushButton_2_clicked(){ init_music_list(); // 更新播放清單}
伺服器部分(提供mp3下載)
#include <stdio.h>#include <string.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <errno.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdlib.h>#include <vector>#include <pthread.h>using namespace std;#define BUF_SIZE 512typedef struct args{ char buf[BUF_SIZE]; int fds;}ARGS;void *tfun(void *arg){ ARGS *args = (ARGS*)arg; printf("將開啟檔案%s\n",args->buf); int fd = open(args->buf,O_RDWR,0777); if (fd == -1) { printf("open error\n"); strcpy(args->buf,"#NULL"); // 開啟檔案失敗,則發送未找到歌曲提示 send(args->fds,args->buf,BUF_SIZE,0); return NULL; } int len = 0; while ((len = read(fd,&args->buf[4],BUF_SIZE-4)) > 0) { // 發送全部檔案內容 write(1,&args->buf[4],*args->buf); *(int*)args->buf = len; printf("%d %d\n",len,*(int*)args->buf); if (send(args->fds,args->buf,BUF_SIZE,0) <= 0) return NULL; } strcpy(args->buf,"#end"); // 發送結束提示 send(args->fds,args->buf,BUF_SIZE,0); printf("傳送檔案完畢\n"); delete args;}void therd_fun(int fd,char *t_buf){ ARGS *args =new ARGS; strcpy(args->buf,t_buf); // 儲存歌曲名 args->fds = fd; // 連結對像的檔案描述符 pthread_t t; pthread_create(&t, NULL,tfun,args); // 建立線程}int main(int argc, char* argv[]){ int fds_server = socket(AF_INET,SOCK_STREAM,0); // 建立通訊端 struct sockaddr_in saddr_server; saddr_server.sin_family = AF_INET; saddr_server.sin_port = htons(1988); saddr_server.sin_addr.s_addr = inet_addr("192.168.1.200"); if(bind(fds_server,(struct sockaddr*)&saddr_server,sizeof(struct sockaddr_in))) { printf("bind error!\n"); // 綁定 return -1; } listen(fds_server,5); // 設為監聽 fd_set set_read; int fds_hwm = fds_server; // 最大檔案描述符 int fds_client = -1; struct sockaddr_in saddr_client; socklen_t len; vector<int> v_fd; // 容器 儲存串連 vector<int>::iterator ite; char buf[BUF_SIZE]; while (1) { FD_ZERO(&set_read); // 初始化讀 FD_SET(fds_server,&set_read); ite = v_fd.begin(); while (ite != v_fd.end()) { // 將串連進的檔案描述符放入 FD_SET(*ite,&set_read); ite++; } select(fds_hwm+1,&set_read,NULL,NULL,NULL); // 有串連 if (FD_ISSET(fds_server,&set_read)) { fds_client = accept(fds_server,(struct sockaddr*) &saddr_client,&len); v_fd.push_back(fds_client); printf("有新串連\n"); if (fds_hwm < fds_client) fds_hwm = fds_client; } // 可讀 ite = v_fd.begin(); while (ite != v_fd.end()) { // 遍曆 if (FD_ISSET(*ite,&set_read)) if (!recv(*ite,buf,BUF_SIZE,0)) { // 有串連斷開 if (fds_hwm == fds_client) fds_hwm--; else ; printf("有串連斷開\n"); v_fd.erase(ite); continue; } else therd_fun(*ite,buf); // 有傳送 MP3 請求,建議線程處理 ite++; } }}