Mercurial > pmdwin
diff pmd_play.c @ 0:c55ea9478c80
Hello Gensokyo!
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 21 May 2013 10:29:21 +0200 |
parents | |
children | c8875256b767 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/pmd_play.c @@ -0,0 +1,252 @@ +#include <stdint.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/stat.h> +#include <math.h> +#include <getopt.h> +#if defined(WIN32) || defined(WIN64) +#include <windows.h> +#include <mmsystem.h> +int wave_out_open(HWAVEOUT *hWaveOut, uint32_t rate, uint8_t channels); +void wave_out_close(HWAVEOUT hWaveOut); +void writeAudio(HWAVEOUT hWaveOut, void *data, uint32_t size); +#else +int oss_audio_open(uint32_t oss_audio_rate, uint8_t nchannels); +int alsa_audio_open(uint32_t *rate, uint8_t channels, uint32_t *_ver, int mode); +int alsa_audio_close(int snd_fd); +long snd_pcm_writei(int snd_fd, const void *buffer, unsigned long size); +int snd_pcm_prepare(int snd_fd); +#ifdef USE_ALSA +#include <errno.h> +#endif +#endif +#include "pmdwin.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +static uint32_t atoui(const uint8_t *str) { + uint32_t c, num = 0; + while(1) { + c = *str++ - 0x30; + if(c > 9) break; + num = (num << 1) + (num << 3) + c; + } + return num; +} + +static const unsigned char static_hdr_portion[20] = { + 0x52, 0x49, 0x46, 0x46, 0xFF, 0xFF, 0xFF, 0x7F, + 0x57, 0x41, 0x56, 0x45, 0x66, 0x6D, 0x74, 0x20, + 0x10, 0x00, 0x00, 0x00 +}; + +typedef struct _WAVHeader { + uint16_t wav_id; + uint16_t channels; + uint32_t samplerate; + uint32_t bitrate; + uint32_t block_align; + uint32_t pad0; + uint32_t pad1; +} __attribute__((packed)) WAVHeader; + +static void write_wav_header(int fd, uint32_t rate) +{ + WAVHeader w; + write(fd, &static_hdr_portion, 20); + + w.wav_id = 1; + w.channels = 1; + w.samplerate = rate; + w.bitrate = rate*2; + w.block_align = 0x00100010; + w.pad0 = 0x61746164; + w.pad1 = 0x7fffffff; + write(fd, &w, sizeof(WAVHeader)); +} + +//============================================================================= +// Load PMD data from file into memory. Uses mmap() on both Windows and *nix. +// Originally in pmdwin.cpp, moved here to minimize platform-specific code +// in pmdwin.cpp +//============================================================================= +#if defined(WIN32) || defined(WIN64) +static int music_load(char *filename) +{ + HANDLE mapped_file, h; + int result; + size_t size; + uchar *musbuf; + struct stat st; + + mapped_file = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + h = CreateFileMapping(mapped_file, NULL, PAGE_READWRITE, 0, 0, NULL); + if(!h) { + return ERR_OPEN_MUSIC_FILE; + } + stat(filename, &st); + musbuf = (uchar*)MapViewOfFile(h, FILE_MAP_READ, 0, 0, 0); + CloseHandle(h); + CloseHandle(mapped_file); + size = st.st_size; + result = music_load3(musbuf, size); + return result; +} +#else +static int music_load(char *filename) +{ + int fd = -1; + int result; + size_t size; + uchar musbuf[mdata_def*1024]; + + if((fd = open(filename, O_RDONLY)) < 0) { + return ERR_OPEN_MUSIC_FILE; + } + + size = read(fd, musbuf, sizeof(musbuf)); + close(fd); + result = music_load3(musbuf, size); + return result; +} +#endif + +#if defined(WIN32) || defined(WIN64) +#define WriteToConsole(str, len) WriteFile(errh, (str), (len), &done, NULL) +#else +#define WriteToConsole(str, len) write(2, (str), (len)) +#endif + +static void usage(void) { +#if defined(WIN32) || defined(WIN64) + DWORD done; + HANDLE errh = GetStdHandle(STD_ERROR_HANDLE); +#endif + const char *usage_str = +"pmdwin -f <output file> -l <loop count> -m <device mask> -c <channel mask> -t <time> [PMD File]\n\ + -f - Write output to wavfile\n\ + -l - Loop n times (default is once - use 0 for infinite loop)\n\ + -m - Device mask: 1 - OPNA, 2 - PSG, 4 - Rhythm (or them together)\n\ + -c - Channel mask: 1,2,4,8,16,32 are OPNA channels 1-6, 64,128,256 are PSG channels 1-3. Once again, or them together\n\ + -t - Play song for n seconds\n\n"; + WriteToConsole(usage_str, strlen(usage_str)); +} + +int main(int argc, char **argv) { + uint32_t i, frameno, samplerate_out = SOUND_44K, infloop = 0; + uint32_t pmd_length = 0, pmd_loopcount = 2; + int opt, j, out_fd = -1; + uint8_t tofile = 0; + int16_t pcmbuf[8192]; + char pmd_title[1024]; + char pmd_compo[1024]; +#if defined(WIN32) || defined(WIN64) + DWORD done; + HANDLE errh = GetStdHandle(STD_ERROR_HANDLE); + HWAVEOUT hWaveOut; +#endif + char buf[1024]; + pmdwininit(); + + while((opt = getopt(argc, argv, "f:l:m:c:t:r:")) != -1) { + switch(opt) { + case 'f': + tofile = 1; + out_fd = open(optarg, O_WRONLY|O_CREAT|O_APPEND|O_BINARY, 0644); + break; + case 'l': + pmd_loopcount = atoui((uint8_t*)optarg); + if(!pmd_loopcount) infloop = 1; + break; + case 'c': + setchanmask(atoui((uint8_t*)optarg)); + break; + case 'm': + setdevmask(atoui((uint8_t*)optarg)); + break; + case 't': + pmd_length = atoui((uint8_t*)optarg)*1000; + break; + case 'r': + samplerate_out = atoui((uint8_t*)optarg); + break; + default: + usage(); + break; + } + } + + if(!tofile) { +#if defined(WIN32) || defined(WIN64) + if(!wave_out_open(&hWaveOut, samplerate_out, 1)) { +#else +#ifdef USE_ALSA + if((out_fd = alsa_audio_open(&samplerate_out, 1, NULL, 0)) < 0) { +#else + if((out_fd = oss_audio_open(samplerate_out, 1)) < 0) { +#endif +#endif + WriteToConsole("Cannot open sound device, exiting.\n", 35); + return -1; + } + } else { + write_wav_header(out_fd, samplerate_out); + } + music_load(argv[optind]); + memcpy(pmd_title, "Title = ", 8); + getmemo3(pmd_title+8, NULL, 0, 1); + i = strlen(pmd_title); pmd_title[i++] = '\n'; +#if defined(WIN32) || defined(WIN64) + SetConsoleOutputCP(65001); // UTF-8 +#endif + WriteToConsole(pmd_title, i); + memcpy(pmd_compo, "Composer = ", 11); + getmemo3(pmd_compo+11, NULL, 0, 2); + i = strlen(pmd_compo); pmd_compo[i++] = '\n'; + WriteToConsole(pmd_compo, i); + setpcmrate(samplerate_out); + music_start(); + i = 0; + if(pmd_length != 0) { + frameno = lrintf(((float)pmd_length*(float)samplerate_out)/(4096.0f*1000.0f)); + } else { + frameno = 0; + } + buf[0] = '\r'; + while(getloopcount() < pmd_loopcount || infloop || i < frameno) { + int ret = getstatus(buf+1, 1022); + WriteToConsole(buf, ret+1); + getpcmdata(pcmbuf, 4096); +#if defined(WIN32) || defined(WIN64) + if(!tofile) { + writeAudio(hWaveOut, pcmbuf, 8192); + } else +#endif +#ifdef USE_ALSA + if(!tofile) { + j = snd_pcm_writei(out_fd, pcmbuf, 2048); + if(j == -EPIPE) { + snd_pcm_prepare(out_fd); + } + } else +#endif + write(out_fd, pcmbuf, 8192); + i++; + } + WriteToConsole("\n", 1); +#if defined(WIN32) || defined(WIN64) + if(!tofile) { + wave_out_close(hWaveOut); + } else +#endif +#ifdef USE_ALSA + alsa_audio_close(out_fd); +#else + close(out_fd); +#endif + return 0; +} +