all repos — xyw @ 22fff66e4740a575cab8d813b2d8d6be68022cd2

A minimal virtual machine and assembler for terminals.

fenster_audio.h

 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
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
#ifndef FENSTER_AUDIO_H
#define FENSTER_AUDIO_H

#ifndef FENSTER_SAMPLE_RATE
#define FENSTER_SAMPLE_RATE 44100
#endif

#ifndef FENSTER_AUDIO_BUFSZ
#define FENSTER_AUDIO_BUFSZ 8192
#endif

#if defined(__APPLE__)
#include <AudioToolbox/AudioQueue.h>
struct fenster_audio {
  AudioQueueRef queue;
  size_t pos;
  float buf[FENSTER_AUDIO_BUFSZ];
  dispatch_semaphore_t drained;
  dispatch_semaphore_t full;
};
#elif defined(_WIN32)
#include <mmsystem.h>
#include <windows.h>
struct fenster_audio {
  WAVEHDR header;
  HWAVEOUT wo;
  WAVEHDR hdr[2];
  int16_t buf[2][FENSTER_AUDIO_BUFSZ];
};
#elif defined(__linux__)
struct fenster_audio {
  void *pcm;
  float buf[FENSTER_AUDIO_BUFSZ];
  size_t pos;
};
#endif

#ifndef FENSTER_API
#define FENSTER_API extern
#endif
FENSTER_API int fenster_audio_open(struct fenster_audio *f);
FENSTER_API int fenster_audio_available(struct fenster_audio *f);
FENSTER_API void fenster_audio_write(struct fenster_audio *f, float *buf,
                                     size_t n);
FENSTER_API void fenster_audio_close(struct fenster_audio *f);

#ifndef FENSTER_HEADER
#if defined(__APPLE__)
static void fenster_audio_cb(void *p, AudioQueueRef q, AudioQueueBufferRef b) {
  struct fenster_audio *fa = (struct fenster_audio *)p;
  dispatch_semaphore_wait(fa->full, DISPATCH_TIME_FOREVER);
  memmove(b->mAudioData, fa->buf, sizeof(fa->buf));
  dispatch_semaphore_signal(fa->drained);
  AudioQueueEnqueueBuffer(q, b, 0, NULL);
}
FENSTER_API int fenster_audio_open(struct fenster_audio *fa) {
  AudioStreamBasicDescription format = {0};
  format.mSampleRate = FENSTER_SAMPLE_RATE;
  format.mFormatID = kAudioFormatLinearPCM;
  format.mFormatFlags = kLinearPCMFormatFlagIsFloat | kAudioFormatFlagIsPacked;
  format.mBitsPerChannel = 32;
  format.mFramesPerPacket = format.mChannelsPerFrame = 1;
  format.mBytesPerPacket = format.mBytesPerFrame = 4;
  fa->drained = dispatch_semaphore_create(1);
  fa->full = dispatch_semaphore_create(0);
  AudioQueueNewOutput(&format, fenster_audio_cb, fa, NULL, NULL, 0, &fa->queue);
  for (int i = 0; i < 2; i++) {
    AudioQueueBufferRef buffer = NULL;
    AudioQueueAllocateBuffer(fa->queue, FENSTER_AUDIO_BUFSZ * 4, &buffer);
    buffer->mAudioDataByteSize = FENSTER_AUDIO_BUFSZ * 4;
    memset(buffer->mAudioData, 0, buffer->mAudioDataByteSize);
    AudioQueueEnqueueBuffer(fa->queue, buffer, 0, NULL);
  }
  AudioQueueStart(fa->queue, NULL);
  return 0;
}
FENSTER_API void fenster_audio_close(struct fenster_audio *fa) {
  AudioQueueStop(fa->queue, false);
  AudioQueueDispose(fa->queue, false);
}
FENSTER_API int fenster_audio_available(struct fenster_audio *fa) {
  if (dispatch_semaphore_wait(fa->drained, DISPATCH_TIME_NOW))
    return 0;
  return FENSTER_AUDIO_BUFSZ - fa->pos;
}
FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf,
                                     size_t n) {
  while (fa->pos < FENSTER_AUDIO_BUFSZ && n > 0) {
    fa->buf[fa->pos++] = *buf++, n--;
  }
  if (fa->pos >= FENSTER_AUDIO_BUFSZ) {
    fa->pos = 0;
    dispatch_semaphore_signal(fa->full);
  }
}
#elif defined(_WIN32)
FENSTER_API int fenster_audio_open(struct fenster_audio *fa) {
  WAVEFORMATEX wfx = {WAVE_FORMAT_PCM, 1, FENSTER_SAMPLE_RATE, FENSTER_SAMPLE_RATE * 2, 1, 16, 0};
  waveOutOpen(&fa->wo, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL);
  for (int i = 0; i < 2; i++) {
    fa->hdr[i].lpData = fa->buf[i];
    fa->hdr[i].dwBufferLength = FENSTER_AUDIO_BUFSZ * 2;
    waveOutPrepareHeader(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));
    waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));
  }
  return 0;
}
FENSTER_API int fenster_audio_available(struct fenster_audio *fa) {
  for (int i = 0; i < 2; i++)
    if (fa->hdr[i].dwFlags & WHDR_DONE)
      return FENSTER_AUDIO_BUFSZ;
  return 0;
}
FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf,
                                     size_t n) {
  for (int i = 0; i < 2; i++) {
    if (fa->hdr[i].dwFlags & WHDR_DONE) {
      for (unsigned j = 0; j < n; j++) {
        fa->buf[i][j] = (int16_t)(buf[j] * 32767);
      }
      waveOutWrite(fa->wo, &fa->hdr[i], sizeof(WAVEHDR));
      return;
    }
  }
}
FENSTER_API void fenster_audio_close(struct fenster_audio *fa) {
  waveOutClose(fa->wo);
}
#elif defined(__linux__)
int snd_pcm_open(void **, const char *, int, int);
int snd_pcm_set_params(void *, int, int, int, int, int, int);
int snd_pcm_avail(void *);
int snd_pcm_writei(void *, const void *, unsigned long);
int snd_pcm_recover(void *, int, int);
int snd_pcm_close(void *);
FENSTER_API int fenster_audio_open(struct fenster_audio *fa) {
  if (snd_pcm_open(&fa->pcm, "default", 0, 0))
    return -1;
  int fmt = (*(unsigned char *)(&(uint16_t){1})) ? 14 : 15;
  return snd_pcm_set_params(fa->pcm, fmt, 3, 1, FENSTER_SAMPLE_RATE, 1, 100000);
}
FENSTER_API int fenster_audio_available(struct fenster_audio *fa) {
  int n = snd_pcm_avail(fa->pcm);
  if (n < 0)
    snd_pcm_recover(fa->pcm, n, 0);
  return n;
}
FENSTER_API void fenster_audio_write(struct fenster_audio *fa, float *buf,
                                     size_t n) {
  int r = snd_pcm_writei(fa->pcm, buf, n);
  if (r < 0)
    snd_pcm_recover(fa->pcm, r, 0);
}
FENSTER_API void fenster_audio_close(struct fenster_audio *fa) {
  snd_pcm_close(fa->pcm);
}
#endif

#endif /* FENSTER_HEADER */
#endif /* FENSTER_AUDIO_H */