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;
+}
+