GEC6818音乐播放器制作
環境
# linux操作系統 uname -a Linux incipe-virtual-machine 5.4.0-31-generic #35-Ubuntu SMP Thu May 7 20:20:34 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux# 交叉編譯器 arm-linux-gcc -v Using built-in specs. Target: arm-none-linux-gnueabi Configured with: /opt/FriendlyARM/mini2440/build-toolschain/working/src/gcc-4.4.3/configure --build=i386-build_redhat-linux-gnu --host=i386-build_redhat-linux-gnu --target=arm-none-linux-gnueabi --prefix=/opt/FriendlyARM/toolschain/4.4.3 --with-sysroot=/opt/FriendlyARM/toolschain/4.4.3/arm-none-linux-gnueabi//sys-root --enable-languages=c,c++ --disable-multilib --with-arch=armv4t --with-cpu=arm920t --with-tune=arm920t --with-float=soft --with-pkgversion=ctng-1.6.1 --disable-sjlj-exceptions --enable-__cxa_atexit --with-gmp=/opt/FriendlyARM/toolschain/4.4.3 --with-mpfr=/opt/FriendlyARM/toolschain/4.4.3 --with-ppl=/opt/FriendlyARM/toolschain/4.4.3 --with-cloog=/opt/FriendlyARM/toolschain/4.4.3 --with-mpc=/opt/FriendlyARM/toolschain/4.4.3 --with-local-prefix=/opt/FriendlyARM/toolschain/4.4.3/arm-none-linux-gnueabi//sys-root --disable-nls --enable-threads=posix --enable-symvers=gnu --enable-c99 --enable-long-long --enable-target-optspace Thread model: posix gcc version 4.4.3 (ctng-1.6.1)開發板 GEC6818 ,詳情介紹
思路
Ⅰ. 總體框架
Ⅱ. 目錄結構
. ├── include │ ├── get_touch.h │ ├── lcd.h │ ├── play_music.h │ ├── show_bmp.h │ └── ts_init.h ├── Makefile ├── README.md └── sources├── lcd.c├── main.c├── play_music.c├── show_bmp.c└── ts_init.cⅢ. 流程圖
Ⅳ. 需要用的知識
代碼
接下來詳細講解每個模塊的具體功能與作用。
Ⅰ. LCD模塊
LCD是GEC6818的顯示屏,要想展示圖片就必須打開LCD顯示屏。
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h>// lcd 文件描述符 int fd = 0; // 共享映射區首地址 unsigned int *plcd = NULL;/*** 打開lcd屏幕和共享映射區* */ void lcd_init() {fd = open("/dev/fb0", O_RDWR);if (fd == -1) {perror("lcd open error: ");exit(-1);}// 打開共享映射區plcd = (unsigned int *)mmap(NULL, 800 * 480 * 4, PROT_READ | PROT_WRITE,MAP_SHARED, fd, 0);if (plcd == MAP_FAILED) {perror("mmap error: ");exit(-1);} }/*** 關閉lcd屏幕和映射區* */ void lcd_uninit() {// 關閉共享映射區munmap(plcd, 800 * 480 * 4);// 關閉文件描述符close(fd); }打開映射區的目的是為了加快顯示圖片的速度,直接使用 write 函數也是可以的。
Ⅱ. 顯示圖片模塊
在這之前先簡單介紹下 bmp 圖片的存儲格式。
BMP文件通常是不壓縮的,通常比同一幅圖像的壓縮圖像文件格式要大很多。可以參考百度百科 ,這里只介紹存儲格式。
BMP文件組成
例如,偏移量從 0x02 ~ 0x05 表示圖片大小,0x12 ~ 0x15 表示圖片寬, 0x16 ~ 0x19 表示圖片高, 0x1c ~ 0x1d 表示圖片的位深。
把這按照小端拼接起來就可以得到圖片大小,寬高和位深信息。
#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h>extern int fd; extern unsigned int* plcd;/*** 繪制圖片* */ void lcd_drawpoint(int w, int h, unsigned int color) {//(w,h)顯示了color色*(plcd + w + h * 800) = color; }/*** 讀取bmp圖片數據* 從x0,y0處開始顯示一張寬w高h的圖片* */ void show_bmp(int x0, int y0, int w, int h, const char* bmp_file) {int bmp = 0;bmp = open(bmp_file, O_RDONLY);if (-1 == bmp) {perror("open bmp error");exit(-1);}//讀取BMP和DIB數據int ret = 0;// BMP頭和DIB數據unsigned char ch[64] = {0};ret = read(bmp, ch, 54);if (-1 == ret) {perror("read bmp error");exit(-1);} else if (0 == ret) {printf("no read data or file end\n");exit(-1);}// 3.處理數據int file_size = 0;int width = 0, hight = 0, pix_bit = 0;unsigned int color = 0;// rgba位圖unsigned char a, r, g, b;//存儲圖像的位圖數據(各個像素點顏色值分量)unsigned char pix[800 * 480 * 4] = {0};file_size = ch[2] | ch[3] << 8 | ch[4] << 16 | ch[5] << 24;width = ch[0x12] | ch[0x13] << 8 | ch[0x14] << 16 | ch[0x15] << 24;hight = ch[0x16] | ch[0x17] << 8 | ch[0x18] << 16 | ch[0x19] << 24;pix_bit = ch[0x1c] | ch[0x1d] << 8;//讀取位圖數據read(bmp, pix, w * h * pix_bit / 8);int i = 0;for (int y = 0; y < h; y++) {for (int x = 0; x < w; x++) {b = pix[i++];g = pix[i++];r = pix[i++];a = (pix_bit == 32) ? pix[i++] : 0;color = a << 24 | r << 16 | g << 8 | b;lcd_drawpoint(x0 + x, y0 + ((h - 1) - y), color);}}close(bmp); }因為前54個字節,我們是不需要的,所以,文件要偏移54個字節,或者把這54個字節讀取出來。
注:bmp 圖片是沒有透明度選項的,即 rgb 顏色標準。
Ⅲ. 打開觸屏文件
打開觸屏的主要目的是實現上一首下一首,播放暫停功能的實現。
#include <fcntl.h> #include <linux/input.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h>int ts = 0; extern bool flag;/*** 獲取觸摸的x,y坐標* */ void get_touch(int *x, int *y) {int ret;struct input_event ev; //輸入事件結構體變量,用來保存讀取的輸入事件// 1) 打開觸摸屏文件ts = open("/dev/input/event0", O_RDWR);if (-1 == ts) {perror("open input error");exit(-1);}// 2) 讀取觸摸屏事件while (1) {if (flag) {break;}ret = read(ts, &ev, sizeof(ev)); //讀取輸入事件保存到結構體ev中if (ret == sizeof(ev)) {if (ev.type == EV_ABS && ev.code == ABS_X) {*x = ev.value * 0.8; //此時的value是觸摸點X軸的坐標}if (ev.type == EV_ABS && ev.code == ABS_Y) {*y = ev.value * 0.8; //此時的value是觸摸點Y軸的坐標}if (ev.type == EV_KEY && ev.code == BTN_TOUCH && ev.value == 0) {//手指從觸摸屏 離開printf("(x = %d, y = %d)\n", *x, *y);break;}}} }/*** 關閉觸屏板* */ void close_ts() { close(ts); }Ⅳ. 實現音樂播放器功能
介紹下 madplay 的使用。
管理madplay的主程序,包括播放,暫停播放,恢復播放,停止播放 system("madplay 1.mp3 &"); // 利用system函數調用madplay播放器播放*.mp3音樂system("madplay 1.mp3 -r &"); // 循環播放:參數-rsystem("killall -9 madplay"); // 利用system函數調用killall命令將madplay終止掉 system("killall -STOP madplay &"); // 利用system函數調用killall命令將madplay暫停system("killall -CONT madplay &"); // 利用system函數調用killall命令恢復madplay的播放system("madplay 1.mp3 -a volume &");// 初始化播放音量,volume表示音量大小,范圍是 -175 to +18 dB// 更多可以使用man命令查看 // man madplay再介紹下信號:
kill - -l -- list signal names or numbers of specified signals -n -- specify signal number -s -- specify signal name -ABRT -BUS -CONT -HUP -INT -PIPE -PROF -QUIT -STKFLT -SYS -TRAP -TTIN -URG -USR2 -WINCH -XFSZ -ALRM -CHLD -FPE -ILL -KILL -POLL -PWR -SEGV -STOP -TERM -TSTP -TTOU -USR1 -VTALRM -XCPU代碼:
#include <fcntl.h> #include <linux/input.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h>char music[7][6] = {"1.mp3", "2.mp3", "3.mp3", "4.mp3","5.mp3", "6.mp3", "7.mp3"};int order = 0;extern bool isFirst; extern bool isPlay; extern int vol;/*** 播放音樂* 如果是第一次播放就開始播放* 如果不是,就繼續播放* */ void play_music() {if (isFirst) {char command[100] = {0};sprintf(command, "madplay %s -a %d &", music[order], vol);printf("%s\n", command);system(command);} else {system("killall -CONT madplay &");} }/*** 暫停音樂* */ void stop_music() { system("killall -STOP madplay &"); }/*** 下一首* */ void next_music() {system("killall -9 madplay");if (order == 6) {order = -1;}char command[100] = {0};sprintf(command, "madplay %s -a %d &", music[++order], vol);printf("%s\n", command);system(command); }/*** 上一首* */ void pre_music() {system("killall -9 madplay");if (order == 0) {order = 7;}char command[100] = {0};sprintf(command, "madplay %s -a %d &", music[--order], vol);printf("%s\n", command);system(command); }這里播放音樂有個邏輯,就是如果是第一次播放的話,就要開始播放音樂,如果不是的話,就要繼續播放音樂。
另外,上一首下一首功能,要防止數組越界,更簡單的直接取模也是可以的。
Ⅴ. 主函數邏輯功能
#include <fcntl.h> #include <signal.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h>extern void lcd_init(); extern void lcd_uninit(); extern void get_touch(int *, int *); extern void close_ts(); extern void show_bmp(int, int, int, int, const char *); extern void play_music(); extern void stop_music(); extern void next_music(); extern void pre_music();bool isFirst = true; bool isPlay = false; bool flag = false; int vol = 0;/*** 處理信號函數* */ void my_handler(int sig) {// 最好不加printf("End of program, end of code: %d\n", sig);flag = true; }int main(int argc, char *argv[]) {// argv[0] 文件名 argv[1] 音量大小if (argc != 2) {vol = 0;} else {switch (atoi(argv[1])) {case 0:vol = -175;break;case 1:vol = -15;break;case 2:vol = 0;break;case 3:vol = 10;break;}}signal(SIGINT, my_handler);const char *background_bmp = "./bmp/background.bmp";const char *next_bmp = "./bmp/next.bmp";const char *pre_bmp = "./bmp/pre.bmp";const char *pause_bmp = "./bmp/pause.bmp";const char *play_bmp = "./bmp/play.bmp";lcd_init();show_bmp(0, 0, 800, 480, background_bmp);show_bmp(44, 340, 100, 100, pre_bmp);show_bmp(375, 340, 100, 100, pause_bmp);show_bmp(639, 340, 100, 100, next_bmp);// 觸屏得到的坐標int x = 0, y = 0;while (1) {if (flag) {// 不讓程序自動處理ctrl + z/csystem("killall -9 madplay");show_bmp(375, 340, 100, 100, pause_bmp);flag = false;break;}get_touch(&x, &y);if (!flag) {if (375 < x && x < 475 && 340 < y && y < 440) {// 如果正在播放音樂,就停止播放音樂// 如果音樂沒有播放,就開始播放音樂if (isPlay) {stop_music();show_bmp(375, 340, 100, 100, pause_bmp);isPlay = false;} else {play_music();show_bmp(375, 340, 100, 100, play_bmp);isPlay = true;isFirst = false;}}// 上一首音樂if (45 < x && x < 145 && 340 < y && y < 440) {pre_music();show_bmp(375, 340, 100, 100, play_bmp);isFirst = false;isPlay = true;}// 下一首音樂if (639 < x && x < 739 && 340 < y && y < 440) {next_music();show_bmp(375, 340, 100, 100, play_bmp);isFirst = false;isPlay = true;}}}close_ts();lcd_uninit();return 0; }主函數增加了一個 ctrl + c/z 信號處理,不想讓程序幫我處理這個信號,我要自己處理,目的是為了解決直接使用 ctrl + c 結束程序,madplay 還在播放音樂的情況。
其次就是通過觸摸屏得到的觸摸點,進行相應的操作邏輯。
總結
代碼經過編譯,可以成功移植到 GEC1818 開發板上,具體操作,可見 README.md 文件。
源碼地址,github
代碼很大一部分是教課設的粵嵌老師造的輪子,自己的代碼實際工作量不大。
不足之處
總結
以上是生活随笔為你收集整理的GEC6818音乐播放器制作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF三层架构的搭建
- 下一篇: 洛阳智能交通网络升级 辐射西部造福百姓