summaryrefslogtreecommitdiff
path: root/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'main.c')
-rw-r--r--main.c588
1 files changed, 588 insertions, 0 deletions
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..43bf52b
--- /dev/null
+++ b/main.c
@@ -0,0 +1,588 @@
+/* This is a program that can read in a text file and convert it to a
+ sound file. Since this is going to depend on ffmpeg's libraries,
+ otherwise I am only able to produce raw audio files, which are too
+ large and not very convenient to use, a makefile is included to
+ make the compiling process easier. */
+
+/* include stuff */
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+/* include FFMPEG stuff */
+#include <libavcodec/avcodec.h>
+
+#include <libavutil/channel_layout.h>
+#include <libavutil/common.h>
+#include <libavutil/frame.h>
+#include <libavutil/samplefmt.h>
+
+/* Self-Dependencies */
+#include "util.h"
+#include "instrument.h"
+#include "parser.h"
+
+/* #include <libavformat/avformat.h> */
+
+/* helper functions */
+
+/* FFMPEG helpers */
+
+/* check that a given sample format is supported by the encoder */
+UNUSED
+static int
+check_sample_fmt(const AVCodec *codec, enum AVSampleFormat sample_fmt)
+{
+ const enum AVSampleFormat *p = codec->sample_fmts;
+
+ /* the array is terminated by -1 */
+ for (int i = 0; *(p+i) != -1; i++)
+ fprintf(stderr, "supports %s\n", av_get_sample_fmt_name(*(p+i)));
+
+ while (*p != AV_SAMPLE_FMT_NONE) {
+ if (*p == sample_fmt)
+ return 1;
+ p++;
+ }
+ return 0;
+}
+
+__attribute__((__hot__, __unused__))
+static void
+/* encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt,
+ * FILE *output) */
+encode(AVCodecContext *ctx, AVFrame *frame, AVPacket *pkt, FILE *output)
+{
+ int ret = 0;
+
+ /* send the frame for encoding */
+ ret = avcodec_send_frame(ctx, frame);
+ if (ret < 0) {
+ fprintf(stderr, "Error sending the frame to the encoder\n");
+ exit(1);
+ }
+
+ /* read all the available output packets (in general there may be any
+ * number of them */
+ while (ret >= 0) {
+ ret = avcodec_receive_packet(ctx, pkt);
+ if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
+ return;
+ else if (ret < 0) {
+ fprintf(stderr, "Error encoding audio frame\n");
+ exit(1);
+ }
+
+ fwrite(pkt->data, 1, pkt->size, output);
+ /* av_write_frame(afc, pkt); */
+ av_packet_unref(pkt);
+ }
+}
+
+/* select layout with the highest channel count */
+UNUSED
+static int
+select_channel_layout(const AVCodec *codec)
+{
+ const uint64_t *p;
+ uint64_t best_ch_layout = 0;
+ int best_nb_channels = 0;
+
+ if (!codec->channel_layouts)
+ return AV_CH_LAYOUT_STEREO;
+
+ p = codec->channel_layouts;
+ while (*p) {
+ int nb_channels = av_get_channel_layout_nb_channels(*p);
+
+ if (nb_channels > best_nb_channels) {
+ best_ch_layout = *p;
+ best_nb_channels = nb_channels;
+ }
+ p++;
+ }
+ return best_ch_layout;
+}
+
+UHA_ATTR
+static inline Pulse
+square_wave(float counter)
+{
+ return (fmod(counter, 2*M_PI) < M_PI) ? 1 : -1;
+}
+
+UHA_ATTR
+static inline Pulse
+synthesize_wave(float theta)
+{
+
+ /* return -1.0f + fmod(theta, 2*M_PI)/M_PI; */
+
+ /* return sin(theta); */
+
+ /* return asin(sin(theta)) * 2/M_PI; */
+
+ /* return pow(theta/2*M_PI, 2); */
+
+ /* return sin(theta)+0.442f*sin(2.0f*theta)+0.315f*sin(3.0f*theta)
+ * +0.083f*sin(4.0f*theta)+cos(theta)+0.442f*cos(2.0f*theta); */
+
+ /* piano like */
+
+ /* return sin(theta)+1.869*sin(2.0f*theta)+0.042f*sin(3.0f*theta)
+ * +0.022f*sin(4.0f*theta)+cos(theta); */
+
+ /* excellent piano like */
+
+ return ((0.65f*sin(theta)+0.5f*sin(2.0f*theta))*exp(-0.004f*theta));
+
+}
+
+/* This is replaced by make_sound function. */
+/* UH_ATTR
+ * static inline WaveFrag
+ * oscillator(Volume v, Hertz h, Seconds s, Instrument *ins)
+ * {
+ *
+ * return 0.0f;
+ * } */
+
+UHA_ATTR
+static inline float
+frequency_modulation(float theta, Hertz h, float fm)
+{
+ return theta+FM_AMP*theta*sin(fm);
+}
+
+UH_ATTR
+static WaveFrag
+hstow(Volume v, Hertz h, Seconds s)
+{
+ LENT sample_num = (LENT) floor(SAMPLE_RATE * s);
+ float attack_step = 1.0f / (float) (sample_num * ATTACK_P);
+ float decay_step = 1.0f / (float) (sample_num * DECAY_P);
+ float sustain_step = 1.0f / (float) (sample_num * SUSTAIN_P);
+ float release_step = 1.0f / (float) (sample_num * RELEASE_P);
+
+ float step = (float) (h * 2 * M_PI) / SAMPLE_RATE;
+
+ float fm_step = (float) ((FM_FREQUENCY)*2*M_PI) / SAMPLE_RATE;
+
+ unsigned char phase = 0, sustain_flag = 0;
+
+ Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num);
+
+ float adsrcounter = 0.0f, counter = 0.0f, fm_counter = 0.0f;
+ float theta = 0.0f, temp = 0.0f;
+
+ for (LENT i = 0; i < sample_num; i++, counter += step, fm_counter += fm_step) {
+ /* theta = frequency_modulation(counter, (float) h*i/((float) SAMPLE_RATE), fm_counter); */
+ theta=counter;
+ temp = synthesize_wave(theta);
+ temp += temp*temp*temp;
+ temp = synthesize_wave(temp);
+ temp += theta*exp(-2.0f*theta);
+ *(w+i) = (float) v * temp;
+ /* (synthesize_wave(temp)/\* +
+ * * 0.01*(2.0f*(float)rand()/(float) RAND_MAX - 1.0f) *\/) *//* *
+ * ((sustain_flag) ? SUSTAIN_LEVEL : adsrcounter) ;*/
+ switch (phase) {
+ case 0: /* attack phase */
+ adsrcounter += attack_step;
+ if (adsrcounter >= 1.0f) {
+ adsrcounter = 1.0f;
+ phase++;
+ }
+ break;
+ case 1: /* decay phase */
+ adsrcounter -= decay_step;
+ if (adsrcounter <= SUSTAIN_LEVEL) {
+ adsrcounter = 0.0f;
+ sustain_flag = 1;
+ phase++;
+ }
+ break;
+ case 2: /* sustain phase */
+ adsrcounter += sustain_step;
+ if (adsrcounter >= 1.0f) {
+ adsrcounter = SUSTAIN_LEVEL;
+ sustain_flag = 0;
+ phase++;
+ }
+ break;
+ default: /* release phase */
+ adsrcounter -= release_step;
+ if (adsrcounter <= 0.0f) adsrcounter = 0.0f;
+ break;
+ }
+ }
+
+ return (WaveFrag) { w, sample_num };
+}
+
+H_ATTR
+static WaveFrag
+sbtow(Volume v, Semitones st, Beats b)
+{
+ return hstow(v, stoh(st), (b * BEAT_DUR));
+}
+
+/* compose */
+
+D_ATTR("play_sheet")
+static WaveFrag
+compose(Volume *vs, Semitones *sts, Beats *bs, LENT len)
+{
+ WaveFrag wf, *temp = MYALLOC(WaveFrag, len);
+
+ LENT total_len = 0;
+
+ for (LENT i = 0; i < len; i++) {
+ if (*(sts+i) >= -50)
+ *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i));
+ else
+ *(temp+i) = sbtow(*(vs+i), *(sts+i), *(bs+i));
+ total_len += (temp+i)->n;
+ }
+
+ wf.n = total_len;
+
+ wf.w = MYALLOC(Pulse, total_len);
+
+ LENT counter = 0;
+
+ for (int i = 0; i < len; i++) {
+ LENT local = (temp+i)->n;
+
+ for (LENT j = 0; j < local; j++)
+ *(wf.w+counter++) = *((temp+i)->w+j);
+
+ free((temp+i)->w);
+ }
+
+ free(temp);
+
+ return wf;
+}
+
+/* Reading */
+
+/* The file should contain an even number of numbers. For any natural
+ number N, the 2*N th number specifies the Semitone of the Nth note,
+ and the 2*N+1 number specifies the beats of the Nth note. */
+
+U_ATTR
+static void
+read_sb(const char *str, int str_len,
+ int *len, Semitones **sts, Beats **bs)
+{
+ int str_counter = 0;
+
+ float temp = 0;
+
+ int semi_len = 0, b_len = 0;
+ Node_t semis = EMPTY_NODE, beats = EMPTY_NODE;
+ Node_t sn = EMPTY_NODE, bn = EMPTY_NODE;
+ Node_t *sp = NULL, *bp = NULL;
+
+ LENT nth = 0;
+
+ for (;str_counter < str_len; nth++) {
+ int number_chars_read = 0;
+ int result = sscanf(str+str_counter, "%f %n", &temp, &number_chars_read);
+ if (result < 1) break;
+
+ str_counter += number_chars_read;
+
+ if (nth % 2) {
+ /* odd */
+ PUSH_NODE(b_len, bp, beats, temp, f);
+ } else {
+ /* even */
+ PUSH_NODE(semi_len, sp, semis, temp, f);
+ }
+ }
+
+ if (nth % 2) {
+ fprintf(stderr, "There should be an even number of floats in "
+ "the file, but found %lu.\n",
+ nth);
+ exit(1);
+ }
+
+ *len = floor(nth/2.0f);
+ *sts = realloc(*sts, sizeof(float) * (*len));
+ *bs = realloc(*bs, sizeof(float) * (*len));
+
+ for (int i = 0; i < *len; i++) {
+ POP_NODE(semi_len, temp, semis, sn, f);
+ *(*sts+*len-1-i) = temp;
+
+ POP_NODE(b_len, temp, beats, bn, f);
+ *(*bs+*len-1-i) = temp;
+ }
+}
+
+/* main */
+
+int main(int argc, char **argv)
+{
+ const AVCodec *codec;
+ AVCodecContext *c = NULL;
+ /* AVOutputFormat *of;
+ * AVFormatContext *oc; */
+ AVFrame *frame;
+ AVPacket *pkt;
+ int ret = 0;
+ float *samples;
+
+ /* output format setup */
+
+ /* if (!(of = av_guess_format(NULL, DEFAULT_OUTPUT_NAME, "audio/aac"))) {
+ * fprintf(stderr, "cannot guess output format\n");
+ * exit(1);
+ * } */
+
+ /* avformat_alloc_output_context2(&oc, of, NULL, NULL); */
+ /* oc = avformat_alloc_context();
+ *
+ * if (!oc) {
+ * fprintf(stderr, "cannot allocate output context\n");
+ * exit(1);
+ * } */
+
+ /* oc->oformat = of; */
+ /* oc->url = MYALLOC(char, sizeof(DEFAULT_OUTPUT_NAME));
+ * for (int i = 0; i < sizeof(DEFAULT_OUTPUT_NAME); i++)
+ * oc->url[i] = DEFAULT_OUTPUT_NAME[i]; */
+
+ /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_READ_WRITE);
+ *
+ * if (ret<0) {
+ * fprintf(stderr, "cannot open %s\n", DEFAULT_OUTPUT_NAME);
+ * exit(1);
+ * } */
+
+ /* find the encoder */
+ codec = avcodec_find_encoder(AV_CODEC_ID_MP3);
+ if (!codec) {
+ fprintf(stderr, "Codec not found\n");
+ exit(1);
+ }
+
+ /* set an option to place MOOV atom first. */
+
+ /* AVDictionary *opt = NULL;
+ * av_dict_set(&opt, "movflags", "faststart", 0);
+ *
+ * printf("of->audio_codec = %d\n", of->audio_codec);
+ *
+ * if (oc->oformat->flags & AVFMT_GLOBALHEADER)
+ * c->flags |= AVFMT_GLOBALHEADER; */
+
+ /* return 0; */
+
+ /* STD_BASE = (float) pow(2, 1.0f/12.0f); */
+
+ UNUSED int notes_len = 0;
+
+ /* UNUSED Volume *vs = NULL;
+ * Semitones *sts = MYALLOC(Semitones, 1);
+ * Beats *bs = MYALLOC(Beats, 1); */
+
+ char *notes_content = MYALLOC(char, 500);
+
+ LENT notes_str_len = 0;
+
+ notes_str_len = (LENT) read_entire_file("notes.txt", &notes_content);
+
+#ifdef DEBUG
+ fprintf(stderr, "notes content = %s\n", notes_content);
+#endif
+
+ PSheet psh = read_sheet(notes_content, notes_str_len);
+
+ if (argc >= 2 && **(argv+1) == 'p')
+ print_sheet(psh);
+
+#ifdef DEBUG
+ fprintf(stderr, "after printing\n");
+#endif
+
+ /* read_sb(notes_content, notes_str_len, &notes_len,
+ * &sts, &bs); */
+
+#ifdef DEBUG
+ fprintf(stderr, "notes len = %d\nfirst bs = %.2f\n",
+ notes_len, *bs);
+#endif
+
+ /* vs = MYALLOC(Volume, notes_len);
+ * for (int i = 0; i < notes_len;) *(vs+i++) = 0.3f; */
+
+ /* WaveFrag wf = compose(vs, sts, bs, notes_len); */
+ WaveFrag wf = play_sheet(psh, 0.3f);
+
+ destroy_sheet(psh);
+
+ printf("wf n = %lu\n", wf.n);
+
+ /* AVStream *os = avformat_new_stream(oc, NULL); */
+
+ c = avcodec_alloc_context3(codec);
+ if (!c) {
+ fprintf(stderr, "Could not allocate audio codec context\n");
+ exit(1);
+ }
+
+ /* check that the encoder supports FLTP pcm input */
+ /* The P marks that this is "planar", i.e. the input is not
+ interleaved between channels. */
+ c->sample_fmt = AV_SAMPLE_FMT_FLTP;
+ if (!check_sample_fmt(codec, c->sample_fmt)) {
+ fprintf(stderr, "Encoder does not support sample format %s",
+ av_get_sample_fmt_name(c->sample_fmt));
+ exit(1);
+ }
+
+ /* set other audio parameters supported by the encoder */
+ c->sample_rate = (int) SAMPLE_RATE;
+ c->channel_layout = select_channel_layout(codec);
+ c->channels = av_get_channel_layout_nb_channels(c->channel_layout);
+
+ /* this should be sizeof(float) * 8 * nb_channels * SAMPLE_RATE
+
+ We divide by 1000 since the unit is KB.*/
+ c->bit_rate = sizeof(float) * 8 * SAMPLE_RATE * c->channels / 1000;
+
+ /* os->time_base = (AVRational) { 1, (int) SAMPLE_RATE }; */
+
+ /* open it */
+ if (avcodec_open2(c, codec, NULL) < 0) {
+ fprintf(stderr, "Could not open codec\n");
+ exit(1);
+ }
+
+ /* /\* set stream data *\/
+ * os->codecpar->sample_rate = SAMPLE_RATE;
+ * os->codecpar->format = c->sample_fmt;
+ * os->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
+ * os->codecpar->codec_id = of->audio_codec;
+ * os->codecpar->bit_rate = c->bit_rate;
+ * os->codecpar->channels = c->channels;
+ * os->codecpar->channel_layout = c->channel_layout;
+ * os->codecpar->frame_size = c->frame_size;
+ *
+ * if (avformat_write_header(oc, &opt) < 0) {
+ * fprintf(stderr, "cannot write header\n");
+ * exit(1);
+ * }
+ *
+ * av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */
+
+ /* return 0; */
+
+ /* audio_st.st->time_base = (AVRational){ 1, c->sample_rate };
+ *
+ * if (oc->oformat->flags & AVFMT_GLOBALHEADER)
+ * c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; */
+
+ /* av_dump_format(oc, 0, DEFAULT_OUTPUT_NAME, 1); */
+
+ /* ret = avio_open(&oc->pb, DEFAULT_OUTPUT_NAME, AVIO_FLAG_WRITE);
+ * if (ret < 0) {
+ * fprintf(stderr, "Could not open '%s': %s\n", DEFAULT_OUTPUT_NAME,
+ * av_err2str(ret));
+ * return 1;
+ * }
+ *
+ * ret = avformat_write_header(oc, &opt);
+ * if (ret < 0) {
+ * fprintf(stderr, "Error occurred when opening output file: %s\n",
+ * av_err2str(ret));
+ * return 1;
+ * } */
+
+ printf("sample rate = %d, channels = %d, bit rate = %lu\n"
+ "frame size = %d\n",
+ c->sample_rate, c->channels, (long) c->bit_rate,
+ c->frame_size);
+ /* return 0; */
+
+ FILE *file = fopen(DEFAULT_OUTPUT_NAME, "wb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s\n", DEFAULT_OUTPUT_NAME);
+ exit(1);
+ }
+
+ /* packet for holding encoded output */
+ pkt = av_packet_alloc();
+ if (!pkt) {
+ fprintf(stderr, "could not allocate the packet\n");
+ exit(1);
+ }
+
+ /* pkt->stream_index = os->index; */
+
+ /* frame containing input raw audio */
+ frame = av_frame_alloc();
+ if (!frame) {
+ fprintf(stderr, "Could not allocate audio frame\n");
+ exit(1);
+ }
+
+ frame->nb_samples = c->frame_size;
+ frame->format = c->sample_fmt;
+ frame->channel_layout = c->channel_layout;
+
+ /* allocate the data buffers */
+ ret = av_frame_get_buffer(frame, 0);
+ if (ret < 0) {
+ fprintf(stderr, "Could not allocate audio data buffers\n");
+ fprintf(stderr, "nbs = %d, format = %s, layout = %d\n",
+ frame->nb_samples, av_get_sample_fmt_name(frame->format),
+ av_get_channel_layout_nb_channels(frame->channel_layout));
+ exit(1);
+ }
+
+ /* write to the frame */
+
+ long frame_num = floor((float) wf.n/(float) c->frame_size) + 1;
+ long w_counter = 0;
+
+ for (int i = 0; i < frame_num; i++, w_counter += c->frame_size) {
+ ret = av_frame_make_writable(frame);
+ if (ret < 0) {
+ fprintf(stderr, "cannot make frame writable\n");
+ exit(1);
+ }
+
+ for (int j = 0; j < c->channels; j++) {
+ samples = (float *)frame->data[j];
+
+ for (int k = 0; k < c->frame_size && k+w_counter < wf.n; k++)
+ samples[k] = *(wf.w+w_counter+k);
+ }
+
+ encode(c, frame, pkt, file);
+ }
+
+ /* fwrite(wf.w, sizeof(float), wf.n, file); */
+
+ /* flush the encoder */
+ encode(c, NULL, pkt, file);
+
+ /* free(vs); */
+ /* free(sts);
+ * free(bs); */
+ free(wf.w);
+ free(notes_content);
+ fclose(file);
+
+ av_frame_free(&frame);
+ av_packet_free(&pkt);
+ avcodec_free_context(&c);
+ /* av_dict_free(&opt); */
+
+ return 0;
+}