summary refs log tree commit diff stats
path: root/src/muxer.cpp
blob: d5409e5d799c9ec28b7789e875c815aeeb1cd216 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#include "muxer.h"
#include <cstdlib>
#include <sndfile.h>
#include <portaudio.h>
#include <list>
#include <cmath>

#define SAMPLE_RATE (44100)

class Sound {
  public:
    Sound(const char* filename, float vol);
    ~Sound();
    
    float* ptr;
    unsigned long pos;
    unsigned long len;
    float vol;
};

struct Muxer {
  std::list<Sound> playing;
  PaStream* stream;
};

inline void dealWithPaError(PaError err)
{
  if (err != paNoError)
  {
    printf("PortAudio error: %s\n", Pa_GetErrorText(err));
    exit(-1);
  }
}

int paMuxerCallback(const void*, void* outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo*, PaStreamCallbackFlags, void* userData)
{
  Muxer* muxer = (Muxer*) userData;
  float* out = (float*) outputBuffer;
  
  for (unsigned long i = 0; i<framesPerBuffer; i++)
  {
    unsigned long curAmount = 0;
    *out = 0;
    
    for (auto& sound : muxer->playing)
    {
      if (sound.pos < sound.len)
      {
        *out += sound.ptr[sound.pos++] * sound.vol;
      }
    }
    
    out++;
  }
  
  return 0;
}

static Muxer* muxer;

void initMuxer()
{
  muxer = new Muxer();
  
  dealWithPaError(Pa_Initialize());
  dealWithPaError(Pa_OpenDefaultStream(&(muxer->stream), 0, 1, paFloat32, SAMPLE_RATE, paFramesPerBufferUnspecified, paMuxerCallback, muxer));
  dealWithPaError(Pa_StartStream(muxer->stream));
}

void destroyMuxer()
{
  dealWithPaError(Pa_AbortStream(muxer->stream));
  dealWithPaError(Pa_CloseStream(muxer->stream));
  dealWithPaError(Pa_Terminate());
  
  delete muxer;
  muxer = 0;
}

void playSound(const char* filename, float vol)
{
  // First, clear out any sounds that have finished playing
  muxer->playing.remove_if([] (Sound& value) { return value.pos >= value.len; });
  
  // Then, add the new sound
  muxer->playing.emplace_back(filename, vol);
}

Sound::Sound(const char* filename, float vol)
{
  SF_INFO info;
  SNDFILE* file = sf_open(filename, SFM_READ, &info);
  if (file == nullptr)
  {
    printf("LibSndFile error: %s\n", sf_strerror(file));
    exit(-1);
  }
  
  ptr = (float*) malloc(info.frames * info.channels * sizeof(float));
  len = info.frames * info.channels;
  pos = 0;
  this->vol = vol;
  
  sf_readf_float(file, ptr, info.frames);
  
  sf_close(file);
}

Sound::~Sound()
{
  free(ptr);
}