專案 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;
}
沒有留言:
張貼留言