]> git.sesse.net Git - c64tapwav/blob - audioreader.cpp
Make leveler minimum level configurable, since it is connected to hysteresis limit.
[c64tapwav] / audioreader.cpp
1 #include <stdio.h>
2
3 extern "C" {
4
5 #include <libavcodec/avcodec.h>
6 #include <libavformat/avformat.h>
7 #include <libswresample/swresample.h>
8
9 }
10
11 #include <memory>
12 #include <vector>
13
14 namespace {
15
16 struct AVFormatCloserAndDeleter {
17         void operator() (AVFormatContext *ctx) {
18                 avformat_close_input(&ctx);
19                 avformat_free_context(ctx);
20         }
21 };
22
23 struct AVCodecContextDeleter {
24         void operator() (AVCodecContext *ctx) {
25                 avcodec_free_context(&ctx);
26         }
27 };
28
29 struct SwrContextDeleter {
30         void operator() (SwrContext *swr) {
31                 swr_free(&swr);
32         }
33 };
34
35 struct AVPacketDeleter {
36         void operator() (AVPacket *pkt) {
37                 av_free_packet(pkt);
38         }
39 };
40
41 struct AVFrameDeleter {
42         void operator() (AVFrame *frame) {
43                 av_frame_free(&frame);
44         }
45 };
46
47 struct AVSampleDeleter {
48         void operator() (uint8_t *data) {
49                 av_freep(&data);
50         }
51 };
52
53 void convert_samples(SwrContext *swr, int sample_rate, const uint8_t **data, int nb_samples, std::vector<float> *samples)
54 {
55         int max_out_samples = nb_samples + swr_get_delay(swr, sample_rate);
56         if (max_out_samples == 0) {
57                 return;
58         }
59         uint8_t *output;
60         av_samples_alloc(&output, nullptr, 1, max_out_samples, AV_SAMPLE_FMT_FLT, 0);
61         std::unique_ptr<uint8_t, AVSampleDeleter> output_deleter(output);
62
63         int out_samples = swr_convert(swr, &output, max_out_samples, data, nb_samples);
64         if (out_samples > 0) {
65                 const float* start = reinterpret_cast<const float *>(output);
66                 const float* end = start + out_samples;
67                 samples->insert(samples->end(), start, end);
68         }
69 }
70
71 int decode_packet(const char *filename, AVCodecContext *codec_ctx, SwrContext *swr, AVFrame *audio_frame, AVPacket *packet, int *got_frame, std::vector<float> *samples)
72 {
73         *got_frame = 0;
74         int len1 = avcodec_decode_audio4(codec_ctx, audio_frame, got_frame, packet);
75         if (len1 < 0 || !*got_frame) {
76                 return len1;
77         }
78
79         if (audio_frame->channel_layout != codec_ctx->channel_layout ||
80             audio_frame->sample_rate != codec_ctx->sample_rate) {
81                 fprintf(stderr, "%s: Channel layout or sample rate changed mid-file\n", filename);
82                 *got_frame = false;
83                 return len1;
84         }
85         convert_samples(swr, codec_ctx->sample_rate, (const uint8_t **)audio_frame->data, audio_frame->nb_samples, samples);
86         return len1;
87 }
88
89 }  // namespace
90
91 bool read_audio_file(const char *filename, std::vector<float> *samples, int *sample_rate)
92 {
93         av_register_all();
94
95         AVFormatContext *format_ctx = nullptr;
96         if (avformat_open_input(&format_ctx, filename, nullptr, nullptr) != 0) {
97                 fprintf(stderr, "Couldn't open %s\n", filename);
98                 return false;
99         }
100         std::unique_ptr<AVFormatContext, AVFormatCloserAndDeleter> format_ctx_closer(format_ctx);
101
102         if (avformat_find_stream_info(format_ctx, nullptr) < 0) {
103                 fprintf(stderr, "%s: Couldn't find stream information\n", filename);
104                 return false;
105         }
106
107         // Find the first audio stream.
108         int audio_stream_index = -1;
109         for (unsigned i = 0; i < format_ctx->nb_streams; ++i) {
110                 if (format_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
111                         audio_stream_index = i;
112                         break;
113                 }
114         }
115         if (audio_stream_index == -1) {
116                 fprintf(stderr, "%s: Couldn't find an audio stream\n", filename);
117                 return false;
118         }
119
120         AVCodec *codec = avcodec_find_decoder(format_ctx->streams[audio_stream_index]->codec->codec_id);
121         if (codec == nullptr) {
122                 fprintf(stderr, "%s: Unsupported codec\n", filename);
123                 return false;
124         }
125
126         AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
127         std::unique_ptr<AVCodecContext, AVCodecContextDeleter> codec_ctx_deleter(codec_ctx);
128         if (avcodec_copy_context(codec_ctx, format_ctx->streams[audio_stream_index]->codec) != 0) {
129                 fprintf(stderr, "%s: Couldn't copy codec context\n", filename);
130                 return false;
131         }
132
133         if (avcodec_open2(codec_ctx, codec, nullptr) < 0) {
134                 fprintf(stderr, "%s: Couldn't open codec\n", filename);
135                 return false;
136         }
137
138         // Init resampler (to downmix to mono and convert to s16).
139         if (codec_ctx->channel_layout == 0) {
140                 codec_ctx->channel_layout = av_get_default_channel_layout(codec_ctx->channels);
141         }
142         SwrContext *swr = swr_alloc_set_opts(
143                 nullptr,
144                 AV_CH_LAYOUT_MONO, AV_SAMPLE_FMT_FLT, codec_ctx->sample_rate,
145                 codec_ctx->channel_layout, codec_ctx->sample_fmt, codec_ctx->sample_rate,
146                 0, nullptr);
147         std::unique_ptr<SwrContext, SwrContextDeleter> swr_deleter(swr);
148         if (swr_init(swr) < 0) {
149                 fprintf(stderr, "%s: Couldn't initialize resampler\n", filename);
150                 return false;
151         }
152
153         AVPacket packet;
154         AVFrame* audio_frame = av_frame_alloc();
155         std::unique_ptr<AVFrame, AVFrameDeleter> audio_frame_deleter(audio_frame);
156         while (av_read_frame(format_ctx, &packet) >= 0) {
157                 std::unique_ptr<AVPacket, AVPacketDeleter> av_packet_deleter(&packet);
158
159                 if (packet.stream_index != audio_stream_index) {
160                         continue;
161                 }
162
163                 while (packet.size > 0) {
164                         int got_frame = 0;
165                         int len1 = decode_packet(filename, codec_ctx, swr, audio_frame, &packet, &got_frame, samples);
166                         if (len1 < 0) {
167                                 fprintf(stderr, "%s: Couldn't decode audio\n", filename);
168                                 return false;
169                         }
170                         if (!got_frame) {
171                                 break;
172                         }
173                         packet.data += len1;
174                         packet.size -= len1;
175                 }
176         }
177
178         // Flush any delayed data from the end.
179         packet.data = nullptr;
180         packet.size = 0;
181         int got_frame = 0;
182         do {
183                 int len1 = decode_packet(filename, codec_ctx, swr, audio_frame, &packet, &got_frame, samples);
184                 if (len1 < 0) {
185                         fprintf(stderr, "%s: Couldn't decode audio\n", filename);
186                         return false;
187                 }
188         } while (got_frame);
189
190         // Convert any leftover samples from the converter.
191         convert_samples(swr, codec_ctx->sample_rate, nullptr, 0, samples);
192
193         *sample_rate = codec_ctx->sample_rate;
194
195         return true;
196 }