Advertisement
Guest User

joulupukkibiisi.cpp

a guest
Nov 23rd, 2014
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.07 KB | None | 0 0
  1. #if 0
  2. #!/bin/sh
  3. exec c++ -Wall -std=c++11 "$0" -o `echo "$0" | sed -e s/\.[^\.]*$//g`
  4. exit
  5. #endif
  6.  
  7. /*
  8.    huumoriBiisi.cpp (F) Petteri Mรคki 2014
  9.    
  10.    This is free and unencumbered software released into the public domain.
  11.    
  12.    Anyone is free to copy, modify, publish, use, compile, sell, or
  13.    distribute this software, either in source code form or as a compiled
  14.    binary, for any purpose, commercial or non-commercial, and by any
  15.    means.
  16.    
  17.    In jurisdictions that recognize copyright laws, the author or authors
  18.    of this software dedicate any and all copyright interest in the
  19.    software to the public domain. We make this dedication for the benefit
  20.    of the public at large and to the detriment of our heirs and
  21.    successors. We intend this dedication to be an overt act of
  22.    relinquishment in perpetuity of all present and future rights to this
  23.    software under copyright law.
  24.    
  25.    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  26.    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  27.    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  28.    IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
  29.    OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  30.    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  31.    OTHER DEALINGS IN THE SOFTWARE.
  32.    
  33.    For more information, please refer to <http://unlicense.org/>
  34.  */
  35.  
  36. #include <algorithm>
  37. #include <cmath>
  38. #include <cstdint>
  39. #include <deque>
  40. #include <fstream>
  41. #include <iostream>
  42. #include <map>
  43. #include <set>
  44. #include <sstream>
  45. #include <string>
  46.  
  47. using namespace std;
  48.  
  49. typedef float Real;
  50. typedef unsigned int uint;
  51.  
  52. // Works with unsigned integers. Not sure about signed integers.
  53. template <typename integer>
  54. class little_endian {
  55. public:
  56.     little_endian(integer x) {
  57.         for (size_t i = 0; i < sizeof(integer); i++, x>>=8)
  58.             data[i] = (x & 0xff);
  59.     }
  60.     operator integer () const {
  61.         integer result = 0;
  62.         for (size_t i = 0; i < sizeof(integer); i++)
  63.             result |= (data[i] << (8*i));
  64.     }
  65. private:
  66.     uint8_t data[sizeof(integer)];
  67. };
  68.  
  69. typedef little_endian<uint32_t> uint32le;
  70. typedef little_endian<uint16_t> uint16le;
  71.  
  72. template <typename T>
  73. class Clamp {
  74. public:
  75.     Clamp(const T & lower, const T & upper) : lower_(lower), upper_(upper) { }
  76.     const T & operator()(const T & a) const { return a < lower_ ? lower_ : upper_ < a ? upper_ : a; }
  77. private:
  78.     T lower_, upper_;
  79. };
  80.  
  81. struct Snd_Buf {
  82.     deque<Real> buf;
  83.     uint smpl_Rt;
  84. };
  85.  
  86. struct Note {
  87.     uint8_t midiPitch;
  88.     uint start, length;
  89. };
  90.  
  91. struct Note_Compare{
  92.     bool operator()(const Note & a, const Note & b) const {
  93.         return a.start != b.start ? a.start < b.start : a.length != b.length ? a.length < b.length : a.midiPitch < b.midiPitch;
  94.     }
  95. };
  96.  
  97. typedef set<Note, Note_Compare> NoteSet;
  98.  
  99. class WriteWav {
  100. public:
  101.     template <class ByteWriter>
  102.     static void execute(const Snd_Buf & sb, ByteWriter bw) {
  103.         // https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
  104.         // write as signed 16-bit mono PCM.
  105.         writestr(bw, "RIFF");
  106.         writeobjbytes(bw, uint32le(36+2*sb.buf.size()));
  107.         writestr(bw, "WAVE");
  108.         writestr(bw, "fmt ");
  109.         writeobjbytes(bw, uint32le(16));
  110.         writeobjbytes(bw, uint16le(1)); // PCM
  111.         writeobjbytes(bw, uint16le(1)); // mono
  112.         writeobjbytes(bw, uint32le(sb.smpl_Rt));
  113.         writeobjbytes(bw, uint32le(2*sb.smpl_Rt));
  114.         writeobjbytes(bw, uint16le(2));
  115.         writeobjbytes(bw, uint16le(16));
  116.         writestr(bw, "data");
  117.         writeobjbytes(bw, uint32le(2*sb.buf.size()));
  118.         for (Real smpl : sb.buf)
  119.             writeobjbytes(bw, uint16le(0x7777*Clamp<Real>(-1.0, 1.0)(smpl)));
  120.     }
  121. private:
  122.     template <class ByteWriter, typename T>
  123.     static void writeobjbytes(ByteWriter bw, const T & a) {
  124.         for (size_t i =0; i < sizeof(T); i++)
  125.             bw(((uint8_t*)&a)[i]);
  126.     }
  127.     template <class ByteWriter, class ByteSequence>
  128.     static void writebytes(ByteWriter bw, const ByteSequence & a) {
  129.         for_each(a.begin(), a.end(), bw);
  130.     }
  131.     template <class ByteWriter>
  132.     static void writestr(ByteWriter bw, const string & a){
  133.         writebytes(bw, a);
  134.     }
  135. };
  136.  
  137. /*
  138.     Format of token: (~|([a-g][#b]?(,*|'*)|r)[1-9][0-9]*)
  139.    
  140.     The bind_next ('~') operator is currently a prefix notation,
  141.     instead of the intended infix notation.
  142.  */
  143. NoteSet parseSongstr(const string & songstr) {
  144.     static const map<char, uint8_t> charPitches{ { 'a', 69 }, { 'b', 71 }, { 'c', 60 }, { 'd', 62 }, { 'e', 64 }, { 'f', 65 }, { 'g', 67 }, { 'r', 0 } };
  145.     NoteSet result;
  146.     istringstream iss(songstr);
  147.     bool bind_next = false;
  148.     uint time = 0;
  149.     for (;;) {
  150.         string token;
  151.         iss >> token;
  152.         if (token.empty())
  153.             break;
  154.         istringstream tok_iss(token);
  155.         int pitchCh = tok_iss.get();
  156.         // token '~' check
  157.         if (pitchCh == EOF || charPitches.count(pitchCh) == 0) {
  158.             if (pitchCh == '~' && tok_iss.get() == EOF) {
  159.                 bind_next = true;
  160.                 continue;
  161.             }
  162.             else
  163.                 break; // error.
  164.         }
  165.         bool rest = (pitchCh == 'r');
  166.         int octaveShift = 0, semitoneShift = 0;
  167.         if (!rest) {
  168.             // [#b]?
  169.             int c = tok_iss.peek();
  170.             if (c == '#') {
  171.                 semitoneShift++;
  172.                 tok_iss.get();
  173.             }
  174.             else if (c == 'b') {
  175.                 semitoneShift--;
  176.                 tok_iss.get();
  177.             }
  178.             // (,*|'*)
  179.             c = tok_iss.peek();
  180.             if (c == ',' || c == '\'') {
  181.                 int shiftDir = (c == '\'' ? +1 : -1);
  182.                 do {
  183.                     octaveShift += shiftDir;
  184.                     tok_iss.get();
  185.                 } while (tok_iss.peek() == c);
  186.             }
  187.         }
  188.         // length the Number
  189.         uint length;
  190.         tok_iss >> length;
  191.         if (tok_iss.fail() && tok_iss.get() != EOF)
  192.             break; // error
  193.         if (!rest)
  194.             result.insert(Note{ uint8_t(charPitches.at(pitchCh)+12*octaveShift+semitoneShift), time, length });
  195.         if (!bind_next)
  196.             time += length;
  197.         bind_next = false;
  198.     }
  199.     return result;
  200. }
  201.  
  202. const uint smpl_Rt = 88200;
  203. const uint sngLngth = 6;
  204. const Real timeunit_s = 1./6.;
  205. const Real volume = 0.5;
  206.  
  207. // Joulupukki (C) P. J. Hannikainen
  208. // https://fi.wikipedia.org/wiki/P._J._Hannikainen
  209. // Copyright probably expired as of 1995.
  210. const string sng = "~ c,,6 c2 d1 e3 ~ e,,3 c3   ~ f,,9 a,2 b,1 c3 a,3   ~ g,,6 f2 e1 d2 c1 ~ e,,3 b,2 g,1   ~ c,,6 c3 c1";
  211.  
  212. namespace waveform {
  213.  
  214. Real sine(Real x) {
  215.     return sin(2*M_PI*x);
  216. }
  217.  
  218. Real saw(Real x) {
  219.     return 2*(x-floor(x+Real(0.5)));
  220. }
  221.  
  222. Real square(Real x) {
  223.     return saw(x)-saw(x+Real(0.5));
  224. }
  225.  
  226. Real triangle(Real x) {
  227.     return 2*abs(saw(x+Real(.5)))-1;
  228. }
  229.  
  230. }
  231.  
  232. int main(int argc, char const * const * argv) {
  233.     Snd_Buf sngSmpls{ deque<Real>(smpl_Rt*sngLngth), smpl_Rt };
  234.     NoteSet notes(parseSongstr(sng));
  235.     for (auto n : notes) {
  236.         cerr << int(n.midiPitch) << ' ' << n.start << ' ' << n.length << endl;
  237.     }
  238.     for (auto n : notes) {
  239.         try {
  240.             for (size_t i = 0; i < n.length*timeunit_s*sngSmpls.smpl_Rt; i++)
  241.                 sngSmpls.buf.at(n.start*timeunit_s*sngSmpls.smpl_Rt + i) += volume*(1.-exp(-100.*((n.length*timeunit_s*sngSmpls.smpl_Rt)-i)/sngSmpls.smpl_Rt))*(1.-exp(-100.*Real(i)/sngSmpls.smpl_Rt))*(.5+.5*exp(-10.*Real(i)/sngSmpls.smpl_Rt))*waveform::saw(i/Real(sngSmpls.smpl_Rt)*pow(2, (n.midiPitch-69.)/12.)*440);
  242.         } catch (exception & e) {
  243.             cerr << e.what() << endl;
  244.         }
  245.     }
  246.     WriteWav::execute(sngSmpls, [](char c){ cout.put(c); });
  247. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement