2024年4月10日 星期三

簡單的 CMakeLists.txt 用來編譯並連結程式庫

專案 mp3play 的目錄結構:
mp3play/
    ./CMakeList.txt
    ./lib/
        ./*.c
    ./src/
        ./main.cpp    
    ./include/
        ./*.h

# 檔案 CMakeLists.txt 內容
cmake_minimum_required(VERSION 3.16)
project(mp3play)
set(mp3Lib  "${PROJECT_SOURCE_DIR}/lib")
set(decoder "${PROJECT_SOURCE_DIR}/src")
include_directories($(decoder)  ${PROJECT_SOURCE_DIR}/include)
file(GLOB_RECURSE  cLib  ${mp3Lib}/*.c)
add_library (myLib ${cLib})
add_executable(mp3play
    ${decoder}/main.cpp
)
target_link_libraries(mp3play  myLib  -lasound)

要編譯時, 先執行以下命令產生 Makefile 檔案
    cd  mp3play &&  make build  &&  cd build  &&  cmake  ..
接著再執行 make 就能產生執行擋了
    cd  mp3play/build   &&  make   &&  ./mp3play

// 主程式: mp3play/src/main.cpp
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include "alsa/asoundlib.h"
#include "mp3dec.h"
struct PCM16 {
    int16_t *data;
    int length;
    int channel;
    int fs;
};
const PCM16 zeroPCM16 = {.data = (int16_t *)0, .length = 0, .channel = 0, .fs = 0};
const char *path_to_close = "./close!";
auto stepDecoder = [](const void *path = nullptr, int repeate = 0) {
    static void *mmapFile = MAP_FAILED; // to management mmap file
    static HMP3Decoder decoder = nullptr;
    static int32_t length = 0;// mmap file length
    static int32_t position = 0; // trace file position
    static int32_t remainRepeate = 0;
    if (path == path_to_close) { // to close decoder
        if (decoder) {
            MP3FreeDecoder(decoder);
            decoder = nullptr;
            length = 0;
            position = 0;
            remainRepeate = 0;
        }
    } else {
        if (path) {
            const char *filename = (const char *)path;
            int fd = open(filename, O_RDONLY);
            if (mmapFile != MAP_FAILED) munmap(mmapFile, length);
            if (fd < 0) {
                printf("%s => not found.\n", filename);
            } else {
                int fileLength = lseek(fd, 0l, SEEK_END);
                if (fileLength >= 512) {
                    mmapFile = mmap(0, fileLength, PROT_READ, MAP_PRIVATE, fd, 0);
                    if (mmapFile == MAP_FAILED)  {
                        printf("%s mmap fail.\n", filename);
                    } else {
                        if (decoder) MP3FreeDecoder(decoder);
                        decoder = MP3InitDecoder();
                        printf("%s file length = %d\n", filename, fileLength);
                        length = fileLength;// mmap sucess
                        position = 0;
                        remainRepeate = repeate;                   
                    }
                }
                close(fd);// After the mmap() call, fd can be closed immediately.
            }
        }
        if (remainRepeate > 0 && (position >= length)) {
            printf("End of file, position = %d, wrap arond to repeate again. remainRepeate = %d\n", position, remainRepeate);
            remainRepeate --;
            position = 0;
        }
        if (position < length) {
            unsigned char *src = (unsigned char *)mmapFile + position;
            static int16_t pcmdist[1152 * 2];
            const uint32_t maxSteps = length - position;
            int tempSteps = maxSteps;// tempSteps will be updated by decoder
            for (int i = 0; i < maxSteps; i ++) {
                if (*src == 0xff && (src[1] >> 4) == 0xf) break;// find sync word
                tempSteps --; // size shrink
                src ++;  // seek one by one
            }
             int result = MP3Decode(decoder, &src, &tempSteps, pcmdist, 0);
            position += maxSteps - tempSteps; // go ahead, and back off by tempSteps
            printf("maxSteps = %8d, tempSteps = %8d, position = %8d, steps = %8d, err = %4x:\t\t\n",
                maxSteps,
                tempSteps,
                position,
                maxSteps - tempSteps,
                (unsigned)result
            );            
            if (result == ERR_MP3_NONE) {
                MP3FrameInfo frameinfo;
                MP3GetLastFrameInfo(decoder, &frameinfo);
                int nChans = frameinfo.nChans;
                return PCM16 {
                    .data = pcmdist,
                    .length = (int)frameinfo.outputSamps / nChans,
                    .channel = nChans,
                    .fs = frameinfo.samprate
                };
            }
        }
    }
    return zeroPCM16;
};
int main(int argc, char const *argv[]) {    
    PCM16 frame = stepDecoder((argc > 1) ? argv[1] : "test.mp3");
     if (frame.length==0) return 0;
    snd_pcm_t *handle;
    if (snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0) == 0) {
        if (snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, frame.channel, frame.fs, 1, frame.fs / 4) == 0) {    
            do {
                 int err = snd_pcm_writei(handle, frame.data, frame.length);
                 if (err < 0) {// try to recover
                     if (snd_pcm_recover(handle, err, 0) < 0) {
                        printf("alsa can't recover\n");
                        break;
                    }
                 }
                frame = stepDecoder();
            } while (frame.length);
        }
        snd_pcm_close(handle);
    }   
    stepDecoder(path_to_close);
    return 0;
}

沒有留言:

張貼留言

使用 pcie 轉接器連接 nvme SSD

之前 AM4 主機板使用 pcie ssd, 但主機板故障了沒辦法上網, 只好翻出以前買的 FM2 舊主機板, 想辦法讓老主機復活, 但舊主機板沒有 nvme 的界面, 因此上網買了 pcie 轉接器用來連接 nvme ssd, 遺憾的是 grub2 bootloader 無法識...