Mercurial > pmdwin
view pmd_play.c @ 4:c8875256b767
Use stdio for console output, update usage, and use sensible exit values.
author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
---|---|
date | Tue, 21 May 2013 12:10:27 +0200 |
parents | c55ea9478c80 |
children | c4218fbe158f |
line wrap: on
line source
#include <stdio.h> #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 static void usage(char *argv0) { const char *usage_str = "%s [-f <output file>] [-l <loop count>] [-m <device mask>] [-c <channel mask>] [-t <time>] [-r <samplerate>] <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\ -r - Set the output sample rate\n\n"; fprintf(stderr, usage_str, argv0); } 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) 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(argv[0]); return 1; } } 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 fprintf(stderr, "Cannot open sound device, exiting.\n"); return 3; } } else { write_wav_header(out_fd, samplerate_out); } if (!argv[optind] || optind + 1 != argc) { usage(argv[0]); return 1; } if (music_load(argv[optind]) == ERR_OPEN_MUSIC_FILE) { fprintf(stderr, "Cannot open music file ā%sā, exiting.\n", argv[optind]); return 2; } getmemo3(pmd_title, NULL, 0, 1); #if defined(WIN32) || defined(WIN64) SetConsoleOutputCP(65001); // UTF-8 #endif printf("Title = %s\n", pmd_title); getmemo3(pmd_compo, NULL, 0, 2); printf("Composer = %s\n", pmd_compo); 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; } while(getloopcount() < pmd_loopcount || infloop || i < frameno) { getstatus(buf, 1023); printf("\r%s", buf); fflush(stdout); 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++; } fputc('\n', stdout); #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; }